schfs: several snapshots at once, and unmount gives the slot back
Two loose ends from the mount-by-name commit, both closed. A slot is a device number, so supporting more than one snapshot is a small array rather than a redesign: SCHFS_SNAP_DEV through SCHFS_SNAP_DEV + SCHFS_SNAP_SLOTS - 1, each with its own volume and its own scratch pages, allocated on first use. Four of them -- "one at a time" is an odd thing to have to explain, and the cost is two pages per slot. Not many, because each one is a device number and those are a small fixed space. The leak mattered more. umount removed the mount table entry and left the slot marked in use, so a snapshot could be unmounted and then never mounted again until reboot. sys_umount2 releases it now, keyed off the device it already has in hand. Verified in the running system: two snapshots mounted at once, both readable, both unmounted, and a third mount afterwards succeeds and reads correctly -- which is the case that failed before. schfs-check passes; bootcheck 108 [ok]. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3919f1446e
commit
b34779111a
6 changed files with 75 additions and 47 deletions
|
|
@ -1417,7 +1417,8 @@ addr_t sys_mount(void)
|
|||
// the first second thing to mount. `@` 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 that is already mounted.
|
||||
int snapmount = 0;
|
||||
int snapmount = 0;
|
||||
uint snapdev = 0;
|
||||
if (source && source[0] == '@') {
|
||||
const char *nm = source + 1;
|
||||
unsigned nlen = 0;
|
||||
|
|
@ -1429,24 +1430,24 @@ addr_t sys_mount(void)
|
|||
// Distinct codes, because "resource busy" for every possible failure
|
||||
// tells the caller nothing and cost an hour of guessing once already.
|
||||
int r = schfs_ksnap_mount(nm, nlen);
|
||||
if (r != 0) {
|
||||
cprintf("schfs: snapshot mount '%s' failed (%d)\n", nm, r);
|
||||
if (r < 0) {
|
||||
switch (r) {
|
||||
case -1: return -ENODEV; /* no volume mounted to look in */
|
||||
case -2: return -EBUSY; /* a snapshot is already mounted */
|
||||
case -2: return -EBUSY; /* every snapshot slot in use */
|
||||
case -3: return -ENOMEM; /* no scratch blocks */
|
||||
default: return -ENOENT; /* no snapshot by that name */
|
||||
}
|
||||
}
|
||||
snapdev = (uint)r;
|
||||
snapmount = 1;
|
||||
}
|
||||
if (!schfs_kmounted())
|
||||
return -ENODEV;
|
||||
|
||||
uint64 root = snapmount ? schfs_ksnap_root() : schfs_kroot();
|
||||
uint64 root = snapmount ? schfs_ksnap_root(snapdev) : schfs_kroot();
|
||||
if (root == 0) {
|
||||
if (snapmount)
|
||||
schfs_ksnap_unmount();
|
||||
schfs_ksnap_unmount(snapdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -1468,12 +1469,12 @@ addr_t sys_mount(void)
|
|||
// the only volume there could be. A snapshot is a second device, and its
|
||||
// mount point is necessarily on the live root -- there is nowhere else to
|
||||
// put it -- so the old spelling refused every snapshot mount.
|
||||
uint fsdev = snapmount ? SCHFS_SNAP_DEV : SCHFS_DEV;
|
||||
uint fsdev = snapmount ? snapdev : SCHFS_DEV;
|
||||
if (dev == fsdev)
|
||||
return -EBUSY;
|
||||
if (mount_add(dev, inum, fsdev, root) != 0) {
|
||||
if (snapmount)
|
||||
schfs_ksnap_unmount();
|
||||
schfs_ksnap_unmount(snapdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -1495,5 +1496,13 @@ addr_t sys_umount2(void)
|
|||
iput(dp);
|
||||
end_op();
|
||||
|
||||
return mount_del_by_root(dev, inum) == 0 ? 0 : -EINVAL;
|
||||
if (mount_del_by_root(dev, inum) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
// Release the snapshot slot with the mount. Without this the table entry
|
||||
// went away and the slot stayed taken, so a volume could be unmounted and
|
||||
// never mounted again until reboot.
|
||||
if (SCHFS_IS_SNAP_DEV(dev))
|
||||
schfs_ksnap_unmount(dev);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue