ChangeSet 1.1018.1.11, 2003/04/04 17:06:13-08:00, greg@kroah.com kobject: cause /sbin/hotplug to be called when kobjects are added and removed This only happens if a kobject belongs to a subsystem that has specified a set of hotplug operations. Based on work done by Kevin Fleming include/linux/kobject.h | 22 +++++- lib/kobject.c | 170 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 183 insertions(+), 9 deletions(-) diff -Nru a/include/linux/kobject.h b/include/linux/kobject.h --- a/include/linux/kobject.h Mon Apr 7 15:13:43 2003 +++ b/include/linux/kobject.h Mon Apr 7 15:13:43 2003 @@ -57,12 +57,24 @@ * of object; multiple ksets can belong to one subsystem. All * ksets of a subsystem share the subsystem's lock. * + * Each kset can support hotplugging; if it does, it will be given + * the opportunity to filter out specific kobjects from being + * reported, as well as to add its own "data" elements to the + * environment being passed to the hotplug helper. */ +struct kset_hotplug_ops { + int (*filter)(struct kset *kset, struct kobject *kobj); + char *(*name)(struct kset *kset, struct kobject *kobj); + int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp, + int num_envp, char *buffer, int buffer_size); +}; + struct kset { struct subsystem * subsys; struct kobj_type * ktype; struct list_head list; struct kobject kobj; + struct kset_hotplug_ops * hotplug_ops; }; @@ -86,6 +98,13 @@ kobject_put(&k->kobj); } +static inline struct kobj_type * get_ktype(struct kobject * k) +{ + if (k->kset && k->kset->ktype) + return k->kset->ktype; + else + return k->ktype; +} extern struct kobject * kset_find_obj(struct kset *, const char *); @@ -95,11 +114,12 @@ struct rw_semaphore rwsem; }; -#define decl_subsys(_name,_type) \ +#define decl_subsys(_name,_type,_hotplug_ops) \ struct subsystem _name##_subsys = { \ .kset = { \ .kobj = { .name = __stringify(_name) }, \ .ktype = _type, \ + .hotplug_ops =_hotplug_ops, \ } \ } diff -Nru a/lib/kobject.c b/lib/kobject.c --- a/lib/kobject.c Mon Apr 7 15:13:43 2003 +++ b/lib/kobject.c Mon Apr 7 15:13:43 2003 @@ -11,14 +11,6 @@ static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED; -static inline struct kobj_type * get_ktype(struct kobject * k) -{ - if (k->kset && k->kset->ktype) - return k->kset->ktype; - else - return k->ktype; -} - /** * populate_dir - populate directory with attributes. * @kobj: object we're working on. @@ -67,6 +59,140 @@ } +#ifdef CONFIG_HOTPLUG +static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) +{ + int length = 1; + struct kobject * parent = kobj; + + /* walk up the ancestors until we hit the one pointing to the + * root. + * Add 1 to strlen for leading '/' of each level. + */ + do { + length += strlen (parent->name) + 1; + parent = parent->parent; + } while (parent); + return length; +} + +static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length) +{ + struct kobject * parent; + + --length; + for (parent = kobj; parent; parent = parent->parent) { + int cur = strlen (parent->name); + /* back up enough to print this name with '/' */ + length -= cur; + strncpy (path + length, parent->name, cur); + *(path + --length) = '/'; + } + + pr_debug("%s: path = '%s'\n",__FUNCTION__,path); +} + +#define BUFFER_SIZE 1024 /* should be enough memory for the env */ +#define NUM_ENVP 32 /* number of env pointers */ +static void kset_hotplug(const char *action, struct kset *kset, + struct kobject *kobj) +{ + char *argv [3]; + char **envp; + char *buffer; + char *scratch; + int i = 0; + int retval; + int kobj_path_length; + char *kobj_path; + char *name = NULL; + + /* If the kset has a filter operation, call it. If it returns + failure, no hotplug event is required. */ + if (kset->hotplug_ops->filter) { + if (!kset->hotplug_ops->filter(kset, kobj)) + return; + } + + pr_debug ("%s\n", __FUNCTION__); + + if (!hotplug_path[0]) + return; + + envp = (char **)kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); + if (!envp) + return; + memset (envp, 0x00, NUM_ENVP * sizeof (char *)); + + buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); + if (!buffer) { + kfree(envp); + return; + } + + if (kset->hotplug_ops->name) + name = kset->hotplug_ops->name(kset, kobj); + if (name == NULL) + name = kset->kobj.name; + + argv [0] = hotplug_path; + argv [1] = name; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + scratch = buffer; + + envp [i++] = scratch; + scratch += sprintf(scratch, "ACTION=%s", action) + 1; + + kobj_path_length = get_kobj_path_length (kset, kobj); + kobj_path = kmalloc (kobj_path_length, GFP_KERNEL); + if (!kobj_path) { + kfree (buffer); + kfree (envp); + return; + } + memset (kobj_path, 0x00, kobj_path_length); + fill_kobj_path (kset, kobj, kobj_path, kobj_path_length); + + envp [i++] = scratch; + scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; + + if (kset->hotplug_ops->hotplug) { + /* have the kset specific function add its stuff */ + retval = kset->hotplug_ops->hotplug (kset, kobj, + &envp[i], NUM_ENVP - i, scratch, + BUFFER_SIZE - (scratch - buffer)); + if (retval) { + pr_debug ("%s - hotplug() returned %d\n", + __FUNCTION__, retval); + goto exit; + } + } + + pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], + envp[0], envp[1], envp[2], envp[3]); + retval = call_usermodehelper (argv[0], argv, envp, 0); + if (retval) + pr_debug ("%s - call_usermodehelper returned %d\n", + __FUNCTION__, retval); + +exit: + kfree (kobj_path); + kfree (buffer); + return; +} +#else +static void kset_hotplug(const char *action, struct kset *kset, + struct kobject *kobj) +{ + return 0; +} +#endif /* CONFIG_HOTPLUG */ + /** * kobject_init - initialize object. * @kobj: object in question. @@ -111,6 +237,7 @@ { int error = 0; struct kobject * parent; + struct kobject * top_kobj; if (!(kobj = kobject_get(kobj))) return -ENOENT; @@ -134,6 +261,19 @@ error = create_dir(kobj); if (error) unlink(kobj); + else { + /* If this kobj does not belong to a kset, + try to find a parent that does. */ + top_kobj = kobj; + if (!top_kobj->kset && top_kobj->parent) { + do { + top_kobj = top_kobj->parent; + } while (!top_kobj->kset && top_kobj->parent); + } + + if (top_kobj->kset && top_kobj->kset->hotplug_ops) + kset_hotplug("add", top_kobj->kset, kobj); + } return error; } @@ -162,6 +302,20 @@ void kobject_del(struct kobject * kobj) { + struct kobject * top_kobj; + + /* If this kobj does not belong to a kset, + try to find a parent that does. */ + top_kobj = kobj; + if (!top_kobj->kset && top_kobj->parent) { + do { + top_kobj = top_kobj->parent; + } while (!top_kobj->kset && top_kobj->parent); + } + + if (top_kobj->kset && top_kobj->kset->hotplug_ops) + kset_hotplug("remove", top_kobj->kset, kobj); + sysfs_remove_dir(kobj); unlink(kobj); }