|
@@ -41,6 +41,7 @@
|
|
#include <linux/ratelimit.h>
|
|
#include <linux/ratelimit.h>
|
|
#include <linux/sunrpc/svcauth_gss.h>
|
|
#include <linux/sunrpc/svcauth_gss.h>
|
|
#include <linux/sunrpc/addr.h>
|
|
#include <linux/sunrpc/addr.h>
|
|
|
|
+#include <linux/hash.h>
|
|
#include "xdr4.h"
|
|
#include "xdr4.h"
|
|
#include "xdr4cb.h"
|
|
#include "xdr4cb.h"
|
|
#include "vfs.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));
|
|
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 *
|
|
static struct nfs4_delegation *
|
|
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
|
|
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");
|
|
dprintk("NFSD alloc_init_deleg\n");
|
|
if (num_delegations > max_delegations)
|
|
if (num_delegations > max_delegations)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
+ if (delegation_blocked(¤t_fh->fh_handle))
|
|
|
|
+ return NULL;
|
|
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
|
|
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
|
|
if (dp == NULL)
|
|
if (dp == NULL)
|
|
return dp;
|
|
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: */
|
|
/* Only place dl_time is set; protected by i_lock: */
|
|
dp->dl_time = get_seconds();
|
|
dp->dl_time = get_seconds();
|
|
|
|
|
|
|
|
+ block_delegations(&dp->dl_fh);
|
|
|
|
+
|
|
nfsd4_cb_recall(dp);
|
|
nfsd4_cb_recall(dp);
|
|
}
|
|
}
|
|
|
|
|