Commit graph SchOS/kernel/fs/schfs/vfs.c
Author SHA1 Message Date
Nexoniarz
3919f1446e schfs: mount a snapshot by name, read-only
`mount -t schfs @golden /mnt` mounts a snapshot. The source argument
finally means something: sys_mount ignored it outright, because there was
one volume and nothing to choose between. `@name` rather than a path
because a snapshot is not a device -- there is no node in /dev for it and
there should not be, since it lives inside the volume already mounted.

A second volume rather than a parameter threaded through all 23 schfs_k*
entry points. Only the read side can ever address it, so the write path
keeps talking to the live volume and needed no change; the four read
entry points grew _dev variants and pick the volume from the device the
inode already carries. One fs_ops table serves both, because every entry
takes an inode and dispatches on it -- a second table would be the same
pointers under a different name.

It needs its own device number: a snapshot's object ids are the *same
numbers* as the live tree's, so one device would make the two views alias
in the inode cache and hand a reader whichever was cached first.

Three things this turned up, none of them new but all of them reachable
for the first time:

  - sys_mount refused with `dev == SCHFS_DEV`, meaning "do not mount SchFS
    onto SchFS". A snapshot's mount point is necessarily on the live root,
    so that refused the entire feature. The real condition is mounting a
    device onto *itself*, which is `dev == fsdev`.
  - createat allocates an inode and then panics if dirlink fails --
    reasonable about a writable filesystem, a crash on a read-only one.
    The refusal now happens before anything is allocated. Unlink and
    rename already handled a refusal properly.
  - a refused create reported ENOENT, so writing to a snapshot said "No
    such file or directory" -- the sentence a shell prints for every
    create failure. It reports EROFS now.

Verified in the running system against an image built with
--snapshot golden: the mount succeeds, ls lists the snapshot's tree,
/mnt2/etc/motd reads back through it, and a write is refused with
"Read-only file system".

One at a time, and no umount path for it yet: mount_del_by_root removes
the mount table entry, but ksnap_unmount is only called on the failure
paths. schfs-check passes; bootcheck 108 [ok].

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 21:10:59 +02:00
Nexoniarz
08e7878d8c schfs: read a whole block per tree descent, not half of one
BOUNCE was 2048 while the comment directly above it said "one page at a
time", and the buffer it sizes is a single kalloc() -- so half of every
bounce buffer went unused and every read did twice the work it needed to.

The chunk size is not a copy-size tuning knob. Each trip through the loop
takes the volume sleeplock and builds a fresh B-tree cursor to find the
extent for that offset, so it really sets how many tree descents happen
per byte read. An 834 KB file took 417 of them; it now takes 209.

Measured in the guest, bracketed inside one command line so nothing
depends on host timing: reading 834 KB goes from 1.73s to 0.90s under
TCG, cold and warm alike. Warm equalling cold is itself the tell -- the
cost was never the disk, it was the per-chunk work, which is why KVM was
6.4x faster on the same read while caching made no difference at all.

This does not fix `ls`, and the numbers say why: 2.80s -> 2.65s cold.
loaduvm already reads a page at a time, so exec was never the bottleneck
there. At 241 entries in 2.65s that is ~11ms per file, spent in the
per-entry lookup path rather than in reading anything.

schfs-check passes; bootcheck 108 [ok].

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 23:41:11 +02:00
Nexoniarz
d0f7acf9eb Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
2026-08-09 13:47:01 +02:00