fs: inode and open-file tables that grow, with a hash for iget
The inode table was NINODE (200) entries, searched end to end on every
iget(), and a panic -- "iget: no inodes" -- when all of them were held.
Held means open, a cwd, a running binary or a directory mid-walk, so a
few busy processes reached it. iget() now finds an inode through a hash
on (dev, inum), takes a free one from a list, and makes more from a
slab cache once the NINODE in the image are all in use.
The open-file table had the same shape, NFILE (512) and then ENFILE,
and gets the same treatment: a free list, and the slab past NFILE.
/proc/fsstat's peaks now read against how many exist.
getrlimit(RLIMIT_NOFILE) said unlimited, which musl turns into
sysconf(_SC_OPEN_MAX) == -1, after which zsh believed it had no
descriptors and `exec {fd}<file` failed on the first one. It reports
NOFILE now. And a comment in sys_fs.c held a "*/" that ended it early.
Checked under QEMU: six processes holding 115 files each, 713 open
descriptions and 341 inodes at once, all back to the baseline after.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
62d2a85ac8
commit
09e07423e8
6 changed files with 161 additions and 40 deletions
|
|
@ -9,18 +9,38 @@
|
|||
#include "fsstat.h"
|
||||
#include "posix.h"
|
||||
#include "errno.h"
|
||||
#include "kmem.h"
|
||||
|
||||
extern struct devsw devsw[];
|
||||
|
||||
// Open file descriptions, shared by every process. NFILE of them in the
|
||||
// kernel image and more from a slab cache once those are all open; a closed
|
||||
// one goes on the free list. This was NFILE and then ENFILE, which a few
|
||||
// processes each holding a hundred descriptors -- an editor, a build, a
|
||||
// shell -- were already a third of the way to.
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct file file[NFILE];
|
||||
struct file *free;
|
||||
struct kmem_cache *more;
|
||||
uint n;
|
||||
} ftable;
|
||||
|
||||
void
|
||||
fileinit(void)
|
||||
{
|
||||
initlock(&ftable.lock, "ftable");
|
||||
for(int i = NFILE - 1; i >= 0; i--){
|
||||
ftable.file[i].fnext = ftable.free;
|
||||
ftable.free = &ftable.file[i];
|
||||
}
|
||||
ftable.n = NFILE;
|
||||
}
|
||||
|
||||
uint
|
||||
file_slots(void)
|
||||
{
|
||||
return ftable.n;
|
||||
}
|
||||
|
||||
struct file*
|
||||
|
|
@ -29,21 +49,28 @@ filealloc(void)
|
|||
struct file *f;
|
||||
|
||||
acquire(&ftable.lock);
|
||||
for(f = ftable.file; f < ftable.file + NFILE; f++){
|
||||
if(f->ref == 0){
|
||||
f->ref = 1;
|
||||
FSSTAT_TAKE(fsstat.file_used, fsstat.file_peak); // 0 -> 1
|
||||
// Cleared here rather than left to the caller: every creator sets
|
||||
// readable/writable/off, but only open() sets nonblock, so a struct file
|
||||
// recycled from a non-blocking device would hand its flag to the next
|
||||
// pipe or socket that happened to land in this slot.
|
||||
f->nonblock = 0;
|
||||
if((f = ftable.free) != 0){
|
||||
ftable.free = f->fnext;
|
||||
} else {
|
||||
// The slab allocator takes only its own lock and kalloc's.
|
||||
if(ftable.more == 0)
|
||||
ftable.more = kmem_cache_create("file", sizeof(struct file));
|
||||
if(ftable.more == 0 || (f = kmem_cache_alloc(ftable.more)) == 0){
|
||||
release(&ftable.lock);
|
||||
return f;
|
||||
return 0;
|
||||
}
|
||||
memset(f, 0, sizeof *f);
|
||||
ftable.n++;
|
||||
}
|
||||
f->ref = 1;
|
||||
FSSTAT_TAKE(fsstat.file_used, fsstat.file_peak); // 0 -> 1
|
||||
// Cleared here rather than left to the caller: every creator sets
|
||||
// readable/writable/off, but only open() sets nonblock, so a struct file
|
||||
// recycled from a non-blocking device would hand its flag to the next
|
||||
// pipe or socket that happened to land in this slot.
|
||||
f->nonblock = 0;
|
||||
release(&ftable.lock);
|
||||
return 0;
|
||||
return f;
|
||||
}
|
||||
|
||||
struct file*
|
||||
|
|
@ -72,6 +99,8 @@ fileclose(struct file *f)
|
|||
ff = *f;
|
||||
f->ref = 0;
|
||||
f->type = FD_NONE;
|
||||
f->fnext = ftable.free;
|
||||
ftable.free = f;
|
||||
FSSTAT_DROP(fsstat.file_used); // 1 -> 0
|
||||
release(&ftable.lock);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue