diff options
author | Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com> | 2019-09-25 17:47:12 +0300 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2019-10-12 23:51:29 +0200 |
commit | c2635b871d1dd03a6608a9255222672decd49e09 (patch) | |
tree | e395a23095d78ba2c3a5f667cd10ecc0aaf05698 | |
parent | 77cfc0739d30c1282f7de24d2ec086d244e34bb7 (diff) | |
download | upstream-c2635b871d1dd03a6608a9255222672decd49e09.tar.gz upstream-c2635b871d1dd03a6608a9255222672decd49e09.tar.bz2 upstream-c2635b871d1dd03a6608a9255222672decd49e09.zip |
trelay: fix deadlock on remove
Upon writing to "remove" file, debugfs_remove_recursive() blocks while
holding rtnl_lock. This is because debugfs' file_ops callbacks are
executed in debugfs_use_file_*() context which prevents file removal.
Fix this by only flagging the device for removal and then do the cleanup
in file_ops.release callback which is executed out of that context.
Signed-off-by: Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>
-rw-r--r-- | package/kernel/trelay/src/trelay.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/package/kernel/trelay/src/trelay.c b/package/kernel/trelay/src/trelay.c index d09dc07246..3871ace070 100644 --- a/package/kernel/trelay/src/trelay.c +++ b/package/kernel/trelay/src/trelay.c @@ -27,6 +27,7 @@ struct trelay { struct list_head list; struct net_device *dev1, *dev2; struct dentry *debugfs; + int to_remove; char name[]; }; @@ -60,13 +61,16 @@ static int trelay_do_remove(struct trelay *tr) { list_del(&tr->list); + /* First and before all, ensure that the debugfs file is removed + * to prevent dangling pointer in file->private_data */ + debugfs_remove_recursive(tr->debugfs); + dev_put(tr->dev1); dev_put(tr->dev2); netdev_rx_handler_unregister(tr->dev1); netdev_rx_handler_unregister(tr->dev2); - debugfs_remove_recursive(tr->debugfs); kfree(tr); return 0; @@ -106,16 +110,25 @@ static ssize_t trelay_remove_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct trelay *tr = file->private_data; - int ret; + tr->to_remove = 1; + return count; +} + +static int trelay_remove_release(struct inode *inode, struct file *file) +{ + struct trelay *tr, *tmp; + + /* This is the only file op that is called outside debugfs_use_file_*() + * context which means that: (1) this file can be removed and + * (2) file->private_data may no longer be valid */ rtnl_lock(); - ret = trelay_do_remove(tr); + list_for_each_entry_safe(tr, tmp, &trelay_devs, list) + if (tr->to_remove) + trelay_do_remove(tr); rtnl_unlock(); - if (ret < 0) - return ret; - - return count; + return 0; } static const struct file_operations fops_remove = { @@ -123,6 +136,7 @@ static const struct file_operations fops_remove = { .open = trelay_open, .write = trelay_remove_write, .llseek = default_llseek, + .release = trelay_remove_release, }; |