From f092fc1d40f3746c417e979985346ba169af7a6d Mon Sep 17 00:00:00 2001
From: Bart Van Assche <bart.vanassche@sandisk.com>
Date: Fri, 4 Nov 2016 10:07:23 -0600
Subject: [PATCH 2/2] Avoid that SCSI device removal through sysfs triggers a
deadlock
The SCSI core holds scan_mutex around SCSI device addition and
removal operations. sysfs serializes attribute read and write
operations against attribute removal through s_active. Avoid that
grabbing scan_mutex during self-removal of a SCSI device triggers
a deadlock by re-grabbing s_active after self-removal has finished
instead of after kernfs_remove_self() has finished.
Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
---
fs/kernfs/dir.c | 19 +++++++++++++------
fs/kernfs/file.c | 7 +++++++
include/linux/kernfs.h | 1 +
3 files changed, 21 insertions(+), 6 deletions(-)
@@ -426,6 +426,8 @@ void kernfs_put_active(struct kernfs_node *kn)
if (unlikely(!kn))
return;
+ WARN_ON_ONCE(kn->flags & KERNFS_BROKE_A_P);
+
if (kernfs_lockdep(kn))
rwsem_release(&kn->dep_map, 1, _RET_IP_);
v = atomic_dec_return(&kn->active);
@@ -1314,6 +1316,9 @@ void kernfs_unbreak_active_protection(struct kernfs_node *kn)
* kernfs_remove_self - remove a kernfs_node from its own method
* @kn: the self kernfs_node to remove
*
+ * If kernfs_remove_self() sets KERNFS_BROKE_A_P the caller must invoke
+ * kernfs_unbreak_active_protection().
+ *
* The caller must be running off of a kernfs operation which is invoked
* with an active reference - e.g. one of kernfs_ops. This can be used to
* implement a file operation which deletes itself.
@@ -1354,6 +1359,7 @@ bool kernfs_remove_self(struct kernfs_node *kn)
*/
if (!(kn->flags & KERNFS_SUICIDAL)) {
kn->flags |= KERNFS_SUICIDAL;
+ kn->flags |= KERNFS_BROKE_A_P;
__kernfs_remove(kn);
kn->flags |= KERNFS_SUICIDED;
ret = true;
@@ -1375,13 +1381,14 @@ bool kernfs_remove_self(struct kernfs_node *kn)
finish_wait(waitq, &wait);
WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb));
ret = false;
- }
- /*
- * This must be done while holding kernfs_mutex; otherwise, waiting
- * for SUICIDED && deactivated could finish prematurely.
- */
- kernfs_unbreak_active_protection(kn);
+ /*
+ * This must be done while holding kernfs_mutex; otherwise,
+ * waiting for SUICIDED && deactivated could finish
+ * prematurely.
+ */
+ kernfs_unbreak_active_protection(kn);
+ }
mutex_unlock(&kernfs_mutex);
return ret;
@@ -319,6 +319,13 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
else
len = -EINVAL;
+ mutex_lock(&kernfs_mutex);
+ if (kn->flags & KERNFS_BROKE_A_P) {
+ kernfs_unbreak_active_protection(kn);
+ kn->flags &= ~KERNFS_BROKE_A_P;
+ }
+ mutex_unlock(&kernfs_mutex);
+
kernfs_put_active(kn);
mutex_unlock(&of->mutex);
@@ -46,6 +46,7 @@ enum kernfs_node_flag {
KERNFS_SUICIDAL = 0x0400,
KERNFS_SUICIDED = 0x0800,
KERNFS_EMPTY_DIR = 0x1000,
+ KERNFS_BROKE_A_P = 0x2000,
};
/* @flags for kernfs_create_root() */
--
2.10.1