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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,12 +53,6 @@
|
|||
// readable in a debugger, which matters more than density here.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Size of the scratch buffer every generator renders into. Must stay equal to
|
||||
// PROC_BUFSZ in kernel/dev/procfs.c: the SchFS and PCI generators there write
|
||||
// up to PROC_BUFSZ regardless of the cap they are handed, so a smaller value
|
||||
// here would be a buffer overrun rather than a truncation.
|
||||
#define PROCFS_GENBUF 2048
|
||||
|
||||
#define PROCFS_ROOT 1ULL
|
||||
#define PROCFS_GLOBAL 0x10000ULL
|
||||
|
||||
|
|
@ -378,15 +372,14 @@ procfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
|
|||
return r;
|
||||
}
|
||||
|
||||
// kmalloc, not kalloc: this buffer is PROCFS_GENBUF and kalloc's unit is a
|
||||
// whole 4 KB page. The comment above about a page not being wasteful was
|
||||
// written when a page was the only thing there was to ask for.
|
||||
if ((buf = kmalloc(PROCFS_GENBUF)) == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
len = gen_file(ip->inum, buf, PROCFS_GENBUF);
|
||||
// The shared buffer in kernel/dev/procfs.c, 64 KB, held until the copy out
|
||||
// is done. It was 2 KB from kmalloc, which cut /proc/cpuinfo off at the
|
||||
// eighteenth processor.
|
||||
int cap;
|
||||
buf = proc_genbuf_get(&cap);
|
||||
len = gen_file(ip->inum, buf, cap);
|
||||
if (off >= (uint64)len) {
|
||||
kmfree(buf);
|
||||
proc_genbuf_put();
|
||||
return 0;
|
||||
}
|
||||
if (off + n > (uint64)len)
|
||||
|
|
@ -400,7 +393,7 @@ procfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
|
|||
memmove((char *)dst, buf + off, n);
|
||||
}
|
||||
|
||||
kmfree(buf);
|
||||
proc_genbuf_put();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -360,16 +360,27 @@ addr_t sys_setgroups(void) { return 0; }
|
|||
|
||||
addr_t sys_sched_yield(void) { return 0; }
|
||||
|
||||
// Every processor that started, since nothing here pins a process anywhere.
|
||||
//
|
||||
// This wrote "CPU 0" and returned 0 -- and the return value is the number of
|
||||
// bytes of mask the kernel wrote, which musl uses to clear the rest. Zero
|
||||
// meant all of it, so the mask came back empty and nproc, make -j and every
|
||||
// thread pool sized from sysconf(_SC_NPROCESSORS_ONLN) saw one processor.
|
||||
addr_t sys_sched_getaffinity(void)
|
||||
{
|
||||
int pid, cpusetsize;
|
||||
addr_t maskp;
|
||||
uchar m[(NCPU + 63) / 64 * 8];
|
||||
argint(0, &pid); argint(1, &cpusetsize); argaddr(2, &maskp);
|
||||
if (maskp && cpusetsize > 0) {
|
||||
char *m = (char*)user_ptr(maskp, (uint64)cpusetsize);
|
||||
if (m) { memset(m, 0, cpusetsize); m[0] = 1; } // CPU 0 only
|
||||
}
|
||||
return 0;
|
||||
if (cpusetsize < (int)sizeof m || (cpusetsize & 7))
|
||||
return -EINVAL;
|
||||
memset(m, 0, sizeof m);
|
||||
for (int i = 0; i < ncpu && i < NCPU; i++)
|
||||
if (cpus[i].started)
|
||||
m[i / 8] |= 1 << (i % 8);
|
||||
if (copy_to_user(maskp, m, sizeof m) < 0)
|
||||
return -EFAULT;
|
||||
return sizeof m;
|
||||
}
|
||||
addr_t sys_sched_setaffinity(void) { return 0; }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue