diff options
| author | Patrick Mochel <mochel@osdl.org> | 2003-01-10 02:57:21 -0600 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2003-01-10 02:57:21 -0600 |
| commit | 601c09192dae1da98e8877e707d7ee58ac243d9b (patch) | |
| tree | 32dea12f7b9a57ebe9d17f50036a72a214845b46 | |
| parent | efa0596f2d555b4a544939c96814cff14b93fd9c (diff) | |
| parent | b2aa16e83d30e24f5e7faea1aa532e3f6ebdec18 (diff) | |
Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-virgin
into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-core
| -rw-r--r-- | Documentation/driver-model/porting.txt | 445 | ||||
| -rw-r--r-- | arch/i386/kernel/edd.c | 55 | ||||
| -rw-r--r-- | drivers/base/bus.c | 55 | ||||
| -rw-r--r-- | drivers/base/class.c | 22 | ||||
| -rw-r--r-- | drivers/base/core.c | 10 | ||||
| -rw-r--r-- | drivers/base/driver.c | 2 | ||||
| -rw-r--r-- | drivers/base/interface.c | 15 | ||||
| -rw-r--r-- | drivers/base/intf.c | 5 | ||||
| -rw-r--r-- | drivers/block/genhd.c | 26 | ||||
| -rw-r--r-- | drivers/pci/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/pci/pool.c | 9 | ||||
| -rw-r--r-- | drivers/pci/proc.c | 21 | ||||
| -rw-r--r-- | drivers/pnp/interface.c | 22 | ||||
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 29 | ||||
| -rw-r--r-- | drivers/scsi/sg.c | 12 | ||||
| -rw-r--r-- | drivers/usb/core/driverfs.c | 31 | ||||
| -rw-r--r-- | fs/partitions/check.c | 32 | ||||
| -rw-r--r-- | fs/sysfs/inode.c | 297 | ||||
| -rw-r--r-- | include/linux/device.h | 16 | ||||
| -rw-r--r-- | include/linux/kobject.h | 4 | ||||
| -rw-r--r-- | include/linux/sysfs.h | 4 | ||||
| -rw-r--r-- | lib/kobject.c | 40 | ||||
| -rw-r--r-- | net/core/dev.c | 13 |
23 files changed, 855 insertions, 311 deletions
diff --git a/Documentation/driver-model/porting.txt b/Documentation/driver-model/porting.txt new file mode 100644 index 000000000000..fef1c44e330a --- /dev/null +++ b/Documentation/driver-model/porting.txt @@ -0,0 +1,445 @@ + +Porting Drivers to the New Driver Model + +Patrick Mochel + +7 January 2003 + + +Overview + +Please refer to Documentation/driver-model/*.txt for definitions of +various driver types and concepts. + +Most of the work of porting devices drivers to the new model happens +at the bus driver layer. This was intentional, to minimize the +negative effect on kernel drivers, and to allow a gradual transition +of bus drivers. + +In a nutshell, the driver model consists of a set of objects that can +be embedded in larger, bus-specific objects. Fields in these generic +objects can replace fields in the bus-specific objects. + +The generic objects must be registered with the driver model core. By +doing so, they will exported via the sysfs filesystem. sysfs can be +mounted by doing + + # mount -t sysfs sysfs /sys + + + +The Process + +Step 0: Read include/linux/device.h for object and function definitions. + +Step 1: Registering the bus driver. + + +- Define a struct bus_type for the bus driver. + +struct bus_type pci_bus_type = { + .name = "pci", +}; + + +- Register the bus type. + This should be done in the initialization function for the bus type, + which is usually the module_init(), or equivalent, function. + +static int __init pci_driver_init(void) +{ + return bus_register(&pci_bus_type); +} + +subsys_initcall(pci_driver_init); + + + The bus type may be unregistered (if the bus driver may be compiled + as a module) by doing: + + bus_unregister(&pci_bus_type); + + +- Export the bus type for others to use. + + Other code may wish to reference the bus type, so declare it in a + shared header file and export the symbol. + +From include/linux/pci.h: + +extern struct bus_type pci_bus_type; + + +From file the above code appears in: + +EXPORT_SYMBOL(pci_bus_type); + + + +- This will cause the bus to show up in /sys/bus/pci/ with two + subdirectories: 'devices' and 'drivers'. + +# tree -d /sys/bus/pci/ +/sys/bus/pci/ +|-- devices +`-- drivers + + + +Step 2: Registering Devices. + +struct device represents a single device. It mainly contains metadata +describing the relationship the device has to other entities. + + +- Embedd a struct device in the bus-specific device type. + + +struct pci_dev { + ... + struct device dev; /* Generic device interface */ + ... +}; + + It is recommended that the generic device not be the first item in + the struct to discourage programmers from doing mindless casts + between the object types. Instead macros, or inline functions, + should be created to convert from the generic object type. + + +#define to_pci_dev(n) container_of(n, struct pci_dev, dev) + +or + +static inline struct pci_dev * to_pci_dev(struct kobject * kobj) +{ + return container_of(n, struct pci_dev, dev); +} + + This allows the compiler to verify type-safety of the operations + that are performed (which is Good). + + +- Initialize the device on registration. + + When devices are discovered or registered with the bus type, the + bus driver should initialize the generic device. The most important + things to initialize are the bus_id, parent, and bus fields. + + The bus_id is an ASCII string that contains the device's address on + the bus. The format of this string is bus-specific. This is + necessary for representing device in sysfs. + + parent is the physical parent of the device. It is important that + the bus driver sets this field correctly. + + The driver model maintains an ordered list of devices that it uses + for power management. This list must be in order to guarantee that + devices are shutdown before their physical parents, and vice versa. + The order of this list is determined by the parent of registered + devices. + + Also, the location of the device's sysfs directory depends on a + device's parent. sysfs exports a directory structure that mirrors + the device hierarchy. Accurately setting the parent guarantees that + sysfs will accurately represent the hierarchy. + + The device's bus field is a pointer to the bus type the device + belongs to. This should be set to the bus_type that was declared + and initialized before. + + Optionally, the bus driver may set the device's name and release + fields. + + The name field is an ASCII string describing the device, like + + "ATI Technologies Inc Radeon QD" + + The release field is a callback that the driver model core calls + when the device has been removed, and all references to it have + been released. More on this in a moment. + + +- Register the device. + + Once the generic device has been initialized, it can be registered + with the driver model core by doing: + + device_register(&dev->dev); + + It can later be unregistered by doing: + + device_unregister(&dev->dev); + + This should happen on buses that support hotpluggable devices. + If a bus driver unregisters a device, it should not immediately free + it. It should instead wait for the driver model core to call the + device's release method, then free the bus-specific object. + (There may be other code that is currently referencing the device + structure, and it would be rude to free the device while that is + happening). + + + When the device is registered, a directory in sysfs is created. + The PCI tree in sysfs looks like: + +/sys/devices/pci0/ +|-- 00:00.0 +|-- 00:01.0 +| `-- 01:00.0 +|-- 00:02.0 +| `-- 02:1f.0 +| `-- 03:00.0 +|-- 00:1e.0 +| `-- 04:04.0 +|-- 00:1f.0 +|-- 00:1f.1 +| |-- ide0 +| | |-- 0.0 +| | `-- 0.1 +| `-- ide1 +| `-- 1.0 +|-- 00:1f.2 +|-- 00:1f.3 +`-- 00:1f.5 + + Also, symlinks are created in the bus's 'devices' directory + that point to the device's directory in the physical hierarchy. + +/sys/bus/pci/devices/ +|-- 00:00.0 -> ../../../devices/pci0/00:00.0 +|-- 00:01.0 -> ../../../devices/pci0/00:01.0 +|-- 00:02.0 -> ../../../devices/pci0/00:02.0 +|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0 +|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0 +|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1 +|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2 +|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3 +|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5 +|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0 +|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0 +|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0 +`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0 + + + +Step 3: Registering Drivers. + +struct device_driver is a simple driver structure that contains a set +of operations that the driver model core may call. + + +- Embed a struct device_driver in the bus-specific driver. + + Just like with devices, do something like: + +struct pci_driver { + ... + struct device_driver driver; +}; + + +- Initialize the generic driver structure. + + When the driver registers with the bus (e.g. doing pci_register_driver()), + initialize the necessary fields of the driver: the name and bus + fields. + + +- Register the driver. + + After the generic driver has been initialized, call + + driver_register(&drv->driver); + + to register the driver with the core. + + When the driver is unregistered from the bus, unregister it from the + core by doing: + + driver_unregister(&drv->driver); + + Note that this will block until all references to the driver have + gone away. Normally, there will not be any. + + +- Sysfs representation. + + Drivers are exported via sysfs in their bus's 'driver's directory. + For example: + +/sys/bus/pci/drivers/ +|-- 3c59x +|-- Ensoniq AudioPCI +|-- agpgart-amdk7 +|-- e100 +`-- serial + + +Step 4: Define Generic Methods for Drivers. + +struct device_driver defines a set of operations that the driver model +core calls. Most of these operations are probably similar to +operations the bus already defines for drivers, but taking different +parameters. + +It would be difficult and tedious to force every driver on a bus to +simultaneously convert their drivers to generic format. Instead, the +bus driver should define single instances of the generic methods that +forward calls to the bus-specific drivers. For instance: + + +static int pci_device_remove(struct device * dev) +{ + struct pci_dev * pci_dev = to_pci_dev(dev); + struct pci_driver * drv = pci_dev->driver; + + if (drv) { + if (drv->remove) + drv->remove(pci_dev); + pci_dev->driver = NULL; + } + return 0; +} + + +The generic driver should be initialized with these methods before it +is registered. + + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &pci_bus_type; + drv->driver.probe = pci_device_probe; + drv->driver.resume = pci_device_resume; + drv->driver.suspend = pci_device_suspend; + drv->driver.remove = pci_device_remove; + + /* register with core */ + driver_register(&drv->driver); + + +Ideally, the bus should only initialize the fields if they are not +already set. This allows the drivers to implement their own generic +methods. + + +Step 5: Support generic driver binding. + +The model assumes that a device or driver can be dynamically +registered with the bus at any time. When registration happens, +devices must be bound to a driver, or drivers must be bound to all +devices that it supports. + +Drivers typically contain a list of device IDs that it supports. The +bus driver compares this ID to the ID of devices registered with it. +The format of the device IDs, and the semantics for comparing them are +bus-specific, so the generic model does attempt to generalize them. + +Instead, a bus may supply a method in struct bus_type that does the +comparison: + + int (*match)(struct device * dev, struct device_driver * drv); + +match should return '1' if the driver supports the device, and '0' +otherwise. + +When a device is registered, the bus's list of drivers is iterated +over. bus->match() is called for each one until a match is found. + +When a driver is registered, the bus's list of devices is iterated +over. bus->match() is called for each device that is not already +claimed by a driver. + +When a device is successfully bound to a device, device->driver is +set, the device is added to a per-driver list of devices, and a +symlink is created in the driver's sysfs directory that points to the +device's physical directory: + +/sys/bus/pci/drivers/ +|-- 3c59x +| `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0 +|-- Ensoniq AudioPCI +|-- agpgart-amdk7 +| `-- 00:00.0 -> ../../../../devices/pci0/00:00.0 +|-- e100 +| `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0 +`-- serial + + +This driver binding should replace the existing driver binding +mechanism the bus currently uses. + + +Step 6: Supply a hotplug callback. + +Whenever a device is registered with the driver model core, the +userspace program /sbin/hotplug is called to notify userspace. +Users can define actions to perform when a device is inserted or +removed. + +The driver model core passes several arguments to userspace via +environment variables, including + +- ACTION: set to 'add' or 'remove' +- DEVPATH: set to the device's physical path in sysfs. + +A bus driver may also supply additional parameters for userspace to +consume. To do this, a bus must implement the 'hotplug' method in +struct bus_type: + + int (*hotplug) (struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size); + +This is called immediately before /sbin/hotplug is executed. + + +Step 7: Cleaning up the bus driver. + +The generic bus, device, and driver structures provide several fields +that can replace those define privately to the bus driver. + +- Device list. + +struct bus_type contains a list of all devices registered with the bus +type. This includes all devices on all instances of that bus type. +An internal list that the bus uses may be removed, in favor of using +this one. + +The core provides an iterator to access these devices. + +int bus_for_each_dev(struct bus_type * bus, struct device * start, + void * data, int (*fn)(struct device *, void *)); + + +- Driver list. + +struct bus_type also contains a list of all drivers registered with +it. An internal list of drivers that the bus driver maintains may +be removed in favor of using the generic one. + +The drivers may be iterated over, like devices: + +int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, + void * data, int (*fn)(struct device_driver *, void *)); + + +Please see drivers/base/bus.c for more information. + + +- rwsem + +struct bus_type contains an rwsem that protects all core accesses to +the device and driver lists. This can be used by the bus driver +internally, and should be used when accessing the device or driver +lists the bus maintains. + + +- Device and driver fields. + +Some of the fields in struct device and struct device_driver duplicate +fields in the bus-specific representations of these objects. Feel free +to remove the bus-specific ones and favor the generic ones. Note +though, that this will likely mean fixing up all the drivers that +reference the bus-specific fields (though those should all be 1-line +changes). + diff --git a/arch/i386/kernel/edd.c b/arch/i386/kernel/edd.c index 132bca8765bb..8cb1e21b56bd 100644 --- a/arch/i386/kernel/edd.c +++ b/arch/i386/kernel/edd.c @@ -61,7 +61,7 @@ MODULE_LICENSE("GPL"); #define EDD_DEVICE_NAME_SIZE 16 #define REPORT_URL "http://domsch.com/linux/edd30/results.html" -#define left (count - (p - buf) - 1) +#define left (PAGE_SIZE - (p - buf) - 1) struct edd_device { struct edd_info *info; @@ -70,8 +70,7 @@ struct edd_device { struct edd_attribute { struct attribute attr; - ssize_t(*show) (struct edd_device * edev, char *buf, size_t count, - loff_t off); + ssize_t(*show) (struct edd_device * edev, char *buf); int (*test) (struct edd_device * edev); }; @@ -104,15 +103,14 @@ edd_dev_set_info(struct edd_device *edev, struct edd_info *info) #define to_edd_device(obj) container_of(obj,struct edd_device,kobj) static ssize_t -edd_attr_show(struct kobject * kobj, struct attribute *attr, - char *buf, size_t count, loff_t off) +edd_attr_show(struct kobject * kobj, struct attribute *attr, char *buf) { struct edd_device *dev = to_edd_device(kobj); struct edd_attribute *edd_attr = to_edd_attr(attr); ssize_t ret = 0; if (edd_attr->show) - ret = edd_attr->show(dev, buf, count, off); + ret = edd_attr->show(dev, buf); return ret; } @@ -156,13 +154,13 @@ edd_dump_raw_data(char *b, int count, void *data, int length) } static ssize_t -edd_show_host_bus(struct edd_device *edev, char *buf, size_t count, loff_t off) +edd_show_host_bus(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; int i; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -200,13 +198,13 @@ edd_show_host_bus(struct edd_device *edev, char *buf, size_t count, loff_t off) } static ssize_t -edd_show_interface(struct edd_device *edev, char *buf, size_t count, loff_t off) +edd_show_interface(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; int i; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -262,7 +260,7 @@ edd_show_interface(struct edd_device *edev, char *buf, size_t count, loff_t off) * Returns: number of bytes written, or 0 on failure */ static ssize_t -edd_show_raw_data(struct edd_device *edev, char *buf, size_t count, loff_t off) +edd_show_raw_data(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); int i, rc, warn_padding = 0, email = 0, nonzero_path = 0, @@ -271,7 +269,7 @@ edd_show_raw_data(struct edd_device *edev, char *buf, size_t count, loff_t off) char *p = buf; struct pci_dev *pci_dev=NULL; struct scsi_device *sd; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -372,11 +370,11 @@ out: } static ssize_t -edd_show_version(struct edd_device *edev, char *buf, size_t count, loff_t off) +edd_show_version(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -385,12 +383,11 @@ edd_show_version(struct edd_device *edev, char *buf, size_t count, loff_t off) } static ssize_t -edd_show_extensions(struct edd_device *edev, char *buf, size_t count, - loff_t off) +edd_show_extensions(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -410,12 +407,11 @@ edd_show_extensions(struct edd_device *edev, char *buf, size_t count, } static ssize_t -edd_show_info_flags(struct edd_device *edev, char *buf, size_t count, - loff_t off) +edd_show_info_flags(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -439,12 +435,11 @@ edd_show_info_flags(struct edd_device *edev, char *buf, size_t count, } static ssize_t -edd_show_default_cylinders(struct edd_device *edev, char *buf, size_t count, - loff_t off) +edd_show_default_cylinders(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -453,12 +448,11 @@ edd_show_default_cylinders(struct edd_device *edev, char *buf, size_t count, } static ssize_t -edd_show_default_heads(struct edd_device *edev, char *buf, size_t count, - loff_t off) +edd_show_default_heads(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -467,12 +461,11 @@ edd_show_default_heads(struct edd_device *edev, char *buf, size_t count, } static ssize_t -edd_show_default_sectors_per_track(struct edd_device *edev, char *buf, - size_t count, loff_t off) +edd_show_default_sectors_per_track(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } @@ -481,11 +474,11 @@ edd_show_default_sectors_per_track(struct edd_device *edev, char *buf, } static ssize_t -edd_show_sectors(struct edd_device *edev, char *buf, size_t count, loff_t off) +edd_show_sectors(struct edd_device *edev, char *buf) { struct edd_info *info = edd_dev_get_info(edev); char *p = buf; - if (!edev || !info || !buf || off) { + if (!edev || !info || !buf) { return 0; } diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 782ff309b887..e682965bb062 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -31,28 +31,26 @@ static ssize_t -drv_attr_show(struct kobject * kobj, struct attribute * attr, - char * buf, size_t count, loff_t off) +drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct driver_attribute * drv_attr = to_drv_attr(attr); struct device_driver * drv = to_driver(kobj); ssize_t ret = 0; if (drv_attr->show) - ret = drv_attr->show(drv,buf,count,off); + ret = drv_attr->show(drv,buf); return ret; } static ssize_t -drv_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count, loff_t off) +drv_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf) { struct driver_attribute * drv_attr = to_drv_attr(attr); struct device_driver * drv = to_driver(kobj); ssize_t ret = 0; if (drv_attr->store) - ret = drv_attr->store(drv,buf,count,off); + ret = drv_attr->store(drv,buf); return ret; } @@ -80,28 +78,26 @@ static struct kobj_type ktype_driver = { static ssize_t -bus_attr_show(struct kobject * kobj, struct attribute * attr, - char * buf, size_t count, loff_t off) +bus_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct bus_attribute * bus_attr = to_bus_attr(attr); struct bus_type * bus = to_bus(kobj); ssize_t ret = 0; if (bus_attr->show) - ret = bus_attr->show(bus,buf,count,off); + ret = bus_attr->show(bus,buf); return ret; } static ssize_t -bus_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count, loff_t off) +bus_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf) { struct bus_attribute * bus_attr = to_bus_attr(attr); struct bus_type * bus = to_bus(kobj); ssize_t ret = 0; if (bus_attr->store) - ret = bus_attr->store(bus,buf,count,off); + ret = bus_attr->store(bus,buf); return ret; } @@ -316,12 +312,14 @@ static int device_attach(struct device * dev) * If bus_match() returns 0 and the @dev->driver is set, we've found * a compatible pair, so we call devclass_add_device() to add the * device to the class. + * + * Note that we ignore the error from bus_match(), since it's perfectly + * valid for a driver not to bind to any devices. */ static int driver_attach(struct device_driver * drv) { struct bus_type * bus = drv->bus; struct list_head * entry; - int error = 0; if (!bus->match) return 0; @@ -333,7 +331,7 @@ static int driver_attach(struct device_driver * drv) devclass_add_device(dev); } } - return error; + return 0; } @@ -387,15 +385,18 @@ static void driver_detach(struct device_driver * drv) int bus_add_device(struct device * dev) { struct bus_type * bus = get_bus(dev->bus); + int error = 0; + if (bus) { down_write(&dev->bus->subsys.rwsem); pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id); list_add_tail(&dev->bus_list,&dev->bus->devices.list); - device_attach(dev); + if ((error = device_attach(dev))) + list_del_init(&dev->bus_list); up_write(&dev->bus->subsys.rwsem); sysfs_create_link(&bus->devices.kobj,&dev->kobj,dev->bus_id); } - return 0; + return error; } /** @@ -429,19 +430,33 @@ void bus_remove_device(struct device * dev) int bus_add_driver(struct device_driver * drv) { struct bus_type * bus = get_bus(drv->bus); + int error = 0; + if (bus) { pr_debug("bus %s: add driver %s\n",bus->name,drv->name); strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN); drv->kobj.kset = &bus->drivers; - kobject_register(&drv->kobj); + + if ((error = kobject_register(&drv->kobj))) { + put_bus(bus); + return error; + } down_write(&bus->subsys.rwsem); - devclass_add_driver(drv); - driver_attach(drv); + if (!(error = devclass_add_driver(drv))) { + if ((error = driver_attach(drv))) { + devclass_remove_driver(drv); + } + } up_write(&bus->subsys.rwsem); + + if (error) { + kobject_unregister(&drv->kobj); + put_bus(bus); + } } - return 0; + return error; } diff --git a/drivers/base/class.c b/drivers/base/class.c index 01e82cb997a5..3bf2636553c0 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -2,7 +2,7 @@ * class.c - basic device class management */ -#define DEBUG +#undef DEBUG #include <linux/device.h> #include <linux/module.h> @@ -14,28 +14,26 @@ #define to_class(obj) container_of(obj,struct device_class,subsys.kset.kobj) static ssize_t -devclass_attr_show(struct kobject * kobj, struct attribute * attr, - char * buf, size_t count, loff_t off) +devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct devclass_attribute * class_attr = to_class_attr(attr); struct device_class * dc = to_class(kobj); ssize_t ret = 0; if (class_attr->show) - ret = class_attr->show(dc,buf,count,off); + ret = class_attr->show(dc,buf); return ret; } static ssize_t -devclass_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count, loff_t off) +devclass_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf) { struct devclass_attribute * class_attr = to_class_attr(attr); struct device_class * dc = to_class(kobj); ssize_t ret = 0; if (class_attr->store) - ret = class_attr->store(dc,buf,count,off); + ret = class_attr->store(dc,buf); return ret; } @@ -100,15 +98,19 @@ void devclass_remove_file(struct device_class * cls, struct devclass_attribute * int devclass_add_driver(struct device_driver * drv) { struct device_class * cls = get_devclass(drv->devclass); + int error = 0; + if (cls) { down_write(&cls->subsys.rwsem); pr_debug("device class %s: adding driver %s:%s\n", cls->name,drv->bus->name,drv->name); - list_add_tail(&drv->class_list,&cls->drivers.list); - devclass_drv_link(drv); + error = devclass_drv_link(drv); + + if (!error) + list_add_tail(&drv->class_list,&cls->drivers.list); up_write(&cls->subsys.rwsem); } - return 0; + return error; } void devclass_remove_driver(struct device_driver * drv) diff --git a/drivers/base/core.c b/drivers/base/core.c index 234337528d15..8e66d54a1c27 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -35,28 +35,26 @@ DECLARE_MUTEX(device_sem); extern struct attribute * dev_default_attrs[]; static ssize_t -dev_attr_show(struct kobject * kobj, struct attribute * attr, - char * buf, size_t count, loff_t off) +dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { struct device_attribute * dev_attr = to_dev_attr(attr); struct device * dev = to_dev(kobj); ssize_t ret = 0; if (dev_attr->show) - ret = dev_attr->show(dev,buf,count,off); + ret = dev_attr->show(dev,buf); return ret; } static ssize_t -dev_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count, loff_t off) +dev_attr_store(struct kobject * kobj, struct attribute * attr, const char * buf) { struct device_attribute * dev_attr = to_dev_attr(attr); struct device * dev = to_dev(kobj); ssize_t ret = 0; if (dev_attr->store) - ret = dev_attr->store(dev,buf,count,off); + ret = dev_attr->store(dev,buf); return ret; } diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 39d9e70efc48..a7b123878ce4 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -3,7 +3,7 @@ * */ -#define DEBUG +#undef DEBUG #include <linux/device.h> #include <linux/module.h> diff --git a/drivers/base/interface.c b/drivers/base/interface.c index 7d06c1e44c34..ebf359fbca74 100644 --- a/drivers/base/interface.c +++ b/drivers/base/interface.c @@ -10,21 +10,21 @@ #include <linux/stat.h> #include <linux/string.h> -static ssize_t device_read_name(struct device * dev, char * buf, size_t count, loff_t off) +static ssize_t device_read_name(struct device * dev, char * buf) { - return off ? 0 : sprintf(buf,"%s\n",dev->name); + return sprintf(buf,"%s\n",dev->name); } static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL); static ssize_t -device_read_power(struct device * dev, char * page, size_t count, loff_t off) +device_read_power(struct device * dev, char * page) { - return off ? 0 : sprintf(page,"%d\n",dev->power_state); + return sprintf(page,"%d\n",dev->power_state); } static ssize_t -device_write_power(struct device * dev, const char * buf, size_t count, loff_t off) +device_write_power(struct device * dev, const char * buf) { char str_command[20]; char str_level[20]; @@ -33,9 +33,6 @@ device_write_power(struct device * dev, const char * buf, size_t count, loff_t o u32 int_level; int error = 0; - if (off) - return 0; - if (!dev->driver) goto done; @@ -83,7 +80,7 @@ device_write_power(struct device * dev, const char * buf, size_t count, loff_t o error = 0; } done: - return error < 0 ? error : count; + return error < 0 ? error : strlen(buf); } static DEVICE_ATTR(power,S_IWUSR | S_IRUGO, diff --git a/drivers/base/intf.c b/drivers/base/intf.c index dbf787225621..809547aed60d 100644 --- a/drivers/base/intf.c +++ b/drivers/base/intf.c @@ -2,7 +2,7 @@ * intf.c - class-specific interface management */ -#define DEBUG +#undef DEBUG #include <linux/device.h> #include <linux/module.h> @@ -66,8 +66,7 @@ int interface_add_data(struct intf_data * data) kobject_register(&data->kobj); list_add_tail(&data->dev_entry,&data->dev->intf_list); - intf_dev_link(data); - return 0; + return intf_dev_link(data); } return -EINVAL; } diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index f0eabbf2df11..8d42f19811b4 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -276,17 +276,17 @@ subsys_initcall(device_init); struct disk_attribute { struct attribute attr; - ssize_t (*show)(struct gendisk *, char *, size_t, loff_t); + ssize_t (*show)(struct gendisk *, char *); }; static ssize_t disk_attr_show(struct kobject * kobj, struct attribute * attr, - char * page, size_t count, loff_t off) + char * page) { struct gendisk * disk = to_disk(kobj); struct disk_attribute * disk_attr = container_of(attr,struct disk_attribute,attr); ssize_t ret = 0; if (disk_attr->show) - ret = disk_attr->show(disk,page,count,off); + ret = disk_attr->show(disk,page); return ret; } @@ -294,21 +294,18 @@ static struct sysfs_ops disk_sysfs_ops = { .show = &disk_attr_show, }; -static ssize_t disk_dev_read(struct gendisk * disk, - char *page, size_t count, loff_t off) +static ssize_t disk_dev_read(struct gendisk * disk, char *page) { dev_t base = MKDEV(disk->major, disk->first_minor); - return off ? 0 : sprintf(page, "%04x\n",base); + return sprintf(page, "%04x\n",base); } -static ssize_t disk_range_read(struct gendisk * disk, - char *page, size_t count, loff_t off) +static ssize_t disk_range_read(struct gendisk * disk, char *page) { - return off ? 0 : sprintf(page, "%d\n",disk->minors); + return sprintf(page, "%d\n",disk->minors); } -static ssize_t disk_size_read(struct gendisk * disk, - char *page, size_t count, loff_t off) +static ssize_t disk_size_read(struct gendisk * disk, char *page) { - return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk)); + return sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk)); } static inline unsigned jiffies_to_msec(unsigned jif) @@ -321,11 +318,10 @@ static inline unsigned jiffies_to_msec(unsigned jif) return (jif / HZ) * 1000 + (jif % HZ) * 1000 / HZ; #endif } -static ssize_t disk_stat_read(struct gendisk * disk, - char *page, size_t count, loff_t off) +static ssize_t disk_stat_read(struct gendisk * disk, char *page) { disk_round_stats(disk); - return off ? 0 : sprintf(page, + return sprintf(page, "%8u %8u %8llu %8u " "%8u %8u %8llu %8u " "%8u %8u %8u" diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 34026e3dc6be..268e5084f7a0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -3,6 +3,7 @@ # config PCI_LEGACY_PROC bool "Legacy /proc/pci interface" + depends on PCI ---help--- This feature enables a procfs file -- /proc/pci -- that provides a summary of PCI devices in the system. diff --git a/drivers/pci/pool.c b/drivers/pci/pool.c index 93cbe06c7786..f9c9d0e1a0e4 100644 --- a/drivers/pci/pool.c +++ b/drivers/pci/pool.c @@ -34,19 +34,16 @@ struct pci_page { /* cacheable header for 'allocation' bytes */ DECLARE_MUTEX (pools_lock); static ssize_t -show_pools (struct device *dev, char *buf, size_t count, loff_t off) +show_pools (struct device *dev, char *buf) { struct pci_dev *pdev; unsigned temp, size; char *next; struct list_head *i, *j; - if (off != 0) - return 0; - pdev = container_of (dev, struct pci_dev, dev); next = buf; - size = count; + size = PAGE_SIZE; temp = snprintf (next, size, "poolinfo - 0.1\n"); size -= temp; @@ -77,7 +74,7 @@ show_pools (struct device *dev, char *buf, size_t count, loff_t off) } up (&pools_lock); - return count - size; + return PAGE_SIZE - size; } static DEVICE_ATTR (pools, S_IRUGO, show_pools, NULL); diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index bbc437e54a0b..d20cfdc656a9 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -374,32 +374,25 @@ static struct seq_operations proc_bus_pci_devices_op = { struct proc_dir_entry *proc_bus_pci_dir; /* driverfs files */ -static ssize_t pci_show_irq(struct device * dev, char * buf, size_t count, loff_t off) +static ssize_t pci_show_irq(struct device * dev, char * buf) { struct pci_dev * pci_dev = to_pci_dev(dev); - return off ? 0 : sprintf(buf,"%u\n",pci_dev->irq); + return sprintf(buf,"%u\n",pci_dev->irq); } static DEVICE_ATTR(irq,S_IRUGO,pci_show_irq,NULL); -static ssize_t pci_show_resources(struct device * dev, char * buf, size_t count, loff_t off) +static ssize_t pci_show_resources(struct device * dev, char * buf) { struct pci_dev * pci_dev = to_pci_dev(dev); char * str = buf; int i; - if (off && off < DEVICE_COUNT_RESOURCE) { + for (i = 0; i < DEVICE_COUNT_RESOURCE && pci_resource_start(pci_dev,i); i++) { str += sprintf(str,LONG_FORMAT LONG_FORMAT LONG_FORMAT "\n", - pci_resource_start(pci_dev,off), - pci_resource_end(pci_dev,off), - pci_resource_flags(pci_dev,off)); - } else if (!off) { - for (i = 0; i < DEVICE_COUNT_RESOURCE && pci_resource_start(pci_dev,i); i++) { - str += sprintf(str,LONG_FORMAT LONG_FORMAT LONG_FORMAT "\n", - pci_resource_start(pci_dev,i), - pci_resource_end(pci_dev,i), - pci_resource_flags(pci_dev,i)); - } + pci_resource_start(pci_dev,i), + pci_resource_end(pci_dev,i), + pci_resource_flags(pci_dev,i)); } return (str - buf); } diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index b7ad96265091..e33e5617819c 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -215,14 +215,13 @@ static void pnp_print_resources(pnp_info_buffer_t *buffer, char *space, struct p pnp_print_mem32(buffer, space, mem32); } -static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf, size_t count, loff_t off) +static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); struct pnp_resources * res = dev->res; int dep = 0; pnp_info_buffer_t *buffer; - if (off) - return 0; + buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t)); if (!buffer) return -ENOMEM; @@ -242,13 +241,12 @@ static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf, size static DEVICE_ATTR(possible,S_IRUGO,pnp_show_possible_resources,NULL); -static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf, size_t count, loff_t off) +static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); char *str = buf; int i; - if (off) - return 0; + if (!dev->active){ str += sprintf(str,"DISABLED\n"); goto done; @@ -282,7 +280,7 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf, size_ } static ssize_t -pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count, loff_t off) +pnp_set_current_resources(struct device * dmdev, const char * buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); char command[20]; @@ -290,8 +288,7 @@ pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count, int num_args; int error = 0; int depnum, mode = 0; - if (off) - return 0; + num_args = sscanf(buf,"%10s %i %10s",command,&depnum,type); if (!num_args) goto done; @@ -328,19 +325,18 @@ pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count, goto done; } done: - return error < 0 ? error : count; + return error < 0 ? error : strlen(buf); } static DEVICE_ATTR(resources,S_IRUGO | S_IWUSR, pnp_show_current_resources,pnp_set_current_resources); -static ssize_t pnp_show_current_ids(struct device *dmdev, char *buf, size_t count, loff_t off) +static ssize_t pnp_show_current_ids(struct device *dmdev, char *buf) { char *str = buf; struct pnp_dev *dev = to_pnp_dev(dmdev); struct pnp_id * pos = dev->id; - if (off) - return 0; + while (pos) { str += sprintf(str,"%s\n", pos->id); pos = pos->next; diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 93097755fbe1..e18776421397 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -23,20 +23,16 @@ * Return: * number of bytes written into page. **/ -static ssize_t scsi_host_class_name_show(struct device *dev, char *page, - size_t count, loff_t off) +static ssize_t scsi_host_class_name_show(struct device *dev, char *page) { struct Scsi_Host *shost; - if (off) - return 0; - shost = to_scsi_host(dev); if (!shost) return 0; - return snprintf(page, count, "scsi%d\n", shost->host_no); + return snprintf(page, 20, "scsi%d\n", shost->host_no); } DEVICE_ATTR(class_name, S_IRUGO, scsi_host_class_name_show, NULL); @@ -138,13 +134,11 @@ void scsi_upper_driver_unregister(struct Scsi_Device_Template *sdev_tp) */ #define show_function(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, char *buf, size_t count, loff_t off) \ +show_##field (struct device *dev, char *buf) \ { \ struct scsi_device *sdev; \ - if (off) \ - return 0; \ sdev = to_scsi_device(dev); \ - return snprintf (buf, count, format_string, sdev->field); \ + return snprintf (buf, 20, format_string, sdev->field); \ } \ /* @@ -164,14 +158,12 @@ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) show_function(field, format_string) \ \ static ssize_t \ -store_##field (struct device *dev, const char *buf, size_t count, loff_t off)\ +store_##field (struct device *dev, const char *buf) \ { \ struct scsi_device *sdev; \ - \ - if (off) \ - return 0; \ sdev = to_scsi_device(dev); \ - return snscanf (buf, count, format_string, &sdev->field); \ + snscanf (buf, 20, format_string, &sdev->field); \ + return strlen(buf); \ } \ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, store_##field) @@ -183,18 +175,15 @@ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, show_##field, store_##field) show_function(field, "%d\n") \ \ static ssize_t \ -store_##field (struct device *dev, const char *buf, size_t count, loff_t off)\ +store_##field (struct device *dev, const char *buf) \ { \ int ret; \ struct scsi_device *sdev; \ - \ - if (off) \ - return 0; \ ret = scsi_sdev_check_buf_bit(buf); \ if (ret >= 0) { \ sdev = to_scsi_device(dev); \ sdev->field = ret; \ - ret = count; \ + ret = strlen(buf); \ } \ return ret; \ } \ diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index b7efb1209e9a..63f9083f08b7 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1350,20 +1350,18 @@ __setup("sg_def_reserved_size=", sg_def_reserved_size_setup); /* Driverfs file support */ static ssize_t -sg_device_kdev_read(struct device *driverfs_dev, char *page, - size_t count, loff_t off) +sg_device_kdev_read(struct device *driverfs_dev, char *page) { Sg_device *sdp = list_entry(driverfs_dev, Sg_device, sg_driverfs_dev); - return off ? 0 : sprintf(page, "%x\n", MKDEV(sdp->disk->major, - sdp->disk->first_minor)); + return sprintf(page, "%x\n", MKDEV(sdp->disk->major, + sdp->disk->first_minor)); } static DEVICE_ATTR(kdev,S_IRUGO,sg_device_kdev_read,NULL); static ssize_t -sg_device_type_read(struct device *driverfs_dev, char *page, - size_t count, loff_t off) +sg_device_type_read(struct device *driverfs_dev, char *page) { - return off ? 0 : sprintf(page, "CHR\n"); + return sprintf(page, "CHR\n"); } static DEVICE_ATTR(type,S_IRUGO,sg_device_type_read,NULL); diff --git a/drivers/usb/core/driverfs.c b/drivers/usb/core/driverfs.c index 2558ca462aa6..cd3bbc14b3e9 100644 --- a/drivers/usb/core/driverfs.c +++ b/drivers/usb/core/driverfs.c @@ -25,13 +25,10 @@ /* Active configuration fields */ #define usb_actconfig_attr(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, char *buf, size_t count, loff_t off) \ +show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ \ - if (off) \ - return 0; \ - \ udev = to_usb_device (dev); \ return sprintf (buf, format_string, udev->actconfig->desc.field); \ } \ @@ -43,13 +40,11 @@ usb_actconfig_attr (bmAttributes, "%2x\n") usb_actconfig_attr (bMaxPower, "%3dmA\n") /* String fields */ -static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t off) +static ssize_t show_product (struct device *dev, char *buf) { struct usb_device *udev; int len; - if (off) - return 0; udev = to_usb_device (dev); len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE); @@ -62,13 +57,11 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t static DEVICE_ATTR(product,S_IRUGO,show_product,NULL); static ssize_t -show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off) +show_manufacturer (struct device *dev, char *buf) { struct usb_device *udev; int len; - if (off) - return 0; udev = to_usb_device (dev); len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE); @@ -81,13 +74,11 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off) static DEVICE_ATTR(manufacturer,S_IRUGO,show_manufacturer,NULL); static ssize_t -show_serial (struct device *dev, char *buf, size_t count, loff_t off) +show_serial (struct device *dev, char *buf) { struct usb_device *udev; int len; - if (off) - return 0; udev = to_usb_device (dev); len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE); @@ -100,13 +91,11 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off) static DEVICE_ATTR(serial,S_IRUGO,show_serial,NULL); static ssize_t -show_speed (struct device *dev, char *buf, size_t count, loff_t off) +show_speed (struct device *dev, char *buf) { struct usb_device *udev; char *speed; - if (off) - return 0; udev = to_usb_device (dev); switch (udev->speed) { @@ -130,13 +119,10 @@ static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); /* Descriptor fields */ #define usb_descriptor_attr(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, char *buf, size_t count, loff_t off) \ +show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ \ - if (off) \ - return 0; \ - \ udev = to_usb_device (dev); \ return sprintf (buf, format_string, udev->descriptor.field); \ } \ @@ -185,14 +171,11 @@ void usb_create_driverfs_dev_files (struct usb_device *udev) /* Interface fields */ #define usb_intf_attr(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, char *buf, size_t count, loff_t off) \ +show_##field (struct device *dev, char *buf) \ { \ struct usb_interface *intf; \ int alt; \ \ - if (off) \ - return 0; \ - \ intf = to_usb_interface (dev); \ alt = intf->act_altsetting; \ \ diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 2fdb53d20016..dbd42deae348 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -274,17 +274,17 @@ static void devfs_remove_partitions(struct gendisk *dev) struct part_attribute { struct attribute attr; - ssize_t (*show)(struct hd_struct *,char *,size_t,loff_t); + ssize_t (*show)(struct hd_struct *,char *); }; -static ssize_t part_attr_show(struct kobject * kobj, struct attribute * attr, - char * page, size_t count, loff_t off) +static ssize_t +part_attr_show(struct kobject * kobj, struct attribute * attr, char * page) { struct hd_struct * p = container_of(kobj,struct hd_struct,kobj); struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr); ssize_t ret = 0; if (part_attr->show) - ret = part_attr->show(p,page,count,off); + ret = part_attr->show(p,page); return ret; } @@ -292,30 +292,26 @@ static struct sysfs_ops part_sysfs_ops = { .show = part_attr_show, }; -static ssize_t part_dev_read(struct hd_struct * p, - char *page, size_t count, loff_t off) +static ssize_t part_dev_read(struct hd_struct * p, char *page) { struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj); int part = p - disk->part + 1; dev_t base = MKDEV(disk->major, disk->first_minor); - return off ? 0 : sprintf(page, "%04x\n",base + part); + return sprintf(page, "%04x\n",base + part); } -static ssize_t part_start_read(struct hd_struct * p, - char *page, size_t count, loff_t off) +static ssize_t part_start_read(struct hd_struct * p, char *page) { - return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->start_sect); + return sprintf(page, "%llu\n",(unsigned long long)p->start_sect); } -static ssize_t part_size_read(struct hd_struct * p, - char *page, size_t count, loff_t off) +static ssize_t part_size_read(struct hd_struct * p, char *page) { - return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->nr_sects); + return sprintf(page, "%llu\n",(unsigned long long)p->nr_sects); } -static ssize_t part_stat_read(struct hd_struct * p, - char *page, size_t count, loff_t off) +static ssize_t part_stat_read(struct hd_struct * p, char *page) { - return off ? 0 : sprintf(page, "%8u %8llu %8u %8llu\n", - p->reads, (unsigned long long)p->read_sectors, - p->writes, (unsigned long long)p->write_sectors); + return sprintf(page, "%8u %8llu %8u %8llu\n", + p->reads, (unsigned long long)p->read_sectors, + p->writes, (unsigned long long)p->write_sectors); } static struct part_attribute part_attr_dev = { .attr = {.name = "dev", .mode = S_IRUGO }, diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 24984f4e439d..8ecf901ee6e1 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -72,6 +72,7 @@ static struct inode *sysfs_get_inode(struct super_block *sb, int mode, int dev) init_special_inode(inode, mode, dev); break; case S_IFREG: + inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; break; case S_IFDIR: @@ -153,27 +154,27 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * * These operations allow subsystems to have files that can be * read/written. */ -ssize_t subsys_attr_show(struct kobject * kobj, struct attribute * attr, - char * page, size_t count, loff_t off) +static ssize_t +subsys_attr_show(struct kobject * kobj, struct attribute * attr, char * page) { struct subsystem * s = to_subsys(kobj); struct subsys_attribute * sattr = to_sattr(attr); ssize_t ret = 0; if (sattr->show) - ret = sattr->show(s,page,count,off); + ret = sattr->show(s,page); return ret; } -ssize_t subsys_attr_store(struct kobject * kobj, struct attribute * attr, - const char * page, size_t count, loff_t off) +static ssize_t +subsys_attr_store(struct kobject * kobj, struct attribute * attr, const char * page) { struct subsystem * s = to_subsys(kobj); struct subsys_attribute * sattr = to_sattr(attr); ssize_t ret = 0; if (sattr->store) - ret = sattr->store(s,page,count,off); + ret = sattr->store(s,page); return ret; } @@ -182,6 +183,73 @@ static struct sysfs_ops subsys_sysfs_ops = { .store = subsys_attr_store, }; + +struct sysfs_buffer { + size_t count; + loff_t pos; + char * page; + struct sysfs_ops * ops; +}; + + +/** + * fill_read_buffer - allocate and fill buffer from object. + * @file: file pointer. + * @buffer: data buffer for file. + * + * Allocate @buffer->page, if it hasn't been already, then call the + * kobject's show() method to fill the buffer with this attribute's + * data. + * This is called only once, on the file's first read. + */ +static int fill_read_buffer(struct file * file, struct sysfs_buffer * buffer) +{ + struct attribute * attr = file->f_dentry->d_fsdata; + struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; + struct sysfs_ops * ops = buffer->ops; + int ret = 0; + size_t count; + + if (!buffer->page) + buffer->page = (char *) __get_free_page(GFP_KERNEL); + if (!buffer->page) + return -ENOMEM; + + count = ops->show(kobj,attr,buffer->page); + if (count >= 0) + buffer->count = count; + else + ret = count; + return ret; +} + + +/** + * flush_read_buffer - push buffer to userspace. + * @buffer: data buffer for file. + * @userbuf: user-passed buffer. + * @count: number of bytes requested. + * @ppos: file position. + * + * Copy the buffer we filled in fill_read_buffer() to userspace. + * This is done at the reader's leisure, copying and advancing + * the amount they specify each time. + * This may be called continuously until the buffer is empty. + */ +static int flush_read_buffer(struct sysfs_buffer * buffer, char * buf, + size_t count, loff_t * ppos) +{ + int error; + + if (count > (buffer->count - *ppos)) + count = buffer->count - *ppos; + + error = copy_to_user(buf,buffer->page + *ppos,count); + if (!error) + *ppos += count; + return error ? error : count; +} + /** * sysfs_read_file - read an attribute. * @file: file pointer. @@ -191,55 +259,79 @@ static struct sysfs_ops subsys_sysfs_ops = { * * Userspace wants to read an attribute file. The attribute descriptor * is in the file's ->d_fsdata. The target object is in the directory's - * ->d_fsdata. + * ->d_fsdata. * - * We allocate a %PAGE_SIZE buffer, and pass it to the object's ->show() - * method (along with the object). We loop doing this until @count is - * satisfied, or ->show() returns %0. + * We call fill_read_buffer() to allocate and fill the buffer from the + * object's show() method exactly once (if the read is happening from + * the beginning of the file). That should fill the entire buffer with + * all the data the object has to offer for that attribute. + * We then call flush_read_buffer() to copy the buffer to userspace + * in the increments specified. */ static ssize_t sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct attribute * attr = file->f_dentry->d_fsdata; - struct sysfs_ops * ops = file->private_data; - struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; - unsigned char *page; + struct sysfs_buffer * buffer = file->private_data; ssize_t retval = 0; - if (count > PAGE_SIZE) - count = PAGE_SIZE; + if (!*ppos) { + if ((retval = fill_read_buffer(file,buffer))) + return retval; + } + pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", + __FUNCTION__,count,*ppos,buffer->page); + return flush_read_buffer(buffer,buf,count,ppos); +} + + +/** + * fill_write_buffer - copy buffer from userspace. + * @buffer: data buffer for file. + * @userbuf: data from user. + * @count: number of bytes in @userbuf. + * + * Allocate @buffer->page if it hasn't been already, then + * copy the user-supplied buffer into it. + */ + +static int +fill_write_buffer(struct sysfs_buffer * buffer, const char * buf, size_t count) +{ + int error; - page = (unsigned char*)__get_free_page(GFP_KERNEL); - if (!page) + if (!buffer->page) + buffer->page = (char *)__get_free_page(GFP_KERNEL); + if (!buffer->page) return -ENOMEM; - while (count > 0) { - ssize_t len; + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; + error = copy_from_user(buffer->page,buf,count); + return error ? error : count; +} - len = ops->show(kobj,attr,page,count,*ppos); - if (len <= 0) { - if (len < 0) - retval = len; - break; - } else if (len > count) - len = count; +/** + * flush_write_buffer - push buffer to kobject. + * @file: file pointer. + * @buffer: data buffer for file. + * + * Get the correct pointers for the kobject and the attribute we're + * dealing with, then call the store() method for the attribute, + * passing the buffer that we acquired in fill_write_buffer(). + */ - if (copy_to_user(buf,page,len)) { - retval = -EFAULT; - break; - } +static int flush_write_buffer(struct file * file, struct sysfs_buffer * buffer) +{ + struct attribute * attr = file->f_dentry->d_fsdata; + struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; + struct sysfs_ops * ops = buffer->ops; - *ppos += len; - count -= len; - buf += len; - retval += len; - } - free_page((unsigned long)page); - return retval; + return ops->store(kobj,attr,buffer->page); } + /** * sysfs_write_file - write an attribute. * @file: file pointer @@ -247,56 +339,34 @@ sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos) * @count: number of bytes * @ppos: starting offset * - * Identical to sysfs_read_file(), though going the opposite direction. - * We allocate a %PAGE_SIZE buffer and copy in the userspace buffer. We - * pass that to the object's ->store() method until we reach @count or - * ->store() returns %0 or less. + * Similar to sysfs_read_file(), though working in the opposite direction. + * We allocate and fill the data from the user in fill_write_buffer(), + * then push it to the kobject in flush_write_buffer(). + * There is no easy way for us to know if userspace is only doing a partial + * write, so we don't support them. We expect the entire buffer to come + * on the first write. + * Hint: if you're writing a value, first read the file, modify only the + * the value you're changing, then write entire buffer back. */ static ssize_t sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos) { - struct attribute * attr = file->f_dentry->d_fsdata; - struct sysfs_ops * ops = file->private_data; - struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; - ssize_t retval = 0; - char * page; - - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; - if (copy_from_user(page,buf,count)) - goto done; - *(page + count) = '\0'; - - while (count > 0) { - ssize_t len; - - len = ops->store(kobj,attr,page + retval,count,*ppos); - - if (len <= 0) { - if (len < 0) - retval = len; - break; - } - retval += len; - count -= len; - *ppos += len; - buf += len; - } - done: - free_page((unsigned long)page); - return retval; + struct sysfs_buffer * buffer = file->private_data; + + count = fill_write_buffer(buffer,buf,count); + if (count > 0) + count = flush_write_buffer(file,buffer); + if (count > 0) + *ppos += count; + return count; } static int check_perm(struct inode * inode, struct file * file) { struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata); struct attribute * attr = file->f_dentry->d_fsdata; + struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; int error = 0; @@ -343,10 +413,16 @@ static int check_perm(struct inode * inode, struct file * file) goto Eaccess; } - /* No error? Great, store the ops in file->private_data - * for easy access in the read/write functions. + /* No error? Great, allocate a buffer for the file, and store it + * it in file->private_data for easy access. */ - file->private_data = ops; + buffer = kmalloc(sizeof(struct sysfs_buffer),GFP_KERNEL); + if (buffer) { + memset(buffer,0,sizeof(struct sysfs_buffer)); + buffer->ops = ops; + file->private_data = buffer; + } else + error = -ENOMEM; goto Done; Einval: @@ -358,6 +434,8 @@ static int check_perm(struct inode * inode, struct file * file) Eperm: error = -EPERM; Done: + if (error && kobj) + kobject_put(kobj); return error; } @@ -369,8 +447,16 @@ static int sysfs_open_file(struct inode * inode, struct file * filp) static int sysfs_release(struct inode * inode, struct file * filp) { struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata; + struct sysfs_buffer * buffer = filp->private_data; + if (kobj) kobject_put(kobj); + + if (buffer) { + if (buffer->page) + free_page((unsigned long)buffer->page); + kfree(buffer); + } return 0; } @@ -618,7 +704,19 @@ static void hash_and_remove(struct dentry * dir, const char * name) (victim->d_parent->d_inode == dir->d_inode)) { simple_unlink(dir->d_inode,victim); d_delete(victim); + + pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name, + atomic_read(&victim->d_count)); + /** + * Drop reference from initial get_dentry(). + */ + dput(victim); } + + /** + * Drop the reference acquired from get_dentry() above. + */ + dput(victim); } up(&dir->d_inode->i_sem); } @@ -662,29 +760,60 @@ void sysfs_remove_link(struct kobject * kobj, char * name) void sysfs_remove_dir(struct kobject * kobj) { struct list_head * node, * next; - struct dentry * dentry = kobj->dentry; + struct dentry * dentry = dget(kobj->dentry); struct dentry * parent; if (!dentry) return; + pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); parent = dget(dentry->d_parent); down(&parent->d_inode->i_sem); down(&dentry->d_inode->i_sem); list_for_each_safe(node,next,&dentry->d_subdirs) { - struct dentry * d = list_entry(node,struct dentry,d_child); - /* make sure dentry is still there */ + struct dentry * d = dget(list_entry(node,struct dentry,d_child)); + /** + * Make sure dentry is still there + */ + pr_debug(" o %s: ",d->d_name.name); if (d->d_inode) { + + pr_debug("removing"); + /** + * Unlink and unhash. + */ simple_unlink(dentry->d_inode,d); - d_delete(dentry); + d_delete(d); + + /** + * Drop reference from initial get_dentry(). + */ + dput(d); } + pr_debug(" done (%d)\n",atomic_read(&d->d_count)); + /** + * drop reference from dget() above. + */ + dput(d); } up(&dentry->d_inode->i_sem); d_invalidate(dentry); simple_rmdir(parent->d_inode,dentry); d_delete(dentry); + + pr_debug(" o %s removing done (%d)\n",dentry->d_name.name, + atomic_read(&dentry->d_count)); + /** + * Drop reference from initial get_dentry(). + */ + dput(dentry); + + /** + * Drop reference from dget() on entrance. + */ + dput(dentry); up(&parent->d_inode->i_sem); dput(parent); } diff --git a/include/linux/device.h b/include/linux/device.h index d5aa116942b8..7c4bc77b695d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -97,8 +97,8 @@ int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, struct bus_attribute { struct attribute attr; - ssize_t (*show)(struct bus_type *, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct bus_type *, const char * buf, size_t count, loff_t off); + ssize_t (*show)(struct bus_type *, char * buf); + ssize_t (*store)(struct bus_type *, const char * buf); }; #define BUS_ATTR(_name,_mode,_show,_store) \ @@ -140,8 +140,8 @@ extern void put_driver(struct device_driver * drv); struct driver_attribute { struct attribute attr; - ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off); + ssize_t (*show)(struct device_driver *, char * buf); + ssize_t (*store)(struct device_driver *, const char * buf); }; #define DRIVER_ATTR(_name,_mode,_show,_store) \ @@ -181,8 +181,8 @@ extern void put_devclass(struct device_class *); struct devclass_attribute { struct attribute attr; - ssize_t (*show)(struct device_class *, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device_class *, const char * buf, size_t count, loff_t off); + ssize_t (*show)(struct device_class *, char * buf); + ssize_t (*store)(struct device_class *, const char * buf); }; #define DEVCLASS_ATTR(_name,_str,_mode,_show,_store) \ @@ -319,8 +319,8 @@ extern void device_release_driver(struct device * dev); struct device_attribute { struct attribute attr; - ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off); - ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off); + ssize_t (*show)(struct device * dev, char * buf); + ssize_t (*store)(struct device * dev, const char * buf); }; #define DEVICE_ATTR(_name,_mode,_show,_store) \ diff --git a/include/linux/kobject.h b/include/linux/kobject.h index ff15fe1bc8fd..0cc01658044d 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -165,8 +165,8 @@ static inline void subsys_put(struct subsystem * s) struct subsys_attribute { struct attribute attr; - ssize_t (*show)(struct subsystem *, char *, size_t, loff_t); - ssize_t (*store)(struct subsystem *, const char *, size_t, loff_t); + ssize_t (*show)(struct subsystem *, char *); + ssize_t (*store)(struct subsystem *, const char *); }; extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 29c3eb8d2c9e..b3cc047255b8 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -17,8 +17,8 @@ struct attribute { }; struct sysfs_ops { - ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t); - ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t); + ssize_t (*show)(struct kobject *, struct attribute *,char *); + ssize_t (*store)(struct kobject *,struct attribute *,const char *); }; extern int diff --git a/lib/kobject.c b/lib/kobject.c index c0857237f8e6..2c05717a26a8 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -79,6 +79,29 @@ void kobject_init(struct kobject * kobj) kobj->kset = kset_get(kobj->kset); } + +/** + * unlink - remove kobject from kset list. + * @kobj: kobject. + * + * Remove the kobject from the kset list and decrement + * its parent's refcount. + * This is separated out, so we can use it in both + * kobject_del() and kobject_add() on error. + */ + +static void unlink(struct kobject * kobj) +{ + if (kobj->kset) { + down_write(&kobj->kset->subsys->rwsem); + list_del_init(&kobj->entry); + up_write(&kobj->kset->subsys->rwsem); + } + if (kobj->parent) + kobject_put(kobj->parent); + kobject_put(kobj); +} + /** * kobject_add - add an object to the hierarchy. * @kobj: object. @@ -109,8 +132,8 @@ int kobject_add(struct kobject * kobj) up_write(&kobj->kset->subsys->rwsem); } error = create_dir(kobj); - if (error && parent) - kobject_put(parent); + if (error) + unlink(kobj); return error; } @@ -140,14 +163,7 @@ int kobject_register(struct kobject * kobj) void kobject_del(struct kobject * kobj) { sysfs_remove_dir(kobj); - if (kobj->kset) { - down_write(&kobj->kset->subsys->rwsem); - list_del_init(&kobj->entry); - up_write(&kobj->kset->subsys->rwsem); - } - if (kobj->parent) - kobject_put(kobj->parent); - kobject_put(kobj); + unlink(kobj); } /** @@ -317,7 +333,7 @@ int subsystem_register(struct subsystem * s) int error; subsystem_init(s); - pr_debug("subsystem %s: registering\n",s->kobj.name); + pr_debug("subsystem %s: registering\n",s->kset.kobj.name); if (!(error = kset_add(&s->kset))) { if (!s->kset.subsys) @@ -328,7 +344,7 @@ int subsystem_register(struct subsystem * s) void subsystem_unregister(struct subsystem * s) { - pr_debug("subsystem %s: unregistering\n",s->kobj.name); + pr_debug("subsystem %s: unregistering\n",s->kset.kobj.name); kset_unregister(&s->kset); } diff --git a/net/core/dev.c b/net/core/dev.c index 227e6d24e0ae..573121fd49c0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2520,6 +2520,11 @@ int register_netdevice(struct net_device *dev) if (d == dev || !strcmp(d->name, dev->name)) goto out_err; } + snprintf(dev->kobj.name,KOBJ_NAME_LEN,dev->name); + kobj_set_kset_s(dev,net_subsys); + if ((ret = kobject_register(&dev->kobj))) + goto out_err; + /* * nil rebuild_header routine, * that should be never called and used as just bug trap. @@ -2547,10 +2552,7 @@ int register_netdevice(struct net_device *dev) notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); net_run_sbin_hotplug(dev, "register"); - - snprintf(dev->kobj.name,KOBJ_NAME_LEN,dev->name); - kobj_set_kset_s(dev,net_subsys); - ret = kobject_register(&dev->kobj); + ret = 0; out: return ret; @@ -2676,8 +2678,6 @@ int unregister_netdevice(struct net_device *dev) goto out; } - kobject_unregister(&dev->kobj); - /* Last reference is our one */ if (atomic_read(&dev->refcnt) == 1) goto out; @@ -2738,6 +2738,7 @@ int unregister_netdevice(struct net_device *dev) } } out: + kobject_unregister(&dev->kobj); dev_put(dev); return 0; } |
