diff options
author | Olga Kornievskaia <kolga@netapp.com> | 2018-07-20 18:19:20 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2018-09-25 20:34:54 -0400 |
commit | e0639dc5805a9d4faaa2c07ad98fa853b9529dd3 (patch) | |
tree | 6807e44cd08cf5035f46d4f01d1a0db486cac7d0 | |
parent | 885e2bf3ea5121975ade0d7866ab6226a8547dc9 (diff) |
NFSD introduce async copy feature
Upon receiving a request for async copy, create a new kthread. If we
get asynchronous request, make sure to copy the needed arguments/state
from the stack before starting the copy. Then start the thread and reply
back to the client indicating copy is asynchronous.
nfsd_copy_file_range() will copy in a loop over the total number of
bytes is needed to copy. In case a failure happens in the middle, we
ignore the error and return how much we copied so far. Once done
creating a workitem for the callback workqueue and send CB_OFFLOAD with
the results.
The lifetime of the copy stateid is bound to the vfs copy. This way we
don't need to keep the nfsd_net structure for the callback. We could
keep it around longer so that an OFFLOAD_STATUS that came late would
still get results, but clients should be able to deal without that.
We handle OFFLOAD_CANCEL by sending a signal to the copy thread and
calling kthread_stop.
A client should cancel any ongoing copies before calling DESTROY_CLIENT;
if not, we return a CLIENT_BUSY error.
If the client is destroyed for some other reason (lease expiration, or
server shutdown), we must clean up any ongoing copies ourselves.
Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
[colin.king@canonical.com: fix leak in error case]
[bfields@fieldses.org: remove signalling, merge patches]
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/nfsd/netns.h | 8 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 261 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 38 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 21 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 1 | ||||
-rw-r--r-- | fs/nfsd/state.h | 9 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 12 |
7 files changed, 326 insertions, 24 deletions
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 426f55005697..32cb8c027483 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h | |||
@@ -123,6 +123,14 @@ struct nfsd_net { | |||
123 | 123 | ||
124 | wait_queue_head_t ntf_wq; | 124 | wait_queue_head_t ntf_wq; |
125 | atomic_t ntf_refcnt; | 125 | atomic_t ntf_refcnt; |
126 | |||
127 | /* | ||
128 | * clientid and stateid data for construction of net unique COPY | ||
129 | * stateids. | ||
130 | */ | ||
131 | u32 s2s_cp_cl_id; | ||
132 | struct idr s2s_cp_stateids; | ||
133 | spinlock_t s2s_cp_lock; | ||
126 | }; | 134 | }; |
127 | 135 | ||
128 | /* Simple check to find out if a given net was properly initialized */ | 136 | /* Simple check to find out if a given net was properly initialized */ |
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 0c7832321010..edff074d38c7 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/file.h> | 36 | #include <linux/file.h> |
37 | #include <linux/falloc.h> | 37 | #include <linux/falloc.h> |
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <linux/kthread.h> | ||
39 | 40 | ||
40 | #include "idmap.h" | 41 | #include "idmap.h" |
41 | #include "cache.h" | 42 | #include "cache.h" |
@@ -1089,37 +1090,236 @@ out: | |||
1089 | return status; | 1090 | return status; |
1090 | } | 1091 | } |
1091 | 1092 | ||
1093 | void nfs4_put_copy(struct nfsd4_copy *copy) | ||
1094 | { | ||
1095 | if (!refcount_dec_and_test(©->refcount)) | ||
1096 | return; | ||
1097 | kfree(copy); | ||
1098 | } | ||
1099 | |||
1100 | static bool | ||
1101 | check_and_set_stop_copy(struct nfsd4_copy *copy) | ||
1102 | { | ||
1103 | bool value; | ||
1104 | |||
1105 | spin_lock(©->cp_clp->async_lock); | ||
1106 | value = copy->stopped; | ||
1107 | if (!copy->stopped) | ||
1108 | copy->stopped = true; | ||
1109 | spin_unlock(©->cp_clp->async_lock); | ||
1110 | return value; | ||
1111 | } | ||
1112 | |||
1113 | static void nfsd4_stop_copy(struct nfsd4_copy *copy) | ||
1114 | { | ||
1115 | /* only 1 thread should stop the copy */ | ||
1116 | if (!check_and_set_stop_copy(copy)) | ||
1117 | kthread_stop(copy->copy_task); | ||
1118 | nfs4_put_copy(copy); | ||
1119 | } | ||
1120 | |||
1121 | static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp) | ||
1122 | { | ||
1123 | struct nfsd4_copy *copy = NULL; | ||
1124 | |||
1125 | spin_lock(&clp->async_lock); | ||
1126 | if (!list_empty(&clp->async_copies)) { | ||
1127 | copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, | ||
1128 | copies); | ||
1129 | refcount_inc(©->refcount); | ||
1130 | } | ||
1131 | spin_unlock(&clp->async_lock); | ||
1132 | return copy; | ||
1133 | } | ||
1134 | |||
1135 | void nfsd4_shutdown_copy(struct nfs4_client *clp) | ||
1136 | { | ||
1137 | struct nfsd4_copy *copy; | ||
1138 | |||
1139 | while ((copy = nfsd4_get_copy(clp)) != NULL) | ||
1140 | nfsd4_stop_copy(copy); | ||
1141 | } | ||
1142 | |||
1143 | static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) | ||
1144 | { | ||
1145 | struct nfsd4_copy *copy = container_of(cb, struct nfsd4_copy, cp_cb); | ||
1146 | |||
1147 | nfs4_put_copy(copy); | ||
1148 | } | ||
1149 | |||
1150 | static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, | ||
1151 | struct rpc_task *task) | ||
1152 | { | ||
1153 | return 1; | ||
1154 | } | ||
1155 | |||
1156 | static const struct nfsd4_callback_ops nfsd4_cb_offload_ops = { | ||
1157 | .release = nfsd4_cb_offload_release, | ||
1158 | .done = nfsd4_cb_offload_done | ||
1159 | }; | ||
1160 | |||
1161 | static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync) | ||
1162 | { | ||
1163 | copy->cp_res.wr_stable_how = NFS_UNSTABLE; | ||
1164 | copy->cp_synchronous = sync; | ||
1165 | gen_boot_verifier(©->cp_res.wr_verifier, copy->cp_clp->net); | ||
1166 | } | ||
1167 | |||
1168 | static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy) | ||
1169 | { | ||
1170 | ssize_t bytes_copied = 0; | ||
1171 | size_t bytes_total = copy->cp_count; | ||
1172 | u64 src_pos = copy->cp_src_pos; | ||
1173 | u64 dst_pos = copy->cp_dst_pos; | ||
1174 | |||
1175 | do { | ||
1176 | if (kthread_should_stop()) | ||
1177 | break; | ||
1178 | bytes_copied = nfsd_copy_file_range(copy->file_src, src_pos, | ||
1179 | copy->file_dst, dst_pos, bytes_total); | ||
1180 | if (bytes_copied <= 0) | ||
1181 | break; | ||
1182 | bytes_total -= bytes_copied; | ||
1183 | copy->cp_res.wr_bytes_written += bytes_copied; | ||
1184 | src_pos += bytes_copied; | ||
1185 | dst_pos += bytes_copied; | ||
1186 | } while (bytes_total > 0 && !copy->cp_synchronous); | ||
1187 | return bytes_copied; | ||
1188 | } | ||
1189 | |||
1190 | static __be32 nfsd4_do_copy(struct nfsd4_copy *copy, bool sync) | ||
1191 | { | ||
1192 | __be32 status; | ||
1193 | ssize_t bytes; | ||
1194 | |||
1195 | bytes = _nfsd_copy_file_range(copy); | ||
1196 | /* for async copy, we ignore the error, client can always retry | ||
1197 | * to get the error | ||
1198 | */ | ||
1199 | if (bytes < 0 && !copy->cp_res.wr_bytes_written) | ||
1200 | status = nfserrno(bytes); | ||
1201 | else { | ||
1202 | nfsd4_init_copy_res(copy, sync); | ||
1203 | status = nfs_ok; | ||
1204 | } | ||
1205 | |||
1206 | fput(copy->file_src); | ||
1207 | fput(copy->file_dst); | ||
1208 | return status; | ||
1209 | } | ||
1210 | |||
1211 | static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) | ||
1212 | { | ||
1213 | dst->cp_src_pos = src->cp_src_pos; | ||
1214 | dst->cp_dst_pos = src->cp_dst_pos; | ||
1215 | dst->cp_count = src->cp_count; | ||
1216 | dst->cp_synchronous = src->cp_synchronous; | ||
1217 | memcpy(&dst->cp_res, &src->cp_res, sizeof(src->cp_res)); | ||
1218 | memcpy(&dst->fh, &src->fh, sizeof(src->fh)); | ||
1219 | dst->cp_clp = src->cp_clp; | ||
1220 | dst->file_dst = get_file(src->file_dst); | ||
1221 | dst->file_src = get_file(src->file_src); | ||
1222 | memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid)); | ||
1223 | } | ||
1224 | |||
1225 | static void cleanup_async_copy(struct nfsd4_copy *copy) | ||
1226 | { | ||
1227 | nfs4_free_cp_state(copy); | ||
1228 | fput(copy->file_dst); | ||
1229 | fput(copy->file_src); | ||
1230 | spin_lock(©->cp_clp->async_lock); | ||
1231 | list_del(©->copies); | ||
1232 | spin_unlock(©->cp_clp->async_lock); | ||
1233 | nfs4_put_copy(copy); | ||
1234 | } | ||
1235 | |||
1236 | static int nfsd4_do_async_copy(void *data) | ||
1237 | { | ||
1238 | struct nfsd4_copy *copy = (struct nfsd4_copy *)data; | ||
1239 | struct nfsd4_copy *cb_copy; | ||
1240 | |||
1241 | copy->nfserr = nfsd4_do_copy(copy, 0); | ||
1242 | cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); | ||
1243 | if (!cb_copy) | ||
1244 | goto out; | ||
1245 | memcpy(&cb_copy->cp_res, ©->cp_res, sizeof(copy->cp_res)); | ||
1246 | cb_copy->cp_clp = copy->cp_clp; | ||
1247 | cb_copy->nfserr = copy->nfserr; | ||
1248 | memcpy(&cb_copy->fh, ©->fh, sizeof(copy->fh)); | ||
1249 | nfsd4_init_cb(&cb_copy->cp_cb, cb_copy->cp_clp, | ||
1250 | &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); | ||
1251 | nfsd4_run_cb(&cb_copy->cp_cb); | ||
1252 | out: | ||
1253 | cleanup_async_copy(copy); | ||
1254 | return 0; | ||
1255 | } | ||
1256 | |||
1092 | static __be32 | 1257 | static __be32 |
1093 | nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 1258 | nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
1094 | union nfsd4_op_u *u) | 1259 | union nfsd4_op_u *u) |
1095 | { | 1260 | { |
1096 | struct nfsd4_copy *copy = &u->copy; | 1261 | struct nfsd4_copy *copy = &u->copy; |
1097 | struct file *src, *dst; | ||
1098 | __be32 status; | 1262 | __be32 status; |
1099 | ssize_t bytes; | 1263 | struct nfsd4_copy *async_copy = NULL; |
1100 | 1264 | ||
1101 | status = nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid, &src, | 1265 | status = nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid, |
1102 | ©->cp_dst_stateid, &dst); | 1266 | ©->file_src, ©->cp_dst_stateid, |
1267 | ©->file_dst); | ||
1103 | if (status) | 1268 | if (status) |
1104 | goto out; | 1269 | goto out; |
1105 | 1270 | ||
1106 | bytes = nfsd_copy_file_range(src, copy->cp_src_pos, | 1271 | copy->cp_clp = cstate->clp; |
1107 | dst, copy->cp_dst_pos, copy->cp_count); | 1272 | memcpy(©->fh, &cstate->current_fh.fh_handle, |
1273 | sizeof(struct knfsd_fh)); | ||
1274 | if (!copy->cp_synchronous) { | ||
1275 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1108 | 1276 | ||
1109 | if (bytes < 0) | 1277 | status = nfserrno(-ENOMEM); |
1110 | status = nfserrno(bytes); | 1278 | async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); |
1111 | else { | 1279 | if (!async_copy) |
1112 | copy->cp_res.wr_bytes_written = bytes; | 1280 | goto out; |
1113 | copy->cp_res.wr_stable_how = NFS_UNSTABLE; | 1281 | if (!nfs4_init_cp_state(nn, copy)) { |
1114 | copy->cp_synchronous = 1; | 1282 | kfree(async_copy); |
1115 | gen_boot_verifier(©->cp_res.wr_verifier, SVC_NET(rqstp)); | 1283 | goto out; |
1284 | } | ||
1285 | refcount_set(&async_copy->refcount, 1); | ||
1286 | memcpy(©->cp_res.cb_stateid, ©->cp_stateid, | ||
1287 | sizeof(copy->cp_stateid)); | ||
1288 | dup_copy_fields(copy, async_copy); | ||
1289 | async_copy->copy_task = kthread_create(nfsd4_do_async_copy, | ||
1290 | async_copy, "%s", "copy thread"); | ||
1291 | if (IS_ERR(async_copy->copy_task)) | ||
1292 | goto out_err; | ||
1293 | spin_lock(&async_copy->cp_clp->async_lock); | ||
1294 | list_add(&async_copy->copies, | ||
1295 | &async_copy->cp_clp->async_copies); | ||
1296 | spin_unlock(&async_copy->cp_clp->async_lock); | ||
1297 | wake_up_process(async_copy->copy_task); | ||
1116 | status = nfs_ok; | 1298 | status = nfs_ok; |
1117 | } | 1299 | } else |
1118 | 1300 | status = nfsd4_do_copy(copy, 1); | |
1119 | fput(src); | ||
1120 | fput(dst); | ||
1121 | out: | 1301 | out: |
1122 | return status; | 1302 | return status; |
1303 | out_err: | ||
1304 | cleanup_async_copy(async_copy); | ||
1305 | goto out; | ||
1306 | } | ||
1307 | |||
1308 | struct nfsd4_copy * | ||
1309 | find_async_copy(struct nfs4_client *clp, stateid_t *stateid) | ||
1310 | { | ||
1311 | struct nfsd4_copy *copy; | ||
1312 | |||
1313 | spin_lock(&clp->async_lock); | ||
1314 | list_for_each_entry(copy, &clp->async_copies, copies) { | ||
1315 | if (memcmp(©->cp_stateid, stateid, NFS4_STATEID_SIZE)) | ||
1316 | continue; | ||
1317 | refcount_inc(©->refcount); | ||
1318 | spin_unlock(&clp->async_lock); | ||
1319 | return copy; | ||
1320 | } | ||
1321 | spin_unlock(&clp->async_lock); | ||
1322 | return NULL; | ||
1123 | } | 1323 | } |
1124 | 1324 | ||
1125 | static __be32 | 1325 | static __be32 |
@@ -1127,7 +1327,18 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp, | |||
1127 | struct nfsd4_compound_state *cstate, | 1327 | struct nfsd4_compound_state *cstate, |
1128 | union nfsd4_op_u *u) | 1328 | union nfsd4_op_u *u) |
1129 | { | 1329 | { |
1130 | return 0; | 1330 | struct nfsd4_offload_status *os = &u->offload_status; |
1331 | __be32 status = 0; | ||
1332 | struct nfsd4_copy *copy; | ||
1333 | struct nfs4_client *clp = cstate->clp; | ||
1334 | |||
1335 | copy = find_async_copy(clp, &os->stateid); | ||
1336 | if (copy) | ||
1337 | nfsd4_stop_copy(copy); | ||
1338 | else | ||
1339 | status = nfserr_bad_stateid; | ||
1340 | |||
1341 | return status; | ||
1131 | } | 1342 | } |
1132 | 1343 | ||
1133 | static __be32 | 1344 | static __be32 |
@@ -1157,7 +1368,19 @@ nfsd4_offload_status(struct svc_rqst *rqstp, | |||
1157 | struct nfsd4_compound_state *cstate, | 1368 | struct nfsd4_compound_state *cstate, |
1158 | union nfsd4_op_u *u) | 1369 | union nfsd4_op_u *u) |
1159 | { | 1370 | { |
1160 | return nfserr_notsupp; | 1371 | struct nfsd4_offload_status *os = &u->offload_status; |
1372 | __be32 status = 0; | ||
1373 | struct nfsd4_copy *copy; | ||
1374 | struct nfs4_client *clp = cstate->clp; | ||
1375 | |||
1376 | copy = find_async_copy(clp, &os->stateid); | ||
1377 | if (copy) { | ||
1378 | os->count = copy->cp_res.wr_bytes_written; | ||
1379 | nfs4_put_copy(copy); | ||
1380 | } else | ||
1381 | status = nfserr_bad_stateid; | ||
1382 | |||
1383 | return status; | ||
1161 | } | 1384 | } |
1162 | 1385 | ||
1163 | static __be32 | 1386 | static __be32 |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b0ca0efd2875..07a57d024f95 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -713,6 +713,36 @@ out_free: | |||
713 | return NULL; | 713 | return NULL; |
714 | } | 714 | } |
715 | 715 | ||
716 | /* | ||
717 | * Create a unique stateid_t to represent each COPY. | ||
718 | */ | ||
719 | int nfs4_init_cp_state(struct nfsd_net *nn, struct nfsd4_copy *copy) | ||
720 | { | ||
721 | int new_id; | ||
722 | |||
723 | idr_preload(GFP_KERNEL); | ||
724 | spin_lock(&nn->s2s_cp_lock); | ||
725 | new_id = idr_alloc_cyclic(&nn->s2s_cp_stateids, copy, 0, 0, GFP_NOWAIT); | ||
726 | spin_unlock(&nn->s2s_cp_lock); | ||
727 | idr_preload_end(); | ||
728 | if (new_id < 0) | ||
729 | return 0; | ||
730 | copy->cp_stateid.si_opaque.so_id = new_id; | ||
731 | copy->cp_stateid.si_opaque.so_clid.cl_boot = nn->boot_time; | ||
732 | copy->cp_stateid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id; | ||
733 | return 1; | ||
734 | } | ||
735 | |||
736 | void nfs4_free_cp_state(struct nfsd4_copy *copy) | ||
737 | { | ||
738 | struct nfsd_net *nn; | ||
739 | |||
740 | nn = net_generic(copy->cp_clp->net, nfsd_net_id); | ||
741 | spin_lock(&nn->s2s_cp_lock); | ||
742 | idr_remove(&nn->s2s_cp_stateids, copy->cp_stateid.si_opaque.so_id); | ||
743 | spin_unlock(&nn->s2s_cp_lock); | ||
744 | } | ||
745 | |||
716 | static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp) | 746 | static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp) |
717 | { | 747 | { |
718 | struct nfs4_stid *stid; | 748 | struct nfs4_stid *stid; |
@@ -1827,6 +1857,8 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
1827 | #ifdef CONFIG_NFSD_PNFS | 1857 | #ifdef CONFIG_NFSD_PNFS |
1828 | INIT_LIST_HEAD(&clp->cl_lo_states); | 1858 | INIT_LIST_HEAD(&clp->cl_lo_states); |
1829 | #endif | 1859 | #endif |
1860 | INIT_LIST_HEAD(&clp->async_copies); | ||
1861 | spin_lock_init(&clp->async_lock); | ||
1830 | spin_lock_init(&clp->cl_lock); | 1862 | spin_lock_init(&clp->cl_lock); |
1831 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); | 1863 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); |
1832 | return clp; | 1864 | return clp; |
@@ -1942,6 +1974,7 @@ __destroy_client(struct nfs4_client *clp) | |||
1942 | } | 1974 | } |
1943 | } | 1975 | } |
1944 | nfsd4_return_all_client_layouts(clp); | 1976 | nfsd4_return_all_client_layouts(clp); |
1977 | nfsd4_shutdown_copy(clp); | ||
1945 | nfsd4_shutdown_callback(clp); | 1978 | nfsd4_shutdown_callback(clp); |
1946 | if (clp->cl_cb_conn.cb_xprt) | 1979 | if (clp->cl_cb_conn.cb_xprt) |
1947 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); | 1980 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); |
@@ -2475,7 +2508,8 @@ static bool client_has_state(struct nfs4_client *clp) | |||
2475 | || !list_empty(&clp->cl_lo_states) | 2508 | || !list_empty(&clp->cl_lo_states) |
2476 | #endif | 2509 | #endif |
2477 | || !list_empty(&clp->cl_delegations) | 2510 | || !list_empty(&clp->cl_delegations) |
2478 | || !list_empty(&clp->cl_sessions); | 2511 | || !list_empty(&clp->cl_sessions) |
2512 | || !list_empty(&clp->async_copies); | ||
2479 | } | 2513 | } |
2480 | 2514 | ||
2481 | __be32 | 2515 | __be32 |
@@ -7161,6 +7195,8 @@ static int nfs4_state_create_net(struct net *net) | |||
7161 | INIT_LIST_HEAD(&nn->close_lru); | 7195 | INIT_LIST_HEAD(&nn->close_lru); |
7162 | INIT_LIST_HEAD(&nn->del_recall_lru); | 7196 | INIT_LIST_HEAD(&nn->del_recall_lru); |
7163 | spin_lock_init(&nn->client_lock); | 7197 | spin_lock_init(&nn->client_lock); |
7198 | spin_lock_init(&nn->s2s_cp_lock); | ||
7199 | idr_init(&nn->s2s_cp_stateids); | ||
7164 | 7200 | ||
7165 | spin_lock_init(&nn->blocked_locks_lock); | 7201 | spin_lock_init(&nn->blocked_locks_lock); |
7166 | INIT_LIST_HEAD(&nn->blocked_locks_lru); | 7202 | INIT_LIST_HEAD(&nn->blocked_locks_lru); |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index b78280a8af73..3de42a729093 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -4231,15 +4231,27 @@ nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
4231 | #endif /* CONFIG_NFSD_PNFS */ | 4231 | #endif /* CONFIG_NFSD_PNFS */ |
4232 | 4232 | ||
4233 | static __be32 | 4233 | static __be32 |
4234 | nfsd42_encode_write_res(struct nfsd4_compoundres *resp, struct nfsd42_write_res *write) | 4234 | nfsd42_encode_write_res(struct nfsd4_compoundres *resp, |
4235 | struct nfsd42_write_res *write, bool sync) | ||
4235 | { | 4236 | { |
4236 | __be32 *p; | 4237 | __be32 *p; |
4238 | p = xdr_reserve_space(&resp->xdr, 4); | ||
4239 | if (!p) | ||
4240 | return nfserr_resource; | ||
4237 | 4241 | ||
4238 | p = xdr_reserve_space(&resp->xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE); | 4242 | if (sync) |
4243 | *p++ = cpu_to_be32(0); | ||
4244 | else { | ||
4245 | __be32 nfserr; | ||
4246 | *p++ = cpu_to_be32(1); | ||
4247 | nfserr = nfsd4_encode_stateid(&resp->xdr, &write->cb_stateid); | ||
4248 | if (nfserr) | ||
4249 | return nfserr; | ||
4250 | } | ||
4251 | p = xdr_reserve_space(&resp->xdr, 8 + 4 + NFS4_VERIFIER_SIZE); | ||
4239 | if (!p) | 4252 | if (!p) |
4240 | return nfserr_resource; | 4253 | return nfserr_resource; |
4241 | 4254 | ||
4242 | *p++ = cpu_to_be32(0); | ||
4243 | p = xdr_encode_hyper(p, write->wr_bytes_written); | 4255 | p = xdr_encode_hyper(p, write->wr_bytes_written); |
4244 | *p++ = cpu_to_be32(write->wr_stable_how); | 4256 | *p++ = cpu_to_be32(write->wr_stable_how); |
4245 | p = xdr_encode_opaque_fixed(p, write->wr_verifier.data, | 4257 | p = xdr_encode_opaque_fixed(p, write->wr_verifier.data, |
@@ -4253,7 +4265,8 @@ nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
4253 | { | 4265 | { |
4254 | __be32 *p; | 4266 | __be32 *p; |
4255 | 4267 | ||
4256 | nfserr = nfsd42_encode_write_res(resp, ©->cp_res); | 4268 | nfserr = nfsd42_encode_write_res(resp, ©->cp_res, |
4269 | copy->cp_synchronous); | ||
4257 | if (nfserr) | 4270 | if (nfserr) |
4258 | return nfserr; | 4271 | return nfserr; |
4259 | 4272 | ||
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7fb9f7c667b1..6384c9b94898 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -1242,6 +1242,7 @@ static __net_init int nfsd_init_net(struct net *net) | |||
1242 | nn->somebody_reclaimed = false; | 1242 | nn->somebody_reclaimed = false; |
1243 | nn->clverifier_counter = prandom_u32(); | 1243 | nn->clverifier_counter = prandom_u32(); |
1244 | nn->clientid_counter = prandom_u32(); | 1244 | nn->clientid_counter = prandom_u32(); |
1245 | nn->s2s_cp_cl_id = nn->clientid_counter++; | ||
1245 | 1246 | ||
1246 | atomic_set(&nn->ntf_refcnt, 0); | 1247 | atomic_set(&nn->ntf_refcnt, 0); |
1247 | init_waitqueue_head(&nn->ntf_wq); | 1248 | init_waitqueue_head(&nn->ntf_wq); |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 6e38d9927448..6aacb325b6a0 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -355,6 +355,8 @@ struct nfs4_client { | |||
355 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ | 355 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ |
356 | /* wait here for slots */ | 356 | /* wait here for slots */ |
357 | struct net *net; | 357 | struct net *net; |
358 | struct list_head async_copies; /* list of async copies */ | ||
359 | spinlock_t async_lock; /* lock for async copies */ | ||
358 | }; | 360 | }; |
359 | 361 | ||
360 | /* struct nfs4_client_reset | 362 | /* struct nfs4_client_reset |
@@ -600,6 +602,7 @@ struct nfsd4_blocked_lock { | |||
600 | 602 | ||
601 | struct nfsd4_compound_state; | 603 | struct nfsd4_compound_state; |
602 | struct nfsd_net; | 604 | struct nfsd_net; |
605 | struct nfsd4_copy; | ||
603 | 606 | ||
604 | extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp, | 607 | extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp, |
605 | struct nfsd4_compound_state *cstate, struct svc_fh *fhp, | 608 | struct nfsd4_compound_state *cstate, struct svc_fh *fhp, |
@@ -609,6 +612,8 @@ __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, | |||
609 | struct nfs4_stid **s, struct nfsd_net *nn); | 612 | struct nfs4_stid **s, struct nfsd_net *nn); |
610 | struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab, | 613 | struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab, |
611 | void (*sc_free)(struct nfs4_stid *)); | 614 | void (*sc_free)(struct nfs4_stid *)); |
615 | int nfs4_init_cp_state(struct nfsd_net *nn, struct nfsd4_copy *copy); | ||
616 | void nfs4_free_cp_state(struct nfsd4_copy *copy); | ||
612 | void nfs4_unhash_stid(struct nfs4_stid *s); | 617 | void nfs4_unhash_stid(struct nfs4_stid *s); |
613 | void nfs4_put_stid(struct nfs4_stid *s); | 618 | void nfs4_put_stid(struct nfs4_stid *s); |
614 | void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid); | 619 | void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid); |
@@ -627,6 +632,7 @@ extern void nfsd4_run_cb(struct nfsd4_callback *cb); | |||
627 | extern int nfsd4_create_callback_queue(void); | 632 | extern int nfsd4_create_callback_queue(void); |
628 | extern void nfsd4_destroy_callback_queue(void); | 633 | extern void nfsd4_destroy_callback_queue(void); |
629 | extern void nfsd4_shutdown_callback(struct nfs4_client *); | 634 | extern void nfsd4_shutdown_callback(struct nfs4_client *); |
635 | extern void nfsd4_shutdown_copy(struct nfs4_client *clp); | ||
630 | extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp); | 636 | extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp); |
631 | extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, | 637 | extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, |
632 | struct nfsd_net *nn); | 638 | struct nfsd_net *nn); |
@@ -634,6 +640,9 @@ extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn); | |||
634 | 640 | ||
635 | struct nfs4_file *find_file(struct knfsd_fh *fh); | 641 | struct nfs4_file *find_file(struct knfsd_fh *fh); |
636 | void put_nfs4_file(struct nfs4_file *fi); | 642 | void put_nfs4_file(struct nfs4_file *fi); |
643 | extern void nfs4_put_copy(struct nfsd4_copy *copy); | ||
644 | extern struct nfsd4_copy * | ||
645 | find_async_copy(struct nfs4_client *clp, stateid_t *staetid); | ||
637 | static inline void get_nfs4_file(struct nfs4_file *fi) | 646 | static inline void get_nfs4_file(struct nfs4_file *fi) |
638 | { | 647 | { |
639 | refcount_inc(&fi->fi_ref); | 648 | refcount_inc(&fi->fi_ref); |
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 06cf218944c5..feeb6d4bdffd 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -532,6 +532,18 @@ struct nfsd4_copy { | |||
532 | struct nfsd4_callback cp_cb; | 532 | struct nfsd4_callback cp_cb; |
533 | __be32 nfserr; | 533 | __be32 nfserr; |
534 | struct knfsd_fh fh; | 534 | struct knfsd_fh fh; |
535 | |||
536 | struct nfs4_client *cp_clp; | ||
537 | |||
538 | struct file *file_src; | ||
539 | struct file *file_dst; | ||
540 | |||
541 | stateid_t cp_stateid; | ||
542 | |||
543 | struct list_head copies; | ||
544 | struct task_struct *copy_task; | ||
545 | refcount_t refcount; | ||
546 | bool stopped; | ||
535 | }; | 547 | }; |
536 | 548 | ||
537 | struct nfsd4_seek { | 549 | struct nfsd4_seek { |