diff options
author | Joel Becker <joel.becker@oracle.com> | 2006-10-06 17:33:23 -0700 |
---|---|---|
committer | Mark Fasheh <mark.fasheh@oracle.com> | 2007-07-10 17:11:01 -0700 |
commit | 299894cc9001b09e3e9685f2709b49e7e1092ccc (patch) | |
tree | 2a2e9bb69d2393bf620a34daea17e52ed9f9e5d7 | |
parent | 6d748924b753d63a57dad130fdf11f64c27ff54b (diff) | |
download | kernel_samsung_crespo-299894cc9001b09e3e9685f2709b49e7e1092ccc.zip kernel_samsung_crespo-299894cc9001b09e3e9685f2709b49e7e1092ccc.tar.gz kernel_samsung_crespo-299894cc9001b09e3e9685f2709b49e7e1092ccc.tar.bz2 |
configfs: accessing item hierarchy during rmdir(2)
Add a notification callback, ops->disconnect_notify(). It has the same
prototype as ->drop_item(), but it will be called just before the item
linkage is broken. This way, configfs users who want to do work while
the object is still in the heirarchy have a chance.
Client drivers will still need to config_item_put() in their
->drop_item(), if they implement it. They need do nothing in
->disconnect_notify(). They don't have to provide it if they don't
care. But someone who wants to be notified before ci_parent is set to
NULL can now be notified.
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
-rw-r--r-- | Documentation/filesystems/configfs/configfs.txt | 12 | ||||
-rw-r--r-- | fs/configfs/dir.c | 29 | ||||
-rw-r--r-- | include/linux/configfs.h | 1 |
3 files changed, 41 insertions, 1 deletions
diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 21f038e..aef74cd 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -238,6 +238,8 @@ config_item_type. struct config_group *(*make_group)(struct config_group *group, const char *name); int (*commit_item)(struct config_item *item); + void (*disconnect_notify)(struct config_group *group, + struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); }; @@ -268,6 +270,16 @@ the item in other threads, the memory is safe. It may take some time for the item to actually disappear from the subsystem's usage. But it is gone from configfs. +When drop_item() is called, the item's linkage has already been torn +down. It no longer has a reference on its parent and has no place in +the item hierarchy. If a client needs to do some cleanup before this +teardown happens, the subsystem can implement the +ct_group_ops->disconnect_notify() method. The method is called after +configfs has removed the item from the filesystem view but before the +item is removed from its parent group. Like drop_item(), +disconnect_notify() is void and cannot fail. Client subsystems should +not drop any references here, as they still must do it in drop_item(). + A config_group cannot be removed while it still has child items. This is implemented in the configfs rmdir(2) code. ->drop_item() will not be called, as the item has not been dropped. rmdir(2) will fail, as the diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index d3b1dbb..1259547 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -714,6 +714,28 @@ static void configfs_detach_group(struct config_item *item) } /* + * After the item has been detached from the filesystem view, we are + * ready to tear it out of the hierarchy. Notify the client before + * we do that so they can perform any cleanup that requires + * navigating the hierarchy. A client does not need to provide this + * callback. The subsystem semaphore MUST be held by the caller, and + * references must be valid for both items. It also assumes the + * caller has validated ci_type. + */ +static void client_disconnect_notify(struct config_item *parent_item, + struct config_item *item) +{ + struct config_item_type *type; + + type = parent_item->ci_type; + BUG_ON(!type); + + if (type->ct_group_ops && type->ct_group_ops->disconnect_notify) + type->ct_group_ops->disconnect_notify(to_config_group(parent_item), + item); +} + +/* * Drop the initial reference from make_item()/make_group() * This function assumes that reference is held on item * and that item holds a valid reference to the parent. Also, it @@ -733,7 +755,7 @@ static void client_drop_item(struct config_item *parent_item, */ if (type->ct_group_ops && type->ct_group_ops->drop_item) type->ct_group_ops->drop_item(to_config_group(parent_item), - item); + item); else config_item_put(item); } @@ -842,11 +864,14 @@ out_unlink: if (ret) { /* Tear down everything we built up */ mutex_lock(&subsys->su_mutex); + + client_disconnect_notify(parent_item, item); if (group) unlink_group(group); else unlink_obj(item); client_drop_item(parent_item, item); + mutex_unlock(&subsys->su_mutex); if (module_got) @@ -911,11 +936,13 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) configfs_detach_group(item); mutex_lock(&subsys->su_mutex); + client_disconnect_notify(parent_item, item); unlink_group(to_config_group(item)); } else { configfs_detach_item(item); mutex_lock(&subsys->su_mutex); + client_disconnect_notify(parent_item, item); unlink_obj(item); } diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 5ce0fc4..8227e73 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -169,6 +169,7 @@ struct configfs_group_operations { struct config_item *(*make_item)(struct config_group *group, const char *name); struct config_group *(*make_group)(struct config_group *group, const char *name); int (*commit_item)(struct config_item *item); + void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); }; |