aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Mayhew <smayhew@redhat.com>2019-03-26 18:06:27 -0400
committerJ. Bruce Fields <bfields@redhat.com>2019-04-24 09:46:34 -0400
commit74725959c33c14114fdce1e36e3504d106584d53 (patch)
treebccd26a521a9e577a74113eb3a9cb38118ed2ba9
parent6b1891052a3f8e3c3217e8512bbed2fd6252977b (diff)
nfsd: un-deprecate nfsdcld
When nfsdcld was released, it was quickly deprecated in favor of the nfsdcltrack usermodehelper, so as to not require another running daemon. That prevents NFSv4 clients from reclaiming locks from nfsd's running in containers, since neither nfsdcltrack nor the legacy client tracking code work in containers. This commit un-deprecates the use of nfsdcld, with one twist: we will populate the reclaim_str_hashtbl on startup. During client tracking initialization, do an upcall ("GraceStart") to nfsdcld to get a list of clients from the database. nfsdcld will do one downcall with a status of -EINPROGRESS for each client record in the database, which in turn will cause an nfs4_client_reclaim to be added to the reclaim_str_hashtbl. When complete, nfsdcld will do a final downcall with a status of 0. This will save nfsd from having to do an upcall to the daemon during nfs4_check_open_reclaim() processing. Even though nfsdcld was quickly deprecated, there is a very small chance of old nfsdcld daemons running in the wild. These will respond to the new "GraceStart" upcall with -EOPNOTSUPP, in which case we will log a message and fall back to the original nfsdcld tracking ops (now called nfsd4_cld_tracking_ops_v0). Signed-off-by: Scott Mayhew <smayhew@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4recover.c225
-rw-r--r--include/uapi/linux/nfsd/cld.h1
2 files changed, 218 insertions, 8 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 2243b909b407..89c2a27956d0 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -780,6 +780,34 @@ cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
780} 780}
781 781
782static ssize_t 782static ssize_t
783__cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg,
784 struct nfsd_net *nn)
785{
786 uint8_t cmd;
787 struct xdr_netobj name;
788 uint16_t namelen;
789
790 if (get_user(cmd, &cmsg->cm_cmd)) {
791 dprintk("%s: error when copying cmd from userspace", __func__);
792 return -EFAULT;
793 }
794 if (cmd == Cld_GraceStart) {
795 if (get_user(namelen, &cmsg->cm_u.cm_name.cn_len))
796 return -EFAULT;
797 name.data = memdup_user(&cmsg->cm_u.cm_name.cn_id, namelen);
798 if (IS_ERR_OR_NULL(name.data))
799 return -EFAULT;
800 name.len = namelen;
801 if (!nfs4_client_to_reclaim(name, nn)) {
802 kfree(name.data);
803 return -EFAULT;
804 }
805 return sizeof(*cmsg);
806 }
807 return -EFAULT;
808}
809
810static ssize_t
783cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) 811cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
784{ 812{
785 struct cld_upcall *tmp, *cup; 813 struct cld_upcall *tmp, *cup;
@@ -788,6 +816,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
788 struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info, 816 struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
789 nfsd_net_id); 817 nfsd_net_id);
790 struct cld_net *cn = nn->cld_net; 818 struct cld_net *cn = nn->cld_net;
819 int16_t status;
791 820
792 if (mlen != sizeof(*cmsg)) { 821 if (mlen != sizeof(*cmsg)) {
793 dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen, 822 dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen,
@@ -801,13 +830,24 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
801 return -EFAULT; 830 return -EFAULT;
802 } 831 }
803 832
833 /*
834 * copy the status so we know whether to remove the upcall from the
835 * list (for -EINPROGRESS, we just want to make sure the xid is
836 * valid, not remove the upcall from the list)
837 */
838 if (get_user(status, &cmsg->cm_status)) {
839 dprintk("%s: error when copying status from userspace", __func__);
840 return -EFAULT;
841 }
842
804 /* walk the list and find corresponding xid */ 843 /* walk the list and find corresponding xid */
805 cup = NULL; 844 cup = NULL;
806 spin_lock(&cn->cn_lock); 845 spin_lock(&cn->cn_lock);
807 list_for_each_entry(tmp, &cn->cn_list, cu_list) { 846 list_for_each_entry(tmp, &cn->cn_list, cu_list) {
808 if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) { 847 if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) {
809 cup = tmp; 848 cup = tmp;
810 list_del_init(&cup->cu_list); 849 if (status != -EINPROGRESS)
850 list_del_init(&cup->cu_list);
811 break; 851 break;
812 } 852 }
813 } 853 }
@@ -819,6 +859,9 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
819 return -EINVAL; 859 return -EINVAL;
820 } 860 }
821 861
862 if (status == -EINPROGRESS)
863 return __cld_pipe_inprogress_downcall(cmsg, nn);
864
822 if (copy_from_user(&cup->cu_msg, src, mlen) != 0) 865 if (copy_from_user(&cup->cu_msg, src, mlen) != 0)
823 return -EFAULT; 866 return -EFAULT;
824 867
@@ -1065,9 +1108,14 @@ out_err:
1065 "record from stable storage: %d\n", ret); 1108 "record from stable storage: %d\n", ret);
1066} 1109}
1067 1110
1068/* Check for presence of a record, and update its timestamp */ 1111/*
1112 * For older nfsdcld's that do not allow us to "slurp" the clients
1113 * from the tracking database during startup.
1114 *
1115 * Check for presence of a record, and update its timestamp
1116 */
1069static int 1117static int
1070nfsd4_cld_check(struct nfs4_client *clp) 1118nfsd4_cld_check_v0(struct nfs4_client *clp)
1071{ 1119{
1072 int ret; 1120 int ret;
1073 struct cld_upcall *cup; 1121 struct cld_upcall *cup;
@@ -1100,8 +1148,61 @@ nfsd4_cld_check(struct nfs4_client *clp)
1100 return ret; 1148 return ret;
1101} 1149}
1102 1150
1151/*
1152 * For newer nfsdcld's that allow us to "slurp" the clients
1153 * from the tracking database during startup.
1154 *
1155 * Check for presence of a record in the reclaim_str_hashtbl
1156 */
1157static int
1158nfsd4_cld_check(struct nfs4_client *clp)
1159{
1160 struct nfs4_client_reclaim *crp;
1161 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1162
1163 /* did we already find that this client is stable? */
1164 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1165 return 0;
1166
1167 /* look for it in the reclaim hashtable otherwise */
1168 crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
1169 if (crp) {
1170 crp->cr_clp = clp;
1171 return 0;
1172 }
1173
1174 return -ENOENT;
1175}
1176
1177static int
1178nfsd4_cld_grace_start(struct nfsd_net *nn)
1179{
1180 int ret;
1181 struct cld_upcall *cup;
1182 struct cld_net *cn = nn->cld_net;
1183
1184 cup = alloc_cld_upcall(cn);
1185 if (!cup) {
1186 ret = -ENOMEM;
1187 goto out_err;
1188 }
1189
1190 cup->cu_msg.cm_cmd = Cld_GraceStart;
1191 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
1192 if (!ret)
1193 ret = cup->cu_msg.cm_status;
1194
1195 free_cld_upcall(cup);
1196out_err:
1197 if (ret)
1198 dprintk("%s: Unable to get clients from userspace: %d\n",
1199 __func__, ret);
1200 return ret;
1201}
1202
1203/* For older nfsdcld's that need cm_gracetime */
1103static void 1204static void
1104nfsd4_cld_grace_done(struct nfsd_net *nn) 1205nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
1105{ 1206{
1106 int ret; 1207 int ret;
1107 struct cld_upcall *cup; 1208 struct cld_upcall *cup;
@@ -1125,11 +1226,118 @@ out_err:
1125 printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret); 1226 printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
1126} 1227}
1127 1228
1128static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { 1229/*
1230 * For newer nfsdcld's that do not need cm_gracetime. We also need to call
1231 * nfs4_release_reclaim() to clear out the reclaim_str_hashtbl.
1232 */
1233static void
1234nfsd4_cld_grace_done(struct nfsd_net *nn)
1235{
1236 int ret;
1237 struct cld_upcall *cup;
1238 struct cld_net *cn = nn->cld_net;
1239
1240 cup = alloc_cld_upcall(cn);
1241 if (!cup) {
1242 ret = -ENOMEM;
1243 goto out_err;
1244 }
1245
1246 cup->cu_msg.cm_cmd = Cld_GraceDone;
1247 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
1248 if (!ret)
1249 ret = cup->cu_msg.cm_status;
1250
1251 free_cld_upcall(cup);
1252out_err:
1253 nfs4_release_reclaim(nn);
1254 if (ret)
1255 printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
1256}
1257
1258static int
1259nfs4_cld_state_init(struct net *net)
1260{
1261 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1262 int i;
1263
1264 nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
1265 sizeof(struct list_head),
1266 GFP_KERNEL);
1267 if (!nn->reclaim_str_hashtbl)
1268 return -ENOMEM;
1269
1270 for (i = 0; i < CLIENT_HASH_SIZE; i++)
1271 INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
1272 nn->reclaim_str_hashtbl_size = 0;
1273
1274 return 0;
1275}
1276
1277static void
1278nfs4_cld_state_shutdown(struct net *net)
1279{
1280 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1281
1282 kfree(nn->reclaim_str_hashtbl);
1283}
1284
1285static int
1286nfsd4_cld_tracking_init(struct net *net)
1287{
1288 int status;
1289 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1290
1291 status = nfs4_cld_state_init(net);
1292 if (status)
1293 return status;
1294
1295 status = nfsd4_init_cld_pipe(net);
1296 if (status)
1297 goto err_shutdown;
1298
1299 status = nfsd4_cld_grace_start(nn);
1300 if (status) {
1301 if (status == -EOPNOTSUPP)
1302 printk(KERN_WARNING "NFSD: Please upgrade nfsdcld.\n");
1303 nfs4_release_reclaim(nn);
1304 goto err_remove;
1305 }
1306 return 0;
1307
1308err_remove:
1309 nfsd4_remove_cld_pipe(net);
1310err_shutdown:
1311 nfs4_cld_state_shutdown(net);
1312 return status;
1313}
1314
1315static void
1316nfsd4_cld_tracking_exit(struct net *net)
1317{
1318 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1319
1320 nfs4_release_reclaim(nn);
1321 nfsd4_remove_cld_pipe(net);
1322 nfs4_cld_state_shutdown(net);
1323}
1324
1325/* For older nfsdcld's */
1326static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v0 = {
1129 .init = nfsd4_init_cld_pipe, 1327 .init = nfsd4_init_cld_pipe,
1130 .exit = nfsd4_remove_cld_pipe, 1328 .exit = nfsd4_remove_cld_pipe,
1131 .create = nfsd4_cld_create, 1329 .create = nfsd4_cld_create,
1132 .remove = nfsd4_cld_remove, 1330 .remove = nfsd4_cld_remove,
1331 .check = nfsd4_cld_check_v0,
1332 .grace_done = nfsd4_cld_grace_done_v0,
1333};
1334
1335/* For newer nfsdcld's */
1336static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
1337 .init = nfsd4_cld_tracking_init,
1338 .exit = nfsd4_cld_tracking_exit,
1339 .create = nfsd4_cld_create,
1340 .remove = nfsd4_cld_remove,
1133 .check = nfsd4_cld_check, 1341 .check = nfsd4_cld_check,
1134 .grace_done = nfsd4_cld_grace_done, 1342 .grace_done = nfsd4_cld_grace_done,
1135}; 1343};
@@ -1514,9 +1722,10 @@ nfsd4_client_tracking_init(struct net *net)
1514 1722
1515 /* Finally, try to use nfsdcld */ 1723 /* Finally, try to use nfsdcld */
1516 nn->client_tracking_ops = &nfsd4_cld_tracking_ops; 1724 nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
1517 printk(KERN_WARNING "NFSD: the nfsdcld client tracking upcall will be " 1725 status = nn->client_tracking_ops->init(net);
1518 "removed in 3.10. Please transition to using " 1726 if (!status)
1519 "nfsdcltrack.\n"); 1727 return status;
1728 nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v0;
1520do_init: 1729do_init:
1521 status = nn->client_tracking_ops->init(net); 1730 status = nn->client_tracking_ops->init(net);
1522 if (status) { 1731 if (status) {
diff --git a/include/uapi/linux/nfsd/cld.h b/include/uapi/linux/nfsd/cld.h
index f8f5cccad749..b1e9de4f07d5 100644
--- a/include/uapi/linux/nfsd/cld.h
+++ b/include/uapi/linux/nfsd/cld.h
@@ -36,6 +36,7 @@ enum cld_command {
36 Cld_Remove, /* remove record of this cm_id */ 36 Cld_Remove, /* remove record of this cm_id */
37 Cld_Check, /* is this cm_id allowed? */ 37 Cld_Check, /* is this cm_id allowed? */
38 Cld_GraceDone, /* grace period is complete */ 38 Cld_GraceDone, /* grace period is complete */
39 Cld_GraceStart,
39}; 40};
40 41
41/* representation of long-form NFSv4 client ID */ 42/* representation of long-form NFSv4 client ID */