Changeset c4cfe4c in mainline for kernel/generic/src/console/console.c


Ignore:
Timestamp:
2025-04-17T16:01:16Z (5 days ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master
Children:
888c06e
Parents:
1db4e2ae (diff), 250a435 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-17 15:51:11)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-17 16:01:16)
Message:

Convert kernel console writing to byte arrays

More buffer per buffer (the original char32_t buffer takes up four
times as much space for the same amount of backlog, which is wasteful).
It is also faster, possibly thanks to bigger chunks being processed in bulk.
Gonna try to figure out if the locking can be improved further.

Also changed to use a syscall for reading KIO buffer from uspace,
to allow better synchronization.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/console/console.c

    r1db4e2ae rc4cfe4c  
    22 * Copyright (c) 2003 Josef Cejka
    33 * Copyright (c) 2005 Jakub Jermar
     4 * Copyright (c) 2025 Jiří Zárevúcky
    45 * All rights reserved.
    56 *
     
    3536
    3637#include <abi/kio.h>
    37 #include <arch.h>
    38 #include <assert.h>
    39 #include <atomic.h>
    4038#include <console/chardev.h>
    4139#include <console/console.h>
    42 #include <ddi/ddi.h>
    43 #include <ddi/irq.h>
    4440#include <errno.h>
    4541#include <ipc/event.h>
    46 #include <ipc/irq.h>
    47 #include <mm/frame.h> /* SIZE2FRAMES */
     42#include <log.h>
    4843#include <panic.h>
    4944#include <preemption.h>
    50 #include <proc/thread.h>
    51 #include <putchar.h>
     45#include <proc/task.h>
    5246#include <stdatomic.h>
    5347#include <stdio.h>
     
    5650#include <synch/mutex.h>
    5751#include <synch/spinlock.h>
    58 #include <synch/waitq.h>
    5952#include <syscall/copy.h>
    6053#include <sysinfo/sysinfo.h>
    61 #include <typedefs.h>
    6254
    6355#define KIO_PAGES    8
    64 #define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE / sizeof(char32_t))
     56#define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE)
    6557
    6658/** Kernel log cyclic buffer */
    67 char32_t kio[KIO_LENGTH] __attribute__((aligned(PAGE_SIZE)));
     59static char kio[KIO_LENGTH];
    6860
    6961/** Kernel log initialized */
     
    7668static MUTEX_INITIALIZE(console_mutex, MUTEX_RECURSIVE);
    7769
    78 /** First kernel log characters */
    79 static size_t kio_start = 0;
    80 
    81 /** Number of valid kernel log characters */
    82 static size_t kio_len = 0;
    83 
    84 /** Number of stored (not printed) kernel log characters */
    85 static size_t kio_stored = 0;
    86 
    87 /** Number of stored kernel log characters for uspace */
    88 static size_t kio_uspace = 0;
     70/** Number of characters written to buffer. Periodically overflows. */
     71static size_t kio_written = 0;
     72
     73/** Number of characters written to output devices. Periodically overflows. */
     74static size_t kio_processed = 0;
     75
     76/** Last notification sent to uspace. */
     77static size_t kio_notified = 0;
    8978
    9079/** Kernel log spinlock */
    91 SPINLOCK_INITIALIZE_NAME(kio_lock, "kio_lock");
    92 
    93 /** Physical memory area used for kio buffer */
    94 static parea_t kio_parea;
     80IRQ_SPINLOCK_INITIALIZE(kio_lock);
     81
     82static IRQ_SPINLOCK_INITIALIZE(flush_lock);
     83
     84static IRQ_SPINLOCK_INITIALIZE(early_mbstate_lock);
     85static mbstate_t early_mbstate;
    9586
    9687static indev_t stdin_sink;
     
    10495};
    10596
    106 static void stdout_write(outdev_t *, char32_t);
     97static void stdout_write(outdev_t *, const char *, size_t);
    10798static void stdout_redraw(outdev_t *);
    10899static void stdout_scroll_up(outdev_t *);
     
    157148}
    158149
    159 static void stdout_write(outdev_t *dev, char32_t ch)
     150static void stdout_write(outdev_t *dev, const char *s, size_t n)
    160151{
    161152        list_foreach(dev->list, link, outdev_t, sink) {
    162153                if ((sink) && (sink->op->write))
    163                         sink->op->write(sink, ch);
     154                        sink->op->write(sink, s, n);
    164155        }
    165156}
     
    190181
    191182/** Initialize kernel logging facility
    192  *
    193  * The shared area contains kernel cyclic buffer. Userspace application may
    194  * be notified on new data with indication of position and size
    195  * of the data within the circular buffer.
    196  *
    197183 */
    198184void kio_init(void)
    199185{
    200         void *faddr = (void *) KA2PA(kio);
    201 
    202         assert((uintptr_t) faddr % FRAME_SIZE == 0);
    203 
    204         ddi_parea_init(&kio_parea);
    205         kio_parea.pbase = (uintptr_t) faddr;
    206         kio_parea.frames = SIZE2FRAMES(sizeof(kio));
    207         kio_parea.unpriv = false;
    208         kio_parea.mapped = false;
    209         ddi_parea_register(&kio_parea);
    210 
    211         sysinfo_set_item_val("kio.faddr", NULL, (sysarg_t) faddr);
    212         sysinfo_set_item_val("kio.pages", NULL, KIO_PAGES);
    213 
    214186        event_set_unmask_callback(EVENT_KIO, kio_update);
    215187        atomic_store(&kio_inited, true);
     
    257229                return;
    258230
    259         spinlock_lock(&kio_lock);
    260 
    261         if (kio_uspace > 0) {
    262                 if (event_notify_3(EVENT_KIO, true, kio_start, kio_len,
    263                     kio_uspace) == EOK)
    264                         kio_uspace = 0;
    265         }
    266 
    267         spinlock_unlock(&kio_lock);
     231        irq_spinlock_lock(&kio_lock, true);
     232
     233        if (kio_notified != kio_written) {
     234                if (event_notify_1(EVENT_KIO, true, kio_written) == EOK)
     235                        kio_notified = kio_written;
     236        }
     237
     238        irq_spinlock_unlock(&kio_lock, true);
    268239}
    269240
     
    278249                return;
    279250
    280         spinlock_lock(&kio_lock);
     251        irq_spinlock_lock(&kio_lock, true);
     252
     253        if (!irq_spinlock_trylock(&flush_lock)) {
     254                /* Someone is currently flushing. */
     255                irq_spinlock_unlock(&kio_lock, true);
     256                return;
     257        }
     258
     259        /* A small-ish local buffer so that we can write to output in chunks. */
     260        char buffer[256];
    281261
    282262        /* Print characters that weren't printed earlier */
    283         while (kio_stored > 0) {
    284                 char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
    285                 kio_stored--;
     263        while (kio_written != kio_processed) {
     264                size_t offset = kio_processed % KIO_LENGTH;
     265                size_t len = min(kio_written - kio_processed, KIO_LENGTH - offset);
     266                len = min(len, sizeof(buffer));
     267
     268                /* Take out a chunk of the big buffer. */
     269                memcpy(buffer, &kio[offset], len);
     270                kio_processed += len;
    286271
    287272                /*
    288                  * We need to give up the spinlock for
    289                  * the physical operation of writing out
    290                  * the character.
     273                 * We need to give up the spinlock for the physical operation of writing
     274                 * out the buffer.
    291275                 */
    292                 spinlock_unlock(&kio_lock);
    293                 stdout->op->write(stdout, tmp);
    294                 spinlock_lock(&kio_lock);
    295         }
    296 
    297         spinlock_unlock(&kio_lock);
    298 }
    299 
    300 /** Put a character into the output buffer.
    301  *
    302  * The caller is required to hold kio_lock
    303  */
    304 void kio_push_char(const char32_t ch)
    305 {
    306         kio[(kio_start + kio_len) % KIO_LENGTH] = ch;
    307         if (kio_len < KIO_LENGTH)
    308                 kio_len++;
    309         else
    310                 kio_start = (kio_start + 1) % KIO_LENGTH;
    311 
    312         if (kio_stored < kio_len)
    313                 kio_stored++;
    314 
    315         /* The character is stored for uspace */
    316         if (kio_uspace < kio_len)
    317                 kio_uspace++;
    318 }
    319 
    320 void putuchar(const char32_t ch)
     276                irq_spinlock_unlock(&kio_lock, true);
     277                stdout->op->write(stdout, buffer, len);
     278                irq_spinlock_lock(&kio_lock, true);
     279        }
     280
     281        irq_spinlock_unlock(&flush_lock, false);
     282        irq_spinlock_unlock(&kio_lock, true);
     283}
     284
     285void kio_push_bytes(const char *s, size_t n)
     286{
     287        /* Skip the section we know we can't keep. */
     288        if (n > KIO_LENGTH) {
     289                size_t lost = n - KIO_LENGTH;
     290                kio_written += lost;
     291                s += lost;
     292                n -= lost;
     293        }
     294
     295        size_t offset = kio_written % KIO_LENGTH;
     296        if (offset + n > KIO_LENGTH) {
     297                size_t first = KIO_LENGTH - offset;
     298                size_t last = n - first;
     299                memcpy(kio + offset, s, first);
     300                memcpy(kio, s + first, last);
     301        } else {
     302                memcpy(kio + offset, s, n);
     303        }
     304
     305        kio_written += n;
     306}
     307
     308static void early_putstr(const char *s, size_t n)
     309{
     310        irq_spinlock_lock(&early_mbstate_lock, true);
     311
     312        size_t offset = 0;
     313        char32_t c;
     314
     315        while ((c = str_decode_r(s, &offset, n, U_SPECIAL, &early_mbstate)))
     316                early_putuchar(c);
     317
     318        irq_spinlock_unlock(&early_mbstate_lock, true);
     319}
     320
     321void putstr(const char *s, size_t n)
    321322{
    322323        bool ordy = ((stdout) && (stdout->op->write));
    323324
    324         spinlock_lock(&kio_lock);
    325         kio_push_char(ch);
    326         spinlock_unlock(&kio_lock);
     325        irq_spinlock_lock(&kio_lock, true);
     326        kio_push_bytes(s, n);
     327        irq_spinlock_unlock(&kio_lock, true);
    327328
    328329        /* Output stored characters */
     
    340341                 * a no-op on certain hardware configurations.
    341342                 */
    342                 early_putuchar(ch);
    343         }
    344 
    345         /* Force notification on newline */
    346         if (ch == '\n')
     343                early_putstr(s, n);
     344        }
     345
     346        /* Force notification when containing a newline */
     347        if (memchr(s, '\n', n) != NULL)
    347348                kio_update(NULL);
     349}
     350
     351/** Reads up to `size` characters from kio buffer starting at character `at`.
     352 *
     353 * @param size  Maximum number of characters that can be stored in buffer.
     354 *              Values greater than KIO_LENGTH are silently treated as KIO_LENGTH
     355 *              for the purposes of calculating the return value.
     356 * @return Number of characters read. Can be more than `size`.
     357 *         In that case, `size` characters are written to user buffer
     358 *         and the extra amount is the number of characters missed.
     359 */
     360sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at)
     361{
     362        errno_t rc;
     363        size_t missed = 0;
     364
     365        irq_spinlock_lock(&kio_lock, true);
     366
     367        if (at == kio_written) {
     368                irq_spinlock_unlock(&kio_lock, true);
     369                return 0;
     370        }
     371
     372        size_t readable_chars = kio_written - at;
     373        if (readable_chars > KIO_LENGTH) {
     374                missed = readable_chars - KIO_LENGTH;
     375                readable_chars = KIO_LENGTH;
     376        }
     377
     378        size_t actual_read = min(readable_chars, size);
     379        size_t offset = (kio_written - readable_chars) % KIO_LENGTH;
     380
     381        if (offset + actual_read > KIO_LENGTH) {
     382                size_t first = KIO_LENGTH - offset;
     383                size_t last = actual_read - first;
     384
     385                rc = copy_to_uspace(buf, &kio[offset], first);
     386                if (rc == EOK)
     387                        rc = copy_to_uspace(buf + first, &kio[0], last);
     388        } else {
     389                rc = copy_to_uspace(buf, &kio[offset], actual_read);
     390        }
     391
     392        irq_spinlock_unlock(&kio_lock, true);
     393
     394        if (rc != EOK) {
     395                log(LF_OTHER, LVL_WARN,
     396                    "[%s(%" PRIu64 ")] Terminating due to invalid memory buffer"
     397                    " in SYS_KIO_READ.\n", TASK->name, TASK->taskid);
     398                task_kill_self(true);
     399        }
     400
     401        return actual_read + missed;
    348402}
    349403
Note: See TracChangeset for help on using the changeset viewer.