Skip to content

Commit 6214894

Browse files
roygerjgross1
authored andcommitted
hvc/xen: prevent concurrent accesses to the shared ring
The hvc machinery registers both a console and a tty device based on the hv ops provided by the specific implementation. Those two interfaces however have different locks, and there's no single locks that's shared between the tty and the console implementations, hence the driver needs to protect itself against concurrent accesses. Otherwise concurrent calls using the split interfaces are likely to corrupt the ring indexes, leaving the console unusable. Introduce a lock to xencons_info to serialize accesses to the shared ring. This is only required when using the shared memory console, concurrent accesses to the hypercall based console implementation are not an issue. Note the conditional logic in domU_read_console() is slightly modified so the notify_daemon() call can be done outside of the locked region: it's an hypercall and there's no need for it to be done with the lock held. Fixes: b536b4b ('xen: use the hvc console infrastructure for Xen console') Signed-off-by: Roger Pau Monné <[email protected]> Reviewed-by: Juergen Gross <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Juergen Gross <[email protected]>
1 parent 7ad2c39 commit 6214894

File tree

1 file changed

+17
-2
lines changed

1 file changed

+17
-2
lines changed

drivers/tty/hvc/hvc_xen.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct xencons_info {
4343
int irq;
4444
int vtermno;
4545
grant_ref_t gntref;
46+
spinlock_t ring_lock;
4647
};
4748

4849
static LIST_HEAD(xenconsoles);
@@ -89,12 +90,15 @@ static int __write_console(struct xencons_info *xencons,
8990
XENCONS_RING_IDX cons, prod;
9091
struct xencons_interface *intf = xencons->intf;
9192
int sent = 0;
93+
unsigned long flags;
9294

95+
spin_lock_irqsave(&xencons->ring_lock, flags);
9396
cons = intf->out_cons;
9497
prod = intf->out_prod;
9598
mb(); /* update queue values before going on */
9699

97100
if ((prod - cons) > sizeof(intf->out)) {
101+
spin_unlock_irqrestore(&xencons->ring_lock, flags);
98102
pr_err_once("xencons: Illegal ring page indices");
99103
return -EINVAL;
100104
}
@@ -104,6 +108,7 @@ static int __write_console(struct xencons_info *xencons,
104108

105109
wmb(); /* write ring before updating pointer */
106110
intf->out_prod = prod;
111+
spin_unlock_irqrestore(&xencons->ring_lock, flags);
107112

108113
if (sent)
109114
notify_daemon(xencons);
@@ -146,16 +151,19 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
146151
int recv = 0;
147152
struct xencons_info *xencons = vtermno_to_xencons(vtermno);
148153
unsigned int eoiflag = 0;
154+
unsigned long flags;
149155

150156
if (xencons == NULL)
151157
return -EINVAL;
152158
intf = xencons->intf;
153159

160+
spin_lock_irqsave(&xencons->ring_lock, flags);
154161
cons = intf->in_cons;
155162
prod = intf->in_prod;
156163
mb(); /* get pointers before reading ring */
157164

158165
if ((prod - cons) > sizeof(intf->in)) {
166+
spin_unlock_irqrestore(&xencons->ring_lock, flags);
159167
pr_err_once("xencons: Illegal ring page indices");
160168
return -EINVAL;
161169
}
@@ -179,10 +187,13 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
179187
xencons->out_cons = intf->out_cons;
180188
xencons->out_cons_same = 0;
181189
}
190+
if (!recv && xencons->out_cons_same++ > 1) {
191+
eoiflag = XEN_EOI_FLAG_SPURIOUS;
192+
}
193+
spin_unlock_irqrestore(&xencons->ring_lock, flags);
194+
182195
if (recv) {
183196
notify_daemon(xencons);
184-
} else if (xencons->out_cons_same++ > 1) {
185-
eoiflag = XEN_EOI_FLAG_SPURIOUS;
186197
}
187198

188199
xen_irq_lateeoi(xencons->irq, eoiflag);
@@ -239,6 +250,7 @@ static int xen_hvm_console_init(void)
239250
info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
240251
if (!info)
241252
return -ENOMEM;
253+
spin_lock_init(&info->ring_lock);
242254
} else if (info->intf != NULL) {
243255
/* already configured */
244256
return 0;
@@ -275,6 +287,7 @@ static int xen_hvm_console_init(void)
275287

276288
static int xencons_info_pv_init(struct xencons_info *info, int vtermno)
277289
{
290+
spin_lock_init(&info->ring_lock);
278291
info->evtchn = xen_start_info->console.domU.evtchn;
279292
/* GFN == MFN for PV guest */
280293
info->intf = gfn_to_virt(xen_start_info->console.domU.mfn);
@@ -325,6 +338,7 @@ static int xen_initial_domain_console_init(void)
325338
info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
326339
if (!info)
327340
return -ENOMEM;
341+
spin_lock_init(&info->ring_lock);
328342
}
329343

330344
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
@@ -482,6 +496,7 @@ static int xencons_probe(struct xenbus_device *dev,
482496
info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
483497
if (!info)
484498
return -ENOMEM;
499+
spin_lock_init(&info->ring_lock);
485500
dev_set_drvdata(&dev->dev, info);
486501
info->xbdev = dev;
487502
info->vtermno = xenbus_devid_to_vtermno(devid);

0 commit comments

Comments
 (0)