Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
Pull nfsd changes from Bruce Fields: "Changes this time include: - 4.1 enabled on the server by default: the last 4.1-specific issues I know of are fixed, so we're not going to find the rest of the bugs without more exposure. - Experimental support for NFSv4.2 MAC Labeling (to allow running selinux over NFS), from Dave Quigley. - Fixes for some delicate cache/upcall races that could cause rare server hangs; thanks to Neil Brown and Bodo Stroesser for extreme debugging persistence. - Fixes for some bugs found at the recent NFS bakeathon, mostly v4 and v4.1-specific, but also a generic bug handling fragmented rpc calls" * 'for-3.11' of git://linux-nfs.org/~bfields/linux: (31 commits) nfsd4: support minorversion 1 by default nfsd4: allow destroy_session over destroyed session svcrpc: fix failures to handle -1 uid's sunrpc: Don't schedule an upcall on a replaced cache entry. net/sunrpc: xpt_auth_cache should be ignored when expired. sunrpc/cache: ensure items removed from cache do not have pending upcalls. sunrpc/cache: use cache_fresh_unlocked consistently and correctly. sunrpc/cache: remove races with queuing an upcall. nfsd4: return delegation immediately if lease fails nfsd4: do not throw away 4.1 lock state on last unlock nfsd4: delegation-based open reclaims should bypass permissions svcrpc: don't error out on small tcp fragment svcrpc: fix handling of too-short rpc's nfsd4: minor read_buf cleanup nfsd4: fix decoding of compounds across page boundaries nfsd4: clean up nfs4_open_delegation NFSD: Don't give out read delegations on creates nfsd4: allow client to send no cb_sec flavors nfsd4: fail attempts to request gss on the backchannel nfsd4: implement minimal SP4_MACH_CRED ...
This commit is contained in:
commit
0ff08ba5d0
20 changed files with 513 additions and 183 deletions
|
@ -139,11 +139,12 @@ void gss_mech_unregister(struct gss_api_mech *gm)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gss_mech_unregister);
|
||||
|
||||
static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
|
||||
struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
|
||||
{
|
||||
__module_get(gm->gm_owner);
|
||||
return gm;
|
||||
}
|
||||
EXPORT_SYMBOL(gss_mech_get);
|
||||
|
||||
static struct gss_api_mech *
|
||||
_gss_mech_get_by_name(const char *name)
|
||||
|
@ -360,6 +361,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gss_pseudoflavor_to_service);
|
||||
|
||||
char *
|
||||
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
|
||||
|
@ -379,6 +381,7 @@ gss_mech_put(struct gss_api_mech * gm)
|
|||
if (gm)
|
||||
module_put(gm->gm_owner);
|
||||
}
|
||||
EXPORT_SYMBOL(gss_mech_put);
|
||||
|
||||
/* The mech could probably be determined from the token instead, but it's just
|
||||
* as easy for now to pass it in. */
|
||||
|
|
|
@ -377,8 +377,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
|
|||
new->handle.data = tmp->handle.data;
|
||||
tmp->handle.data = NULL;
|
||||
new->mechctx = NULL;
|
||||
new->cred.cr_group_info = NULL;
|
||||
new->cred.cr_principal = NULL;
|
||||
init_svc_cred(&new->cred);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -392,9 +391,7 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
|
|||
memset(&new->seqdata, 0, sizeof(new->seqdata));
|
||||
spin_lock_init(&new->seqdata.sd_lock);
|
||||
new->cred = tmp->cred;
|
||||
tmp->cred.cr_group_info = NULL;
|
||||
new->cred.cr_principal = tmp->cred.cr_principal;
|
||||
tmp->cred.cr_principal = NULL;
|
||||
init_svc_cred(&tmp->cred);
|
||||
}
|
||||
|
||||
static struct cache_head *
|
||||
|
@ -487,7 +484,7 @@ static int rsc_parse(struct cache_detail *cd,
|
|||
len = qword_get(&mesg, buf, mlen);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
gm = gss_mech_get_by_name(buf);
|
||||
gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
|
||||
status = -EOPNOTSUPP;
|
||||
if (!gm)
|
||||
goto out;
|
||||
|
@ -517,7 +514,6 @@ static int rsc_parse(struct cache_detail *cd,
|
|||
rscp = rsc_update(cd, &rsci, rscp);
|
||||
status = 0;
|
||||
out:
|
||||
gss_mech_put(gm);
|
||||
rsc_free(&rsci);
|
||||
if (rscp)
|
||||
cache_put(&rscp->h, cd);
|
||||
|
|
|
@ -50,12 +50,6 @@ static void cache_init(struct cache_head *h)
|
|||
h->last_refresh = now;
|
||||
}
|
||||
|
||||
static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
|
||||
{
|
||||
return (h->expiry_time < seconds_since_boot()) ||
|
||||
(detail->flush_time > h->last_refresh);
|
||||
}
|
||||
|
||||
struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
|
||||
struct cache_head *key, int hash)
|
||||
{
|
||||
|
@ -201,7 +195,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
|
|||
return sunrpc_cache_pipe_upcall(cd, h);
|
||||
}
|
||||
|
||||
static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h)
|
||||
static inline int cache_is_valid(struct cache_head *h)
|
||||
{
|
||||
if (!test_bit(CACHE_VALID, &h->flags))
|
||||
return -EAGAIN;
|
||||
|
@ -227,16 +221,15 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
|
|||
int rv;
|
||||
|
||||
write_lock(&detail->hash_lock);
|
||||
rv = cache_is_valid(detail, h);
|
||||
if (rv != -EAGAIN) {
|
||||
write_unlock(&detail->hash_lock);
|
||||
return rv;
|
||||
rv = cache_is_valid(h);
|
||||
if (rv == -EAGAIN) {
|
||||
set_bit(CACHE_NEGATIVE, &h->flags);
|
||||
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
|
||||
rv = -ENOENT;
|
||||
}
|
||||
set_bit(CACHE_NEGATIVE, &h->flags);
|
||||
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
|
||||
write_unlock(&detail->hash_lock);
|
||||
cache_fresh_unlocked(h, detail);
|
||||
return -ENOENT;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -260,7 +253,7 @@ int cache_check(struct cache_detail *detail,
|
|||
long refresh_age, age;
|
||||
|
||||
/* First decide return status as best we can */
|
||||
rv = cache_is_valid(detail, h);
|
||||
rv = cache_is_valid(h);
|
||||
|
||||
/* now see if we want to start an upcall */
|
||||
refresh_age = (h->expiry_time - h->last_refresh);
|
||||
|
@ -269,19 +262,17 @@ int cache_check(struct cache_detail *detail,
|
|||
if (rqstp == NULL) {
|
||||
if (rv == -EAGAIN)
|
||||
rv = -ENOENT;
|
||||
} else if (rv == -EAGAIN || age > refresh_age/2) {
|
||||
} else if (rv == -EAGAIN ||
|
||||
(h->expiry_time != 0 && age > refresh_age/2)) {
|
||||
dprintk("RPC: Want update, refage=%ld, age=%ld\n",
|
||||
refresh_age, age);
|
||||
if (!test_and_set_bit(CACHE_PENDING, &h->flags)) {
|
||||
switch (cache_make_upcall(detail, h)) {
|
||||
case -EINVAL:
|
||||
clear_bit(CACHE_PENDING, &h->flags);
|
||||
cache_revisit_request(h);
|
||||
rv = try_to_negate_entry(detail, h);
|
||||
break;
|
||||
case -EAGAIN:
|
||||
clear_bit(CACHE_PENDING, &h->flags);
|
||||
cache_revisit_request(h);
|
||||
cache_fresh_unlocked(h, detail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -293,7 +284,7 @@ int cache_check(struct cache_detail *detail,
|
|||
* Request was not deferred; handle it as best
|
||||
* we can ourselves:
|
||||
*/
|
||||
rv = cache_is_valid(detail, h);
|
||||
rv = cache_is_valid(h);
|
||||
if (rv == -EAGAIN)
|
||||
rv = -ETIMEDOUT;
|
||||
}
|
||||
|
@ -310,7 +301,7 @@ EXPORT_SYMBOL_GPL(cache_check);
|
|||
* a current pointer into that list and into the table
|
||||
* for that entry.
|
||||
*
|
||||
* Each time clean_cache is called it finds the next non-empty entry
|
||||
* Each time cache_clean is called it finds the next non-empty entry
|
||||
* in the current table and walks the list in that entry
|
||||
* looking for entries that can be removed.
|
||||
*
|
||||
|
@ -457,9 +448,8 @@ static int cache_clean(void)
|
|||
current_index ++;
|
||||
spin_unlock(&cache_list_lock);
|
||||
if (ch) {
|
||||
if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
|
||||
cache_dequeue(current_detail, ch);
|
||||
cache_revisit_request(ch);
|
||||
set_bit(CACHE_CLEANED, &ch->flags);
|
||||
cache_fresh_unlocked(ch, d);
|
||||
cache_put(ch, d);
|
||||
}
|
||||
} else
|
||||
|
@ -1036,23 +1026,32 @@ static int cache_release(struct inode *inode, struct file *filp,
|
|||
|
||||
static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
|
||||
{
|
||||
struct cache_queue *cq;
|
||||
struct cache_queue *cq, *tmp;
|
||||
struct cache_request *cr;
|
||||
struct list_head dequeued;
|
||||
|
||||
INIT_LIST_HEAD(&dequeued);
|
||||
spin_lock(&queue_lock);
|
||||
list_for_each_entry(cq, &detail->queue, list)
|
||||
list_for_each_entry_safe(cq, tmp, &detail->queue, list)
|
||||
if (!cq->reader) {
|
||||
struct cache_request *cr = container_of(cq, struct cache_request, q);
|
||||
cr = container_of(cq, struct cache_request, q);
|
||||
if (cr->item != ch)
|
||||
continue;
|
||||
if (test_bit(CACHE_PENDING, &ch->flags))
|
||||
/* Lost a race and it is pending again */
|
||||
break;
|
||||
if (cr->readers != 0)
|
||||
continue;
|
||||
list_del(&cr->q.list);
|
||||
spin_unlock(&queue_lock);
|
||||
cache_put(cr->item, detail);
|
||||
kfree(cr->buf);
|
||||
kfree(cr);
|
||||
return;
|
||||
list_move(&cr->q.list, &dequeued);
|
||||
}
|
||||
spin_unlock(&queue_lock);
|
||||
while (!list_empty(&dequeued)) {
|
||||
cr = list_entry(dequeued.next, struct cache_request, q.list);
|
||||
list_del(&cr->q.list);
|
||||
cache_put(cr->item, detail);
|
||||
kfree(cr->buf);
|
||||
kfree(cr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1166,6 +1165,7 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
|
|||
|
||||
char *buf;
|
||||
struct cache_request *crq;
|
||||
int ret = 0;
|
||||
|
||||
if (!detail->cache_request)
|
||||
return -EINVAL;
|
||||
|
@ -1174,6 +1174,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
|
|||
warn_no_listener(detail);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (test_bit(CACHE_CLEANED, &h->flags))
|
||||
/* Too late to make an upcall */
|
||||
return -EAGAIN;
|
||||
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
|
@ -1191,10 +1194,18 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
|
|||
crq->len = 0;
|
||||
crq->readers = 0;
|
||||
spin_lock(&queue_lock);
|
||||
list_add_tail(&crq->q.list, &detail->queue);
|
||||
if (test_bit(CACHE_PENDING, &h->flags))
|
||||
list_add_tail(&crq->q.list, &detail->queue);
|
||||
else
|
||||
/* Lost a race, no longer PENDING, so don't enqueue */
|
||||
ret = -EAGAIN;
|
||||
spin_unlock(&queue_lock);
|
||||
wake_up(&queue_wait);
|
||||
return 0;
|
||||
if (ret == -EAGAIN) {
|
||||
kfree(buf);
|
||||
kfree(crq);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
|
||||
|
||||
|
|
|
@ -347,13 +347,13 @@ ip_map_cached_get(struct svc_xprt *xprt)
|
|||
spin_lock(&xprt->xpt_lock);
|
||||
ipm = xprt->xpt_auth_cache;
|
||||
if (ipm != NULL) {
|
||||
if (!cache_valid(&ipm->h)) {
|
||||
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
|
||||
if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
|
||||
/*
|
||||
* The entry has been invalidated since it was
|
||||
* remembered, e.g. by a second mount from the
|
||||
* same IP address.
|
||||
*/
|
||||
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
|
||||
xprt->xpt_auth_cache = NULL;
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
cache_put(&ipm->h, sn->ip_map_cache);
|
||||
|
@ -493,8 +493,6 @@ static int unix_gid_parse(struct cache_detail *cd,
|
|||
if (rv)
|
||||
return -EINVAL;
|
||||
uid = make_kuid(&init_user_ns, id);
|
||||
if (!uid_valid(uid))
|
||||
return -EINVAL;
|
||||
ug.uid = uid;
|
||||
|
||||
expiry = get_expiry(&mesg);
|
||||
|
|
|
@ -917,7 +917,10 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
|
|||
len = svsk->sk_datalen;
|
||||
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
for (i = 0; i < npages; i++) {
|
||||
BUG_ON(svsk->sk_pages[i] == NULL);
|
||||
if (svsk->sk_pages[i] == NULL) {
|
||||
WARN_ON_ONCE(1);
|
||||
continue;
|
||||
}
|
||||
put_page(svsk->sk_pages[i]);
|
||||
svsk->sk_pages[i] = NULL;
|
||||
}
|
||||
|
@ -1092,8 +1095,10 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
|
|||
goto err_noclose;
|
||||
}
|
||||
|
||||
if (svc_sock_reclen(svsk) < 8)
|
||||
if (svsk->sk_datalen < 8) {
|
||||
svsk->sk_datalen = 0;
|
||||
goto err_delete; /* client is nuts. */
|
||||
}
|
||||
|
||||
rqstp->rq_arg.len = svsk->sk_datalen;
|
||||
rqstp->rq_arg.page_base = 0;
|
||||
|
|
|
@ -2534,7 +2534,6 @@ static struct rpc_xprt_ops bc_tcp_ops = {
|
|||
.reserve_xprt = xprt_reserve_xprt,
|
||||
.release_xprt = xprt_release_xprt,
|
||||
.alloc_slot = xprt_alloc_slot,
|
||||
.rpcbind = xs_local_rpcbind,
|
||||
.buf_alloc = bc_malloc,
|
||||
.buf_free = bc_free,
|
||||
.send_request = bc_send_request,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue