Skip to contentSkip to conversation

sysfs: allow suicide

PATCH In review Tejun Heo 6 participants 31 messages Sep 20, 2007 – Sep 28, 2007

Subsystems (5)
SCSI s390 Driver core Module support +1 more subsystems
lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 2/4 sysfs: allow suicide

Hello, all.

This is the second patchset of four sysfs update patchset series[1] and to be applied on top of the first patchset[1].

Currently, sysfs files which want to kill themselves should ask someone else (workqueue) to kill it, which is so inhumane. This patchset updates sysfs file implementation such that sysfs files can commit suicide peacefully.

Global sledgehammer module unload inhibition/delay mechanism is implemented and used to prevent premature unload while suicide is in progress. Suicide attempt is detected by scanning sysfs_buffers for matching accessor. If suicide is detected, active references the accessor were holding are dropped early such that the suiciding node can be deactivated without deadlock.

As active references go away early, the module basing the code the accessor is running can go away before it finishes. Global module unload inhibition is used here to prevent that until accessor callback is complete.

This patchset contains the following four patches.

0001-module-implement-module_inhibit_unload.patch 0002-sysfs-make-the-sysfs_addrm_cxt-removed-list-FIFO.patch 0003-sysfs-care-free-suicide-for-sysfs-files.patch 0004-sysfs-make-suicidal-nodes-just-do-it-directly.patch

0001 needs Rusty Russell's ack.

Thanks.

--
tejun

[1] http://thread.gmane.org/gmane.linux.kernel/582105

author
#23 lore raw

PATCH 2/4 sysfs: make the sysfs_addrm_cxt->removed list FIFO

Add sysfs_addrm_cxt->removed_tail to make the ->removed list FIFO. This will be used to implement care-free suicide.

---
 fs/sysfs/dir.c   |    7 +++++--
 fs/sysfs/sysfs.h |    2 +-
 2 files changed, 6 insertions(+), 3 deletions(-)
fs/sysfs/dir.c +5 -2
index ad3394a..c902bdc 100644
@@ -361,6 +361,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
 
 	memset(acxt, 0, sizeof(*acxt));
 	acxt->parent_sd = parent_sd;
+	acxt->removed_tail = &acxt->removed;
 
 	/* Lookup parent inode.  inode initialization and I_NEW
 	 * clearing are protected by sysfs_mutex.  By grabbing it and
@@ -448,8 +449,10 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 	sysfs_unlink_sibling(sd);
 
 	sd->s_flags |= SYSFS_FLAG_REMOVED;
-	sd->s_sibling = acxt->removed;
-	acxt->removed = sd;
+	*acxt->removed_tail = sd;
+	/* keep them in FIFO order, suicide check depends on it */
+	acxt->removed_tail = &sd->s_sibling;
+	*acxt->removed_tail = NULL;
 
 	if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
 		drop_nlink(acxt->parent_inode);
fs/sysfs/sysfs.h +1 -1
index 269c845..be16947 100644
@@ -71,7 +71,7 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
 struct sysfs_addrm_cxt {
 	struct sysfs_dirent	*parent_sd;
 	struct inode		*parent_inode;
-	struct sysfs_dirent	*removed;
+	struct sysfs_dirent	*removed, **removed_tail;
 	int			cnt;
 };
 
Signature · 1 line
1.5.0.3
author
#24 lore raw

PATCH 3/4 sysfs: care-free suicide for sysfs files

In reply to Tejun Heo

Life can be weary and some sysfs files choose to commit suicide (kills itself when written to). This is troublesome because while a sysfs file is being accessed, the accessing task holds active references to the node and its parent. Removing a sysfs node waits for active references to be drained. The suicidal thread ends up waiting for its own active reference and thus is sadly forced to live till the end of the time.

Till now, this has been dealt with by requiring suicidal node to ask someone else (workqueue) to kill it. In recognition of the inhumanitrian nature of this solution, this patch implements care-free suicide support.

Suicide attempt is automagically detected and the active references the suiciding task is holding are put early to avoid the above described deadlock. Module unload is inhibited till the sysfs file access is complete to avoid freeing the code region too early.

This patch only implements care-free suicide support. The next patch will convert the users.

---
 fs/sysfs/dir.c   |    2 +
 fs/sysfs/file.c  |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/sysfs/sysfs.h |    1 +
 3 files changed, 68 insertions(+), 2 deletions(-)
fs/sysfs/dir.c +2 -0
index c902bdc..fe8270c 100644
@@ -546,6 +546,8 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
 		sd->s_sibling = NULL;
 
 		sysfs_drop_dentry(sd);
+		if (sysfs_type(sd) == SYSFS_KOBJ_ATTR)
+			sysfs_file_check_suicide(sd);
 		sysfs_deactivate(sd);
 		sysfs_put(sd);
 	}
fs/sysfs/file.c +65 -2

92 lines not on this page · raw message

fs/sysfs/sysfs.h +1 -0
index be16947..58f517b 100644
@@ -142,6 +142,7 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
  */
 extern const struct file_operations sysfs_file_operations;
 
+void sysfs_file_check_suicide(struct sysfs_dirent *sd);
 int sysfs_add_file(struct sysfs_dirent *dir_sd,
 		   const struct attribute *attr, int type);
 
Signature · 1 line
1.5.0.3
author
#1 lore raw

PATCH 1/4 module: implement module_inhibit_unload()

In reply to Tejun Heo

As its name suggests, module_inhibit_unload() inhibits all module unloading till the matching module_allow_unload() is called. This unload inhibition doesn't affect whether a module can be unloaded or not. It just stalls the final module free till the inhibition is lifted.

This sledgehammer mechanism is to be used briefly in obscure cases where identifying or getting the module to prevent from unloading is difficult or not worth the effort. Note that module unloading is siberia-cold path. If the inhibion is relatively brief in human scale, that is, upto a few secs at maximum, it should be fine. So, if this sledgehammer simplifies API and fixes premature unload bugs which unfortunately aren't too rare, there isn't much reason not to use it.

Even if something goes wrong with unload inhibition (e.g. someone forgets to lift the inhibition), it doesn't prevent modules from being loaded.

Cc Rusty Russell <[email protected]>
---
 include/linux/module.h |   17 +++++++++++++
 kernel/module.c        |   59 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 72 insertions(+), 4 deletions(-)
include/linux/module.h +17 -0
index b6a646c..a835659 100644
@@ -418,6 +418,9 @@ static inline int try_module_get(struct module *module)
 
 extern void module_put(struct module *module);
 
+void module_inhibit_unload(void);
+void module_allow_unload(void);
+
 #else /*!CONFIG_MODULE_UNLOAD*/
 static inline int try_module_get(struct module *module)
 {
@@ -431,6 +434,12 @@ static inline void __module_get(struct module *module)
 }
 #define symbol_put(x) do { } while(0)
 #define symbol_put_addr(p) do { } while(0)
+static inline void module_inhibit_unload(void)
+{
+}
+static inline void module_allow_unload(void)
+{
+}
 
 #endif /* CONFIG_MODULE_UNLOAD */
 
@@ -516,6 +525,14 @@ static inline void module_put(struct module *module)
 {
 }
 
+static inline void module_inhibit_unload(void)
+{
+}
+
+static inline void module_allow_unload(void)
+{
+}
+
 #define module_name(mod) "kernel"
 
 #define __unsafe(mod)
kernel/module.c +55 -4

91 lines not on this page · raw message

Signature · 1 line
1.5.0.3
author
#25 lore raw

PATCH 4/4 sysfs: make suicidal nodes just do it directly

In reply to Tejun Heo

Sysfs now allows direct suicide. Make suicidial sysfs nodes to use it and remove now unncessary callback mechanism.

---
 drivers/base/core.c         |   33 ----------------------
 drivers/s390/cio/ccwgroup.c |   25 +++-------------
 drivers/scsi/scsi_sysfs.c   |   13 +--------
 fs/sysfs/file.c             |   63 -------------------------------------------
 include/linux/device.h      |    6 ----
 include/linux/sysfs.h       |    9 ------
 6 files changed, 6 insertions(+), 143 deletions(-)
drivers/base/core.c +0 -33
index d290ceb..a4942f1 100644
@@ -464,39 +464,6 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
 }
 EXPORT_SYMBOL_GPL(device_remove_bin_file);
 
-/**
- * device_schedule_callback_owner - helper to schedule a callback for a device
- * @dev: device.
- * @func: callback function to invoke later.
- * @owner: module owning the callback routine
- *
- * Attribute methods must not unregister themselves or their parent device
- * (which would amount to the same thing).  Attempts to do so will deadlock,
- * since unregistration is mutually exclusive with driver callbacks.
- *
- * Instead methods can call this routine, which will attempt to allocate
- * and schedule a workqueue request to call back @func with @dev as its
- * argument in the workqueue's process context.  @dev will be pinned until
- * @func returns.
- *
- * This routine is usually called via the inline device_schedule_callback(),
- * which automatically sets @owner to THIS_MODULE.
- *
- * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated, -ENODEV if a reference to @owner isn't available.
- *
- * NOTE: This routine won't work if CONFIG_SYSFS isn't set!  It uses an
- * underlying sysfs routine (since it is intended for use by attribute
- * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
- */
-int device_schedule_callback_owner(struct device *dev,
-		void (*func)(struct device *), struct module *owner)
-{
-	return sysfs_schedule_callback(&dev->kobj,
-			(void (*)(void *)) func, dev, owner);
-}
-EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
-
 static void klist_children_get(struct klist_node *n)
 {
 	struct device *dev = container_of(n, struct device, knode_parent);
drivers/s390/cio/ccwgroup.c +5 -20
index 5d967c4..7085305 100644
@@ -66,20 +66,6 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
 	
 }
 
-/*
- * Provide an 'ungroup' attribute so the user can remove group devices no
- * longer needed or accidentially created. Saves memory :)
- */
-static void ccwgroup_ungroup_callback(struct device *dev)
-{
-	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
-
-	mutex_lock(&gdev->reg_mutex);
-	__ccwgroup_remove_symlinks(gdev);
-	device_unregister(dev);
-	mutex_unlock(&gdev->reg_mutex);
-}
-
 static ssize_t
 ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
@@ -91,12 +77,11 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
 	if (gdev->state != CCWGROUP_OFFLINE)
 		return -EINVAL;
 
-	/* Note that we cannot unregister the device from one of its
-	 * attribute methods, so we have to use this roundabout approach.
-	 */
-	rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
-	if (rc)
-		count = rc;
+	mutex_lock(&gdev->reg_mutex);
+	__ccwgroup_remove_symlinks(gdev);
+	device_unregister(dev);
+	mutex_unlock(&gdev->reg_mutex);
+
 	return count;
 }
 
drivers/scsi/scsi_sysfs.c +1 -12
index ede9986..fdac321 100644
@@ -466,22 +466,11 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha
 }
 static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
 
-static void sdev_store_delete_callback(struct device *dev)
-{
-	scsi_remove_device(to_scsi_device(dev));
-}
-
 static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf,
 				 size_t count)
 {
-	int rc;
+	scsi_remove_device(to_scsi_device(dev));
 
-	/* An attribute cannot be unregistered by one of its own methods,
-	 * so we have to use this roundabout approach.
-	 */
-	rc = device_schedule_callback(dev, sdev_store_delete_callback);
-	if (rc)
-		count = rc;
 	return count;
 };
 static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
fs/sysfs/file.c +0 -63

68 lines not on this page · raw message

include/linux/device.h +0 -6
index 2e15822..61fc85d 100644
@@ -356,12 +356,6 @@ extern int __must_check device_create_bin_file(struct device *dev,
 					       struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
 				   struct bin_attribute *attr);
-extern int device_schedule_callback_owner(struct device *dev,
-		void (*func)(struct device *), struct module *owner);
-
-/* This is a macro to avoid include problems with THIS_MODULE */
-#define device_schedule_callback(dev, func)			\
-	device_schedule_callback_owner(dev, func, THIS_MODULE)
 
 /* device resource management */
 typedef void (*dr_release_t)(struct device *dev, void *res);
include/linux/sysfs.h +0 -9
index db5dd24..8af072e 100644
@@ -76,9 +76,6 @@ struct sysfs_ops {
 
 #ifdef CONFIG_SYSFS
 
-int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
-			    void *data, struct module *owner);
-
 int __must_check sysfs_create_dir(struct kobject *kobj);
 void sysfs_remove_dir(struct kobject *kobj);
 int __must_check sysfs_rename_dir(struct kobject *kobj, const char *new_name);
@@ -114,12 +111,6 @@ extern int __must_check sysfs_init(void);
 
 #else /* CONFIG_SYSFS */
 
-static inline int sysfs_schedule_callback(struct kobject *kobj,
-		void (*func)(void *), void *data, struct module *owner)
-{
-	return -ENOSYS;
-}
-
 static inline int sysfs_create_dir(struct kobject *kobj)
 {
 	return 0;
Signature · 1 line
1.5.0.3
#26 lore raw

Re: [PATCH 4/4] sysfs: make suicidal nodes just do it directly

Quoted 4 lines · Tejun Heo

Sysfs now allows direct suicide. Make suicidial sysfs nodes to use it and remove now unncessary callback mechanism.

Quoted 9 lines
---
 drivers/base/core.c         |   33 ----------------------
 drivers/s390/cio/ccwgroup.c |   25 +++-------------
 drivers/scsi/scsi_sysfs.c   |   13 +--------
 fs/sysfs/file.c             |   63 -------------------------------------------
 include/linux/device.h      |    6 ----
 include/linux/sysfs.h       |    9 ------
 6 files changed, 6 insertions(+), 143 deletions(-)

Just browsing through this (I'll review and do some tests later), but you missed drivers/s390/cio/device.c::ccw_device_remove_disconnected().

Following (not event compile tested) patch (which is basically a revert of 59a8a6e227cf0bc42e5be741ebfea97c222ab9ef) should take care of it.

---
 drivers/s390/cio/device.c |   51 +++++++++-------------------------------------
 1 files changed, 10 insertions(+), 41 deletions(-)
drivers/s390/cio/device.c +10 -41
@@ -288,62 +288,31 @@ static void ccw_device_unregister(struct
 		device_del(&cdev->dev);
 }
 
-static void ccw_device_remove_orphan_cb(struct device *dev)
-{
-	struct ccw_device *cdev = to_ccwdev(dev);
-
-	ccw_device_unregister(cdev);
-	put_device(&cdev->dev);
-}
-
-static void ccw_device_remove_sch_cb(struct device *dev)
-{
-	struct subchannel *sch;
-
-	sch = to_subchannel(dev);
-	css_sch_device_unregister(sch);
-	/* Reset intparm to zeroes. */
-	sch->schib.pmcw.intparm = 0;
-	cio_modify(sch);
-	put_device(&sch->dev);
-}
-
 static void
 ccw_device_remove_disconnected(struct ccw_device *cdev)
 {
+	struct subchannel *sch;
 	unsigned long flags;
-	int rc;
 
 	/*
 	 * Forced offline in disconnected state means
 	 * 'throw away device'.
 	 */
 	if (ccw_device_is_orphan(cdev)) {
-		/*
-		 * Deregister ccw device.
-		 * Unfortunately, we cannot do this directly from the
-		 * attribute method.
-		 */
+		/* Deregister ccw device. */
 		spin_lock_irqsave(cdev->ccwlock, flags);
 		cdev->private->state = DEV_STATE_NOT_OPER;
 		spin_unlock_irqrestore(cdev->ccwlock, flags);
-		rc = device_schedule_callback(&cdev->dev,
-					      ccw_device_remove_orphan_cb);
-		if (rc)
-			CIO_MSG_EVENT(2, "Couldn't unregister orphan "
-				      "0.%x.%04x\n",
-				      cdev->private->dev_id.ssid,
-				      cdev->private->dev_id.devno);
+		ccw_device_unregister(cdev);
+		put_device(&cdev->dev);
 		return;
 	}
-	/* Deregister subchannel, which will kill the ccw device. */
-	rc = device_schedule_callback(cdev->dev.parent,
-				      ccw_device_remove_sch_cb);
-	if (rc)
-		CIO_MSG_EVENT(2, "Couldn't unregister disconnected device "
-			      "0.%x.%04x\n",
-			      cdev->private->dev_id.ssid,
-			      cdev->private->dev_id.devno);
+	sch = to_subchannel(cdev->dev.parent);
+	css_sch_device_unregister(sch);
+	/* Reset intparm to zeroes. */
+	sch->schib.pmcw.intparm = 0;
+	cio_modify(sch);
+	put_device(&sch->dev);
 }
 
 /**
author
#27 lore raw

Re: [PATCH 4/4] sysfs: make suicidal nodes just do it directly

Quoted 17 lines · Cornelia Huck
Quoted 4 lines · Tejun Heo

Sysfs now allows direct suicide. Make suicidial sysfs nodes to use it and remove now unncessary callback mechanism.

Quoted 9 lines
---
 drivers/base/core.c         |   33 ----------------------
 drivers/s390/cio/ccwgroup.c |   25 +++-------------
 drivers/scsi/scsi_sysfs.c   |   13 +--------
 fs/sysfs/file.c             |   63 -------------------------------------------
 include/linux/device.h      |    6 ----
 include/linux/sysfs.h       |    9 ------
 6 files changed, 6 insertions(+), 143 deletions(-)
Quoted 7 lines

Just browsing through this (I'll review and do some tests later), but you missed drivers/s390/cio/device.c::ccw_device_remove_disconnected().

Following (not event compile tested) patch (which is basically a revert of 59a8a6e227cf0bc42e5be741ebfea97c222ab9ef) should take care of it.

Thanks for spotting this. After converting ccwgroup, I thought that was it for s390.

Signature · 1 line
tejun
#2 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

In reply to Tejun Heo

Hi, Tejun,

I was just looking over these changes...

kernel/module.c sys_delete_module(const char __user *name_user, unsigned int flags) · line 735

Quoted 9 lines
kernel/module.c +9 -0
+	/* Don't proceed till inhibition is lifted. */
+	add_wait_queue(&module_unload_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	if (atomic_read(&module_unload_inhibit_cnt))
+		schedule();
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&module_unload_wait, &wait);
+
+	mutex_lock(&module_mutex);

Maybe I'm missing something, but this looks racy to me. There's no check after schedule() to see if module_unload_inhibit_cnt is really zero, and nothing to keep somebody else from slipping in and raising it again afterward.

Given your description of this tool as a "sledgehammer," might it not be easier to just take and hold module_mutex for the duration of the unload block?

jon

author
#3 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 4 lines · Jonathan Corbet

Hi, Tejun,

I was just looking over these changes...

Quoted 14 lines

kernel/module.c sys_delete_module(const char __user *name_user, unsigned int flags) · line 735

Quoted 9 lines
kernel/module.c +9 -0
+	/* Don't proceed till inhibition is lifted. */
+	add_wait_queue(&module_unload_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	if (atomic_read(&module_unload_inhibit_cnt))
+		schedule();
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&module_unload_wait, &wait);
+
+	mutex_lock(&module_mutex);

Maybe I'm missing something, but this looks racy to me. There's no check after schedule() to see if module_unload_inhibit_cnt is really zero, and nothing to keep somebody else from slipping in and raising it again afterward.

The unloading can proceed once module_unload_inhibit_cnt reaches zero. An unloading thread only has to care about inhibition put in effect before unloading has started, so there's no need to check again.

Quoted 3 lines

Given your description of this tool as a "sledgehammer," might it not be easier to just take and hold module_mutex for the duration of the unload block?

That would be easier but...

* It would serialize users of the sledgehammer.
* It would block loading modules (which is often more important than unloading them) when things go south.

Signature · 1 line
tejun
#4 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 9 lines · Tejun Heo
Quoted 3 lines

Given your description of this tool as a "sledgehammer," might it not be easier to just take and hold module_mutex for the duration of the unload block?

That would be easier but...

* It would serialize users of the sledgehammer.
* It would block loading modules (which is often more important than unloading them) when things go south.

My concern is that you're dropping the module mutex around ->exit now. I don't *think* this should matter, but it's worth considering.

I really wonder if an explicit "kill_this_attribute()" is a better way to go than this...

Rusty.

author
#5 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 4 lines · Rusty Russell
Quoted 3 lines · Tejun Heo
Quoted 3 lines

Given your description of this tool as a "sledgehammer," might it not be easier to just take and hold module_mutex for the duration of the unload block?

Quoted 8 lines
Quoted 5 lines

That would be easier but...

* It would serialize users of the sledgehammer.
* It would block loading modules (which is often more important than unloading them) when things go south.

My concern is that you're dropping the module mutex around ->exit now. I don't *think* this should matter, but it's worth considering.

We always did that. Before the patch the code segment looked like the following.

	/* Final destruction now noone is using it. */
	if (mod->exit != NULL) {
		mutex_unlock(&module_mutex);
		mod->exit();
		mutex_lock(&module_mutex);
	}	
Quoted 2 lines

I really wonder if an explicit "kill_this_attribute()" is a better way to go than this...

I think this sort of temporary unload blocking would be useful for other cases like this.

Thanks.

Signature · 1 line
tejun
#6 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 6 lines · Tejun Heo
Quoted 2 lines · Rusty Russell

My concern is that you're dropping the module mutex around ->exit now. I don't *think* this should matter, but it's worth considering.

We always did that. Before the patch the code segment looked like the following.

Hi Tejun,

Thanks, misread patch.

Quoted 5 lines
Quoted 2 lines

I really wonder if an explicit "kill_this_attribute()" is a better way to go than this...

I think this sort of temporary unload blocking would be useful for other cases like this.

I hope not: this doesn't work in general. Calling into a module after ->exit has called assumes that the exit function doesn't free up or overwrite stuff the other functions need.

Cheers,
Rusty.

author
#7 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 8 lines · Rusty Russell
Quoted 4 lines
Quoted 2 lines

I really wonder if an explicit "kill_this_attribute()" is a better way to go than this...

I think this sort of temporary unload blocking would be useful for other cases like this.

I hope not: this doesn't work in general. Calling into a module after ->exit has called assumes that the exit function doesn't free up or overwrite stuff the other functions need.

Right, the sole purpose the unload inhibition is to hold onto the 'code' section from going away. The rest of object lifetime management should be implemented using separate mechanisms anyway. I was talking about similar cases where the 'code' should be protected for a short time.

Thanks.

Signature · 1 line
tejun
#8 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 6 lines · Tejun Heo
Quoted 5 lines · Rusty Russell
Quoted 4 lines
Quoted 2 lines

I really wonder if an explicit "kill_this_attribute()" is a better way to go than this...

I think this sort of temporary unload blocking would be useful for other cases like this.

Quoted 8 lines
Quoted 3 lines

I hope not: this doesn't work in general. Calling into a module after ->exit has called assumes that the exit function doesn't free up or overwrite stuff the other functions need.

Right, the sole purpose the unload inhibition is to hold onto the 'code' section from going away. The rest of object lifetime management should be implemented using separate mechanisms anyway. I was talking about similar cases where the 'code' should be protected for a short time.

As stated you cannot protect arbitrary code this way, as you are trying to do. I do not think you've broken any of the current code, but I cannot tell. You're certainly going to surprise unsuspecting future authors.

Can you really not figure out the module owner of the sysfs entry to inc its use count during this procedure? (__module_get()).

Rusty.

author
#9 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 4 lines · Rusty Russell

As stated you cannot protect arbitrary code this way, as you are trying to do. I do not think you've broken any of the current code, but I cannot tell. You're certainly going to surprise unsuspecting future authors.

Can you elaborate a bit? Why can't it protect the code?

Quoted 2 lines

Can you really not figure out the module owner of the sysfs entry to inc its use count during this procedure? (__module_get()).

I can but I don't think it's worth the effort. It will involve passing @owner parameter down through kobject to sysfs but the path is pretty obscure and thus difficult to test. I think it's too much work for the users of the API and it will be easy to pass the wrong @owner and go unnoticed.

Thanks.

Signature · 1 line
tejun
#10 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 7 lines · Tejun Heo
Quoted 4 lines · Rusty Russell

As stated you cannot protect arbitrary code this way, as you are trying to do. I do not think you've broken any of the current code, but I cannot tell. You're certainly going to surprise unsuspecting future authors.

Can you elaborate a bit? Why can't it protect the code?

Because you don't know what that code does. After all, it's assumed that module code doesn't get called after exit and you're deliberately violating that assumption.

Quoted 6 lines
Quoted 2 lines

Can you really not figure out the module owner of the sysfs entry to inc its use count during this procedure? (__module_get()).

I can but I don't think it's worth the effort. It will involve passing @owner parameter down through kobject to sysfs but the path is pretty obscure and thus difficult to test.

Have you tested that *this* path works? Let's take your first change as an example:

+       mutex_lock(&gdev->reg_mutex);
+       __ccwgroup_remove_symlinks(gdev);
+       device_unregister(dev);
+       mutex_unlock(&gdev->reg_mutex);

Now, are you sure that calling cleanup_ccwgroup just after device_unregister() works?

static void __exit
cleanup_ccwgroup (void)

{
	bus_unregister (&ccwgroup_bus_type);
}
Quoted 3 lines

I think it's too much work for the
users of the API and it will be easy to pass the wrong @owner and go unnoticed.

But your shortcut insists that all module authors be aware that functions can be running after exit() is called. That's a recipe for instability and disaster.

Rusty.

#11 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 8 lines · Rusty Russell

Have you tested that *this* path works? Let's take your first change as an example:

(quoted diff) +4 -0
+       mutex_lock(&gdev->reg_mutex);
+       __ccwgroup_remove_symlinks(gdev);
+       device_unregister(dev);
+       mutex_unlock(&gdev->reg_mutex);
Quoted 9 lines

Now, are you sure that calling cleanup_ccwgroup just after device_unregister() works?

static void __exit
cleanup_ccwgroup (void)

{
	bus_unregister (&ccwgroup_bus_type);
}

ccwgroup is a bit special. The ccwgroup drivers (say, qeth) will unregister their ccwgroup_driver in their exit function. ccwgroup will then unregister all devices bound to this driver (a ccwgroup device without a driver does not make sense, since they are artifically created by writing to a 'group' attribute provided by the driver). This makes sure that ccwgroup cannot be unloaded while there are still devices on the bus, so your example won't hit.

Quoted 7 lines
Quoted 3 lines

I think it's too much work for the
users of the API and it will be easy to pass the wrong @owner and go unnoticed.

But your shortcut insists that all module authors be aware that functions can be running after exit() is called. That's a recipe for instability and disaster.

There are already problems like this with ->release().

<And no, I still haven't gotten around to testing and reviewing all those patchsets, sorry>

author
#12 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

In reply to Rusty Russell
Quoted 11 lines · Rusty Russell
Quoted 6 lines · Tejun Heo
Quoted 4 lines · Rusty Russell

As stated you cannot protect arbitrary code this way, as you are trying to do. I do not think you've broken any of the current code, but I cannot tell. You're certainly going to surprise unsuspecting future authors.

Can you elaborate a bit? Why can't it protect the code?

Because you don't know what that code does. After all, it's assumed that module code doesn't get called after exit and you're deliberately violating that assumption.

What I meant by protecting 'code' was the 'code' itself. Those pages containing instructions that cpu executes. It of course can't protect against all the things they do.

Quoted 14 lines
Quoted 5 lines
Quoted 2 lines

Can you really not figure out the module owner of the sysfs entry to inc its use count during this procedure? (__module_get()).

I can but I don't think it's worth the effort. It will involve passing @owner parameter down through kobject to sysfs but the path is pretty obscure and thus difficult to test.

Have you tested that *this* path works? Let's take your first change as an example:

(quoted diff) +4 -0
+       mutex_lock(&gdev->reg_mutex);
+       __ccwgroup_remove_symlinks(gdev);
+       device_unregister(dev);
+       mutex_unlock(&gdev->reg_mutex);
Quoted 8 lines

Now, are you sure that calling cleanup_ccwgroup just after device_unregister() works?

static void __exit
cleanup_ccwgroup (void)

{
	bus_unregister (&ccwgroup_bus_type);
}

It should. After ->exit() is called, there can't be any object left behind. If a module is hosting objects which can't be destroyed from ->exit(), its module ref count shouldn't be zero. So, either 1. refcount != 0 or 2. ->exit() can destroy all objects. As Cornelia explains, for ccwgroup, it's #1. Note that unload inhibition doesn't change anything about this.

Quoted 7 lines
Quoted 3 lines

I think it's too much work for the
users of the API and it will be easy to pass the wrong @owner and go unnoticed.

But your shortcut insists that all module authors be aware that functions can be running after exit() is called. That's a recipe for instability and disaster.

No, it doesn't change that at all. All unload inhibition does is postponing removal of code (and data too of course) section a bit so that a module can host code which issues unloading of itself. Object synchronization rules remain exactly the same. Formerly broken code is still broken and I don't even think unload inhibition would mask them too much either.

I think the naming is too ambiguous. Maybe it should be named something like "hold_module_for_suicide".

Thanks.

Signature · 1 line
tejun
author
#13 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 10 lines · Tejun Heo
Quoted 8 lines · Rusty Russell

Now, are you sure that calling cleanup_ccwgroup just after device_unregister() works?

static void __exit
cleanup_ccwgroup (void)

{
	bus_unregister (&ccwgroup_bus_type);
}
Quoted 6 lines

It should. After ->exit() is called, there can't be any object left behind. If a module is hosting objects which can't be destroyed from ->exit(), its module ref count shouldn't be zero. So, either 1. refcount != 0 or 2. ->exit() can destroy all objects. As Cornelia explains, for ccwgroup, it's #1. Note that unload inhibition doesn't change anything about this.

Hmmm.... There doesn't seem to any reason why the blocking should be after calling ->exit(). And, yeah, it would be more useful and intuitive if blocking happens before ->exit(). What do you think?

Thanks.

Signature · 1 line
tejun
#14 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 3 lines · Tejun Heo

Hmmm.... There doesn't seem to any reason why the blocking should be after calling ->exit(). And, yeah, it would be more useful and intuitive if blocking happens before ->exit(). What do you think?

*That* I have no problem with.

I was going to say "just grab a reference to every module" except if a new module is loaded you don't know about it.

If you move your blocking, it seems fine.

Thanks!
Rusty.

author
#15 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 11 lines · Rusty Russell
Quoted 3 lines · Tejun Heo

Hmmm.... There doesn't seem to any reason why the blocking should be after calling ->exit(). And, yeah, it would be more useful and intuitive if blocking happens before ->exit(). What do you think?

*That* I have no problem with.

I was going to say "just grab a reference to every module" except if a new module is loaded you don't know about it.

If you move your blocking, it seems fine.

Great, I think I was too occupied with the sysfs case when I was writing it. I'll update the patch. Thanks a lot.

Signature · 1 line
tejun
#16 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

In reply to Tejun Heo
Quoted 20 lines · Tejun Heo
Quoted 4 lines · Jonathan Corbet

Hi, Tejun,

I was just looking over these changes...

Quoted 14 lines

kernel/module.c sys_delete_module(const char __user *name_user, unsigned int flags) · line 735

Quoted 9 lines
kernel/module.c +9 -0
+	/* Don't proceed till inhibition is lifted. */
+	add_wait_queue(&module_unload_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	if (atomic_read(&module_unload_inhibit_cnt))
+		schedule();
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&module_unload_wait, &wait);
+
+	mutex_lock(&module_mutex);

Maybe I'm missing something, but this looks racy to me. There's no check after schedule() to see if module_unload_inhibit_cnt is really zero, and nothing to keep somebody else from slipping in and raising it again afterward.

Quoted 3 lines

The unloading can proceed once module_unload_inhibit_cnt reaches zero. An unloading thread only has to care about inhibition put in effect before unloading has started, so there's no need to check again.

You haven't fully answered Jon's question. Suppose module_unload_inhibit_cnt is nonzero, so the task adds itself to the module_unload_wait queue, changes to TASK_UNINTERRUPTIBLE, and calls schedule. There's nothing to prevent somebody else from waking the task back up before the original inhibition has been lifted.

Alan Stern

author
#17 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 9 lines · Alan Stern
Quoted 3 lines

The unloading can proceed once module_unload_inhibit_cnt reaches zero. An unloading thread only has to care about inhibition put in effect before unloading has started, so there's no need to check again.

You haven't fully answered Jon's question. Suppose module_unload_inhibit_cnt is nonzero, so the task adds itself to the module_unload_wait queue, changes to TASK_UNINTERRUPTIBLE, and calls schedule. There's nothing to prevent somebody else from waking the task back up before the original inhibition has been lifted.

Hmmm... I might be missing something here. Who else can wake up a thread in uninterruptible sleep?

Thanks.

Signature · 1 line
tejun
#18 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 5 lines · Tejun Heo
Quoted 4 lines · Alan Stern
Quoted 3 lines

The unloading can proceed once module_unload_inhibit_cnt reaches zero. An unloading thread only has to care about inhibition put in effect before unloading has started, so there's no need to check again.

Quoted 8 lines
Quoted 5 lines

You haven't fully answered Jon's question. Suppose module_unload_inhibit_cnt is nonzero, so the task adds itself to the module_unload_wait queue, changes to TASK_UNINTERRUPTIBLE, and calls schedule. There's nothing to prevent somebody else from waking the task back up before the original inhibition has been lifted.

Hmmm... I might be missing something here. Who else can wake up a thread in uninterruptible sleep?

In principle, anything can. There has never been any guarantee in the kernel that a task sleeping on a waitqueue will remain asleep until the waitqueue is signalled. That's part of the reason why things like __wait_event() are coded as loops.

Alan Stern

#30 lore raw
In reply to Tejun Heo
Quoted 4 lines · Tejun Heo

Hello, all.

This is the second patchset of four sysfs update patchset series[1] and to be applied on top of the first patchset[1].

I have no specific objection to this patchset, but it seems that others still do, so I'll hold off on applying them to my tree until it all gets sorted out :)

thanks,

greg k-h

author
#19 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

In reply to Alan Stern
Quoted 11 lines · Alan Stern
Quoted 9 lines · Tejun Heo
Quoted 8 lines · Alan Stern
Quoted 3 lines

The unloading can proceed once module_unload_inhibit_cnt reaches zero. An unloading thread only has to care about inhibition put in effect before unloading has started, so there's no need to check again.

You haven't fully answered Jon's question. Suppose module_unload_inhibit_cnt is nonzero, so the task adds itself to the module_unload_wait queue, changes to TASK_UNINTERRUPTIBLE, and calls schedule. There's nothing to prevent somebody else from waking the task back up before the original inhibition has been lifted.

Quoted 7 lines
Quoted 2 lines

Hmmm... I might be missing something here. Who else can wake up a thread in uninterruptible sleep?

In principle, anything can. There has never been any guarantee in the kernel that a task sleeping on a waitqueue will remain asleep until the waitqueue is signalled. That's part of the reason why things like __wait_event() are coded as loops.

Hmmm... I always thought the queue was because the condition can change inbetween waking up and actually running. For example, if the condition is !(queue empty), another task can enter the critical section and consume the element which triggered wake up before the woken up task do.

I have no problem with changing the condition check to loop but it would be great if someone can point me to a code where this unexpected wake up is used.

Thanks.

Signature · 1 line
tejun
#20 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 3 lines · Tejun Heo

I have no problem with changing the condition check to loop but it would be great if someone can point me to a code where this unexpected wake up is used.

This is one of those areas where we're conservative. Historically there have been random wakes, and noone is quite sure that signal code or the freezer or whatever won't do it under some circumstances.

Thus it's always seen as better to wait on a specific condition.

Cheers,
Rusty.

author
#21 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

Quoted 10 lines · Rusty Russell
Quoted 3 lines · Tejun Heo

I have no problem with changing the condition check to loop but it would be great if someone can point me to a code where this unexpected wake up is used.

This is one of those areas where we're conservative. Historically there have been random wakes, and noone is quite sure that signal code or the freezer or whatever won't do it under some circumstances.

Thus it's always seen as better to wait on a specific condition.

I see. I'll update the code then. Thanks.

Signature · 1 line
tejun
#22 lore raw

Re: [PATCH 1/4] module: implement module_inhibit_unload()

In reply to Tejun Heo
Quoted 8 lines · Tejun Heo
Quoted 7 lines
Quoted 2 lines

Hmmm... I might be missing something here. Who else can wake up a thread in uninterruptible sleep?

In principle, anything can. There has never been any guarantee in the kernel that a task sleeping on a waitqueue will remain asleep until the waitqueue is signalled. That's part of the reason why things like __wait_event() are coded as loops.

Quoted 4 lines

Hmmm... I always thought the queue was because the condition can change inbetween waking up and actually running. For example, if the condition is !(queue empty), another task can enter the critical section and consume the element which triggered wake up before the woken up task do.

That's the other part of the reason for using a loop. :-)

Alan Stern

#28 lore raw

Re: [PATCH 4/4] sysfs: make suicidal nodes just do it directly

In reply to Tejun Heo
Quoted 4 lines · Tejun Heo

Sysfs now allows direct suicide. Make suicidial sysfs nodes to use it and remove now unncessary callback mechanism.

Quoted 8 lines
---
 drivers/base/core.c         |   33 ----------------------
 drivers/s390/cio/ccwgroup.c |   25 +++-------------
 drivers/scsi/scsi_sysfs.c   |   13 +--------
 fs/sysfs/file.c             |   63 -------------------------------------------
 include/linux/device.h      |    6 ----
 include/linux/sysfs.h       |    9 ------
 6 files changed, 6 insertions(+), 143 deletions(-)

ccwgroup was implicitly relying on a held reference, fix that.

---
 drivers/s390/cio/ccwgroup.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletion(-)
drivers/s390/cio/ccwgroup.c +2 -1
@@ -72,7 +72,7 @@ ccwgroup_ungroup_store(struct device *de
 	struct ccwgroup_device *gdev;
 	int rc;
 
-	gdev = to_ccwgroupdev(dev);
+	gdev = to_ccwgroupdev(get_device(dev));
 
 	if (gdev->state != CCWGROUP_OFFLINE)
 		return -EINVAL;
@@ -81,6 +81,7 @@ ccwgroup_ungroup_store(struct device *de
 	__ccwgroup_remove_symlinks(gdev);
 	device_unregister(dev);
 	mutex_unlock(&gdev->reg_mutex);
+	put_device(dev);
 
 	return count;
 }
author
#29 lore raw

Re: [PATCH 4/4] sysfs: make suicidal nodes just do it directly

Quoted 16 lines · Cornelia Huck
Quoted 4 lines · Tejun Heo

Sysfs now allows direct suicide. Make suicidial sysfs nodes to use it and remove now unncessary callback mechanism.

Quoted 8 lines
---
 drivers/base/core.c         |   33 ----------------------
 drivers/s390/cio/ccwgroup.c |   25 +++-------------
 drivers/scsi/scsi_sysfs.c   |   13 +--------
 fs/sysfs/file.c             |   63 -------------------------------------------
 include/linux/device.h      |    6 ----
 include/linux/sysfs.h       |    9 ------
 6 files changed, 6 insertions(+), 143 deletions(-)
Quoted 3 lines

ccwgroup was implicitly relying on a held reference, fix that.

Thanks. Will incorporate into the next series.

Signature · 1 line
tejun