Răsfoiți Sursa

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>
Joel Becker 18 ani în urmă
părinte
comite
299894cc90
3 a modificat fișierele cu 41 adăugiri și 1 ștergeri
  1. 12 0
      Documentation/filesystems/configfs/configfs.txt
  2. 28 1
      fs/configfs/dir.c
  3. 1 0
      include/linux/configfs.h

+ 12 - 0
Documentation/filesystems/configfs/configfs.txt

@@ -238,6 +238,8 @@ config_item_type.
 		struct config_group *(*make_group)(struct config_group *group,
 		struct config_group *(*make_group)(struct config_group *group,
 						   const char *name);
 						   const char *name);
 		int (*commit_item)(struct config_item *item);
 		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,
 		void (*drop_item)(struct config_group *group,
 				  struct config_item *item);
 				  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
 for the item to actually disappear from the subsystem's usage.  But it
 is gone from configfs.
 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
 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
 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
 called, as the item has not been dropped.  rmdir(2) will fail, as the

+ 28 - 1
fs/configfs/dir.c

@@ -713,6 +713,28 @@ static void configfs_detach_group(struct config_item *item)
 	configfs_detach_item(item);
 	configfs_detach_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()
  * Drop the initial reference from make_item()/make_group()
  * This function assumes that reference is held on item
  * This function assumes that reference is held on item
@@ -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)
 	if (type->ct_group_ops && type->ct_group_ops->drop_item)
 		type->ct_group_ops->drop_item(to_config_group(parent_item),
 		type->ct_group_ops->drop_item(to_config_group(parent_item),
-						item);
+					      item);
 	else
 	else
 		config_item_put(item);
 		config_item_put(item);
 }
 }
@@ -842,11 +864,14 @@ out_unlink:
 	if (ret) {
 	if (ret) {
 		/* Tear down everything we built up */
 		/* Tear down everything we built up */
 		mutex_lock(&subsys->su_mutex);
 		mutex_lock(&subsys->su_mutex);
+
+		client_disconnect_notify(parent_item, item);
 		if (group)
 		if (group)
 			unlink_group(group);
 			unlink_group(group);
 		else
 		else
 			unlink_obj(item);
 			unlink_obj(item);
 		client_drop_item(parent_item, item);
 		client_drop_item(parent_item, item);
+
 		mutex_unlock(&subsys->su_mutex);
 		mutex_unlock(&subsys->su_mutex);
 
 
 		if (module_got)
 		if (module_got)
@@ -911,11 +936,13 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
 		configfs_detach_group(item);
 		configfs_detach_group(item);
 
 
 		mutex_lock(&subsys->su_mutex);
 		mutex_lock(&subsys->su_mutex);
+		client_disconnect_notify(parent_item, item);
 		unlink_group(to_config_group(item));
 		unlink_group(to_config_group(item));
 	} else {
 	} else {
 		configfs_detach_item(item);
 		configfs_detach_item(item);
 
 
 		mutex_lock(&subsys->su_mutex);
 		mutex_lock(&subsys->su_mutex);
+		client_disconnect_notify(parent_item, item);
 		unlink_obj(item);
 		unlink_obj(item);
 	}
 	}
 
 

+ 1 - 0
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_item *(*make_item)(struct config_group *group, const char *name);
 	struct config_group *(*make_group)(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);
 	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);
 	void (*drop_item)(struct config_group *group, struct config_item *item);
 };
 };