On Tue, 3 Feb 2026, Jan Beulich wrote:
> On 29.01.2026 23:08, Stefano Stabellini wrote:
> > --- a/xen/drivers/char/console.c
> > +++ b/xen/drivers/char/console.c
> > @@ -518,11 +518,16 @@ static unsigned int __read_mostly console_rx = 0;
> > struct domain *console_get_domain(void)
> > {
> > struct domain *d;
> > + unsigned int rx;
> >
> > - if ( console_rx == 0 )
> > + nrspin_lock(&console_lock);
> > + rx = console_rx;
> > + nrspin_unlock(&console_lock);
>
> Did you test this in a debug build, and it didn't blow up? The console lock
> is an IRQ-safe one, so I'd expect check_lock() to complain that you do not
> disable IRQs here. At the same time I don't think you can rely on IRQs being
> off upon entry into the function.
>
> Anyway - is locking here really needed? Wouldn't suitable use of ACCESS_ONCE()
> (also elsewhere) do? (Such a switchover likely could be a separate, prereq
> patch.)
I created a prereq patch which introduces ACCESS_ONCE everywhere for
console_rx
> Further, if already you add locking on the read sides, what about ...
>
> > @@ -540,6 +545,12 @@ void console_put_domain(struct domain *d)
> > rcu_unlock_domain(d);
> > }
> >
> > +static bool is_focus_domain(const struct domain *d)
> > +{
> > + ASSERT(rspin_is_locked(&console_lock));
> > + return d != NULL && d->domain_id == console_rx - 1;
> > +}
> > +
> > static void console_switch_input(void)
> > {
> > unsigned int next_rx = console_rx;
>
> ... this read?
>
> > @@ -555,7 +566,10 @@ static void console_switch_input(void)
> >
> > if ( next_rx++ >= max_console_rx )
> > {
> > + nrspin_lock_irq(&console_lock);
>
> As indicated earlier, you can't know IRQ state in anything down the call
> tree from serial_rx().
I'll switch to the irqsave/restore versions in console_switch_input
>
> > @@ -742,17 +765,36 @@ static long
> > guest_console_write(XEN_GUEST_HANDLE_PARAM(char) buffer,
> > if ( copy_from_guest(kbuf, buffer, kcount) )
> > return -EFAULT;
> >
> > - if ( is_hardware_domain(cd) )
> > + /*
> > + * Take both cons->lock and console_lock:
> > + * - cons->lock protects cons->buf and cons->idx
> > + * - console_lock protects console_send() and is_focus_domain()
> > + * checks
> > + *
> > + * The order must be respected. guest_printk() takes the
> > + * console_lock so it is important that cons->lock is taken
> > + * first.
> > + */
> > + spin_lock(&cons->lock);
> > + nrspin_lock_irq(&console_lock);
>
> While guest_printk() does matter here, it taking (down the call tree)
> console_lock isn't alone relevant. It being called with cons->lock held
> is, .
I updated the in-code comment
>
> > @@ -816,22 +857,39 @@ long do_console_io(
> > break;
> >
> > rc = 0;
> > + nrspin_lock_irq(&console_lock);
> > + if ( !is_focus_domain(current->domain) )
> > + count = 0;
> > while ( (serial_rx_cons != serial_rx_prod) && (rc < count) )
> > {
> > idx = SERIAL_RX_MASK(serial_rx_cons);
> > len = serial_rx_prod - serial_rx_cons;
> > + nrspin_unlock_irq(&console_lock);
>
> Can we please have blank lines on both sides of this?
sure
> > if ( (idx + len) > SERIAL_RX_SIZE )
> > len = SERIAL_RX_SIZE - idx;
> > if ( (rc + len) > count )
> > len = count - rc;
> > if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx],
> > len) )
>
> Have I perhaps talked you into moving the unlock too early? serial_rx_ring[]
> accesses look like they need to be with the lock still held. Or, to avoid
> calling copy_to_guest_offset() with the lock held, a local copy would need
> making.
I introduced a local copy