Skip to content

Commit 3e1b011

Browse files
westerismb49
authored andcommitted
thunderbolt: Take domain lock in switch sysfs attribute callbacks
BugLink: https://bugs.launchpad.net/bugs/1837517 [ Upstream commit 09f11b6 ] switch_lock was introduced because it allowed serialization of device authorization requests from userspace without need to take the big domain lock (tb->lock). This was fine because device authorization with ICM is just one command that is sent to the firmware. Now that we start to handle all tunneling in the driver switch_lock is not enough because we need to walk over the topology to establish paths. For this reason drop switch_lock from the driver completely in favour of big domain lock. There is one complication, though. If userspace is waiting for the lock in tb_switch_set_authorized(), it keeps the device_del() from removing the sysfs attribute because it waits for active users to release the attribute first which leads into following splat: INFO: task kworker/u8:3:73 blocked for more than 61 seconds. Tainted: G W 5.1.0-rc1+ #244 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kworker/u8:3 D12976 73 2 0x80000000 Workqueue: thunderbolt0 tb_handle_hotplug [thunderbolt] Call Trace: ? __schedule+0x2e5/0x740 ? _raw_spin_lock_irqsave+0x12/0x40 ? prepare_to_wait_event+0xc5/0x160 schedule+0x2d/0x80 __kernfs_remove.part.17+0x183/0x1f0 ? finish_wait+0x80/0x80 kernfs_remove_by_name_ns+0x4a/0x90 remove_files.isra.1+0x2b/0x60 sysfs_remove_group+0x38/0x80 sysfs_remove_groups+0x24/0x40 device_remove_attrs+0x3d/0x70 device_del+0x14c/0x360 device_unregister+0x15/0x50 tb_switch_remove+0x9e/0x1d0 [thunderbolt] tb_handle_hotplug+0x119/0x5a0 [thunderbolt] ? process_one_work+0x1b7/0x420 process_one_work+0x1b7/0x420 worker_thread+0x37/0x380 ? _raw_spin_unlock_irqrestore+0xf/0x30 ? process_one_work+0x420/0x420 kthread+0x118/0x130 ? kthread_create_on_node+0x60/0x60 ret_from_fork+0x35/0x40 We deal this by following what network stack did for some of their attributes and use mutex_trylock() with restart_syscall(). This makes userspace release the attribute allowing sysfs attribute removal to progress before the write is restarted and eventually fail when the attribute is removed. Signed-off-by: Mika Westerberg <[email protected]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Khalid Elmously <[email protected]> Signed-off-by: Stefan Bader <[email protected]>
1 parent f2601d7 commit 3e1b011

File tree

2 files changed

+20
-28
lines changed

2 files changed

+20
-28
lines changed

drivers/thunderbolt/switch.c

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
#include <linux/idr.h>
1111
#include <linux/nvmem-provider.h>
1212
#include <linux/pm_runtime.h>
13+
#include <linux/sched/signal.h>
1314
#include <linux/sizes.h>
1415
#include <linux/slab.h>
1516
#include <linux/vmalloc.h>
1617

1718
#include "tb.h"
1819

19-
/* Switch authorization from userspace is serialized by this lock */
20-
static DEFINE_MUTEX(switch_lock);
21-
2220
/* Switch NVM support */
2321

2422
#define NVM_DEVID 0x05
@@ -254,8 +252,8 @@ static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
254252
struct tb_switch *sw = priv;
255253
int ret = 0;
256254

257-
if (mutex_lock_interruptible(&switch_lock))
258-
return -ERESTARTSYS;
255+
if (!mutex_trylock(&sw->tb->lock))
256+
return restart_syscall();
259257

260258
/*
261259
* Since writing the NVM image might require some special steps,
@@ -275,7 +273,7 @@ static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
275273
memcpy(sw->nvm->buf + offset, val, bytes);
276274

277275
unlock:
278-
mutex_unlock(&switch_lock);
276+
mutex_unlock(&sw->tb->lock);
279277

280278
return ret;
281279
}
@@ -364,10 +362,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
364362
}
365363
nvm->non_active = nvm_dev;
366364

367-
mutex_lock(&switch_lock);
368365
sw->nvm = nvm;
369-
mutex_unlock(&switch_lock);
370-
371366
return 0;
372367

373368
err_nvm_active:
@@ -384,10 +379,8 @@ static void tb_switch_nvm_remove(struct tb_switch *sw)
384379
{
385380
struct tb_switch_nvm *nvm;
386381

387-
mutex_lock(&switch_lock);
388382
nvm = sw->nvm;
389383
sw->nvm = NULL;
390-
mutex_unlock(&switch_lock);
391384

392385
if (!nvm)
393386
return;
@@ -716,8 +709,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
716709
{
717710
int ret = -EINVAL;
718711

719-
if (mutex_lock_interruptible(&switch_lock))
720-
return -ERESTARTSYS;
712+
if (!mutex_trylock(&sw->tb->lock))
713+
return restart_syscall();
721714

722715
if (sw->authorized)
723716
goto unlock;
@@ -760,7 +753,7 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
760753
}
761754

762755
unlock:
763-
mutex_unlock(&switch_lock);
756+
mutex_unlock(&sw->tb->lock);
764757
return ret;
765758
}
766759

@@ -817,15 +810,15 @@ static ssize_t key_show(struct device *dev, struct device_attribute *attr,
817810
struct tb_switch *sw = tb_to_switch(dev);
818811
ssize_t ret;
819812

820-
if (mutex_lock_interruptible(&switch_lock))
821-
return -ERESTARTSYS;
813+
if (!mutex_trylock(&sw->tb->lock))
814+
return restart_syscall();
822815

823816
if (sw->key)
824817
ret = sprintf(buf, "%*phN\n", TB_SWITCH_KEY_SIZE, sw->key);
825818
else
826819
ret = sprintf(buf, "\n");
827820

828-
mutex_unlock(&switch_lock);
821+
mutex_unlock(&sw->tb->lock);
829822
return ret;
830823
}
831824

@@ -842,8 +835,8 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr,
842835
else if (hex2bin(key, buf, sizeof(key)))
843836
return -EINVAL;
844837

845-
if (mutex_lock_interruptible(&switch_lock))
846-
return -ERESTARTSYS;
838+
if (!mutex_trylock(&sw->tb->lock))
839+
return restart_syscall();
847840

848841
if (sw->authorized) {
849842
ret = -EBUSY;
@@ -858,7 +851,7 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr,
858851
}
859852
}
860853

861-
mutex_unlock(&switch_lock);
854+
mutex_unlock(&sw->tb->lock);
862855
return ret;
863856
}
864857
static DEVICE_ATTR(key, 0600, key_show, key_store);
@@ -904,8 +897,8 @@ static ssize_t nvm_authenticate_store(struct device *dev,
904897
bool val;
905898
int ret;
906899

907-
if (mutex_lock_interruptible(&switch_lock))
908-
return -ERESTARTSYS;
900+
if (!mutex_trylock(&sw->tb->lock))
901+
return restart_syscall();
909902

910903
/* If NVMem devices are not yet added */
911904
if (!sw->nvm) {
@@ -953,7 +946,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
953946
}
954947

955948
exit_unlock:
956-
mutex_unlock(&switch_lock);
949+
mutex_unlock(&sw->tb->lock);
957950

958951
if (ret)
959952
return ret;
@@ -967,8 +960,8 @@ static ssize_t nvm_version_show(struct device *dev,
967960
struct tb_switch *sw = tb_to_switch(dev);
968961
int ret;
969962

970-
if (mutex_lock_interruptible(&switch_lock))
971-
return -ERESTARTSYS;
963+
if (!mutex_trylock(&sw->tb->lock))
964+
return restart_syscall();
972965

973966
if (sw->safe_mode)
974967
ret = -ENODATA;
@@ -977,7 +970,7 @@ static ssize_t nvm_version_show(struct device *dev,
977970
else
978971
ret = sprintf(buf, "%x.%x\n", sw->nvm->major, sw->nvm->minor);
979972

980-
mutex_unlock(&switch_lock);
973+
mutex_unlock(&sw->tb->lock);
981974

982975
return ret;
983976
}

drivers/thunderbolt/tb.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ struct tb_switch_nvm {
8080
* @depth: Depth in the chain this switch is connected (ICM only)
8181
*
8282
* When the switch is being added or removed to the domain (other
83-
* switches) you need to have domain lock held. For switch authorization
84-
* internal switch_lock is enough.
83+
* switches) you need to have domain lock held.
8584
*/
8685
struct tb_switch {
8786
struct device dev;

0 commit comments

Comments
 (0)