Skip to content

Commit c0dccad

Browse files
roygerjgross1
authored andcommitted
hvc/xen: lock console list traversal
The currently lockless access to the xen console list in vtermno_to_xencons() is incorrect, as additions and removals from the list can happen anytime, and as such the traversal of the list to get the private console data for a given termno needs to happen with the lock held. Note users that modify the list already do so with the lock taken. Adjust current lock takers to use the _irq{save,restore} helpers, since the context in which vtermno_to_xencons() is called can have interrupts disabled. Use the _irq{save,restore} set of helpers to switch the current callers to disable interrupts in the locked region. I haven't checked if existing users could instead use the _irq variant, as I think it's safer to use _irq{save,restore} upfront. While there switch from using list_for_each_entry_safe to list_for_each_entry: the current entry cursor won't be removed as part of the code in the loop body, so using the _safe variant is pointless. Fixes: 02e19f9 ('hvc_xen: implement multiconsole support') Signed-off-by: Roger Pau Monné <[email protected]> Reviewed-by: Stefano Stabellini <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Juergen Gross <[email protected]>
1 parent 37c1785 commit c0dccad

File tree

1 file changed

+29
-17
lines changed

1 file changed

+29
-17
lines changed

drivers/tty/hvc/hvc_xen.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,22 @@ static DEFINE_SPINLOCK(xencons_lock);
5252

5353
static struct xencons_info *vtermno_to_xencons(int vtermno)
5454
{
55-
struct xencons_info *entry, *n, *ret = NULL;
55+
struct xencons_info *entry, *ret = NULL;
56+
unsigned long flags;
5657

57-
if (list_empty(&xenconsoles))
58-
return NULL;
58+
spin_lock_irqsave(&xencons_lock, flags);
59+
if (list_empty(&xenconsoles)) {
60+
spin_unlock_irqrestore(&xencons_lock, flags);
61+
return NULL;
62+
}
5963

60-
list_for_each_entry_safe(entry, n, &xenconsoles, list) {
64+
list_for_each_entry(entry, &xenconsoles, list) {
6165
if (entry->vtermno == vtermno) {
6266
ret = entry;
6367
break;
6468
}
6569
}
70+
spin_unlock_irqrestore(&xencons_lock, flags);
6671

6772
return ret;
6873
}
@@ -223,7 +228,7 @@ static int xen_hvm_console_init(void)
223228
{
224229
int r;
225230
uint64_t v = 0;
226-
unsigned long gfn;
231+
unsigned long gfn, flags;
227232
struct xencons_info *info;
228233

229234
if (!xen_hvm_domain())
@@ -258,9 +263,9 @@ static int xen_hvm_console_init(void)
258263
goto err;
259264
info->vtermno = HVC_COOKIE;
260265

261-
spin_lock(&xencons_lock);
266+
spin_lock_irqsave(&xencons_lock, flags);
262267
list_add_tail(&info->list, &xenconsoles);
263-
spin_unlock(&xencons_lock);
268+
spin_unlock_irqrestore(&xencons_lock, flags);
264269

265270
return 0;
266271
err:
@@ -283,6 +288,7 @@ static int xencons_info_pv_init(struct xencons_info *info, int vtermno)
283288
static int xen_pv_console_init(void)
284289
{
285290
struct xencons_info *info;
291+
unsigned long flags;
286292

287293
if (!xen_pv_domain())
288294
return -ENODEV;
@@ -299,16 +305,17 @@ static int xen_pv_console_init(void)
299305
/* already configured */
300306
return 0;
301307
}
302-
spin_lock(&xencons_lock);
308+
spin_lock_irqsave(&xencons_lock, flags);
303309
xencons_info_pv_init(info, HVC_COOKIE);
304-
spin_unlock(&xencons_lock);
310+
spin_unlock_irqrestore(&xencons_lock, flags);
305311

306312
return 0;
307313
}
308314

309315
static int xen_initial_domain_console_init(void)
310316
{
311317
struct xencons_info *info;
318+
unsigned long flags;
312319

313320
if (!xen_initial_domain())
314321
return -ENODEV;
@@ -323,9 +330,9 @@ static int xen_initial_domain_console_init(void)
323330
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
324331
info->vtermno = HVC_COOKIE;
325332

326-
spin_lock(&xencons_lock);
333+
spin_lock_irqsave(&xencons_lock, flags);
327334
list_add_tail(&info->list, &xenconsoles);
328-
spin_unlock(&xencons_lock);
335+
spin_unlock_irqrestore(&xencons_lock, flags);
329336

330337
return 0;
331338
}
@@ -380,10 +387,12 @@ static void xencons_free(struct xencons_info *info)
380387

381388
static int xen_console_remove(struct xencons_info *info)
382389
{
390+
unsigned long flags;
391+
383392
xencons_disconnect_backend(info);
384-
spin_lock(&xencons_lock);
393+
spin_lock_irqsave(&xencons_lock, flags);
385394
list_del(&info->list);
386-
spin_unlock(&xencons_lock);
395+
spin_unlock_irqrestore(&xencons_lock, flags);
387396
if (info->xbdev != NULL)
388397
xencons_free(info);
389398
else {
@@ -464,6 +473,7 @@ static int xencons_probe(struct xenbus_device *dev,
464473
{
465474
int ret, devid;
466475
struct xencons_info *info;
476+
unsigned long flags;
467477

468478
devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
469479
if (devid == 0)
@@ -482,9 +492,9 @@ static int xencons_probe(struct xenbus_device *dev,
482492
ret = xencons_connect_backend(dev, info);
483493
if (ret < 0)
484494
goto error;
485-
spin_lock(&xencons_lock);
495+
spin_lock_irqsave(&xencons_lock, flags);
486496
list_add_tail(&info->list, &xenconsoles);
487-
spin_unlock(&xencons_lock);
497+
spin_unlock_irqrestore(&xencons_lock, flags);
488498

489499
return 0;
490500

@@ -584,10 +594,12 @@ static int __init xen_hvc_init(void)
584594

585595
info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);
586596
if (IS_ERR(info->hvc)) {
597+
unsigned long flags;
598+
587599
r = PTR_ERR(info->hvc);
588-
spin_lock(&xencons_lock);
600+
spin_lock_irqsave(&xencons_lock, flags);
589601
list_del(&info->list);
590-
spin_unlock(&xencons_lock);
602+
spin_unlock_irqrestore(&xencons_lock, flags);
591603
if (info->irq)
592604
unbind_from_irqhandler(info->irq, NULL);
593605
kfree(info);

0 commit comments

Comments
 (0)