smp: 64 processors, and say how many there are
NCPU was 16, under what a desktop has today. 64 costs 48 more TSS I/O bitmaps and scheduler stacks, about 800 KB of BSS. Past 254 the APIC would need x2APIC mode, which is a separate change. With more than 16 there were two other ceilings. sched_getaffinity wrote "CPU 0" and returned 0, and musl clears every byte past the count returned -- so the mask came back empty and nproc, make -j and sysconf(_SC_NPROCESSORS_ONLN) all said 1, on any machine. It returns the mask of processors that started, and its size, now. And /proc rendered every file into 2 KB, which /proc/cpuinfo ran out of at the eighteenth processor without a word; /proc/pci, /proc/interrupts and /proc/mounts were each one bigger machine from the same cut. There is one 64 KB buffer for it now behind a sleeplock, since there is no allocator for contiguous pages, and the generators that write raw are given the real capacity instead of a constant. Checked under QEMU with -smp 24: nproc 24, /proc/cpuinfo whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
09e07423e8
commit
c1af31f75a
5 changed files with 69 additions and 36 deletions
|
|
@ -43,7 +43,34 @@
|
|||
#include "blkdev.h"
|
||||
#include "fat.h"
|
||||
|
||||
#define PROC_BUFSZ 2048
|
||||
// Every generated file is rendered whole into one buffer and then sliced, so
|
||||
// this is the largest any of them can be. It was 2048, which a 24-processor
|
||||
// /proc/cpuinfo passed at the eighteenth CPU, and /proc/pci, /proc/mounts and
|
||||
// /proc/interrupts were each one larger machine away from the same quiet cut.
|
||||
//
|
||||
// One buffer, not one per read: 64 KB is sixteen pages and there is no
|
||||
// allocator for contiguous pages. The lock serialises /proc reads, which are
|
||||
// short and not a hot path -- top reads a few files a second.
|
||||
#define PROC_BUFSZ (64 * 1024)
|
||||
|
||||
static char genbuf[PROC_BUFSZ] __attribute__((aligned(4096)));
|
||||
static struct sleeplock genlock = {
|
||||
.lk = { .name = "procgen" }, .name = "procgen",
|
||||
};
|
||||
|
||||
char *
|
||||
proc_genbuf_get(int *cap)
|
||||
{
|
||||
acquiresleep(&genlock);
|
||||
*cap = PROC_BUFSZ;
|
||||
return genbuf;
|
||||
}
|
||||
|
||||
void
|
||||
proc_genbuf_put(void)
|
||||
{
|
||||
releasesleep(&genlock);
|
||||
}
|
||||
|
||||
// The formatting helpers these files are built out of now live in
|
||||
// include/sbuf.h, because the debugger's introspection registry needs exactly
|
||||
|
|
@ -383,18 +410,18 @@ proc_gen_global(int id, char *buf, int cap)
|
|||
case PROC_FILESYSTEMS: gen_filesystems(&sb); break;
|
||||
// SchFS renders straight into the buffer rather than through sbuf, because
|
||||
// it lives in kernel/fs/schfs and must not depend on this file's helpers.
|
||||
case PROC_SCHFS: sb.len = schfs_kinfo(buf, PROC_BUFSZ); break;
|
||||
case PROC_SCHFS: sb.len = schfs_kinfo(buf, cap); break;
|
||||
// Same arrangement as SchFS above: pci.c renders its own lines so that it
|
||||
// does not depend on this file's helpers.
|
||||
case PROC_PCI: sb.len = pci_info(buf, PROC_BUFSZ); break;
|
||||
case PROC_PCI: sb.len = pci_info(buf, cap); break;
|
||||
// Likewise irq.c: it owns the table, so it renders it.
|
||||
case PROC_INTERRUPTS: sb.len = irq_info(buf, PROC_BUFSZ); break;
|
||||
case PROC_INTERRUPTS: sb.len = irq_info(buf, cap); break;
|
||||
case 16: gen_schinit(&sb); break;
|
||||
// And likewise net.c, which is the only file here allowed to include lwIP's
|
||||
// headers -- reaching into the stack from this one would put them on every
|
||||
// kernel file's include path.
|
||||
case PROC_NETIF: sb.len = net_info(buf, PROC_BUFSZ); break;
|
||||
case PROC_RESOLV: sb.len = net_resolv(buf, PROC_BUFSZ); break;
|
||||
case PROC_NETIF: sb.len = net_info(buf, cap); break;
|
||||
case PROC_RESOLV: sb.len = net_resolv(buf, cap); break;
|
||||
// How this machine was booted, for the programs that start during one.
|
||||
//
|
||||
// schinit and the jobs it runs have the same problem the kernel does -- most
|
||||
|
|
@ -412,7 +439,7 @@ proc_gen_global(int id, char *buf, int cap)
|
|||
break;
|
||||
case PROC_DEVICES: gen_devices(&sb); break;
|
||||
// The block layer renders its own table, like PCI and SchFS above.
|
||||
case PROC_PARTITIONS: sb.len = blkdev_info(buf, PROC_BUFSZ); break;
|
||||
case PROC_PARTITIONS: sb.len = blkdev_info(buf, cap); break;
|
||||
default: return 0;
|
||||
}
|
||||
return sb.len;
|
||||
|
|
@ -433,22 +460,19 @@ procread(struct inode *ip, uint64 off, char *dst, int n)
|
|||
// above has warned about exactly that since it was written. It stayed a
|
||||
// local because a page from kalloc() for 2 KB felt worse; kmalloc is the
|
||||
// answer that was missing.
|
||||
char *buf = kmalloc(PROC_BUFSZ);
|
||||
int len;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
len = proc_gen_global(ip->minor, buf, PROC_BUFSZ);
|
||||
int cap;
|
||||
char *buf = proc_genbuf_get(&cap);
|
||||
int len = proc_gen_global(ip->minor, buf, cap);
|
||||
|
||||
if (len <= 0 || (int)off >= len) {
|
||||
kmfree(buf);
|
||||
proc_genbuf_put();
|
||||
return 0; // EOF
|
||||
}
|
||||
int avail = len - (int)off;
|
||||
if (n > avail)
|
||||
n = avail;
|
||||
memmove(dst, buf + off, n);
|
||||
kmfree(buf);
|
||||
proc_genbuf_put();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue