aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 */