char authname[IDMAP_NAMESZ];
};
-#define DefineSimpleCacheLookupMap(STRUCT, FUNC) \
- DefineCacheLookup(struct STRUCT, h, FUNC##_lookup, \
- (struct STRUCT *item, int set), /*no setup */, \
- & FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp), \
- STRUCT##_init(new, item), STRUCT##_update(tmp, item), 0)
-
/* Common entry handling */
#define ENT_HASHBITS 8
#define ENT_HASHMAX (1 << ENT_HASHBITS)
#define ENT_HASHMASK (ENT_HASHMAX - 1)
-static inline void
-ent_init(struct ent *new, struct ent *itm)
+static void
+ent_init(struct cache_head *cnew, struct cache_head *citm)
{
+ struct ent *new = container_of(cnew, struct ent, h);
+ struct ent *itm = container_of(citm, struct ent, h);
+
new->id = itm->id;
new->type = itm->type;
strlcpy(new->authname, itm->authname, sizeof(new->name));
}
-static inline void
-ent_update(struct ent *new, struct ent *itm)
+static void
+ent_put(struct kref *ref)
{
- ent_init(new, itm);
+ struct ent *map = container_of(ref, struct ent, h.ref);
+ kfree(map);
}
-void
-ent_put(struct cache_head *ch, struct cache_detail *cd)
+static struct cache_head *
+ent_alloc(void)
{
- if (cache_put(ch, cd)) {
- struct ent *map = container_of(ch, struct ent, h);
- kfree(map);
- }
+ struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL);
+ if (e)
+ return &e->h;
+ else
+ return NULL;
}
/*
(*bpp)[-1] = '\n';
}
-static inline int
-idtoname_match(struct ent *a, struct ent *b)
+static int
+idtoname_match(struct cache_head *ca, struct cache_head *cb)
{
+ struct ent *a = container_of(ca, struct ent, h);
+ struct ent *b = container_of(cb, struct ent, h);
+
return (a->id == b->id && a->type == b->type &&
strcmp(a->authname, b->authname) == 0);
}
return 0;
}
+static void
+warn_no_idmapd(struct cache_detail *detail)
+{
+ printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n",
+ detail->last_close? "died" : "not been started");
+}
+
+
static int idtoname_parse(struct cache_detail *, char *, int);
-static struct ent *idtoname_lookup(struct ent *, int);
+static struct ent *idtoname_lookup(struct ent *);
+static struct ent *idtoname_update(struct ent *, struct ent *);
-struct cache_detail idtoname_cache = {
+static struct cache_detail idtoname_cache = {
+ .owner = THIS_MODULE,
.hash_size = ENT_HASHMAX,
.hash_table = idtoname_table,
.name = "nfs4.idtoname",
.cache_request = idtoname_request,
.cache_parse = idtoname_parse,
.cache_show = idtoname_show,
+ .warn_no_listener = warn_no_idmapd,
+ .match = idtoname_match,
+ .init = ent_init,
+ .update = ent_init,
+ .alloc = ent_alloc,
};
int
if (ent.h.expiry_time == 0)
goto out;
+ error = -ENOMEM;
+ res = idtoname_lookup(&ent);
+ if (!res)
+ goto out;
+
/* Name */
error = qword_get(&buf, buf1, PAGE_SIZE);
if (error == -EINVAL)
memcpy(ent.name, buf1, sizeof(ent.name));
}
error = -ENOMEM;
- if ((res = idtoname_lookup(&ent, 1)) == NULL)
+ res = idtoname_update(&ent, res);
+ if (res == NULL)
goto out;
- ent_put(&res->h, &idtoname_cache);
+ cache_put(&res->h, &idtoname_cache);
error = 0;
out:
return error;
}
-static DefineSimpleCacheLookupMap(ent, idtoname);
+
+static struct ent *
+idtoname_lookup(struct ent *item)
+{
+ struct cache_head *ch = sunrpc_cache_lookup(&idtoname_cache,
+ &item->h,
+ idtoname_hash(item));
+ if (ch)
+ return container_of(ch, struct ent, h);
+ else
+ return NULL;
+}
+
+static struct ent *
+idtoname_update(struct ent *new, struct ent *old)
+{
+ struct cache_head *ch = sunrpc_cache_update(&idtoname_cache,
+ &new->h, &old->h,
+ idtoname_hash(new));
+ if (ch)
+ return container_of(ch, struct ent, h);
+ else
+ return NULL;
+}
+
/*
* Name -> ID cache
return hash_str(ent->name, ENT_HASHBITS);
}
-void
+static void
nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
int *blen)
{
(*bpp)[-1] = '\n';
}
-static inline int
-nametoid_match(struct ent *a, struct ent *b)
+static int
+nametoid_match(struct cache_head *ca, struct cache_head *cb)
{
+ struct ent *a = container_of(ca, struct ent, h);
+ struct ent *b = container_of(cb, struct ent, h);
+
return (a->type == b->type && strcmp(a->name, b->name) == 0 &&
strcmp(a->authname, b->authname) == 0);
}
return 0;
}
-static struct ent *nametoid_lookup(struct ent *, int);
-int nametoid_parse(struct cache_detail *, char *, int);
+static struct ent *nametoid_lookup(struct ent *);
+static struct ent *nametoid_update(struct ent *, struct ent *);
+static int nametoid_parse(struct cache_detail *, char *, int);
-struct cache_detail nametoid_cache = {
+static struct cache_detail nametoid_cache = {
+ .owner = THIS_MODULE,
.hash_size = ENT_HASHMAX,
.hash_table = nametoid_table,
.name = "nfs4.nametoid",
.cache_request = nametoid_request,
.cache_parse = nametoid_parse,
.cache_show = nametoid_show,
+ .warn_no_listener = warn_no_idmapd,
+ .match = nametoid_match,
+ .init = ent_init,
+ .update = ent_init,
+ .alloc = ent_alloc,
};
-int
+static int
nametoid_parse(struct cache_detail *cd, char *buf, int buflen)
{
struct ent ent, *res;
set_bit(CACHE_NEGATIVE, &ent.h.flags);
error = -ENOMEM;
- if ((res = nametoid_lookup(&ent, 1)) == NULL)
+ res = nametoid_lookup(&ent);
+ if (res == NULL)
+ goto out;
+ res = nametoid_update(&ent, res);
+ if (res == NULL)
goto out;
- ent_put(&res->h, &nametoid_cache);
+ cache_put(&res->h, &nametoid_cache);
error = 0;
out:
kfree(buf1);
return (error);
}
-static DefineSimpleCacheLookupMap(ent, nametoid);
+
+static struct ent *
+nametoid_lookup(struct ent *item)
+{
+ struct cache_head *ch = sunrpc_cache_lookup(&nametoid_cache,
+ &item->h,
+ nametoid_hash(item));
+ if (ch)
+ return container_of(ch, struct ent, h);
+ else
+ return NULL;
+}
+
+static struct ent *
+nametoid_update(struct ent *new, struct ent *old)
+{
+ struct cache_head *ch = sunrpc_cache_update(&nametoid_cache,
+ &new->h, &old->h,
+ nametoid_hash(new));
+ if (ch)
+ return container_of(ch, struct ent, h);
+ else
+ return NULL;
+}
/*
* Exported API
void
nfsd_idmap_shutdown(void)
{
- cache_unregister(&idtoname_cache);
- cache_unregister(&nametoid_cache);
+ if (cache_unregister(&idtoname_cache))
+ printk(KERN_ERR "nfsd: failed to unregister idtoname cache\n");
+ if (cache_unregister(&nametoid_cache))
+ printk(KERN_ERR "nfsd: failed to unregister nametoid cache\n");
}
/*
atomic_t count;
};
-static void
+static inline void
put_mdr(struct idmap_defer_req *mdr)
{
if (atomic_dec_and_test(&mdr->count))
kfree(mdr);
}
+static inline void
+get_mdr(struct idmap_defer_req *mdr)
+{
+ atomic_inc(&mdr->count);
+}
+
static void
idmap_revisit(struct cache_deferred_req *dreq, int toomany)
{
container_of(req, struct idmap_defer_req, req);
mdr->deferred_req.revisit = idmap_revisit;
+ get_mdr(mdr);
return (&mdr->deferred_req);
}
-static int threads_waiting = 0;
+static inline int
+do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key,
+ struct cache_detail *detail, struct ent **item,
+ struct idmap_defer_req *mdr)
+{
+ *item = lookup_fn(key);
+ if (!*item)
+ return -ENOMEM;
+ return cache_check(detail, &(*item)->h, &mdr->req);
+}
static inline int
-idmap_lookup_wait(struct idmap_defer_req *mdr, wait_queue_t waitq, struct
- svc_rqst *rqstp) {
- int ret = -ETIMEDOUT;
-
- set_task_state(current, TASK_INTERRUPTIBLE);
- lock_kernel();
- /* XXX: Does it matter that threads_waiting isn't per-server? */
- /* Note: BKL prevents races with nfsd_svc and other lookups */
- if (2 * threads_waiting > rqstp->rq_server->sv_nrthreads)
- goto out;
- threads_waiting++;
- schedule_timeout(10 * HZ);
- threads_waiting--;
- ret = 0;
-out:
- unlock_kernel();
- remove_wait_queue(&mdr->waitq, &waitq);
- set_task_state(current, TASK_RUNNING);
- put_mdr(mdr);
+do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *),
+ struct ent *key, struct cache_detail *detail,
+ struct ent **item)
+{
+ int ret = -ENOMEM;
+
+ *item = lookup_fn(key);
+ if (!*item)
+ goto out_err;
+ ret = -ETIMEDOUT;
+ if (!test_bit(CACHE_VALID, &(*item)->h.flags)
+ || (*item)->h.expiry_time < get_seconds()
+ || detail->flush_time > (*item)->h.last_refresh)
+ goto out_put;
+ ret = -ENOENT;
+ if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
+ goto out_put;
+ return 0;
+out_put:
+ cache_put(&(*item)->h, detail);
+out_err:
+ *item = NULL;
return ret;
}
static int
idmap_lookup(struct svc_rqst *rqstp,
- struct ent *(*lookup_fn)(struct ent *, int), struct ent *key,
+ struct ent *(*lookup_fn)(struct ent *), struct ent *key,
struct cache_detail *detail, struct ent **item)
{
struct idmap_defer_req *mdr;
- DECLARE_WAITQUEUE(waitq, current);
int ret;
- *item = lookup_fn(key, 0);
- if (!*item)
- return -ENOMEM;
mdr = kmalloc(sizeof(*mdr), GFP_KERNEL);
+ if (!mdr)
+ return -ENOMEM;
memset(mdr, 0, sizeof(*mdr));
+ atomic_set(&mdr->count, 1);
init_waitqueue_head(&mdr->waitq);
- add_wait_queue(&mdr->waitq, &waitq);
- atomic_set(&mdr->count, 2);
mdr->req.defer = idmap_defer;
- ret = cache_check(detail, &(*item)->h, &mdr->req);
+ ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr);
if (ret == -EAGAIN) {
- ret = idmap_lookup_wait(mdr, waitq, rqstp);
- if (ret)
- goto out;
- /* Try again, but don't wait. */
- *item = lookup_fn(key, 0);
- ret = -ENOMEM;
- if (!*item)
- goto out;
- ret = -ETIMEDOUT;
- if (!test_bit(CACHE_VALID, &(*item)->h.flags)) {
- ent_put(&(*item)->h, detail);
- goto out;
- }
- ret = cache_check(detail, &(*item)->h, NULL);
+ wait_event_interruptible_timeout(mdr->waitq,
+ test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ);
+ ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item);
}
-out:
+ put_mdr(mdr);
return ret;
}
key.name[namelen] = '\0';
strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname));
ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item);
+ if (ret == -ENOENT)
+ ret = -ESRCH; /* nfserr_badname */
if (ret)
return ret;
*id = item->id;
- ent_put(&item->h, &nametoid_cache);
+ cache_put(&item->h, &nametoid_cache);
return 0;
}
strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname));
ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item);
+ if (ret == -ENOENT)
+ return sprintf(name, "%u", id);
if (ret)
return ret;
ret = strlen(item->name);
BUG_ON(ret > IDMAP_NAMESZ);
memcpy(name, item->name, ret);
- ent_put(&item->h, &idtoname_cache);
+ cache_put(&item->h, &idtoname_cache);
return ret;
}