![Tejun Heo](/assets/img/avatar_default.png)
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
573 lines
14 KiB
C
573 lines
14 KiB
C
/*
|
|
* PCI HotPlug Controller Core
|
|
*
|
|
* Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
|
|
* Copyright (C) 2001-2002 IBM Corp.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
|
* NON INFRINGEMENT. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* Send feedback to <kristen.c.accardi@intel.com>
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/list.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/init.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_hotplug.h>
|
|
#include <asm/uaccess.h>
|
|
#include "../pci.h"
|
|
|
|
#define MY_NAME "pci_hotplug"
|
|
|
|
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
|
|
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
|
|
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
|
|
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
|
|
|
|
|
|
/* local variables */
|
|
static int debug;
|
|
|
|
#define DRIVER_VERSION "0.5"
|
|
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
|
|
#define DRIVER_DESC "PCI Hot Plug PCI Core"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
static LIST_HEAD(pci_hotplug_slot_list);
|
|
static DEFINE_MUTEX(pci_hp_mutex);
|
|
|
|
#ifdef CONFIG_HOTPLUG_PCI_CPCI
|
|
extern int cpci_hotplug_init(int debug);
|
|
extern void cpci_hotplug_exit(void);
|
|
#else
|
|
static inline int cpci_hotplug_init(int debug) { return 0; }
|
|
static inline void cpci_hotplug_exit(void) { }
|
|
#endif
|
|
|
|
/* Weee, fun with macros... */
|
|
#define GET_STATUS(name,type) \
|
|
static int get_##name (struct hotplug_slot *slot, type *value) \
|
|
{ \
|
|
struct hotplug_slot_ops *ops = slot->ops; \
|
|
int retval = 0; \
|
|
if (!try_module_get(ops->owner)) \
|
|
return -ENODEV; \
|
|
if (ops->get_##name) \
|
|
retval = ops->get_##name(slot, value); \
|
|
else \
|
|
*value = slot->info->name; \
|
|
module_put(ops->owner); \
|
|
return retval; \
|
|
}
|
|
|
|
GET_STATUS(power_status, u8)
|
|
GET_STATUS(attention_status, u8)
|
|
GET_STATUS(latch_status, u8)
|
|
GET_STATUS(adapter_status, u8)
|
|
|
|
static ssize_t power_read_file(struct pci_slot *slot, char *buf)
|
|
{
|
|
int retval;
|
|
u8 value;
|
|
|
|
retval = get_power_status(slot->hotplug, &value);
|
|
if (retval)
|
|
goto exit;
|
|
retval = sprintf (buf, "%d\n", value);
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
unsigned long lpower;
|
|
u8 power;
|
|
int retval = 0;
|
|
|
|
lpower = simple_strtoul (buf, NULL, 10);
|
|
power = (u8)(lpower & 0xff);
|
|
dbg ("power = %d\n", power);
|
|
|
|
if (!try_module_get(slot->ops->owner)) {
|
|
retval = -ENODEV;
|
|
goto exit;
|
|
}
|
|
switch (power) {
|
|
case 0:
|
|
if (slot->ops->disable_slot)
|
|
retval = slot->ops->disable_slot(slot);
|
|
break;
|
|
|
|
case 1:
|
|
if (slot->ops->enable_slot)
|
|
retval = slot->ops->enable_slot(slot);
|
|
break;
|
|
|
|
default:
|
|
err ("Illegal value specified for power\n");
|
|
retval = -EINVAL;
|
|
}
|
|
module_put(slot->ops->owner);
|
|
|
|
exit:
|
|
if (retval)
|
|
return retval;
|
|
return count;
|
|
}
|
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_power = {
|
|
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
.show = power_read_file,
|
|
.store = power_write_file
|
|
};
|
|
|
|
static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
|
|
{
|
|
int retval;
|
|
u8 value;
|
|
|
|
retval = get_attention_status(slot->hotplug, &value);
|
|
if (retval)
|
|
goto exit;
|
|
retval = sprintf(buf, "%d\n", value);
|
|
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct hotplug_slot_ops *ops = slot->hotplug->ops;
|
|
unsigned long lattention;
|
|
u8 attention;
|
|
int retval = 0;
|
|
|
|
lattention = simple_strtoul (buf, NULL, 10);
|
|
attention = (u8)(lattention & 0xff);
|
|
dbg (" - attention = %d\n", attention);
|
|
|
|
if (!try_module_get(ops->owner)) {
|
|
retval = -ENODEV;
|
|
goto exit;
|
|
}
|
|
if (ops->set_attention_status)
|
|
retval = ops->set_attention_status(slot->hotplug, attention);
|
|
module_put(ops->owner);
|
|
|
|
exit:
|
|
if (retval)
|
|
return retval;
|
|
return count;
|
|
}
|
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_attention = {
|
|
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
.show = attention_read_file,
|
|
.store = attention_write_file
|
|
};
|
|
|
|
static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
|
|
{
|
|
int retval;
|
|
u8 value;
|
|
|
|
retval = get_latch_status(slot->hotplug, &value);
|
|
if (retval)
|
|
goto exit;
|
|
retval = sprintf (buf, "%d\n", value);
|
|
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_latch = {
|
|
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
|
|
.show = latch_read_file,
|
|
};
|
|
|
|
static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
|
|
{
|
|
int retval;
|
|
u8 value;
|
|
|
|
retval = get_adapter_status(slot->hotplug, &value);
|
|
if (retval)
|
|
goto exit;
|
|
retval = sprintf (buf, "%d\n", value);
|
|
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_presence = {
|
|
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
|
|
.show = presence_read_file,
|
|
};
|
|
|
|
static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
unsigned long ltest;
|
|
u32 test;
|
|
int retval = 0;
|
|
|
|
ltest = simple_strtoul (buf, NULL, 10);
|
|
test = (u32)(ltest & 0xffffffff);
|
|
dbg ("test = %d\n", test);
|
|
|
|
if (!try_module_get(slot->ops->owner)) {
|
|
retval = -ENODEV;
|
|
goto exit;
|
|
}
|
|
if (slot->ops->hardware_test)
|
|
retval = slot->ops->hardware_test(slot, test);
|
|
module_put(slot->ops->owner);
|
|
|
|
exit:
|
|
if (retval)
|
|
return retval;
|
|
return count;
|
|
}
|
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_test = {
|
|
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
.store = test_write_file
|
|
};
|
|
|
|
static bool has_power_file(struct pci_slot *pci_slot)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
if ((!slot) || (!slot->ops))
|
|
return false;
|
|
if ((slot->ops->enable_slot) ||
|
|
(slot->ops->disable_slot) ||
|
|
(slot->ops->get_power_status))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool has_attention_file(struct pci_slot *pci_slot)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
if ((!slot) || (!slot->ops))
|
|
return false;
|
|
if ((slot->ops->set_attention_status) ||
|
|
(slot->ops->get_attention_status))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool has_latch_file(struct pci_slot *pci_slot)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
if ((!slot) || (!slot->ops))
|
|
return false;
|
|
if (slot->ops->get_latch_status)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool has_adapter_file(struct pci_slot *pci_slot)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
if ((!slot) || (!slot->ops))
|
|
return false;
|
|
if (slot->ops->get_adapter_status)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool has_test_file(struct pci_slot *pci_slot)
|
|
{
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
if ((!slot) || (!slot->ops))
|
|
return false;
|
|
if (slot->ops->hardware_test)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static int fs_add_slot(struct pci_slot *slot)
|
|
{
|
|
int retval = 0;
|
|
|
|
/* Create symbolic link to the hotplug driver module */
|
|
pci_hp_create_module_link(slot);
|
|
|
|
if (has_power_file(slot)) {
|
|
retval = sysfs_create_file(&slot->kobj,
|
|
&hotplug_slot_attr_power.attr);
|
|
if (retval)
|
|
goto exit_power;
|
|
}
|
|
|
|
if (has_attention_file(slot)) {
|
|
retval = sysfs_create_file(&slot->kobj,
|
|
&hotplug_slot_attr_attention.attr);
|
|
if (retval)
|
|
goto exit_attention;
|
|
}
|
|
|
|
if (has_latch_file(slot)) {
|
|
retval = sysfs_create_file(&slot->kobj,
|
|
&hotplug_slot_attr_latch.attr);
|
|
if (retval)
|
|
goto exit_latch;
|
|
}
|
|
|
|
if (has_adapter_file(slot)) {
|
|
retval = sysfs_create_file(&slot->kobj,
|
|
&hotplug_slot_attr_presence.attr);
|
|
if (retval)
|
|
goto exit_adapter;
|
|
}
|
|
|
|
if (has_test_file(slot)) {
|
|
retval = sysfs_create_file(&slot->kobj,
|
|
&hotplug_slot_attr_test.attr);
|
|
if (retval)
|
|
goto exit_test;
|
|
}
|
|
|
|
goto exit;
|
|
|
|
exit_test:
|
|
if (has_adapter_file(slot))
|
|
sysfs_remove_file(&slot->kobj,
|
|
&hotplug_slot_attr_presence.attr);
|
|
exit_adapter:
|
|
if (has_latch_file(slot))
|
|
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
|
exit_latch:
|
|
if (has_attention_file(slot))
|
|
sysfs_remove_file(&slot->kobj,
|
|
&hotplug_slot_attr_attention.attr);
|
|
exit_attention:
|
|
if (has_power_file(slot))
|
|
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
|
exit_power:
|
|
pci_hp_remove_module_link(slot);
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static void fs_remove_slot(struct pci_slot *slot)
|
|
{
|
|
if (has_power_file(slot))
|
|
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
|
|
|
|
if (has_attention_file(slot))
|
|
sysfs_remove_file(&slot->kobj,
|
|
&hotplug_slot_attr_attention.attr);
|
|
|
|
if (has_latch_file(slot))
|
|
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
|
|
|
|
if (has_adapter_file(slot))
|
|
sysfs_remove_file(&slot->kobj,
|
|
&hotplug_slot_attr_presence.attr);
|
|
|
|
if (has_test_file(slot))
|
|
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
|
|
|
|
pci_hp_remove_module_link(slot);
|
|
}
|
|
|
|
static struct hotplug_slot *get_slot_from_name (const char *name)
|
|
{
|
|
struct hotplug_slot *slot;
|
|
struct list_head *tmp;
|
|
|
|
list_for_each (tmp, &pci_hotplug_slot_list) {
|
|
slot = list_entry (tmp, struct hotplug_slot, slot_list);
|
|
if (strcmp(hotplug_slot_name(slot), name) == 0)
|
|
return slot;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
|
|
* @bus: bus this slot is on
|
|
* @slot: pointer to the &struct hotplug_slot to register
|
|
* @devnr: device number
|
|
* @name: name registered with kobject core
|
|
* @owner: caller module owner
|
|
* @mod_name: caller module name
|
|
*
|
|
* Registers a hotplug slot with the pci hotplug subsystem, which will allow
|
|
* userspace interaction to the slot.
|
|
*
|
|
* Returns 0 if successful, anything else for an error.
|
|
*/
|
|
int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
|
|
int devnr, const char *name,
|
|
struct module *owner, const char *mod_name)
|
|
{
|
|
int result;
|
|
struct pci_slot *pci_slot;
|
|
|
|
if (slot == NULL)
|
|
return -ENODEV;
|
|
if ((slot->info == NULL) || (slot->ops == NULL))
|
|
return -EINVAL;
|
|
if (slot->release == NULL) {
|
|
dbg("Why are you trying to register a hotplug slot "
|
|
"without a proper release function?\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
slot->ops->owner = owner;
|
|
slot->ops->mod_name = mod_name;
|
|
|
|
mutex_lock(&pci_hp_mutex);
|
|
/*
|
|
* No problems if we call this interface from both ACPI_PCI_SLOT
|
|
* driver and call it here again. If we've already created the
|
|
* pci_slot, the interface will simply bump the refcount.
|
|
*/
|
|
pci_slot = pci_create_slot(bus, devnr, name, slot);
|
|
if (IS_ERR(pci_slot)) {
|
|
result = PTR_ERR(pci_slot);
|
|
goto out;
|
|
}
|
|
|
|
slot->pci_slot = pci_slot;
|
|
pci_slot->hotplug = slot;
|
|
|
|
list_add(&slot->slot_list, &pci_hotplug_slot_list);
|
|
|
|
result = fs_add_slot(pci_slot);
|
|
kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
|
|
dbg("Added slot %s to the list\n", name);
|
|
out:
|
|
mutex_unlock(&pci_hp_mutex);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
|
|
* @hotplug: pointer to the &struct hotplug_slot to deregister
|
|
*
|
|
* The @slot must have been registered with the pci hotplug subsystem
|
|
* previously with a call to pci_hp_register().
|
|
*
|
|
* Returns 0 if successful, anything else for an error.
|
|
*/
|
|
int pci_hp_deregister(struct hotplug_slot *hotplug)
|
|
{
|
|
struct hotplug_slot *temp;
|
|
struct pci_slot *slot;
|
|
|
|
if (!hotplug)
|
|
return -ENODEV;
|
|
|
|
mutex_lock(&pci_hp_mutex);
|
|
temp = get_slot_from_name(hotplug_slot_name(hotplug));
|
|
if (temp != hotplug) {
|
|
mutex_unlock(&pci_hp_mutex);
|
|
return -ENODEV;
|
|
}
|
|
|
|
list_del(&hotplug->slot_list);
|
|
|
|
slot = hotplug->pci_slot;
|
|
fs_remove_slot(slot);
|
|
dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug));
|
|
|
|
hotplug->release(hotplug);
|
|
slot->hotplug = NULL;
|
|
pci_destroy_slot(slot);
|
|
mutex_unlock(&pci_hp_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* pci_hp_change_slot_info - changes the slot's information structure in the core
|
|
* @hotplug: pointer to the slot whose info has changed
|
|
* @info: pointer to the info copy into the slot's info structure
|
|
*
|
|
* @slot must have been registered with the pci
|
|
* hotplug subsystem previously with a call to pci_hp_register().
|
|
*
|
|
* Returns 0 if successful, anything else for an error.
|
|
*/
|
|
int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
|
|
struct hotplug_slot_info *info)
|
|
{
|
|
struct pci_slot *slot;
|
|
if (!hotplug || !info)
|
|
return -ENODEV;
|
|
slot = hotplug->pci_slot;
|
|
|
|
memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init pci_hotplug_init (void)
|
|
{
|
|
int result;
|
|
|
|
result = cpci_hotplug_init(debug);
|
|
if (result) {
|
|
err ("cpci_hotplug_init with error %d\n", result);
|
|
goto err_cpci;
|
|
}
|
|
|
|
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
|
|
|
err_cpci:
|
|
return result;
|
|
}
|
|
|
|
static void __exit pci_hotplug_exit (void)
|
|
{
|
|
cpci_hotplug_exit();
|
|
}
|
|
|
|
module_init(pci_hotplug_init);
|
|
module_exit(pci_hotplug_exit);
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
MODULE_LICENSE("GPL");
|
|
module_param(debug, bool, 0644);
|
|
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|
|
|
|
EXPORT_SYMBOL_GPL(__pci_hp_register);
|
|
EXPORT_SYMBOL_GPL(pci_hp_deregister);
|
|
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
|