drivers/tty/hvc/hvc_xen.c | 46 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-)
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: 02e19f9c7cac ('hvc_xen: implement multiconsole support')
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Changes since v1:
- Switch current lock users to disable interrupts in the locked
region.
---
drivers/tty/hvc/hvc_xen.c | 46 ++++++++++++++++++++++++---------------
1 file changed, 29 insertions(+), 17 deletions(-)
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index e63c1761a361..d9d023275328 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -53,17 +53,22 @@ static DEFINE_SPINLOCK(xencons_lock);
static struct xencons_info *vtermno_to_xencons(int vtermno)
{
- struct xencons_info *entry, *n, *ret = NULL;
+ struct xencons_info *entry, *ret = NULL;
+ unsigned long flags;
- if (list_empty(&xenconsoles))
- return NULL;
+ spin_lock_irqsave(&xencons_lock, flags);
+ if (list_empty(&xenconsoles)) {
+ spin_unlock_irqrestore(&xencons_lock, flags);
+ return NULL;
+ }
- list_for_each_entry_safe(entry, n, &xenconsoles, list) {
+ list_for_each_entry(entry, &xenconsoles, list) {
if (entry->vtermno == vtermno) {
ret = entry;
break;
}
}
+ spin_unlock_irqrestore(&xencons_lock, flags);
return ret;
}
@@ -234,7 +239,7 @@ static int xen_hvm_console_init(void)
{
int r;
uint64_t v = 0;
- unsigned long gfn;
+ unsigned long gfn, flags;
struct xencons_info *info;
if (!xen_hvm_domain())
@@ -270,9 +275,9 @@ static int xen_hvm_console_init(void)
goto err;
info->vtermno = HVC_COOKIE;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
err:
@@ -296,6 +301,7 @@ static int xencons_info_pv_init(struct xencons_info *info, int vtermno)
static int xen_pv_console_init(void)
{
struct xencons_info *info;
+ unsigned long flags;
if (!xen_pv_domain())
return -ENODEV;
@@ -312,9 +318,9 @@ static int xen_pv_console_init(void)
/* already configured */
return 0;
}
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
xencons_info_pv_init(info, HVC_COOKIE);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
}
@@ -322,6 +328,7 @@ static int xen_pv_console_init(void)
static int xen_initial_domain_console_init(void)
{
struct xencons_info *info;
+ unsigned long flags;
if (!xen_initial_domain())
return -ENODEV;
@@ -337,9 +344,9 @@ static int xen_initial_domain_console_init(void)
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
info->vtermno = HVC_COOKIE;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
}
@@ -394,10 +401,12 @@ static void xencons_free(struct xencons_info *info)
static int xen_console_remove(struct xencons_info *info)
{
+ unsigned long flags;
+
xencons_disconnect_backend(info);
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_del(&info->list);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
if (info->xbdev != NULL)
xencons_free(info);
else {
@@ -478,6 +487,7 @@ static int xencons_probe(struct xenbus_device *dev,
{
int ret, devid;
struct xencons_info *info;
+ unsigned long flags;
devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
if (devid == 0)
@@ -497,9 +507,9 @@ static int xencons_probe(struct xenbus_device *dev,
ret = xencons_connect_backend(dev, info);
if (ret < 0)
goto error;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
@@ -599,10 +609,12 @@ static int __init xen_hvc_init(void)
info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);
if (IS_ERR(info->hvc)) {
+ unsigned long flags;
+
r = PTR_ERR(info->hvc);
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_del(&info->list);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
if (info->irq)
unbind_from_irqhandler(info->irq, NULL);
kfree(info);
--
2.37.3
On 30.11.22 17:36, Roger Pau Monne wrote: > 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: 02e19f9c7cac ('hvc_xen: implement multiconsole support') > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Pushed to xen/tip.git for-linus-6.2 Juergen
On Wed, 30 Nov 2022, Roger Pau Monne wrote: > 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: 02e19f9c7cac ('hvc_xen: implement multiconsole support') > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> > --- > Changes since v1: > - Switch current lock users to disable interrupts in the locked > region. > --- > drivers/tty/hvc/hvc_xen.c | 46 ++++++++++++++++++++++++--------------- > 1 file changed, 29 insertions(+), 17 deletions(-) > > diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c > index e63c1761a361..d9d023275328 100644 > --- a/drivers/tty/hvc/hvc_xen.c > +++ b/drivers/tty/hvc/hvc_xen.c > @@ -53,17 +53,22 @@ static DEFINE_SPINLOCK(xencons_lock); > > static struct xencons_info *vtermno_to_xencons(int vtermno) > { > - struct xencons_info *entry, *n, *ret = NULL; > + struct xencons_info *entry, *ret = NULL; > + unsigned long flags; > > - if (list_empty(&xenconsoles)) > - return NULL; > + spin_lock_irqsave(&xencons_lock, flags); > + if (list_empty(&xenconsoles)) { > + spin_unlock_irqrestore(&xencons_lock, flags); > + return NULL; > + } > > - list_for_each_entry_safe(entry, n, &xenconsoles, list) { > + list_for_each_entry(entry, &xenconsoles, list) { > if (entry->vtermno == vtermno) { > ret = entry; > break; > } > } > + spin_unlock_irqrestore(&xencons_lock, flags); > > return ret; > } > @@ -234,7 +239,7 @@ static int xen_hvm_console_init(void) > { > int r; > uint64_t v = 0; > - unsigned long gfn; > + unsigned long gfn, flags; > struct xencons_info *info; > > if (!xen_hvm_domain()) > @@ -270,9 +275,9 @@ static int xen_hvm_console_init(void) > goto err; > info->vtermno = HVC_COOKIE; > > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > list_add_tail(&info->list, &xenconsoles); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > > return 0; > err: > @@ -296,6 +301,7 @@ static int xencons_info_pv_init(struct xencons_info *info, int vtermno) > static int xen_pv_console_init(void) > { > struct xencons_info *info; > + unsigned long flags; > > if (!xen_pv_domain()) > return -ENODEV; > @@ -312,9 +318,9 @@ static int xen_pv_console_init(void) > /* already configured */ > return 0; > } > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > xencons_info_pv_init(info, HVC_COOKIE); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > > return 0; > } > @@ -322,6 +328,7 @@ static int xen_pv_console_init(void) > static int xen_initial_domain_console_init(void) > { > struct xencons_info *info; > + unsigned long flags; > > if (!xen_initial_domain()) > return -ENODEV; > @@ -337,9 +344,9 @@ static int xen_initial_domain_console_init(void) > info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false); > info->vtermno = HVC_COOKIE; > > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > list_add_tail(&info->list, &xenconsoles); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > > return 0; > } > @@ -394,10 +401,12 @@ static void xencons_free(struct xencons_info *info) > > static int xen_console_remove(struct xencons_info *info) > { > + unsigned long flags; > + > xencons_disconnect_backend(info); > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > list_del(&info->list); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > if (info->xbdev != NULL) > xencons_free(info); > else { > @@ -478,6 +487,7 @@ static int xencons_probe(struct xenbus_device *dev, > { > int ret, devid; > struct xencons_info *info; > + unsigned long flags; > > devid = dev->nodename[strlen(dev->nodename) - 1] - '0'; > if (devid == 0) > @@ -497,9 +507,9 @@ static int xencons_probe(struct xenbus_device *dev, > ret = xencons_connect_backend(dev, info); > if (ret < 0) > goto error; > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > list_add_tail(&info->list, &xenconsoles); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > > return 0; > > @@ -599,10 +609,12 @@ static int __init xen_hvc_init(void) > > info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256); > if (IS_ERR(info->hvc)) { > + unsigned long flags; > + > r = PTR_ERR(info->hvc); > - spin_lock(&xencons_lock); > + spin_lock_irqsave(&xencons_lock, flags); > list_del(&info->list); > - spin_unlock(&xencons_lock); > + spin_unlock_irqrestore(&xencons_lock, flags); > if (info->irq) > unbind_from_irqhandler(info->irq, NULL); > kfree(info); > -- > 2.37.3 >
© 2016 - 2024 Red Hat, Inc.