summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Becker <joel.becker@oracle.com>2006-10-06 17:33:23 -0700
committerMark Fasheh <mark.fasheh@oracle.com>2007-07-10 17:11:01 -0700
commit299894cc9001b09e3e9685f2709b49e7e1092ccc (patch)
tree2a2e9bb69d2393bf620a34daea17e52ed9f9e5d7
parent6d748924b753d63a57dad130fdf11f64c27ff54b (diff)
downloadlinux-299894cc9001b09e3e9685f2709b49e7e1092ccc.tar.gz
linux-299894cc9001b09e3e9685f2709b49e7e1092ccc.tar.bz2
linux-299894cc9001b09e3e9685f2709b49e7e1092ccc.zip
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.txt12
-rw-r--r--fs/configfs/dir.c29
-rw-r--r--include/linux/configfs.h1
3 files changed, 41 insertions, 1 deletions
diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt
index 21f038e66724..aef74cdecc21 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 d3b1dbb9b5b8..125954723eb7 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 5ce0fc4e3b5b..8227e730dac7 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);
};