/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * File system devices. * Follows device config in Ken's file server. * Builds mirrors, concatenations, interleavings, and partitions * of devices out of other (inner) devices. * It is ok if inner devices are provided by this driver. * * Built files are grouped on different directories * (called trees, and used to represent disks). * The "#k/fs" tree is always available and never goes away. * Configuration changes happen only while no I/O is in progress. * * Default sector size is one byte unless changed by the "disk" ctl. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "ureg.h"#include "../port/error.h" enum{ Fnone, Fmirror, /* mirror of others */ Fcat, /* catenation of others */ Finter, /* interleaving of others */ Fpart, /* part of other */ Fclear, /* start over */ Fdel, /* delete a configure device */ Fdisk, /* set default tree and sector sz*/ Sectorsz = 1, Blksize = 8*1024, /* for Finter only */ Incr = 5, /* Increments for the dev array */ /* * All qids are decorated with the tree number. * #k/fs is tree number 0, is automatically added and * its first qid is for the ctl file. It never goes away. */ Qtop = 0, /* #k */ Qdir, /* directory (#k/fs) */ Qctl, /* ctl, only for #k/fs/ctl */ Qfirst, /* first qid assigned for device */ Iswrite = 0, Isread, Optional = 0, Mustexist, /* tunable parameters */ Maxconf = 4*1024, /* max length for config */ Ndevs = 32, /* max. inner devs per command */ Ntrees = 128, /* max. number of trees */ Maxretries = 3, /* max. retries of i/o errors */ Retrypause = 5000, /* ms. to pause between retries */}; typedef struct Inner Inner;typedef struct Fsdev Fsdev;typedef struct Tree Tree; struct Inner{ char *iname; /* inner device name */ vlong isize; /* size of inner device */ Chan *idev; /* inner device */}; struct Fsdev{ Ref; /* one per Chan doing I/O */ int gone; /* true if removed */ int vers; /* qid version for this device */ int type; /* Fnone, Fmirror, ... */ char *name; /* name for this fsdev */ Tree* tree; /* where the device is kept */ vlong size; /* min(inner[X].isize) */ vlong start; /* start address (for Fpart) */ uint ndevs; /* number of inner devices */ int perm; /* minimum of inner device perms */ Inner *inner[Ndevs]; /* inner devices */}; struct Tree{ char *name; /* name for #k/<name> */ Fsdev **devs; /* devices in dir. */ uint ndevs; /* number of devices */ uint nadevs; /* number of allocated devices in devs */}; #define dprint if(debug)print extern Dev fsdevtab; /* forward */ static RWlock lck; /* r: use devices; w: change config */static Tree fstree; /* The main "fs" tree. Never goes away */static Tree *trees[Ntrees]; /* internal representation of config */static int ntrees; /* max number of trees */static int qidvers; static char *disk; /* default tree name used */static char *source; /* default inner device used */static int sectorsz = Sectorsz; /* default sector size */ static char confstr[Maxconf]; /* textual configuration */ static int debug; static char cfgstr[] = "fsdev:\n"; static Qid tqid = {Qtop, 0, QTDIR};static Qid cqid = {Qctl, 0, 0}; static char* tnames[] = { [Fmirror] "mirror", [Fcat] "cat", [Finter] "inter", [Fpart] "part",}; static Cmdtab configs[] = { Fmirror,"mirror", 0, Fcat, "cat", 0, Finter, "inter", 0, Fpart, "part", 0, Fclear, "clear", 1, Fdel, "del", 2, Fdisk, "disk", 0,}; static char Egone[] = "device is gone"; /* file has been removed */ static char*seprintdev(char *s, char *e, Fsdev *mp){ int i; if(mp == nil) return seprint(s, e, "<null Fsdev>"); if(mp->type < 0 || mp->type >= nelem(tnames) || tnames[mp->type] == nil) return seprint(s, e, "bad device type %d\n", mp->type); s = strecpy(s, e, tnames[mp->type]); if(mp->tree != &fstree) s = seprint(s, e, " %s/%s", mp->tree->name, mp->name); else s = seprint(s, e, " %s", mp->name); for(i = 0; i < mp->ndevs; i++) s = seprint(s, e, " %s", mp->inner[i]->iname); switch(mp->type){ case Fmirror: case Fcat: case Finter: s = strecpy(s, e, "\n"); break; case Fpart: s = seprint(s, e, " %ulld %ulld\n", mp->start, mp->size); break; default: panic("#k: seprintdev bug"); } return s;} static vlongmkpath(int tree, int devno){ return (tree&0xFFFF)<<16 | devno&0xFFFF;} static intpath2treeno(int q){ return q>>16 & 0xFFFF;} static intpath2devno(int q){ return q & 0xFFFF;} static Tree*gettree(int i, int mustexist){ dprint("gettree %d\n", i); if(i < 0) panic("#k: bug: bad tree index %d in gettree", i); if(i >= ntrees || trees[i] == nil) if(mustexist) error(Enonexist); else return nil; return trees[i];} static Fsdev*getdev(Tree *t, int i, int mustexist){ dprint("getdev %d\n", i); if(i < 0) panic("#k: bug: bad dev index %d in getdev", i); if(i >= t->nadevs || t->devs[i] == nil) if(mustexist) error(Enonexist); else return nil; return t->devs[i];} static Fsdev*path2dev(int q){ Tree *t; dprint("path2dev %ux\n", q); t = gettree(path2treeno(q), Mustexist); return getdev(t, path2devno(q) - Qfirst, Mustexist);} static Tree*treealloc(char *name){ int i; Tree *t; dprint("treealloc %s\n", name); for(i = 0; i < nelem(trees); i++) if(trees[i] == nil) break; if(i == nelem(trees)) return nil; t = trees[i] = mallocz(sizeof(Tree), 1); if(t == nil) return nil; if(i == ntrees) ntrees++; kstrdup(&t->name, name); return t;} static Tree*lookuptree(char *name){ int i; dprint("lookuptree %s\n", name); for(i = 0; i < ntrees; i++) if(trees[i] != nil && strcmp(trees[i]->name, name) == 0) return trees[i]; return nil;} static Fsdev*devalloc(Tree *t, char *name){ int i, ndevs; Fsdev *mp, **devs; dprint("devalloc %s %s\n", t->name, name); mp = mallocz(sizeof(Fsdev), 1); if(mp == nil) return nil; for(i = 0; i < t->nadevs; i++) if(t->devs[i] == nil) break; if(i >= t->nadevs){ if(t->nadevs % Incr == 0){ ndevs = t->nadevs + Incr; devs = realloc(t->devs, ndevs * sizeof(Fsdev*)); if(devs == nil){ free(mp); return nil; } t->devs = devs; } t->devs[t->nadevs] = nil; t->nadevs++; } kstrdup(&mp->name, name); mp->vers = ++qidvers; mp->tree = t; t->devs[i] = mp; t->ndevs++; return mp;} static voiddeltree(Tree *t){ int i; dprint("deltree %s\n", t->name); for(i = 0; i < ntrees; i++) if(trees[i] == t){ if(i > 0){ /* "fs" never goes away */ free(t->name); free(t->devs); free(t); trees[i] = nil; } return; } panic("#k: deltree: bug: tree not found");} /* * A device is gone and we know that all its users are gone. * A tree is gone when all its devices are gone ("fs" is never gone). * Must close devices outside locks, so we could nest our own devices. */static voidmdeldev(Fsdev *mp){ int i; Inner *in; Tree *t; dprint("deldev %s gone %d ref %uld\n", mp->name, mp->gone, mp->ref); mp->gone = 1; mp->vers = ++qidvers; wlock(&lck); t = mp->tree; for(i = 0; i < t->nadevs; i++) if(t->devs[i] == mp){ t->devs[i] = nil; t->ndevs--; if(t->ndevs == 0) deltree(t); break; } wunlock(&lck); free(mp->name); for(i = 0; i < mp->ndevs; i++){ in = mp->inner[i]; if(in->idev != nil) cclose(in->idev); free(in->iname); free(in); } if(debug) memset(mp, 9, sizeof *mp); /* poison */ free(mp);} /* * Delete one or all devices in one or all trees. */static voidmdelctl(char *tname, char *dname){ int i, alldevs, alltrees, some; Fsdev *mp; Tree *t; dprint("delctl %s\n", dname); alldevs = strcmp(dname, "*") == 0; alltrees = strcmp(tname, "*") == 0; some = 0;Again: wlock(&lck); for(i = 0; i < ntrees; i++){ t = trees[i]; if(t == nil) continue; if(alltrees == 0 && strcmp(t->name, tname) != 0) continue; for(i = 0; i < t->nadevs; i++){ mp = t->devs[i]; if(t->devs[i] == nil) continue; if(alldevs == 0 && strcmp(mp->name, dname) != 0) continue; /* * Careful: must close outside locks and that * may change the file tree we are looking at. */ some++; mp->gone = 1; if(mp->ref == 0){ incref(mp); /* keep it there */ wunlock(&lck); mdeldev(mp); goto Again; /* tree can change */ } } } wunlock(&lck); if(some == 0 && alltrees == 0) error(Enonexist);} static voidsetdsize(Fsdev* mp, vlong *ilen){ int i; vlong inlen; Inner *in; dprint("setdsize %s\n", mp->name); for (i = 0; i < mp->ndevs; i++){ in = mp->inner[i]; in->isize = ilen[i]; inlen = in->isize; switch(mp->type){ case Finter: /* truncate to multiple of Blksize */ inlen &= ~(Blksize-1); in->isize = inlen; /* fall through */ case Fmirror: /* use size of smallest inner device */ if (mp->size == 0 || mp->size > inlen) mp->size = inlen; break; case Fcat: mp->size += inlen; break; case Fpart: if(mp->start > inlen) error("partition starts after device end"); if(inlen < mp->start + mp->size){ print("#k: %s: partition truncated from " "%lld to %lld bytes\n", mp->name, mp->size, inlen - mp->start); mp->size = inlen - mp->start; } break; } } if(mp->type == Finter) mp->size *= mp->ndevs;} static voidvaliddevname(Tree *t, char *dname){ int i; for(i = 0; i < t->nadevs; i++) if(t->devs[i] != nil && strcmp(t->devs[i]->name, dname) == 0) error(Eexist);} static voidparseconfig(char *a, long n, Cmdbuf **cbp, Cmdtab **ctp){ Cmdbuf *cb; Cmdtab *ct; *cbp = cb = parsecmd(a, n); *ctp = ct = lookupcmd(cb, configs, nelem(configs)); cb->f++; /* skip command */ cb->nf--; switch(ct->index){ case Fmirror: case Fcat: case Finter: if(cb->nf < 2) error("too few arguments for ctl"); if(cb->nf - 1 > Ndevs) error("too many devices in ctl"); break; case Fdisk: if(cb->nf < 1 || cb->nf > 3) error("ctl usage: disk name [sz dev]"); break; case Fpart: if(cb->nf != 4 && (cb->nf != 3 || source == nil)) error("ctl usage: part new [file] off len"); break; }} static voidparsename(char *name, char *disk, char **tree, char **dev){ char *slash; Lines 1–500 of 1294. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * File system devices. * Follows device config in Ken's file server. * Builds mirrors, concatenations, interleavings, and partitions * of devices out of other (inner) devices. * It is ok if inner devices are provided by this driver. * * Built files are grouped on different directories * (called trees, and used to represent disks). * The "#k/fs" tree is always available and never goes away. * Configuration changes happen only while no I/O is in progress. * * Default sector size is one byte unless changed by the "disk" ctl. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" enum { Fnone, Fmirror, /* mirror of others */ Fcat, /* catenation of others */ Finter, /* interleaving of others */ Fpart, /* part of other */ Fclear, /* start over */ Fdel, /* delete a configure device */ Fdisk, /* set default tree and sector sz*/ Sectorsz = 1, Blksize = 8*1024, /* for Finter only */ Incr = 5, /* Increments for the dev array */ /* * All qids are decorated with the tree number. * #k/fs is tree number 0, is automatically added and * its first qid is for the ctl file. It never goes away. */ Qtop = 0, /* #k */ Qdir, /* directory (#k/fs) */ Qctl, /* ctl, only for #k/fs/ctl */ Qfirst, /* first qid assigned for device */ Iswrite = 0, Isread, Optional = 0, Mustexist, /* tunable parameters */ Maxconf = 4*1024, /* max length for config */ Ndevs = 32, /* max. inner devs per command */ Ntrees = 128, /* max. number of trees */ Maxretries = 3, /* max. retries of i/o errors */ Retrypause = 5000, /* ms. to pause between retries */ }; typedef struct Inner Inner; typedef struct Fsdev Fsdev; typedef struct Tree Tree; struct Inner { char *iname; /* inner device name */ vlong isize; /* size of inner device */ Chan *idev; /* inner device */ }; struct Fsdev { Ref; /* one per Chan doing I/O */ int gone; /* true if removed */ int vers; /* qid version for this device */ int type; /* Fnone, Fmirror, ... */ char *name; /* name for this fsdev */ Tree* tree; /* where the device is kept */ vlong size; /* min(inner[X].isize) */ vlong start; /* start address (for Fpart) */ uint ndevs; /* number of inner devices */ int perm; /* minimum of inner device perms */ Inner *inner[Ndevs]; /* inner devices */ }; struct Tree { char *name; /* name for #k/<name> */ Fsdev **devs; /* devices in dir. */ uint ndevs; /* number of devices */ uint nadevs; /* number of allocated devices in devs */ }; #define dprint if(debug)print extern Dev fsdevtab; /* forward */ static RWlock lck; /* r: use devices; w: change config */ static Tree fstree; /* The main "fs" tree. Never goes away */ static Tree *trees[Ntrees]; /* internal representation of config */ static int ntrees; /* max number of trees */ static int qidvers; static char *disk; /* default tree name used */ static char *source; /* default inner device used */ static int sectorsz = Sectorsz; /* default sector size */ static char confstr[Maxconf]; /* textual configuration */ static int debug; static char cfgstr[] = "fsdev:\n"; static Qid tqid = {Qtop, 0, QTDIR}; static Qid cqid = {Qctl, 0, 0}; static char* tnames[] = { [Fmirror] "mirror", [Fcat] "cat", [Finter] "inter", [Fpart] "part", }; static Cmdtab configs[] = { Fmirror,"mirror", 0, Fcat, "cat", 0, Finter, "inter", 0, Fpart, "part", 0, Fclear, "clear", 1, Fdel, "del", 2, Fdisk, "disk", 0, }; static char Egone[] = "device is gone"; /* file has been removed */ static char* seprintdev(char *s, char *e, Fsdev *mp) { int i; if(mp == nil) return seprint(s, e, "<null Fsdev>"); if(mp->type < 0 || mp->type >= nelem(tnames) || tnames[mp->type] == nil) return seprint(s, e, "bad device type %d\n", mp->type); s = strecpy(s, e, tnames[mp->type]); if(mp->tree != &fstree) s = seprint(s, e, " %s/%s", mp->tree->name, mp->name); else s = seprint(s, e, " %s", mp->name); for(i = 0; i < mp->ndevs; i++) s = seprint(s, e, " %s", mp->inner[i]->iname); switch(mp->type){ case Fmirror: case Fcat: case Finter: s = strecpy(s, e, "\n"); break; case Fpart: s = seprint(s, e, " %ulld %ulld\n", mp->start, mp->size); break; default: panic("#k: seprintdev bug"); } return s; } static vlong mkpath(int tree, int devno) { return (tree&0xFFFF)<<16 | devno&0xFFFF; } static int path2treeno(int q) { return q>>16 & 0xFFFF; } static int path2devno(int q) { return q & 0xFFFF; } static Tree* gettree(int i, int mustexist) { dprint("gettree %d\n", i); if(i < 0) panic("#k: bug: bad tree index %d in gettree", i); if(i >= ntrees || trees[i] == nil) if(mustexist) error(Enonexist); else return nil; return trees[i]; } static Fsdev* getdev(Tree *t, int i, int mustexist) { dprint("getdev %d\n", i); if(i < 0) panic("#k: bug: bad dev index %d in getdev", i); if(i >= t->nadevs || t->devs[i] == nil) if(mustexist) error(Enonexist); else return nil; return t->devs[i]; } static Fsdev* path2dev(int q) { Tree *t; dprint("path2dev %ux\n", q); t = gettree(path2treeno(q), Mustexist); return getdev(t, path2devno(q) - Qfirst, Mustexist); } static Tree* treealloc(char *name) { int i; Tree *t; dprint("treealloc %s\n", name); for(i = 0; i < nelem(trees); i++) if(trees[i] == nil) break; if(i == nelem(trees)) return nil; t = trees[i] = mallocz(sizeof(Tree), 1); if(t == nil) return nil; if(i == ntrees) ntrees++; kstrdup(&t->name, name); return t; } static Tree* lookuptree(char *name) { int i; dprint("lookuptree %s\n", name); for(i = 0; i < ntrees; i++) if(trees[i] != nil && strcmp(trees[i]->name, name) == 0) return trees[i]; return nil; } static Fsdev* devalloc(Tree *t, char *name) { int i, ndevs; Fsdev *mp, **devs; dprint("devalloc %s %s\n", t->name, name); mp = mallocz(sizeof(Fsdev), 1); if(mp == nil) return nil; for(i = 0; i < t->nadevs; i++) if(t->devs[i] == nil) break; if(i >= t->nadevs){ if(t->nadevs % Incr == 0){ ndevs = t->nadevs + Incr; devs = realloc(t->devs, ndevs * sizeof(Fsdev*)); if(devs == nil){ free(mp); return nil; } t->devs = devs; } t->devs[t->nadevs] = nil; t->nadevs++; } kstrdup(&mp->name, name); mp->vers = ++qidvers; mp->tree = t; t->devs[i] = mp; t->ndevs++; return mp; } static void deltree(Tree *t) { int i; dprint("deltree %s\n", t->name); for(i = 0; i < ntrees; i++) if(trees[i] == t){ if(i > 0){ /* "fs" never goes away */ free(t->name); free(t->devs); free(t); trees[i] = nil; } return; } panic("#k: deltree: bug: tree not found"); } /* * A device is gone and we know that all its users are gone. * A tree is gone when all its devices are gone ("fs" is never gone). * Must close devices outside locks, so we could nest our own devices. */ static void mdeldev(Fsdev *mp) { int i; Inner *in; Tree *t; dprint("deldev %s gone %d ref %uld\n", mp->name, mp->gone, mp->ref); mp->gone = 1; mp->vers = ++qidvers; wlock(&lck); t = mp->tree; for(i = 0; i < t->nadevs; i++) if(t->devs[i] == mp){ t->devs[i] = nil; t->ndevs--; if(t->ndevs == 0) deltree(t); break; } wunlock(&lck); free(mp->name); for(i = 0; i < mp->ndevs; i++){ in = mp->inner[i]; if(in->idev != nil) cclose(in->idev); free(in->iname); free(in); } if(debug) memset(mp, 9, sizeof *mp); /* poison */ free(mp); } /* * Delete one or all devices in one or all trees. */ static void mdelctl(char *tname, char *dname) { int i, alldevs, alltrees, some; Fsdev *mp; Tree *t; dprint("delctl %s\n", dname); alldevs = strcmp(dname, "*") == 0; alltrees = strcmp(tname, "*") == 0; some = 0; Again: wlock(&lck); for(i = 0; i < ntrees; i++){ t = trees[i]; if(t == nil) continue; if(alltrees == 0 && strcmp(t->name, tname) != 0) continue; for(i = 0; i < t->nadevs; i++){ mp = t->devs[i]; if(t->devs[i] == nil) continue; if(alldevs == 0 && strcmp(mp->name, dname) != 0) continue; /* * Careful: must close outside locks and that * may change the file tree we are looking at. */ some++; mp->gone = 1; if(mp->ref == 0){ incref(mp); /* keep it there */ wunlock(&lck); mdeldev(mp); goto Again; /* tree can change */ } } } wunlock(&lck); if(some == 0 && alltrees == 0) error(Enonexist); } static void setdsize(Fsdev* mp, vlong *ilen) { int i; vlong inlen; Inner *in; dprint("setdsize %s\n", mp->name); for (i = 0; i < mp->ndevs; i++){ in = mp->inner[i]; in->isize = ilen[i]; inlen = in->isize; switch(mp->type){ case Finter: /* truncate to multiple of Blksize */ inlen &= ~(Blksize-1); in->isize = inlen; /* fall through */ case Fmirror: /* use size of smallest inner device */ if (mp->size == 0 || mp->size > inlen) mp->size = inlen; break; case Fcat: mp->size += inlen; break; case Fpart: if(mp->start > inlen) error("partition starts after device end"); if(inlen < mp->start + mp->size){ print("#k: %s: partition truncated from " "%lld to %lld bytes\n", mp->name, mp->size, inlen - mp->start); mp->size = inlen - mp->start; } break; } } if(mp->type == Finter) mp->size *= mp->ndevs; } static void validdevname(Tree *t, char *dname) { int i; for(i = 0; i < t->nadevs; i++) if(t->devs[i] != nil && strcmp(t->devs[i]->name, dname) == 0) error(Eexist); } static void parseconfig(char *a, long n, Cmdbuf **cbp, Cmdtab **ctp) { Cmdbuf *cb; Cmdtab *ct; *cbp = cb = parsecmd(a, n); *ctp = ct = lookupcmd(cb, configs, nelem(configs)); cb->f++; /* skip command */ cb->nf--; switch(ct->index){ case Fmirror: case Fcat: case Finter: if(cb->nf < 2) error("too few arguments for ctl"); if(cb->nf - 1 > Ndevs) error("too many devices in ctl"); break; case Fdisk: if(cb->nf < 1 || cb->nf > 3) error("ctl usage: disk name [sz dev]"); break; case Fpart: if(cb->nf != 4 && (cb->nf != 3 || source == nil)) error("ctl usage: part new [file] off len"); break; } } static void parsename(char *name, char *disk, char **tree, char **dev) { char *slash;