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

Configure Feed

Select the types of activity you want to include in your feed.

nfsd: cancel async COPY operations when admin revokes filesystem state

Async COPY operations hold copy stateids that represent NFSv4 state.
Thus, when the NFS server administrator revokes all NFSv4 state for
a filesystem via the unlock_fs interface, ongoing async COPY
operations referencing that filesystem must also be canceled.

Each cancelled copy triggers a CB_OFFLOAD callback carrying the
NFS4ERR_ADMIN_REVOKED status to notify the client that the server
terminated the operation.

The static drop_client() function is renamed to nfsd4_put_client()
and exported. The function must be exported because both the new
nfsd4_cancel_copy_by_sb() and the CB_OFFLOAD release callback in
nfs4proc.c need to release client references.

Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

+133 -18
+113 -11
fs/nfsd/nfs4proc.c
··· 1427 1427 kfree(copy); 1428 1428 } 1429 1429 1430 + static void release_copy_files(struct nfsd4_copy *copy); 1431 + 1430 1432 static void nfsd4_stop_copy(struct nfsd4_copy *copy) 1431 1433 { 1432 1434 trace_nfsd_copy_async_cancel(copy); 1433 1435 if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) { 1434 1436 kthread_stop(copy->copy_task); 1435 - copy->nfserr = nfs_ok; 1437 + if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags)) 1438 + copy->nfserr = nfs_ok; 1436 1439 set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags); 1437 1440 } 1441 + 1442 + /* 1443 + * The copy was removed from async_copies before this function 1444 + * was called, so the reaper cannot clean it up. Release files 1445 + * here regardless of who won the STOPPED race. If the thread 1446 + * set STOPPED, it has finished using the files. If STOPPED 1447 + * was set here, kthread_stop() waited for the thread to exit. 1448 + */ 1449 + release_copy_files(copy); 1438 1450 nfs4_put_copy(copy); 1439 1451 } 1440 1452 ··· 1474 1462 while ((copy = nfsd4_unhash_copy(clp)) != NULL) 1475 1463 nfsd4_stop_copy(copy); 1476 1464 } 1465 + 1466 + static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy, 1467 + const struct super_block *sb) 1468 + { 1469 + if (copy->nf_src && 1470 + file_inode(copy->nf_src->nf_file)->i_sb == sb) 1471 + return true; 1472 + if (copy->nf_dst && 1473 + file_inode(copy->nf_dst->nf_file)->i_sb == sb) 1474 + return true; 1475 + return false; 1476 + } 1477 + 1478 + /** 1479 + * nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb 1480 + * @net: net namespace containing the copy operations 1481 + * @sb: targeted superblock 1482 + */ 1483 + void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) 1484 + { 1485 + struct nfsd_net *nn = net_generic(net, nfsd_net_id); 1486 + struct nfsd4_copy *copy, *tmp; 1487 + struct nfs4_client *clp; 1488 + unsigned int idhashval; 1489 + LIST_HEAD(to_cancel); 1490 + 1491 + spin_lock(&nn->client_lock); 1492 + for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) { 1493 + struct list_head *head = &nn->conf_id_hashtbl[idhashval]; 1494 + 1495 + list_for_each_entry(clp, head, cl_idhash) { 1496 + spin_lock(&clp->async_lock); 1497 + list_for_each_entry_safe(copy, tmp, 1498 + &clp->async_copies, copies) { 1499 + if (nfsd4_copy_on_sb(copy, sb)) { 1500 + refcount_inc(&copy->refcount); 1501 + /* 1502 + * Hold a reference on the client while 1503 + * nfsd4_stop_copy() runs. Unlike 1504 + * nfsd4_unhash_copy(), cp_clp is not 1505 + * NULLed here because nfsd4_send_cb_offload() 1506 + * needs a valid client to send CB_OFFLOAD. 1507 + * That function takes its own reference to 1508 + * survive callback flight. 1509 + */ 1510 + kref_get(&clp->cl_nfsdfs.cl_ref); 1511 + copy->nfserr = nfserr_admin_revoked; 1512 + set_bit(NFSD4_COPY_F_CB_ERROR, 1513 + &copy->cp_flags); 1514 + list_move(&copy->copies, &to_cancel); 1515 + } 1516 + } 1517 + spin_unlock(&clp->async_lock); 1518 + } 1519 + } 1520 + spin_unlock(&nn->client_lock); 1521 + 1522 + list_for_each_entry_safe(copy, tmp, &to_cancel, copies) { 1523 + struct nfs4_client *clp = copy->cp_clp; 1524 + 1525 + list_del_init(&copy->copies); 1526 + nfsd4_stop_copy(copy); 1527 + nfsd4_put_client(clp); 1528 + } 1529 + } 1530 + 1477 1531 #ifdef CONFIG_NFSD_V4_2_INTER_SSC 1478 1532 1479 1533 extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, ··· 1829 1751 container_of(cbo, struct nfsd4_copy, cp_cb_offload); 1830 1752 1831 1753 set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags); 1754 + nfsd4_put_client(cb->cb_clp); 1832 1755 } 1833 1756 1834 1757 static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, ··· 1949 1870 1950 1871 static void release_copy_files(struct nfsd4_copy *copy) 1951 1872 { 1952 - if (copy->nf_src) 1873 + if (copy->nf_src) { 1953 1874 nfsd_file_put(copy->nf_src); 1954 - if (copy->nf_dst) 1875 + copy->nf_src = NULL; 1876 + } 1877 + if (copy->nf_dst) { 1955 1878 nfsd_file_put(copy->nf_dst); 1879 + copy->nf_dst = NULL; 1880 + } 1956 1881 } 1957 1882 1958 1883 static void cleanup_async_copy(struct nfsd4_copy *copy) ··· 1975 1892 static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) 1976 1893 { 1977 1894 struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload; 1895 + struct nfs4_client *clp = copy->cp_clp; 1896 + 1897 + /* 1898 + * cp_clp is NULL when called via nfsd4_shutdown_copy() during 1899 + * client destruction. Skip the callback; the client is gone. 1900 + */ 1901 + if (!clp) { 1902 + set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags); 1903 + return; 1904 + } 1978 1905 1979 1906 memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res)); 1980 1907 memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh)); 1981 1908 cbo->co_nfserr = copy->nfserr; 1982 1909 cbo->co_retries = 5; 1983 1910 1984 - nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, 1911 + /* 1912 + * Hold a reference on the client while the callback is in flight. 1913 + * Released in nfsd4_cb_offload_release(). 1914 + */ 1915 + kref_get(&clp->cl_nfsdfs.cl_ref); 1916 + 1917 + nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops, 1985 1918 NFSPROC4_CLNT_CB_OFFLOAD); 1986 1919 nfsd41_cb_referring_call(&cbo->co_cb, &cbo->co_referring_sessionid, 1987 1920 cbo->co_referring_slotid, 1988 1921 cbo->co_referring_seqno); 1989 - trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid, 1922 + trace_nfsd_cb_offload(clp, &cbo->co_res.cb_stateid, 1990 1923 &cbo->co_fh, copy->cp_count, copy->nfserr); 1991 1924 nfsd4_try_run_cb(&cbo->co_cb); 1992 1925 } ··· 2017 1918 static int nfsd4_do_async_copy(void *data) 2018 1919 { 2019 1920 struct nfsd4_copy *copy = (struct nfsd4_copy *)data; 1921 + __be32 nfserr = nfs_ok; 2020 1922 2021 1923 trace_nfsd_copy_async(copy); 2022 1924 if (nfsd4_ssc_is_inter(copy)) { ··· 2028 1928 if (IS_ERR(filp)) { 2029 1929 switch (PTR_ERR(filp)) { 2030 1930 case -EBADF: 2031 - copy->nfserr = nfserr_wrong_type; 1931 + nfserr = nfserr_wrong_type; 2032 1932 break; 2033 1933 default: 2034 - copy->nfserr = nfserr_offload_denied; 1934 + nfserr = nfserr_offload_denied; 2035 1935 } 2036 1936 /* ss_mnt will be unmounted by the laundromat */ 2037 1937 goto do_callback; 2038 1938 } 2039 - copy->nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, 2040 - false); 1939 + nfserr = nfsd4_do_copy(copy, filp, copy->nf_dst->nf_file, 1940 + false); 2041 1941 nfsd4_cleanup_inter_ssc(copy->ss_nsui, filp, copy->nf_dst); 2042 1942 } else { 2043 - copy->nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, 2044 - copy->nf_dst->nf_file, false); 1943 + nfserr = nfsd4_do_copy(copy, copy->nf_src->nf_file, 1944 + copy->nf_dst->nf_file, false); 2045 1945 } 2046 1946 2047 1947 do_callback: 1948 + if (!test_bit(NFSD4_COPY_F_CB_ERROR, &copy->cp_flags)) 1949 + copy->nfserr = nfserr; 2048 1950 /* The kthread exits forthwith. Ensure that a subsequent 2049 1951 * OFFLOAD_CANCEL won't try to kill it again. */ 2050 1952 set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
+13 -7
fs/nfsd/nfs4state.c
··· 2413 2413 kmem_cache_free(client_slab, clp); 2414 2414 } 2415 2415 2416 - static void drop_client(struct nfs4_client *clp) 2416 + /** 2417 + * nfsd4_put_client - release a reference on an nfs4_client 2418 + * @clp: the client to be released 2419 + * 2420 + * When the last reference is released, the client is freed. 2421 + */ 2422 + void nfsd4_put_client(struct nfs4_client *clp) 2417 2423 { 2418 2424 kref_put(&clp->cl_nfsdfs.cl_ref, __free_client); 2419 2425 } ··· 2441 2435 clp->cl_nfsd_dentry = NULL; 2442 2436 wake_up_all(&expiry_wq); 2443 2437 } 2444 - drop_client(clp); 2438 + nfsd4_put_client(clp); 2445 2439 } 2446 2440 2447 2441 /* must be called under the client_lock */ ··· 2839 2833 spin_unlock(&clp->cl_lock); 2840 2834 seq_puts(m, "\n"); 2841 2835 2842 - drop_client(clp); 2836 + nfsd4_put_client(clp); 2843 2837 2844 2838 return 0; 2845 2839 } ··· 3105 3099 3106 3100 ret = seq_open(file, &states_seq_ops); 3107 3101 if (ret) { 3108 - drop_client(clp); 3102 + nfsd4_put_client(clp); 3109 3103 return ret; 3110 3104 } 3111 3105 s = file->private_data; ··· 3119 3113 struct nfs4_client *clp = m->private; 3120 3114 3121 3115 /* XXX: alternatively, we could get/drop in seq start/stop */ 3122 - drop_client(clp); 3116 + nfsd4_put_client(clp); 3123 3117 return seq_release(inode, file); 3124 3118 } 3125 3119 ··· 3175 3169 if (!clp) 3176 3170 return -ENXIO; 3177 3171 force_expire_client(clp); 3178 - drop_client(clp); 3172 + nfsd4_put_client(clp); 3179 3173 return 7; 3180 3174 } 3181 3175 ··· 3210 3204 { 3211 3205 struct nfs4_client *clp = cb->cb_clp; 3212 3206 3213 - drop_client(clp); 3207 + nfsd4_put_client(clp); 3214 3208 } 3215 3209 3216 3210 static int
+1
fs/nfsd/nfsctl.c
··· 285 285 * 2. Is that directory a mount point, or 286 286 * 3. Is that directory the root of an exported file system? 287 287 */ 288 + nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb); 288 289 error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); 289 290 mutex_lock(&nfsd_mutex); 290 291 nn = net_generic(netns(file), nfsd_net_id);
+5
fs/nfsd/state.h
··· 822 822 823 823 extern void nfsd4_shutdown_callback(struct nfs4_client *); 824 824 extern void nfsd4_shutdown_copy(struct nfs4_client *clp); 825 + void nfsd4_put_client(struct nfs4_client *clp); 825 826 void nfsd4_async_copy_reaper(struct nfsd_net *nn); 826 827 bool nfsd4_has_active_async_copies(struct nfs4_client *clp); 827 828 extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, ··· 843 842 844 843 #ifdef CONFIG_NFSD_V4 845 844 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); 845 + void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); 846 846 #else 847 847 static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) 848 + { 849 + } 850 + static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) 848 851 { 849 852 } 850 853 #endif
+1
fs/nfsd/xdr4.h
··· 732 732 #define NFSD4_COPY_F_COMMITTED (3) 733 733 #define NFSD4_COPY_F_COMPLETED (4) 734 734 #define NFSD4_COPY_F_OFFLOAD_DONE (5) 735 + #define NFSD4_COPY_F_CB_ERROR (6) 735 736 736 737 /* response */ 737 738 __be32 nfserr;