Skip to content

Commit 5d5652b

Browse files
westerigregkh
authored andcommitted
thunderbolt: Take domain lock in switch sysfs attribute callbacks
[ 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]>
1 parent afee27f commit 5d5652b

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
@@ -9,15 +9,13 @@
99
#include <linux/idr.h>
1010
#include <linux/nvmem-provider.h>
1111
#include <linux/pm_runtime.h>
12+
#include <linux/sched/signal.h>
1213
#include <linux/sizes.h>
1314
#include <linux/slab.h>
1415
#include <linux/vmalloc.h>
1516

1617
#include "tb.h"
1718

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

2321
#define NVM_DEVID 0x05
@@ -253,8 +251,8 @@ static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
253251
struct tb_switch *sw = priv;
254252
int ret = 0;
255253

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

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

276274
unlock:
277-
mutex_unlock(&switch_lock);
275+
mutex_unlock(&sw->tb->lock);
278276

279277
return ret;
280278
}
@@ -363,10 +361,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
363361
}
364362
nvm->non_active = nvm_dev;
365363

366-
mutex_lock(&switch_lock);
367364
sw->nvm = nvm;
368-
mutex_unlock(&switch_lock);
369-
370365
return 0;
371366

372367
err_nvm_active:
@@ -383,10 +378,8 @@ static void tb_switch_nvm_remove(struct tb_switch *sw)
383378
{
384379
struct tb_switch_nvm *nvm;
385380

386-
mutex_lock(&switch_lock);
387381
nvm = sw->nvm;
388382
sw->nvm = NULL;
389-
mutex_unlock(&switch_lock);
390383

391384
if (!nvm)
392385
return;
@@ -717,8 +710,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
717710
{
718711
int ret = -EINVAL;
719712

720-
if (mutex_lock_interruptible(&switch_lock))
721-
return -ERESTARTSYS;
713+
if (!mutex_trylock(&sw->tb->lock))
714+
return restart_syscall();
722715

723716
if (sw->authorized)
724717
goto unlock;
@@ -761,7 +754,7 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
761754
}
762755

763756
unlock:
764-
mutex_unlock(&switch_lock);
757+
mutex_unlock(&sw->tb->lock);
765758
return ret;
766759
}
767760

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

821-
if (mutex_lock_interruptible(&switch_lock))
822-
return -ERESTARTSYS;
814+
if (!mutex_trylock(&sw->tb->lock))
815+
return restart_syscall();
823816

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

829-
mutex_unlock(&switch_lock);
822+
mutex_unlock(&sw->tb->lock);
830823
return ret;
831824
}
832825

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

846-
if (mutex_lock_interruptible(&switch_lock))
847-
return -ERESTARTSYS;
839+
if (!mutex_trylock(&sw->tb->lock))
840+
return restart_syscall();
848841

849842
if (sw->authorized) {
850843
ret = -EBUSY;
@@ -859,7 +852,7 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr,
859852
}
860853
}
861854

862-
mutex_unlock(&switch_lock);
855+
mutex_unlock(&sw->tb->lock);
863856
return ret;
864857
}
865858
static DEVICE_ATTR(key, 0600, key_show, key_store);
@@ -905,8 +898,8 @@ static ssize_t nvm_authenticate_store(struct device *dev,
905898
bool val;
906899
int ret;
907900

908-
if (mutex_lock_interruptible(&switch_lock))
909-
return -ERESTARTSYS;
901+
if (!mutex_trylock(&sw->tb->lock))
902+
return restart_syscall();
910903

911904
/* If NVMem devices are not yet added */
912905
if (!sw->nvm) {
@@ -954,7 +947,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
954947
}
955948

956949
exit_unlock:
957-
mutex_unlock(&switch_lock);
950+
mutex_unlock(&sw->tb->lock);
958951

959952
if (ret)
960953
return ret;
@@ -968,8 +961,8 @@ static ssize_t nvm_version_show(struct device *dev,
968961
struct tb_switch *sw = tb_to_switch(dev);
969962
int ret;
970963

971-
if (mutex_lock_interruptible(&switch_lock))
972-
return -ERESTARTSYS;
964+
if (!mutex_trylock(&sw->tb->lock))
965+
return restart_syscall();
973966

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

981-
mutex_unlock(&switch_lock);
974+
mutex_unlock(&sw->tb->lock);
982975

983976
return ret;
984977
}

drivers/thunderbolt/tb.h

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

0 commit comments

Comments
 (0)