iodev: Protect iodevs with spinlocks

This makes SMP IRQ reports less flaky

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-11-04 03:53:39 +09:00
parent 85ca5b94cb
commit a383ef0b5a
7 changed files with 61 additions and 11 deletions

View file

@ -301,6 +301,7 @@ const struct iodev_ops iodev_fb_ops = {
struct iodev iodev_fb = {
.ops = &iodev_fb_ops,
.usage = USAGE_CONSOLE,
.lock = SPINLOCK_INIT,
};
static void fb_clear_console(void)

View file

@ -36,7 +36,10 @@ ssize_t iodev_can_read(iodev_id_t id)
if (!iodevs[id]->ops->can_read)
return 0;
return iodevs[id]->ops->can_read(iodevs[id]->opaque);
spin_lock(&iodevs[id]->lock);
ssize_t ret = iodevs[id]->ops->can_read(iodevs[id]->opaque);
spin_unlock(&iodevs[id]->lock);
return ret;
}
bool iodev_can_write(iodev_id_t id)
@ -44,7 +47,12 @@ bool iodev_can_write(iodev_id_t id)
if (!iodevs[id]->ops->can_write)
return false;
return iodevs[id]->ops->can_write(iodevs[id]->opaque);
if (mmu_active())
spin_lock(&iodevs[id]->lock);
bool ret = iodevs[id]->ops->can_write(iodevs[id]->opaque);
if (mmu_active())
spin_unlock(&iodevs[id]->lock);
return ret;
}
ssize_t iodev_read(iodev_id_t id, void *buf, size_t length)
@ -52,7 +60,10 @@ ssize_t iodev_read(iodev_id_t id, void *buf, size_t length)
if (!iodevs[id]->ops->read)
return -1;
return iodevs[id]->ops->read(iodevs[id]->opaque, buf, length);
spin_lock(&iodevs[id]->lock);
ssize_t ret = iodevs[id]->ops->read(iodevs[id]->opaque, buf, length);
spin_unlock(&iodevs[id]->lock);
return ret;
}
ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length)
@ -60,7 +71,12 @@ ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length)
if (!iodevs[id]->ops->write)
return -1;
return iodevs[id]->ops->write(iodevs[id]->opaque, buf, length);
if (mmu_active())
spin_lock(&iodevs[id]->lock);
ssize_t ret = iodevs[id]->ops->write(iodevs[id]->opaque, buf, length);
if (mmu_active())
spin_unlock(&iodevs[id]->lock);
return ret;
}
ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length)
@ -68,7 +84,10 @@ ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length)
if (!iodevs[id]->ops->queue)
return iodev_write(id, buf, length);
return iodevs[id]->ops->queue(iodevs[id]->opaque, buf, length);
spin_lock(&iodevs[id]->lock);
ssize_t ret = iodevs[id]->ops->queue(iodevs[id]->opaque, buf, length);
spin_unlock(&iodevs[id]->lock);
return ret;
}
void iodev_flush(iodev_id_t id)
@ -76,7 +95,9 @@ void iodev_flush(iodev_id_t id)
if (!iodevs[id]->ops->flush)
return;
spin_lock(&iodevs[id]->lock);
iodevs[id]->ops->flush(iodevs[id]->opaque);
spin_unlock(&iodevs[id]->lock);
}
int in_iodev = 0;
@ -89,8 +110,8 @@ void iodev_console_write(const void *buf, size_t length)
if (!do_lock && !is_primary_core()) {
if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) {
iodev_write(IODEV_UART, "*", 1);
iodev_write(IODEV_UART, buf, length);
iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "*", 1);
iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length);
}
return;
}
@ -100,8 +121,8 @@ void iodev_console_write(const void *buf, size_t length)
if (in_iodev) {
if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) {
iodev_write(IODEV_UART, "*", 1);
iodev_write(IODEV_UART, buf, length);
iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "+", 1);
iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length);
}
if (do_lock)
spin_unlock(&console_lock);
@ -183,8 +204,16 @@ void iodev_console_write(const void *buf, size_t length)
void iodev_handle_events(iodev_id_t id)
{
if (in_iodev)
bool do_lock = mmu_active();
if (do_lock)
spin_lock(&console_lock);
if (in_iodev) {
if (do_lock)
spin_unlock(&console_lock);
return;
}
in_iodev++;
@ -195,6 +224,9 @@ void iodev_handle_events(iodev_id_t id)
if (iodev_can_write(id))
iodev_console_write(NULL, 0);
if (do_lock)
spin_unlock(&console_lock);
}
void iodev_console_kick(void)

View file

@ -34,6 +34,7 @@ struct iodev_ops {
struct iodev {
const struct iodev_ops *ops;
spinlock_t lock;
iodev_usage_t usage;
void *opaque;
};

View file

@ -176,4 +176,5 @@ static struct iodev_ops iodev_uart_ops = {
struct iodev iodev_uart = {
.ops = &iodev_uart_ops,
.usage = USAGE_CONSOLE | USAGE_UARTPROXY,
.lock = SPINLOCK_INIT,
};

View file

@ -210,10 +210,12 @@ struct iodev iodev_usb[USB_INSTANCES] = {
{
.ops = &iodev_usb_ops,
.usage = USAGE_CONSOLE | USAGE_UARTPROXY,
.lock = SPINLOCK_INIT,
},
{
.ops = &iodev_usb_ops,
.usage = USAGE_CONSOLE | USAGE_UARTPROXY,
.lock = SPINLOCK_INIT,
},
};
@ -221,10 +223,12 @@ struct iodev iodev_usb_sec[USB_INSTANCES] = {
{
.ops = &iodev_usb_sec_ops,
.usage = 0,
.lock = SPINLOCK_INIT,
},
{
.ops = &iodev_usb_sec_ops,
.usage = 0,
.lock = SPINLOCK_INIT,
},
};

View file

@ -102,6 +102,12 @@ void flush_and_reboot(void)
reboot();
}
void spin_init(spinlock_t *lock)
{
lock->lock = -1;
lock->count = 0;
}
void spin_lock(spinlock_t *lock)
{
s64 me = smp_id();

View file

@ -371,8 +371,13 @@ typedef struct {
int count;
} spinlock_t ALIGNED(64);
#define DECLARE_SPINLOCK(n) spinlock_t n = {-1, 0}
#define SPINLOCK_INIT \
{ \
-1, 0 \
}
#define DECLARE_SPINLOCK(n) spinlock_t n = SPINLOCK_INIT;
void spin_init(spinlock_t *lock);
void spin_lock(spinlock_t *lock);
void spin_unlock(spinlock_t *lock);