|
36 | 36 | #include <linux/file.h>
|
37 | 37 | #include <linux/falloc.h>
|
38 | 38 | #include <linux/slab.h>
|
| 39 | +#include <linux/kthread.h> |
39 | 40 |
|
40 | 41 | #include "idmap.h"
|
41 | 42 | #include "cache.h"
|
@@ -1089,45 +1090,255 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
1089 | 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 | 1257 | static __be32
|
1093 | 1258 | nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
1094 | 1259 | union nfsd4_op_u *u)
|
1095 | 1260 | {
|
1096 | 1261 | struct nfsd4_copy *copy = &u->copy;
|
1097 |
| - struct file *src, *dst; |
1098 | 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, |
1102 |
| - ©->cp_dst_stateid, &dst); |
| 1265 | + status = nfsd4_verify_copy(rqstp, cstate, ©->cp_src_stateid, |
| 1266 | + ©->file_src, ©->cp_dst_stateid, |
| 1267 | + ©->file_dst); |
1103 | 1268 | if (status)
|
1104 | 1269 | goto out;
|
1105 | 1270 |
|
1106 |
| - bytes = nfsd_copy_file_range(src, copy->cp_src_pos, |
1107 |
| - dst, copy->cp_dst_pos, copy->cp_count); |
| 1271 | + copy->cp_clp = cstate->clp; |
| 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) |
1110 |
| - status = nfserrno(bytes); |
1111 |
| - else { |
1112 |
| - copy->cp_res.wr_bytes_written = bytes; |
1113 |
| - copy->cp_res.wr_stable_how = NFS_UNSTABLE; |
1114 |
| - copy->cp_synchronous = 1; |
1115 |
| - gen_boot_verifier(©->cp_res.wr_verifier, SVC_NET(rqstp)); |
| 1277 | + status = nfserrno(-ENOMEM); |
| 1278 | + async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); |
| 1279 | + if (!async_copy) |
| 1280 | + goto out; |
| 1281 | + if (!nfs4_init_cp_state(nn, copy)) { |
| 1282 | + kfree(async_copy); |
| 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 | 1298 | status = nfs_ok;
|
1117 |
| - } |
1118 |
| - |
1119 |
| - fput(src); |
1120 |
| - fput(dst); |
| 1299 | + } else |
| 1300 | + status = nfsd4_do_copy(copy, 1); |
1121 | 1301 | out:
|
1122 | 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 | 1325 | static __be32
|
1126 | 1326 | nfsd4_offload_cancel(struct svc_rqst *rqstp,
|
1127 | 1327 | struct nfsd4_compound_state *cstate,
|
1128 | 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 | 1344 | static __be32
|
@@ -1157,7 +1368,19 @@ nfsd4_offload_status(struct svc_rqst *rqstp,
|
1157 | 1368 | struct nfsd4_compound_state *cstate,
|
1158 | 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 | 1386 | static __be32
|
|
0 commit comments