akaros/kern/drivers/dev/srv.c
<<
>>
Prefs
   1/* Copyright (c) 2014 The Regents of the University of California
   2 * Barret Rhoden <brho@cs.berkeley.edu>
   3 * See LICENSE for details.
   4 *
   5 * #s (srv) - a chan sharing service.  This was originally based off the Inferno
   6 * #s, but it's been completely rewritten to act like what I remember the plan9
   7 * one to be like.
   8 *
   9 *
  10 * I tried a style where we hang reference counted objects off c->aux, specific
  11 * to each chan.  Instead of looking up via qid.path, we just look at the c->aux
  12 * for our struct.  I originally tried having those be reference counted
  13 * structs, but that fails for a bunch of reasons.  Without them being reference
  14 * counted, we're really just using c->aux as if it was qid.path.
  15 *
  16 * We can't hang an external reference to an item off c->aux, and have that item
  17 * change as we gen (but we can use it as a weak ref, uncounted ref).  The main
  18 * thing is that devclone makes a 'half-chan' with a copy of c->aux.  This chan
  19 * may or may not be closed later.  If we transfer refs via a gen, we first
  20 * assumed we had a ref in the first place (devclone doesn't incref our srv),
  21 * and then we might not close.  This ends up decreffing top_dir too much, and
  22 * giving it's refs to some other file in the walk. */
  23
  24#include <slab.h>
  25#include <kmalloc.h>
  26#include <kref.h>
  27#include <string.h>
  28#include <stdio.h>
  29#include <assert.h>
  30#include <error.h>
  31#include <cpio.h>
  32#include <pmap.h>
  33#include <smp.h>
  34#include <net/ip.h>
  35#include <sys/queue.h>
  36
  37struct dev srvdevtab;
  38
  39static char *devname(void)
  40{
  41        return srvdevtab.name;
  42}
  43
  44#define Qtopdir                 1
  45#define Qsrvfile                2
  46
  47struct srvfile {
  48        TAILQ_ENTRY(srvfile) link;
  49        char *name;
  50        struct chan *chan;
  51        struct kref ref;        /* +1 for existing on create, -1 on remove */
  52        char *user;
  53        uint32_t perm;
  54        atomic_t opens;         /* used for exclusive open checks */
  55};
  56
  57struct srvfile *top_dir;
  58TAILQ_HEAD(srvfilelist, srvfile) srvfiles = TAILQ_HEAD_INITIALIZER(srvfiles);
  59/* the lock protects the list and its members.  we don't incref from a list ref
  60 * without the lock. (if you're on the list, we can grab a ref). */
  61spinlock_t srvlock = SPINLOCK_INITIALIZER;
  62
  63atomic_t nr_srvs = 0;           /* debugging - concerned about leaking mem */
  64
  65/* Given a pointer (internal ref), we attempt to get a kref */
  66static bool grab_ref(struct srvfile *srv)
  67{
  68        bool ret = FALSE;
  69        struct srvfile *srv_i;
  70
  71        spin_lock(&srvlock);
  72        TAILQ_FOREACH(srv_i, &srvfiles, link) {
  73                if (srv_i == srv) {
  74                        ret = kref_get_not_zero(&srv_i->ref, 1);
  75                        break;
  76                }
  77        }
  78        spin_unlock(&srvlock);
  79        return ret;
  80}
  81
  82static void srv_release(struct kref *kref)
  83{
  84        struct srvfile *srv = container_of(kref, struct srvfile, ref);
  85
  86        kfree(srv->user);
  87        kfree(srv->name);
  88        if (srv->chan)
  89                cclose(srv->chan);
  90        kfree(srv);
  91        atomic_dec(&nr_srvs);
  92}
  93
  94static int srvgen(struct chan *c, char *name, struct dirtab *tab,
  95                                  int ntab, int s, struct dir *dp)
  96{
  97        struct srvfile *prev, *next;
  98        struct qid q;
  99
 100        if (s == DEVDOTDOT) {
 101                /* changing whatever c->aux was to be topdir */
 102                mkqid(&q, Qtopdir, 0, QTDIR);
 103                devdir(c, q, devname(), 0, eve.name, 0555, dp);
 104                return 1;
 105        }
 106        spin_lock(&srvlock);
 107        TAILQ_FOREACH(next, &srvfiles, link) {
 108                /* come in with s == 0 on the first run */
 109                if (s-- == 0)
 110                        break;
 111        }
 112        if (!next) {
 113                spin_unlock(&srvlock);
 114                return -1;
 115        }
 116        /* update c to point to our new srvfile.  this keeps the chan and its
 117         * srv in sync with what we're genning. */
 118        c->aux = next;  /* uncounted ref */
 119        mkqid(&q, Qsrvfile, 0, QTFILE);
 120        /* once we release the lock, next could disappear, including next->name
 121         */
 122        strlcpy(get_cur_genbuf(), next->name, GENBUF_SZ);
 123        devdir(c, q, get_cur_genbuf(), 1 /* length */ , next->user, next->perm,
 124               dp);
 125        spin_unlock(&srvlock);
 126        return 1;
 127}
 128
 129static void __srvinit(void)
 130{
 131        top_dir = kzmalloc(sizeof(struct srvfile), MEM_WAIT);
 132        /* kstrdup, just in case we free this later */
 133        kstrdup(&top_dir->name, "srv");
 134        kstrdup(&top_dir->user, current ? current->user.name : "eve");
 135        top_dir->perm = DMDIR | 0770;
 136        /* +1 for existing, should never decref this */
 137        kref_init(&top_dir->ref, fake_release, 1);
 138        atomic_set(&top_dir->opens, 0);
 139}
 140
 141static void srvinit(void)
 142{
 143        run_once(__srvinit());
 144}
 145
 146static struct chan *srvattach(char *spec)
 147{
 148        /* the inferno attach was pretty complicated, but
 149         * we're not sure that complexity is needed. */
 150        struct chan *c = devattach(devname(), spec);
 151
 152        mkqid(&c->qid, Qtopdir, 0, QTDIR);
 153        /* c->aux is an uncounted ref */
 154        c->aux = top_dir;
 155        return c;
 156}
 157
 158static struct walkqid *srvwalk(struct chan *c, struct chan *nc, char **name,
 159                                                           unsigned int nname)
 160{
 161        return devwalk(c, nc, name, nname, 0, 0, srvgen);
 162}
 163
 164static size_t srvstat(struct chan *c, uint8_t *db, size_t n)
 165{
 166        return devstat(c, db, n, 0, 0, srvgen);
 167}
 168
 169char *srvname(struct chan *c)
 170{
 171        struct srvfile *srv_i;
 172        char *s;
 173
 174        spin_lock(&srvlock);
 175        TAILQ_FOREACH(srv_i, &srvfiles, link) {
 176                if(srv_i->chan == c){
 177                        int len = 3 + strlen(srv_i->name) + 1;
 178                        s = kzmalloc(len, 0);
 179                        snprintf(s, len, "#s/%s", srv_i->name);
 180                        spin_unlock(&srvlock);
 181                        return s;
 182                }
 183        }
 184        spin_unlock(&srvlock);
 185        return NULL;
 186}
 187
 188static struct chan *srvopen(struct chan *c, int omode)
 189{
 190        ERRSTACK(1);
 191        struct srvfile *srv;
 192
 193        /* used as an error checker in plan9, does little now */
 194        openmode(omode);
 195        if (c->qid.type & QTDIR) {
 196                if (omode & O_WRITE)
 197                        error(EISDIR, ERROR_FIXME);
 198                c->mode = openmode(omode);
 199                c->flag |= COPEN;
 200                c->offset = 0;
 201                return c;
 202        }
 203        srv = c->aux;
 204        if (!grab_ref(srv))
 205                error(EFAIL, "Unable to open srv file, concurrent removal");
 206        if (waserror()) {
 207                kref_put(&srv->ref);
 208                nexterror();
 209        }
 210        devpermcheck(srv->user, srv->perm, omode);
 211        if ((srv->perm & DMEXCL) && atomic_read(&srv->opens))
 212                error(EBUSY, ERROR_FIXME);
 213        /* srv->chan is write-once, so we don't need to sync. */
 214        if (!srv->chan)
 215                error(EFAIL, "srv file has no chan yet");
 216        /* this is more than just the ref - 1, since there will be refs in
 217         * flight as gens work their way through the list */
 218        atomic_inc(&srv->opens);
 219        /* the magic of srv: open c, get c->srv->chan back */
 220        cclose(c);
 221        c = srv->chan;
 222        chan_incref(c);
 223        poperror();
 224        kref_put(&srv->ref);
 225        return c;
 226}
 227
 228static void srvcreate(struct chan *c, char *name, int omode, uint32_t perm,
 229                      char *ext)
 230{
 231        struct srvfile *srv;
 232
 233        if (perm & DMSYMLINK)
 234                error(EINVAL, "#%s doesn't support symlinks", devname());
 235        srv = kzmalloc(sizeof(struct srvfile), MEM_WAIT);
 236        kstrdup(&srv->name, name);
 237        kstrdup(&srv->user, current ? current->user.name : "eve");
 238        srv->perm = 0770;       /* TODO need some security thoughts */
 239        atomic_set(&srv->opens, 1);     /* we return it opened */
 240        mkqid(&c->qid, Qsrvfile, 0, QTFILE);
 241        c->aux = srv;
 242        c->mode = openmode(omode);
 243        /* one ref for being on the list */
 244        kref_init(&srv->ref, srv_release, 1);
 245        spin_lock(&srvlock);
 246        TAILQ_INSERT_TAIL(&srvfiles, srv, link);
 247        spin_unlock(&srvlock);
 248        atomic_inc(&nr_srvs);
 249}
 250
 251static void srvremove(struct chan *c)
 252{
 253        struct srvfile *srv_i, *temp;
 254
 255        spin_lock(&srvlock);
 256        TAILQ_FOREACH_SAFE(srv_i, &srvfiles, link, temp) {
 257                if (srv_i == c->aux) {
 258                        TAILQ_REMOVE(&srvfiles, srv_i, link);
 259                        break;
 260                }
 261        }
 262        spin_unlock(&srvlock);
 263        if (srv_i)
 264                kref_put(&srv_i->ref);  /* dropping ref from the list */
 265}
 266
 267static void srvclose(struct chan *c)
 268{
 269        struct srvfile *srv = c->aux;
 270
 271        if (!grab_ref(srv))
 272                return;
 273        atomic_dec(&srv->opens);
 274        kref_put(&srv->ref);
 275        if (c->flag & O_REMCLO)
 276                srvremove(c);
 277}
 278
 279/* N.B. srvopen gives the chan back. The only 'reading' we do
 280 * in srv is of the top level directory.
 281 */
 282static size_t srvread(struct chan *c, void *va, size_t count, off64_t offset)
 283{
 284        return devdirread(c, va, count, 0, 0, srvgen);
 285}
 286
 287static size_t srvwrite(struct chan *c, void *va, size_t count, off64_t offset)
 288{
 289        ERRSTACK(2);
 290        struct srvfile *srv;
 291        struct chan *new_chan;
 292        char *kbuf = 0;
 293        int fd;
 294
 295        if (c->qid.type & QTDIR)
 296                error(EPERM, ERROR_FIXME);
 297        srv = c->aux;
 298        if (!grab_ref(srv))
 299                error(EFAIL, "Unable to write srv file, concurrent removal");
 300        if (waserror()) {
 301                kref_put(&srv->ref);
 302                nexterror();
 303        }
 304        if (srv->chan)
 305                error(EFAIL, "srv file already has a stored chan!");
 306        if (waserror()) {
 307                kfree(kbuf);
 308                nexterror();
 309        }
 310        kbuf = kmalloc(count + 1, MEM_WAIT);
 311        strlcpy(kbuf, va, count + 1);
 312        fd = strtoul(kbuf, 0, 10);
 313        /* the magic of srv: srv stores the chan corresponding to the fd.  -1
 314         * for mode, so we just get the chan with no checks (RDWR would work
 315         * too). */
 316        new_chan = fdtochan(&current->open_files, fd, -1, FALSE, TRUE);
 317        /* fdtochan already increffed for us */
 318        if (!__sync_bool_compare_and_swap(&srv->chan, 0, new_chan)) {
 319                cclose(new_chan);
 320                error(EFAIL, "srv file already has a stored chan!");
 321        }
 322        poperror();
 323        kfree(kbuf);
 324        poperror();
 325        kref_put(&srv->ref);
 326        return count;
 327}
 328
 329struct dev srvdevtab __devtab = {
 330        .name = "srv",
 331
 332        .reset = devreset,
 333        .init = srvinit,
 334        .shutdown = devshutdown,
 335        .attach = srvattach,
 336        .walk = srvwalk,
 337        .stat = srvstat,
 338        .open = srvopen,
 339        .create = srvcreate,
 340        .close = srvclose,
 341        .read = srvread,
 342        .bread = devbread,
 343        .write = srvwrite,
 344        .bwrite = devbwrite,
 345        .remove = srvremove,
 346        .wstat = devwstat,
 347        .power = devpower,
 348        .chaninfo = devchaninfo,
 349};
 350