From 35551cf5753fe89869f24fc06f6b787b02a10acc Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Thu, 21 Nov 2002 01:50:58 -0600 Subject: sysfs: fix file deletion again. After looking into again, I realize that I was again getting file and directory deletion a little wrong. This patch should go back to mimmicking the VFS again. - The extra dget() in sysfs_mknod() and sysfs_symlink() has been readded. This is identical to the way ramfs does creation. - We do d_delete() after simple_unlink() and simple_rmdir(), instead of d_invalidate() before it. This is how vfs_rmdir() and vfs_unlink() do it, and the way we used to. --- fs/sysfs/inode.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 73633052e993..2b914285eeaf 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -96,9 +96,10 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t if (!dentry->d_inode) { inode = sysfs_get_inode(dir->i_sb, mode, dev); - if (inode) + if (inode) { d_instantiate(dentry, inode); - else + dget(dentry); + } else error = -ENOSPC; } else error = -EEXIST; @@ -135,9 +136,10 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * if (inode) { int l = strlen(symname)+1; error = page_symlink(inode, symname, l); - if (!error) + if (!error) { d_instantiate(dentry, inode); - else + dget(dentry); + } else iput(inode); } return error; @@ -571,8 +573,8 @@ static void hash_and_remove(struct dentry * dir, const char * name) /* make sure dentry is really there */ if (victim->d_inode && (victim->d_parent->d_inode == dir->d_inode)) { - d_invalidate(victim); simple_unlink(dir->d_inode,victim); + d_delete(victim); } } up(&dir->d_inode->i_sem); @@ -631,15 +633,15 @@ void sysfs_remove_dir(struct kobject * kobj) struct dentry * d = list_entry(node,struct dentry,d_child); /* make sure dentry is still there */ if (d->d_inode) { - d_invalidate(d); simple_unlink(dentry->d_inode,d); + d_delete(dentry); } } up(&dentry->d_inode->i_sem); d_invalidate(dentry); simple_rmdir(parent->d_inode,dentry); - + d_delete(dentry); up(&parent->d_inode->i_sem); dput(parent); } -- cgit v1.2.3 From 2ce5e0bca93f06683b40fbae3b5b4031dd02b2cc Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Thu, 21 Nov 2002 05:01:54 -0600 Subject: Allow subsystem to have attributes, too. This is really handy for subsystems that have any attributes they want to export, esp. if they have just a few, since they don't have to define all of the infrastructure for creating and tearing down the attributes for the high level type that they are. - define struct subsys_attribute for declaring attributes of subsystems themselves. - define subsys_create_file() and subsys_remove_file(). - define subsys_sysfs_ops for forwarding sysfs reads and writes to the subsystem attribute callbacks. - Set the sysfs_ops to be the subsystem ones if the kobject doesn't belong to a subsystem itself (meaning that it is a subsystem). --- fs/sysfs/inode.c | 44 +++++++++++++++++++++++++++++++++++++++++++- include/linux/kobject.h | 9 +++++++++ lib/kobject.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 2b914285eeaf..684038810f77 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -145,6 +145,43 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char * return error; } +#define to_subsys(k) container_of(k,struct subsystem,kobj) +#define to_sattr(a) container_of(a,struct subsys_attribute,attr) + +/** + * Subsystem file operations. + * 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) +{ + 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); + return ret; +} + +ssize_t subsys_attr_store(struct kobject * kobj, struct attribute * attr, + const char * page, size_t count, loff_t off) +{ + 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); + return ret; +} + +static struct sysfs_ops subsys_sysfs_ops = { + .show = subsys_attr_show, + .store = subsys_attr_store, +}; + /** * sysfs_read_file - read an attribute. * @file: file pointer. @@ -265,9 +302,14 @@ static int check_perm(struct inode * inode, struct file * file) if (!kobj || !attr) goto Einval; - + + /* if the kobject has no subsystem, then it is a subsystem itself, + * so give it the subsys_sysfs_ops. + */ if (kobj->subsys) ops = kobj->subsys->sysfs_ops; + else + ops = &subsys_sysfs_ops; /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 7b3157a78f7e..6b528c5a61cb 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -60,4 +60,13 @@ static inline void subsys_put(struct subsystem * s) kobject_put(&s->kobj); } +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); +}; + +extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); +extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *); + #endif /* _KOBJECT_H_ */ diff --git a/lib/kobject.c b/lib/kobject.c index 33e4f8c9ce58..bdf6636d2b2e 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -229,6 +229,38 @@ void subsystem_unregister(struct subsystem * s) } +/** + * subsystem_create_file - export sysfs attribute file. + * @s: subsystem. + * @a: subsystem attribute descriptor. + */ + +int subsys_create_file(struct subsystem * s, struct subsys_attribute * a) +{ + int error = 0; + if (subsys_get(s)) { + error = sysfs_create_file(&s->kobj,&a->attr); + subsys_put(s); + } + return error; +} + + +/** + * subsystem_remove_file - remove sysfs attribute file. + * @s: subsystem. + * @a: attribute desciptor. + */ + +void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) +{ + if (subsys_get(s)) { + sysfs_remove_file(&s->kobj,&a->attr); + subsys_put(s); + } +} + + EXPORT_SYMBOL(kobject_init); EXPORT_SYMBOL(kobject_register); EXPORT_SYMBOL(kobject_unregister); @@ -238,3 +270,5 @@ EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(subsystem_init); EXPORT_SYMBOL(subsystem_register); EXPORT_SYMBOL(subsystem_unregister); +EXPORT_SYMBOL(subsys_create_file); +EXPORT_SYMBOL(subsys_remove_file); -- cgit v1.2.3 From 8ab1bc19e974fdebe76c065fe444979c84ba2f74 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Nov 2002 04:39:35 -0600 Subject: move dma_mask into struct device Attached is a patch which moves dma_mask into struct device and cleans up the scsi mid-layer to use it (instead of using struct pci_dev). The advantage to doing this is probably most apparent on non-pci bus architectures where currently you have to construct a fake pci_dev just so you can get the bounce buffers to work correctly. The patch tries to perturb the minimum amount of code, so dma_mask in struct device is simply a pointer to the one in pci_dev. However, it will make it easy for me now to add generic device to MCA without having to go the fake pci route. --- drivers/pci/probe.c | 1 + include/linux/device.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5dfb1af81376..cfe5444f9fac 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -448,6 +448,7 @@ struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp) /* now put in global tree */ strcpy(dev->dev.bus_id,dev->slot_name); + dev->dev.dma_mask = &dev->dma_mask; device_register(&dev->dev); return dev; diff --git a/include/linux/device.h b/include/linux/device.h index 6ce7d11cc8f9..44262d7f34c3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -303,6 +303,7 @@ struct device { being off. */ unsigned char *saved_state; /* saved device state */ + u64 *dma_mask; /* dma mask (if dma'able device) */ void (*release)(struct device * dev); }; -- cgit v1.2.3 From b26ca10f9fb9db8b164bb9225424cceee13cbeea Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 25 Nov 2002 03:46:55 -0600 Subject: fix up block device usage of kobjects. alloc_disk() should set the kobject's subsystem before calling kobject_init(), which would increment the subsystem's refcount (to be decremented in kobject_cleanup()). Since it was being set after the call, the subsystem's refcount was being pushed to 0 if the floppy driver was enabled, but there were no floppy drives found (the driver would alloc_disk(), then put_disk() if no drives were found). Partitions use kobject_register(), so they don't have to do kobject_init() (it's done for them). add_disk() should use kobject_add() instead of kobject_register(), since it's already done kobject_init() in alloc_disk(). Also, del_gendisk() doesn't have to do extra refcount and call kobject_unregister(); it should just call kobject_del(). The block device will be freed up later when put_disk() pushes the refcount to 0. --- drivers/block/genhd.c | 4 ++-- fs/partitions/check.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 5bba83e69485..97fb55011fe7 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -408,11 +408,11 @@ struct gendisk *alloc_disk(int minors) disk->minors = minors; while (minors >>= 1) disk->minor_shift++; - kobject_init(&disk->kobj); disk->kobj.subsys = &block_subsys; + kobject_init(&disk->kobj); INIT_LIST_HEAD(&disk->full_list); + rand_initialize_disk(disk); } - rand_initialize_disk(disk); return disk; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 102e9f1f651b..797343c18942 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -377,7 +377,6 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) p->start_sect = start; p->nr_sects = len; devfs_register_partition(disk, part); - kobject_init(&p->kobj); snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part); p->kobj.parent = &disk->kobj; p->kobj.subsys = &part_subsys; @@ -406,7 +405,7 @@ void register_disk(struct gendisk *disk) s = strchr(disk->kobj.name, '/'); if (s) *s = '!'; - kobject_register(&disk->kobj); + kobject_add(&disk->kobj); disk_sysfs_symlinks(disk); if (disk->flags & GENHD_FL_CD) @@ -529,8 +528,7 @@ void del_gendisk(struct gendisk *disk) sysfs_remove_link(&disk->driverfs_dev->kobj, "block"); put_device(disk->driverfs_dev); } - kobject_get(&disk->kobj); /* kobject model is fucked in head */ - kobject_unregister(&disk->kobj); + kobject_del(&disk->kobj); } struct dev_name { -- cgit v1.2.3 From fdb47bc680c33d211a64768e758860ef5dfa0fae Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 25 Nov 2002 03:48:43 -0600 Subject: NUMA: make sure that the node device class is registered before the node driver. This fixes an Oops on boot on NUMA systems, since the driver tries to access the device class when it's registered. --- drivers/base/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/node.c b/drivers/base/node.c index 631cdc395e05..815567bd72fe 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -93,7 +93,7 @@ int __init register_node(struct node *node, int num, struct node *parent) static int __init register_node_type(void) { - driver_register(&node_driver); - return devclass_register(&node_devclass); + devclass_register(&node_devclass); + return driver_register(&node_driver); } postcore_initcall(register_node_type); -- cgit v1.2.3 From fc8aae5360008cf232c5f5bdc65b772a73a248c2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Dec 2002 11:22:05 -0600 Subject: driver model: reinstate bus iterators. This replaces the bus iterators bus_for_each_dev() and bus_for_each_drv(). Though no one in the kernel was using these, the MCA bus updates that are about to appear depend on them. They both now take a start pointer, which is the item to begin walking the list from, if it is not NULL. --- drivers/base/bus.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/device.h | 9 +++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d18effa7f981..ca5ef19aa4a9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -132,6 +132,91 @@ struct subsystem bus_subsys = { }; +/** + * bus_for_each_dev - device iterator. + * @bus: bus type. + * @start: device to start iterating from. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over @bus's list of devices, and call @fn for each, + * passing it @data. If @start is not NULL, we use that device to + * begin iterating from. + * + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. + * + * NOTE: The device that returns a non-zero value is not retained + * in any way, nor is its refcount incremented. If the caller needs + * to retain this data, it should do, and increment the reference + * count in the supplied callback. + */ +int bus_for_each_dev(struct bus_type * bus, struct device * start, + void * data, int (*fn)(struct device *, void *)) +{ + struct list_head * head, * entry; + int error = 0; + + if (!(bus = get_bus(bus))) + return -EINVAL; + + head = start ? &start->bus_list : &bus->devices; + + down_read(&bus->subsys.rwsem); + list_for_each(entry,head) { + struct device * dev = get_device(to_dev(entry)); + error = fn(dev,data); + put_device(dev); + if (error) + break; + } + up_read(&bus->subsys.rwsem); + return error; +} + +/** + * bus_for_each_drv - driver iterator + * @bus: bus we're dealing with. + * @start: driver to start iterating on. + * @data: data to pass to the callback. + * @fn: function to call for each driver. + * + * This is nearly identical to the device iterator above. + * We iterate over each driver that belongs to @bus, and call + * @fn for each. If @fn returns anything but 0, we break out + * and return it. If @start is not NULL, we use it as the head + * of the list. + * + * NOTE: we don't return the driver that returns a non-zero + * value, nor do we leave the reference count incremented for that + * driver. If the caller needs to know that info, it must set it + * in the callback. It must also be sure to increment the refcount + * so it doesn't disappear before returning to the caller. + */ + +int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, + void * data, int (*fn)(struct device_driver *, void *)) +{ + struct list_head * head, * entry; + int error = 0; + + if(!(bus = get_bus(bus))) + return -EINVAL; + + head = start ? &start->bus_list : &bus->drivers; + + down_read(&bus->subsys.rwsem); + list_for_each(entry,head) { + struct device_driver * drv = get_driver(to_drv(entry)); + error = fn(drv,data); + put_driver(drv); + if(error) + break; + } + up_read(&bus->subsys.rwsem); + return error; +} + /** * attach - add device to driver. * @dev: device. @@ -190,7 +275,7 @@ static int bus_match(struct device * dev, struct device_driver * drv) */ static int device_attach(struct device * dev) { - struct bus_type * bus = dev->bus; + struct bus_type * bus = dev->bus; struct list_head * entry; int error = 0; @@ -455,6 +540,9 @@ static int __init bus_subsys_init(void) core_initcall(bus_subsys_init); +EXPORT_SYMBOL(bus_for_each_dev); +EXPORT_SYMBOL(bus_for_each_drv); + EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); diff --git a/include/linux/device.h b/include/linux/device.h index 9835d4fcdb12..435159cb14b8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -85,6 +85,15 @@ extern struct bus_type * get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); +/* iterator helpers for buses */ + +int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, + int (*fn)(struct device *, void *)); + +int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, + void * data, int (*fn)(struct device_driver *, void *)); + + /* driverfs interface for exporting bus attributes */ struct bus_attribute { -- cgit v1.2.3 From 659c204070e5d33cbe12dfc580ec8f53b66d2618 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sun, 1 Dec 2002 11:24:34 -0600 Subject: kobjects: don't do cleanup if kobject_add() fails. kobject_register() has the rude behavior that it will attempt to clean up the kobject if kobject_add() fails. This replaces that with a WARN_ON() for the return value, and leaves the cleanup to the caller. --- lib/kobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/kobject.c b/lib/kobject.c index bdf6636d2b2e..04b1fd760d45 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -110,8 +110,7 @@ int kobject_register(struct kobject * kobj) if (kobj) { kobject_init(kobj); error = kobject_add(kobj); - if (error) - kobject_cleanup(kobj); + WARN_ON(error); } else error = -EINVAL; return error; -- cgit v1.2.3 From c445065596bb32adc9bf3f31b50657de41496b1c Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 2 Dec 2002 01:35:26 -0600 Subject: driver model: get rid of global device list; minor cleanups. Since the device subsystem contains a global list of registered objects, we don't need to keep our own separate list, so we axe it. This also results in a savings of sizeof(void *) x 2 in the size of each device. drivers/base/power.c is updated to take the subsystem's rwsem and iterate over that list of objects. This leaves only the children list of struct device protected by the driver model semaphore. This could be removed, but there is one user of it (EDD), so it can't happen yet. This patch also removes the driver model spinlock, which isn't used anymore. --- drivers/base/base.h | 8 -------- drivers/base/core.c | 23 ++++++++--------------- drivers/base/power.c | 22 ++++++++++++---------- include/linux/device.h | 7 ------- 4 files changed, 20 insertions(+), 40 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 6194d88242ec..0eaf5a2f0673 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -1,13 +1,5 @@ #undef DEBUG -#ifdef DEBUG -# define DBG(x...) printk(x) -#else -# define DBG(x...) -#endif - -extern struct list_head global_device_list; -extern spinlock_t device_lock; extern struct semaphore device_sem; extern int bus_add_device(struct device * dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 6425fc8e0634..e6504bef4b5d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -18,15 +18,11 @@ #include "base.h" -LIST_HEAD(global_device_list); - int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL; DECLARE_MUTEX(device_sem); -spinlock_t device_lock = SPIN_LOCK_UNLOCKED; - #define to_dev(obj) container_of(obj,struct device,kobj) @@ -146,11 +142,9 @@ void device_initialize(struct device *dev) kobject_init(&dev->kobj); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->g_list); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->intf_list); -// spin_lock_init(&dev->lock); } /** @@ -188,13 +182,11 @@ int device_add(struct device *dev) goto register_done; /* now take care of our own registration */ - down(&device_sem); if (parent) { - list_add_tail(&dev->g_list,&dev->parent->g_list); + down(&device_sem); list_add_tail(&dev->node,&parent->children); - } else - list_add_tail(&dev->g_list,&global_device_list); - up(&device_sem); + up(&device_sem); + } bus_add_device(dev); @@ -276,10 +268,11 @@ void device_del(struct device * dev) { struct device * parent = dev->parent; - down(&device_sem); - list_del_init(&dev->node); - list_del_init(&dev->g_list); - up(&device_sem); + if (parent) { + down(&device_sem); + list_del_init(&dev->node); + up(&device_sem); + } /* Notify the platform of the removal, in case they * need to do anything... diff --git a/drivers/base/power.c b/drivers/base/power.c index 594a793e24f9..9e38aa766ef1 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -15,7 +15,9 @@ #include #include "base.h" -#define to_dev(node) container_of(node,struct device,g_list) +#define to_dev(node) container_of(node,struct device,kobj.entry) + +extern struct subsystem device_subsys; /** * device_suspend - suspend/remove all devices on the device ree @@ -35,8 +37,8 @@ int device_suspend(u32 state, u32 level) printk(KERN_EMERG "Suspending devices\n"); - down(&device_sem); - list_for_each(node,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each(node,&device_subsys.list) { struct device * dev = to_dev(node); if (dev->driver && dev->driver->suspend) { pr_debug("suspending device %s\n",dev->name); @@ -45,7 +47,7 @@ int device_suspend(u32 state, u32 level) printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error); } } - up(&device_sem); + up_write(&device_subsys.rwsem); return error; } @@ -61,15 +63,15 @@ void device_resume(u32 level) { struct list_head * node; - down(&device_sem); - list_for_each_prev(node,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each_prev(node,&device_subsys.list) { struct device * dev = to_dev(node); if (dev->driver && dev->driver->resume) { pr_debug("resuming device %s\n",dev->name); dev->driver->resume(dev,level); } } - up(&device_sem); + up_write(&device_subsys.rwsem); printk(KERN_EMERG "Devices Resumed\n"); } @@ -83,15 +85,15 @@ void device_shutdown(void) printk(KERN_EMERG "Shutting down devices\n"); - down(&device_sem); - list_for_each(entry,&global_device_list) { + down_write(&device_subsys.rwsem); + list_for_each(entry,&device_subsys.list) { struct device * dev = to_dev(entry); if (dev->driver && dev->driver->shutdown) { pr_debug("shutting down %s\n",dev->name); dev->driver->shutdown(dev); } } - up(&device_sem); + up_write(&device_subsys.rwsem); } EXPORT_SYMBOL(device_suspend); diff --git a/include/linux/device.h b/include/linux/device.h index 435159cb14b8..c1918942c4cd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -250,7 +250,6 @@ extern int interface_add_data(struct intf_data *); struct device { - struct list_head g_list; /* node in depth-first order list */ struct list_head node; /* node in sibling list */ struct list_head bus_list; /* node in bus's list */ struct list_head driver_list; @@ -290,12 +289,6 @@ list_to_dev(struct list_head *node) return list_entry(node, struct device, node); } -static inline struct device * -g_list_to_dev(struct list_head *g_list) -{ - return list_entry(g_list, struct device, g_list); -} - static inline void * dev_get_drvdata (struct device *dev) { -- cgit v1.2.3 From 6f614c99601d8d5f2b67a186806bbd21e5108fc5 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 3 Dec 2002 00:40:21 -0600 Subject: driver model: clean up interface handling. - Make sure devices are added to an interface if the interface is registered e.g. as a module. - Make sure interface data descriptors are inserted into the interface's list by adding a kobject to them and calling kobject_register. - Create interface_remove_data() for explicit removal of data descriptor. - Break common pieces into separate functions. - Add lots of comments. - Add class_list to struct device, so we can access the devices registered with the class. --- drivers/base/base.h | 4 +- drivers/base/class.c | 12 ++- drivers/base/core.c | 1 + drivers/base/intf.c | 275 +++++++++++++++++++++++++++++++++++++++---------- include/linux/device.h | 9 +- 5 files changed, 238 insertions(+), 63 deletions(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 0eaf5a2f0673..ab31ff74207c 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -14,8 +14,8 @@ extern void devclass_remove_device(struct device *); extern int devclass_add_driver(struct device_driver *); extern void devclass_remove_driver(struct device_driver *); -extern int interface_add(struct device_class *, struct device *); -extern void interface_remove(struct device_class *, struct device *); +extern int interface_add_dev(struct device *); +extern void interface_remove_dev(struct device *); #ifdef CONFIG_HOTPLUG diff --git a/drivers/base/class.c b/drivers/base/class.c index fbc6326312d8..86c7775c42bd 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -2,7 +2,7 @@ * class.c - basic device class management */ -#undef DEBUG +#define DEBUG #include #include @@ -98,7 +98,6 @@ 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); @@ -172,9 +171,11 @@ int devclass_add_device(struct device * dev) error = cls->add_device(dev); if (!error) { enum_device(cls,dev); - interface_add(cls,dev); + interface_add_dev(dev); } + list_add_tail(&dev->class_list,&cls->devices); + /* notify userspace (call /sbin/hotplug) */ class_hotplug (dev, "add"); @@ -196,9 +197,11 @@ void devclass_remove_device(struct device * dev) down_write(&cls->subsys.rwsem); pr_debug("device class %s: removing device %s\n", cls->name,dev->name); - interface_remove(cls,dev); + interface_remove_dev(dev); unenum_device(cls,dev); + list_del(&dev->class_list); + /* notify userspace (call /sbin/hotplug) */ class_hotplug (dev, "remove"); @@ -224,6 +227,7 @@ void put_devclass(struct device_class * cls) int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); + INIT_LIST_HEAD(&cls->devices); pr_debug("device class '%s': registering\n",cls->name); strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN); diff --git a/drivers/base/core.c b/drivers/base/core.c index e6504bef4b5d..a5ab807d9f49 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -144,6 +144,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->class_list); INIT_LIST_HEAD(&dev->intf_list); } diff --git a/drivers/base/intf.c b/drivers/base/intf.c index d31ad38bf3d2..63c5a4d12cd4 100644 --- a/drivers/base/intf.c +++ b/drivers/base/intf.c @@ -2,7 +2,7 @@ * intf.c - class-specific interface management */ -#undef DEBUG +#define DEBUG #include #include @@ -10,98 +10,267 @@ #include "base.h" -#define to_intf(node) container_of(node,struct device_interface,kobj.entry) +#define to_intf(node) container_of(node,struct device_interface,subsys.kobj.entry) + +#define to_data(e) container_of(e,struct intf_data,kobj.entry) + +#define intf_from_data(d) container_of(d->kobj.subsys,struct device_interface, subsys); + /** - * intf_dev_link - symlink from interface's directory to device's directory + * intf_dev_link - create sysfs symlink for interface. + * @data: interface data descriptor. * + * Create a symlink 'phys' in the interface's directory to */ + static int intf_dev_link(struct intf_data * data) { - char linkname[16]; - - snprintf(linkname,16,"%u",data->intf_num); - return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname); + char name[16]; + snprintf(name,16,"%d",data->intf_num); + return sysfs_create_link(&data->intf->subsys.kobj,&data->dev->kobj,name); } +/** + * intf_dev_unlink - remove symlink for interface. + * @intf: interface data descriptor. + * + */ + static void intf_dev_unlink(struct intf_data * data) { - char linkname[16]; - snprintf(linkname,16,"%u",data->intf_num); - sysfs_remove_link(&data->intf->kobj,linkname); + char name[16]; + snprintf(name,16,"%d",data->intf_num); + sysfs_remove_link(&data->intf->subsys.kobj,name); +} + + +/** + * interface_add_data - attach data descriptor + * @data: interface data descriptor. + * + * This attaches the per-instance interface object to the + * interface (by registering its kobject) and the device + * itself (by inserting it into the device's list). + * + * Note that there is no explicit protection done in this + * function. This should be called from the interface's + * add_device() method, which is called under the protection + * of the class's rwsem. + */ + +int interface_add_data(struct intf_data * data) +{ + struct device_interface * intf = intf_from_data(data); + + data->intf_num = data->intf->devnum++; + data->kobj.subsys = &intf->subsys; + kobject_register(&data->kobj); + + list_add_tail(&data->dev_entry,&data->dev->intf_list); + intf_dev_link(data); + return 0; +} + + +/** + * interface_remove_data - detach data descriptor. + * @data: interface data descriptor. + * + * This detaches the per-instance data descriptor by removing + * it from the device's list and unregistering the kobject from + * the subsystem. + */ + +void interface_remove_data(struct intf_data * data) +{ + intf_dev_unlink(data); + list_del_init(&data->dev_entry); + kobject_unregister(&data->kobj); } +/** + * add - attach device to interface + * @intf: interface. + * @dev: device. + * + * This is just a simple helper. Check the interface's interface + * helper and call it. This is called when adding an interface + * the class's devices, or a device to the class's interfaces. + */ + +static int add(struct device_interface * intf, struct device * dev) +{ + int error = 0; + + if (intf->add_device) + error = intf->add_device(dev); + pr_debug(" -> %s (%d)\n",dev->bus_id,error); + return error; +} + +/** + * del - detach device from interface. + * @data: interface data descriptor. + * + * Another simple helper. Remove the data descriptor from + * the device and the interface, then call the interface's + * remove_device() method. + */ + +static void del(struct intf_data * data) +{ + struct device_interface * intf = intf_from_data(data); + + pr_debug(" -> %s ",data->intf->name); + interface_remove_data(data); + if (intf->remove_device) + intf->remove_device(data); +} + +#define to_dev(entry) container_of(entry,struct device,class_list) + + +/** + * add_intf - add class's devices to interface. + * @intf: interface. + * + * Loop over the devices registered with the class, and call + * the interface's add_device() method for each. + * + * On an error, we won't break, but we will print debugging info. + */ +static void add_intf(struct device_interface * intf) +{ + struct device_class * cls = intf->devclass; + struct list_head * entry; + + down_write(&cls->subsys.rwsem); + list_for_each(entry,&cls->devices) + add(intf,to_dev(entry)); + up_write(&cls->subsys.rwsem); +} + +/** + * interface_register - register an interface with a device class. + * @intf: interface. + * + * An interface may be loaded after drivers and devices have been + * added to the class. So, we must add each device already known to + * the class to the interface as its registered. + */ + int interface_register(struct device_interface * intf) { struct device_class * cls = get_devclass(intf->devclass); - int error = 0; if (cls) { pr_debug("register interface '%s' with class '%s'\n", intf->name,cls->name); - strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN); - intf->kobj.subsys = &cls->subsys; - kobject_register(&intf->kobj); - } else - error = -EINVAL; - return error; + + strncpy(intf->subsys.kobj.name,intf->name,KOBJ_NAME_LEN); + intf->subsys.kobj.subsys = &cls->subsys; + subsystem_register(&intf->subsys); + add_intf(intf); + } + return 0; } + +/** + * del_intf - remove devices from interface. + * @intf: interface being unloaded. + * + * This loops over the devices registered with a class and + * calls the interface's remove_device() method for each. + * This is called when an interface is being unregistered. + */ + +static void del_intf(struct device_interface * intf) +{ + struct list_head * entry; + + down_write(&intf->devclass->subsys.rwsem); + list_for_each(entry,&intf->subsys.list) { + struct intf_data * data = to_data(entry); + del(data); + } + up_write(&intf->devclass->subsys.rwsem); +} + +/** + * interface_unregister - remove interface from class. + * @intf: interface. + * + * This is called when an interface in unloaded, giving it a + * chance to remove itself from devicse that have been added to + * it. + */ + void interface_unregister(struct device_interface * intf) { - pr_debug("unregistering interface '%s' from class '%s'\n", - intf->name,intf->devclass->name); - kobject_unregister(&intf->kobj); + struct device_class * cls = intf->devclass; + if (cls) { + pr_debug("unregistering interface '%s' from class '%s'\n", + intf->name,cls->name); + del_intf(intf); + subsystem_unregister(&intf->subsys); + put_devclass(cls); + } } -int interface_add(struct device_class * cls, struct device * dev) + +/** + * interface_add_dev - add device to interfaces. + * @dev: device. + * + * This is a helper for the class driver core. When a + * device is being added to a class, this is called to add + * the device to all the interfaces in the class. + * + * The operation is simple enough: loop over the interfaces + * and call add() [above] for each. The class rwsem is assumed + * to be held. + */ + +int interface_add_dev(struct device * dev) { + struct device_class * cls = dev->driver->devclass; struct list_head * node; - int error = 0; - pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name); + pr_debug("interfaces: adding device %s\n",dev->name); list_for_each(node,&cls->subsys.list) { struct device_interface * intf = to_intf(node); - if (intf->add_device) { - error = intf->add_device(dev); - if (error) - pr_debug("%s:%s: adding '%s' failed: %d\n", - cls->name,intf->name,dev->name,error); - } - + add(intf,dev); } return 0; } -void interface_remove(struct device_class * cls, struct device * dev) -{ - struct list_head * node; - struct list_head * next; - pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name); +/** + * interface_remove_dev - remove device from interfaces. + * @dev: device. + * + * This is another helper for the class driver core, and called + * when the device is being removed from the class. + * + * We iterate over the list of interface data descriptors attached + * to the device, and call del() [above] for each. Again, the + * class's rwsem is assumed to be held during this. + */ - list_for_each_safe(node,next,&dev->intf_list) { - struct intf_data * intf_data = container_of(node,struct intf_data,node); - list_del_init(&intf_data->node); +void interface_remove_dev(struct device * dev) +{ + struct list_head * entry, * next; - intf_dev_unlink(intf_data); - pr_debug("%s ",intf_data->intf->name); - if (intf_data->intf->remove_device) - intf_data->intf->remove_device(intf_data); - } - pr_debug("\n"); -} + pr_debug("interfaces: removing device %s\n",dev->name); -int interface_add_data(struct intf_data * data) -{ - down_write(&data->intf->devclass->subsys.rwsem); - list_add_tail(&data->node,&data->dev->intf_list); - data->intf_num = data->intf->devnum++; - intf_dev_link(data); - up_write(&data->intf->devclass->subsys.rwsem); - return 0; + list_for_each_safe(entry,next,&dev->intf_list) { + struct intf_data * intf_data = to_data(entry); + del(intf_data); + } } EXPORT_SYMBOL(interface_register); diff --git a/include/linux/device.h b/include/linux/device.h index c1918942c4cd..48612267bd65 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -168,6 +168,7 @@ struct device_class { struct subsystem devsubsys; struct subsystem drvsubsys; struct list_head drivers; + struct list_head devices; int (*add_device)(struct device *); void (*remove_device)(struct device *); @@ -216,9 +217,7 @@ struct device_interface { char * name; struct device_class * devclass; - struct kobject kobj; - struct list_head devices; - + struct subsystem subsys; u32 devnum; int (*add_device) (struct device *); @@ -239,10 +238,11 @@ extern void interface_unregister(struct device_interface *); * and create a driverfs symlink for it. */ struct intf_data { - struct list_head node; struct device_interface * intf; struct device * dev; u32 intf_num; + struct list_head dev_entry; + struct kobject kobj; }; extern int interface_add_data(struct intf_data *); @@ -252,6 +252,7 @@ extern int interface_add_data(struct intf_data *); struct device { struct list_head node; /* node in sibling list */ struct list_head bus_list; /* node in bus's list */ + struct list_head class_list; struct list_head driver_list; struct list_head children; struct list_head intf_list; -- cgit v1.2.3