diff options
-rw-r--r-- | fs/nfsd/nfs4recover.c | 225 | ||||
-rw-r--r-- | include/uapi/linux/nfsd/cld.h | 1 |
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 | ||
782 | static ssize_t | 782 | static 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 | |||
810 | static ssize_t | ||
783 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | 811 | cld_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 | */ | ||
1069 | static int | 1117 | static int |
1070 | nfsd4_cld_check(struct nfs4_client *clp) | 1118 | nfsd4_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 | */ | ||
1157 | static int | ||
1158 | nfsd4_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 | |||
1177 | static int | ||
1178 | nfsd4_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); | ||
1196 | out_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 */ | ||
1103 | static void | 1204 | static void |
1104 | nfsd4_cld_grace_done(struct nfsd_net *nn) | 1205 | nfsd4_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 | ||
1128 | static 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 | */ | ||
1233 | static void | ||
1234 | nfsd4_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); | ||
1252 | out_err: | ||
1253 | nfs4_release_reclaim(nn); | ||
1254 | if (ret) | ||
1255 | printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret); | ||
1256 | } | ||
1257 | |||
1258 | static int | ||
1259 | nfs4_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 | |||
1277 | static void | ||
1278 | nfs4_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 | |||
1285 | static int | ||
1286 | nfsd4_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 | |||
1308 | err_remove: | ||
1309 | nfsd4_remove_cld_pipe(net); | ||
1310 | err_shutdown: | ||
1311 | nfs4_cld_state_shutdown(net); | ||
1312 | return status; | ||
1313 | } | ||
1314 | |||
1315 | static void | ||
1316 | nfsd4_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 */ | ||
1326 | static 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 */ | ||
1336 | static 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; | ||
1520 | do_init: | 1729 | do_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 */ |