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
|
|
@ -58,10 +58,10 @@ static int kmounted;
|
|||
// A second volume rather than a parameter threaded through all 23 entry points,
|
||||
// because only the read side can ever address it -- a snapshot cannot be
|
||||
// written to, so every mutation keeps talking to kvol and needs no change.
|
||||
static struct schfs_vol ksnapvol;
|
||||
static int ksnap_mounted;
|
||||
static char *ksnapcurblk;
|
||||
static char *ksnapdatablk;
|
||||
static struct schfs_vol ksnapvol[SCHFS_SNAP_SLOTS];
|
||||
static int ksnap_mounted[SCHFS_SNAP_SLOTS];
|
||||
static char *ksnapcurblk[SCHFS_SNAP_SLOTS];
|
||||
static char *ksnapdatablk[SCHFS_SNAP_SLOTS];
|
||||
|
||||
// Block buffers, allocated once at mount.
|
||||
//
|
||||
|
|
@ -267,44 +267,49 @@ schfs_kmount(uint dev)
|
|||
static struct schfs_vol *
|
||||
vol_for(uint dev)
|
||||
{
|
||||
return dev == SCHFS_SNAP_DEV ? &ksnapvol : &kvol;
|
||||
return SCHFS_IS_SNAP_DEV(dev) ? &ksnapvol[dev - SCHFS_SNAP_DEV] : &kvol;
|
||||
}
|
||||
|
||||
static char *
|
||||
curblk_for(uint dev)
|
||||
{
|
||||
return dev == SCHFS_SNAP_DEV ? ksnapcurblk : kcurblk;
|
||||
return SCHFS_IS_SNAP_DEV(dev) ? ksnapcurblk[dev - SCHFS_SNAP_DEV] : kcurblk;
|
||||
}
|
||||
|
||||
static char *
|
||||
datablk_for(uint dev)
|
||||
{
|
||||
return dev == SCHFS_SNAP_DEV ? ksnapdatablk : kdatablk;
|
||||
return SCHFS_IS_SNAP_DEV(dev) ? ksnapdatablk[dev - SCHFS_SNAP_DEV] : kdatablk;
|
||||
}
|
||||
|
||||
static int
|
||||
mounted_for(uint dev)
|
||||
{
|
||||
return dev == SCHFS_SNAP_DEV ? ksnap_mounted : kmounted;
|
||||
return SCHFS_IS_SNAP_DEV(dev) ? ksnap_mounted[dev - SCHFS_SNAP_DEV] : kmounted;
|
||||
}
|
||||
|
||||
// Mount a snapshot by name, read-only.
|
||||
// Mount a snapshot by name, read-only. Returns the device number of the slot
|
||||
// it landed in, or a negative error.
|
||||
//
|
||||
// The snapshot's records live in the *live* tree, so the name is resolved
|
||||
// there; what comes back is a root, and the second volume is a copy of the
|
||||
// first with its tree pointed at it. Nothing else differs -- same device, same
|
||||
// there; what comes back is a root, and the slot's volume is a copy of the live
|
||||
// one with its tree pointed at it. Nothing else differs -- same device, same
|
||||
// node format, same checksums.
|
||||
int
|
||||
schfs_ksnap_mount(const char *name, unsigned namelen)
|
||||
{
|
||||
if (!kmounted)
|
||||
return -1;
|
||||
if (ksnap_mounted)
|
||||
return -2; /* one at a time, for now */
|
||||
|
||||
if (ksnapcurblk == 0) ksnapcurblk = kalloc();
|
||||
if (ksnapdatablk == 0) ksnapdatablk = kalloc();
|
||||
if (ksnapcurblk == 0 || ksnapdatablk == 0)
|
||||
int slot = -1;
|
||||
for (int i = 0; i < SCHFS_SNAP_SLOTS; i++)
|
||||
if (!ksnap_mounted[i]) { slot = i; break; }
|
||||
if (slot < 0)
|
||||
return -2; /* every slot in use */
|
||||
|
||||
if (ksnapcurblk[slot] == 0) ksnapcurblk[slot] = kalloc();
|
||||
if (ksnapdatablk[slot] == 0) ksnapdatablk[slot] = kalloc();
|
||||
if (ksnapcurblk[slot] == 0 || ksnapdatablk[slot] == 0)
|
||||
return -3;
|
||||
|
||||
acquiresleep(&schfs_lock);
|
||||
|
|
@ -349,31 +354,35 @@ schfs_ksnap_mount(const char *name, unsigned namelen)
|
|||
// Same volume, older root. The device, the space map and the block callbacks
|
||||
// are all the live volume's -- a snapshot is not a separate filesystem, it is
|
||||
// a second way into this one.
|
||||
ksnapvol = kvol;
|
||||
ksnapvol.tree.root = sn.root;
|
||||
ksnapvol.tree.root_crc = sn.root_crc;
|
||||
ksnapvol.tree.root_gen = sn.root_generation;
|
||||
ksnapvol[slot] = kvol;
|
||||
ksnapvol[slot].tree.root = sn.root;
|
||||
ksnapvol[slot].tree.root_crc = sn.root_crc;
|
||||
ksnapvol[slot].tree.root_gen = sn.root_generation;
|
||||
// Its own scratch, or a read through the snapshot would tread on a cursor
|
||||
// the live volume still has positioned.
|
||||
ksnapvol.ctx.a = ksnapcurblk;
|
||||
ksnapvol[slot].ctx.a = ksnapcurblk[slot];
|
||||
|
||||
ksnap_mounted = 1;
|
||||
ksnap_mounted[slot] = 1;
|
||||
releasesleep(&schfs_lock);
|
||||
return 0;
|
||||
return SCHFS_SNAP_DEV + slot;
|
||||
}
|
||||
|
||||
void
|
||||
schfs_ksnap_unmount(void)
|
||||
schfs_ksnap_unmount(uint dev)
|
||||
{
|
||||
if (!SCHFS_IS_SNAP_DEV(dev))
|
||||
return;
|
||||
acquiresleep(&schfs_lock);
|
||||
ksnap_mounted = 0;
|
||||
ksnap_mounted[dev - SCHFS_SNAP_DEV] = 0;
|
||||
releasesleep(&schfs_lock);
|
||||
}
|
||||
|
||||
uint64
|
||||
schfs_ksnap_root(void)
|
||||
schfs_ksnap_root(uint dev)
|
||||
{
|
||||
return ksnap_mounted ? ksnapvol.root_objectid : 0;
|
||||
if (!SCHFS_IS_SNAP_DEV(dev) || !ksnap_mounted[dev - SCHFS_SNAP_DEV])
|
||||
return 0;
|
||||
return ksnapvol[dev - SCHFS_SNAP_DEV].root_objectid;
|
||||
}
|
||||
|
||||
// The read path, addressable per device so a snapshot can be read through the
|
||||
|
|
|
|||
Loading…
Reference in a new issue