Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFSv4: fairly test all delegations on a SEQ4_ revocation

When the client is required to use TEST_STATEID to discover which
delegation(s) have been revoked, it may continually test delegations at the
head of the list if the server continues to be unsatisfied and send
SEQ4_STATUS_RECALLABLE_STATE_REVOKED. For a large number of delegations
this behavior is prone to live-lock because the client may never be able to
test and free revoked state at the end of the list since the
SEQ4_STATUS_RECALLABLE_STATE_REVOKED will cause us to flag delegations at
the head of the list to be tested. This problem is further exacerbated by
the state manager's willingness to be scheduled out on a busy system while
testing the list of delegations.

Keep a generation counter for each attempt to test all delegations, and
skip delegations that have already been tested in the current pass.

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
Tested-by: Torkil Svensgaard <torkil@drcmr.dk>
Tested-by: Ruben Vestergaard <rubenv@drcmr.dk>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>

authored by

Benjamin Coddington and committed by
Trond Myklebust
a9b8d90f 59464b26

+8 -1
+6 -1
fs/nfs/delegation.c
··· 448 448 delegation->cred = get_cred(cred); 449 449 delegation->inode = inode; 450 450 delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 451 + delegation->test_gen = 0; 451 452 spin_lock_init(&delegation->lock); 452 453 453 454 spin_lock(&clp->cl_lock); ··· 1295 1294 struct inode *inode; 1296 1295 const struct cred *cred; 1297 1296 nfs4_stateid stateid; 1297 + unsigned long gen = ++server->delegation_gen; 1298 + 1298 1299 restart: 1299 1300 rcu_read_lock(); 1300 1301 restart_locked: ··· 1306 1303 test_bit(NFS_DELEGATION_RETURNING, 1307 1304 &delegation->flags) || 1308 1305 test_bit(NFS_DELEGATION_TEST_EXPIRED, 1309 - &delegation->flags) == 0) 1306 + &delegation->flags) == 0 || 1307 + delegation->test_gen == gen) 1310 1308 continue; 1311 1309 inode = nfs_delegation_grab_inode(delegation); 1312 1310 if (inode == NULL) ··· 1316 1312 cred = get_cred_rcu(delegation->cred); 1317 1313 nfs4_stateid_copy(&stateid, &delegation->stateid); 1318 1314 spin_unlock(&delegation->lock); 1315 + delegation->test_gen = gen; 1319 1316 clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 1320 1317 rcu_read_unlock(); 1321 1318 nfs_delegation_test_free_expired(inode, &stateid, cred);
+1
fs/nfs/delegation.h
··· 21 21 fmode_t type; 22 22 unsigned long pagemod_limit; 23 23 __u64 change_attr; 24 + unsigned long test_gen; 24 25 unsigned long flags; 25 26 refcount_t refcount; 26 27 spinlock_t lock;
+1
include/linux/nfs_fs_sb.h
··· 239 239 struct list_head delegations; 240 240 struct list_head ss_copies; 241 241 242 + unsigned long delegation_gen; 242 243 unsigned long mig_gen; 243 244 unsigned long mig_status; 244 245 #define NFS_MIG_IN_TRANSITION (1)