Merge branch 'for-3.16' of git://linux-nfs.org/~bfields/linux
Pull nfsd bugfixes from Bruce Fields: "Fixes for a new regression from the xdr encoding rewrite, and a delegation problem we've had for a while (made somewhat more annoying by the vfs delegation support added in 3.13)" * 'for-3.16' of git://linux-nfs.org/~bfields/linux: NFSD: fix bug for readdir of pseudofs NFSD: Don't hand out delegations for 30 seconds after recalling them.
This commit is contained in:
commit
147f1404db
@ -41,6 +41,7 @@
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/addr.h>
|
||||
#include <linux/hash.h>
|
||||
#include "xdr4.h"
|
||||
#include "xdr4cb.h"
|
||||
#include "vfs.h"
|
||||
@ -364,6 +365,79 @@ static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp)
|
||||
return openlockstateid(nfs4_alloc_stid(clp, stateid_slab));
|
||||
}
|
||||
|
||||
/*
|
||||
* When we recall a delegation, we should be careful not to hand it
|
||||
* out again straight away.
|
||||
* To ensure this we keep a pair of bloom filters ('new' and 'old')
|
||||
* in which the filehandles of recalled delegations are "stored".
|
||||
* If a filehandle appear in either filter, a delegation is blocked.
|
||||
* When a delegation is recalled, the filehandle is stored in the "new"
|
||||
* filter.
|
||||
* Every 30 seconds we swap the filters and clear the "new" one,
|
||||
* unless both are empty of course.
|
||||
*
|
||||
* Each filter is 256 bits. We hash the filehandle to 32bit and use the
|
||||
* low 3 bytes as hash-table indices.
|
||||
*
|
||||
* 'state_lock', which is always held when block_delegations() is called,
|
||||
* is used to manage concurrent access. Testing does not need the lock
|
||||
* except when swapping the two filters.
|
||||
*/
|
||||
static struct bloom_pair {
|
||||
int entries, old_entries;
|
||||
time_t swap_time;
|
||||
int new; /* index into 'set' */
|
||||
DECLARE_BITMAP(set[2], 256);
|
||||
} blocked_delegations;
|
||||
|
||||
static int delegation_blocked(struct knfsd_fh *fh)
|
||||
{
|
||||
u32 hash;
|
||||
struct bloom_pair *bd = &blocked_delegations;
|
||||
|
||||
if (bd->entries == 0)
|
||||
return 0;
|
||||
if (seconds_since_boot() - bd->swap_time > 30) {
|
||||
spin_lock(&state_lock);
|
||||
if (seconds_since_boot() - bd->swap_time > 30) {
|
||||
bd->entries -= bd->old_entries;
|
||||
bd->old_entries = bd->entries;
|
||||
memset(bd->set[bd->new], 0,
|
||||
sizeof(bd->set[0]));
|
||||
bd->new = 1-bd->new;
|
||||
bd->swap_time = seconds_since_boot();
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
}
|
||||
hash = arch_fast_hash(&fh->fh_base, fh->fh_size, 0);
|
||||
if (test_bit(hash&255, bd->set[0]) &&
|
||||
test_bit((hash>>8)&255, bd->set[0]) &&
|
||||
test_bit((hash>>16)&255, bd->set[0]))
|
||||
return 1;
|
||||
|
||||
if (test_bit(hash&255, bd->set[1]) &&
|
||||
test_bit((hash>>8)&255, bd->set[1]) &&
|
||||
test_bit((hash>>16)&255, bd->set[1]))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_delegations(struct knfsd_fh *fh)
|
||||
{
|
||||
u32 hash;
|
||||
struct bloom_pair *bd = &blocked_delegations;
|
||||
|
||||
hash = arch_fast_hash(&fh->fh_base, fh->fh_size, 0);
|
||||
|
||||
__set_bit(hash&255, bd->set[bd->new]);
|
||||
__set_bit((hash>>8)&255, bd->set[bd->new]);
|
||||
__set_bit((hash>>16)&255, bd->set[bd->new]);
|
||||
if (bd->entries == 0)
|
||||
bd->swap_time = seconds_since_boot();
|
||||
bd->entries += 1;
|
||||
}
|
||||
|
||||
static struct nfs4_delegation *
|
||||
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
|
||||
{
|
||||
@ -372,6 +446,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
|
||||
dprintk("NFSD alloc_init_deleg\n");
|
||||
if (num_delegations > max_delegations)
|
||||
return NULL;
|
||||
if (delegation_blocked(¤t_fh->fh_handle))
|
||||
return NULL;
|
||||
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
|
||||
if (dp == NULL)
|
||||
return dp;
|
||||
@ -2770,6 +2846,8 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
|
||||
/* Only place dl_time is set; protected by i_lock: */
|
||||
dp->dl_time = get_seconds();
|
||||
|
||||
block_delegations(&dp->dl_fh);
|
||||
|
||||
nfsd4_cb_recall(dp);
|
||||
}
|
||||
|
||||
|
@ -2687,6 +2687,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
|
||||
nfserr = nfserr_toosmall;
|
||||
goto fail;
|
||||
case nfserr_noent:
|
||||
xdr_truncate_encode(xdr, start_offset);
|
||||
goto skip_entry;
|
||||
default:
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user