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
|
|
@ -178,6 +178,11 @@ struct inode {
|
||||||
// Graft it over `target`; 0 or a negative errno. In sysfs.c.
|
// Graft it over `target`; 0 or a negative errno. In sysfs.c.
|
||||||
int sysfs_mount(const char *target);
|
int sysfs_mount(const char *target);
|
||||||
|
|
||||||
|
// The buffer /proc renders a file into, and how big it is; one at a time, so
|
||||||
|
// every get is followed by a put. In kernel/dev/procfs.c.
|
||||||
|
char *proc_genbuf_get(int *cap);
|
||||||
|
void proc_genbuf_put(void);
|
||||||
|
|
||||||
// Mounted FAT volumes, FAT_DEV onward. See include/fat.h.
|
// Mounted FAT volumes, FAT_DEV onward. See include/fat.h.
|
||||||
|
|
||||||
// Linux's limits, for every filesystem behind the VFS. A path component can be
|
// Linux's limits, for every filesystem behind the VFS. A path component can be
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
#define NPROC 128 // maximum number of processes
|
#define NPROC 128 // maximum number of processes
|
||||||
#define KSTACKSIZE 8192 // per-process kernel stack; see kstacks[] in proc.c
|
#define KSTACKSIZE 8192 // per-process kernel stack; see kstacks[] in proc.c
|
||||||
#define KSTACK_CANARY 0xDEADC0DEDEADC0DEULL // sentinel at kstack[0], see kstack_check_canary()
|
#define KSTACK_CANARY 0xDEADC0DEDEADC0DEULL // sentinel at kstack[0], see kstack_check_canary()
|
||||||
#define NCPU 16 // maximum number of CPUs
|
#define NCPU 64 // maximum number of CPUs
|
||||||
#define NOFILE 128 // open files per process
|
#define NOFILE 128 // open files per process
|
||||||
#define NFILE 512 // open files per system
|
#define NFILE 512 // open files per system
|
||||||
#define NINODE 200 // maximum number of active i-nodes
|
#define NINODE 200 // maximum number of active i-nodes
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,34 @@
|
||||||
#include "blkdev.h"
|
#include "blkdev.h"
|
||||||
#include "fat.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
|
// The formatting helpers these files are built out of now live in
|
||||||
// include/sbuf.h, because the debugger's introspection registry needs exactly
|
// 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;
|
case PROC_FILESYSTEMS: gen_filesystems(&sb); break;
|
||||||
// SchFS renders straight into the buffer rather than through sbuf, because
|
// 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.
|
// 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
|
// Same arrangement as SchFS above: pci.c renders its own lines so that it
|
||||||
// does not depend on this file's helpers.
|
// 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.
|
// 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;
|
case 16: gen_schinit(&sb); break;
|
||||||
// And likewise net.c, which is the only file here allowed to include lwIP's
|
// 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
|
// headers -- reaching into the stack from this one would put them on every
|
||||||
// kernel file's include path.
|
// kernel file's include path.
|
||||||
case PROC_NETIF: sb.len = net_info(buf, PROC_BUFSZ); break;
|
case PROC_NETIF: sb.len = net_info(buf, cap); break;
|
||||||
case PROC_RESOLV: sb.len = net_resolv(buf, PROC_BUFSZ); break;
|
case PROC_RESOLV: sb.len = net_resolv(buf, cap); break;
|
||||||
// How this machine was booted, for the programs that start during one.
|
// 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
|
// 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;
|
break;
|
||||||
case PROC_DEVICES: gen_devices(&sb); break;
|
case PROC_DEVICES: gen_devices(&sb); break;
|
||||||
// The block layer renders its own table, like PCI and SchFS above.
|
// 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;
|
default: return 0;
|
||||||
}
|
}
|
||||||
return sb.len;
|
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
|
// 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
|
// local because a page from kalloc() for 2 KB felt worse; kmalloc is the
|
||||||
// answer that was missing.
|
// answer that was missing.
|
||||||
char *buf = kmalloc(PROC_BUFSZ);
|
int cap;
|
||||||
int len;
|
char *buf = proc_genbuf_get(&cap);
|
||||||
|
int len = proc_gen_global(ip->minor, buf, cap);
|
||||||
if (!buf)
|
|
||||||
return -ENOMEM;
|
|
||||||
len = proc_gen_global(ip->minor, buf, PROC_BUFSZ);
|
|
||||||
|
|
||||||
if (len <= 0 || (int)off >= len) {
|
if (len <= 0 || (int)off >= len) {
|
||||||
kmfree(buf);
|
proc_genbuf_put();
|
||||||
return 0; // EOF
|
return 0; // EOF
|
||||||
}
|
}
|
||||||
int avail = len - (int)off;
|
int avail = len - (int)off;
|
||||||
if (n > avail)
|
if (n > avail)
|
||||||
n = avail;
|
n = avail;
|
||||||
memmove(dst, buf + off, n);
|
memmove(dst, buf + off, n);
|
||||||
kmfree(buf);
|
proc_genbuf_put();
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,6 @@
|
||||||
// readable in a debugger, which matters more than density here.
|
// 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_ROOT 1ULL
|
||||||
#define PROCFS_GLOBAL 0x10000ULL
|
#define PROCFS_GLOBAL 0x10000ULL
|
||||||
|
|
||||||
|
|
@ -378,15 +372,14 @@ procfs_read(struct inode *ip, int user_dst, uint64 dst, uint64 off, uint n)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// kmalloc, not kalloc: this buffer is PROCFS_GENBUF and kalloc's unit is a
|
// The shared buffer in kernel/dev/procfs.c, 64 KB, held until the copy out
|
||||||
// whole 4 KB page. The comment above about a page not being wasteful was
|
// is done. It was 2 KB from kmalloc, which cut /proc/cpuinfo off at the
|
||||||
// written when a page was the only thing there was to ask for.
|
// eighteenth processor.
|
||||||
if ((buf = kmalloc(PROCFS_GENBUF)) == 0)
|
int cap;
|
||||||
return -ENOMEM;
|
buf = proc_genbuf_get(&cap);
|
||||||
|
len = gen_file(ip->inum, buf, cap);
|
||||||
len = gen_file(ip->inum, buf, PROCFS_GENBUF);
|
|
||||||
if (off >= (uint64)len) {
|
if (off >= (uint64)len) {
|
||||||
kmfree(buf);
|
proc_genbuf_put();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (off + n > (uint64)len)
|
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);
|
memmove((char *)dst, buf + off, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
kmfree(buf);
|
proc_genbuf_put();
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -360,16 +360,27 @@ addr_t sys_setgroups(void) { return 0; }
|
||||||
|
|
||||||
addr_t sys_sched_yield(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)
|
addr_t sys_sched_getaffinity(void)
|
||||||
{
|
{
|
||||||
int pid, cpusetsize;
|
int pid, cpusetsize;
|
||||||
addr_t maskp;
|
addr_t maskp;
|
||||||
|
uchar m[(NCPU + 63) / 64 * 8];
|
||||||
argint(0, &pid); argint(1, &cpusetsize); argaddr(2, &maskp);
|
argint(0, &pid); argint(1, &cpusetsize); argaddr(2, &maskp);
|
||||||
if (maskp && cpusetsize > 0) {
|
if (cpusetsize < (int)sizeof m || (cpusetsize & 7))
|
||||||
char *m = (char*)user_ptr(maskp, (uint64)cpusetsize);
|
return -EINVAL;
|
||||||
if (m) { memset(m, 0, cpusetsize); m[0] = 1; } // CPU 0 only
|
memset(m, 0, sizeof m);
|
||||||
}
|
for (int i = 0; i < ncpu && i < NCPU; i++)
|
||||||
return 0;
|
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; }
|
addr_t sys_sched_setaffinity(void) { return 0; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue