Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFSD add nfs4 inter ssc to nfsd4_copy

Given a universal address, mount the source server from the destination
server. Use an internal mount. Call the NFS client nfs42_ssc_open to
obtain the NFS struct file suitable for nfsd_copy_range.

Ability to do "inter" server-to-server depends on the an nfsd kernel
parameter "inter_copy_offload_enable".

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>

authored by

Olga Kornievskaia and committed by
J. Bruce Fields
ce0887ac b9e8638e

+296 -28
+272 -23
fs/nfsd/nfs4proc.c
··· 1144 1144 while ((copy = nfsd4_get_copy(clp)) != NULL) 1145 1145 nfsd4_stop_copy(copy); 1146 1146 } 1147 + #ifdef CONFIG_NFSD_V4_2_INTER_SSC 1148 + 1149 + extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, 1150 + struct nfs_fh *src_fh, 1151 + nfs4_stateid *stateid); 1152 + extern void nfs42_ssc_close(struct file *filep); 1153 + 1154 + extern void nfs_sb_deactive(struct super_block *sb); 1155 + 1156 + #define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys" 1157 + 1158 + /** 1159 + * Support one copy source server for now. 1160 + */ 1161 + static __be32 1162 + nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp, 1163 + struct vfsmount **mount) 1164 + { 1165 + struct file_system_type *type; 1166 + struct vfsmount *ss_mnt; 1167 + struct nfs42_netaddr *naddr; 1168 + struct sockaddr_storage tmp_addr; 1169 + size_t tmp_addrlen, match_netid_len = 3; 1170 + char *startsep = "", *endsep = "", *match_netid = "tcp"; 1171 + char *ipaddr, *dev_name, *raw_data; 1172 + int len, raw_len, status = -EINVAL; 1173 + 1174 + naddr = &nss->u.nl4_addr; 1175 + tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr, 1176 + naddr->addr_len, 1177 + (struct sockaddr *)&tmp_addr, 1178 + sizeof(tmp_addr)); 1179 + if (tmp_addrlen == 0) 1180 + goto out_err; 1181 + 1182 + if (tmp_addr.ss_family == AF_INET6) { 1183 + startsep = "["; 1184 + endsep = "]"; 1185 + match_netid = "tcp6"; 1186 + match_netid_len = 4; 1187 + } 1188 + 1189 + if (naddr->netid_len != match_netid_len || 1190 + strncmp(naddr->netid, match_netid, naddr->netid_len)) 1191 + goto out_err; 1192 + 1193 + /* Construct the raw data for the vfs_kern_mount call */ 1194 + len = RPC_MAX_ADDRBUFLEN + 1; 1195 + ipaddr = kzalloc(len, GFP_KERNEL); 1196 + if (!ipaddr) 1197 + goto out_err; 1198 + 1199 + rpc_ntop((struct sockaddr *)&tmp_addr, ipaddr, len); 1200 + 1201 + /* 2 for ipv6 endsep and startsep. 3 for ":/" and trailing '/0'*/ 1202 + 1203 + raw_len = strlen(NFSD42_INTERSSC_MOUNTOPS) + strlen(ipaddr); 1204 + raw_data = kzalloc(raw_len, GFP_KERNEL); 1205 + if (!raw_data) 1206 + goto out_free_ipaddr; 1207 + 1208 + snprintf(raw_data, raw_len, NFSD42_INTERSSC_MOUNTOPS, ipaddr); 1209 + 1210 + status = -ENODEV; 1211 + type = get_fs_type("nfs"); 1212 + if (!type) 1213 + goto out_free_rawdata; 1214 + 1215 + /* Set the server:<export> for the vfs_kern_mount call */ 1216 + dev_name = kzalloc(len + 5, GFP_KERNEL); 1217 + if (!dev_name) 1218 + goto out_free_rawdata; 1219 + snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep); 1220 + 1221 + /* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */ 1222 + ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data); 1223 + module_put(type->owner); 1224 + if (IS_ERR(ss_mnt)) 1225 + goto out_free_devname; 1226 + 1227 + status = 0; 1228 + *mount = ss_mnt; 1229 + 1230 + out_free_devname: 1231 + kfree(dev_name); 1232 + out_free_rawdata: 1233 + kfree(raw_data); 1234 + out_free_ipaddr: 1235 + kfree(ipaddr); 1236 + out_err: 1237 + return status; 1238 + } 1239 + 1240 + static void 1241 + nfsd4_interssc_disconnect(struct vfsmount *ss_mnt) 1242 + { 1243 + nfs_sb_deactive(ss_mnt->mnt_sb); 1244 + mntput(ss_mnt); 1245 + } 1246 + 1247 + /** 1248 + * nfsd4_setup_inter_ssc 1249 + * 1250 + * Verify COPY destination stateid. 1251 + * Connect to the source server with NFSv4.1. 1252 + * Create the source struct file for nfsd_copy_range. 1253 + * Called with COPY cstate: 1254 + * SAVED_FH: source filehandle 1255 + * CURRENT_FH: destination filehandle 1256 + * 1257 + * Returns errno (not nfserrxxx) 1258 + */ 1259 + static __be32 1260 + nfsd4_setup_inter_ssc(struct svc_rqst *rqstp, 1261 + struct nfsd4_compound_state *cstate, 1262 + struct nfsd4_copy *copy, struct vfsmount **mount) 1263 + { 1264 + struct svc_fh *s_fh = NULL; 1265 + stateid_t *s_stid = &copy->cp_src_stateid; 1266 + __be32 status = -EINVAL; 1267 + 1268 + /* Verify the destination stateid and set dst struct file*/ 1269 + status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, 1270 + &copy->cp_dst_stateid, 1271 + WR_STATE, &copy->nf_dst, NULL); 1272 + if (status) 1273 + goto out; 1274 + 1275 + status = nfsd4_interssc_connect(&copy->cp_src, rqstp, mount); 1276 + if (status) 1277 + goto out; 1278 + 1279 + s_fh = &cstate->save_fh; 1280 + 1281 + copy->c_fh.size = s_fh->fh_handle.fh_size; 1282 + memcpy(copy->c_fh.data, &s_fh->fh_handle.fh_base, copy->c_fh.size); 1283 + copy->stateid.seqid = s_stid->si_generation; 1284 + memcpy(copy->stateid.other, (void *)&s_stid->si_opaque, 1285 + sizeof(stateid_opaque_t)); 1286 + 1287 + status = 0; 1288 + out: 1289 + return status; 1290 + } 1291 + 1292 + static void 1293 + nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src, 1294 + struct nfsd_file *dst) 1295 + { 1296 + nfs42_ssc_close(src->nf_file); 1297 + nfsd_file_put(src); 1298 + nfsd_file_put(dst); 1299 + mntput(ss_mnt); 1300 + } 1301 + 1302 + #else /* CONFIG_NFSD_V4_2_INTER_SSC */ 1303 + 1304 + static __be32 1305 + nfsd4_setup_inter_ssc(struct svc_rqst *rqstp, 1306 + struct nfsd4_compound_state *cstate, 1307 + struct nfsd4_copy *copy, 1308 + struct vfsmount **mount) 1309 + { 1310 + *mount = NULL; 1311 + return -EINVAL; 1312 + } 1313 + 1314 + static void 1315 + nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src, 1316 + struct nfsd_file *dst) 1317 + { 1318 + } 1319 + 1320 + static void 1321 + nfsd4_interssc_disconnect(struct vfsmount *ss_mnt) 1322 + { 1323 + } 1324 + 1325 + static struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, 1326 + struct nfs_fh *src_fh, 1327 + nfs4_stateid *stateid) 1328 + { 1329 + return NULL; 1330 + } 1331 + #endif /* CONFIG_NFSD_V4_2_INTER_SSC */ 1332 + 1333 + static __be32 1334 + nfsd4_setup_intra_ssc(struct svc_rqst *rqstp, 1335 + struct nfsd4_compound_state *cstate, 1336 + struct nfsd4_copy *copy) 1337 + { 1338 + return nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid, 1339 + &copy->nf_src, &copy->cp_dst_stateid, 1340 + &copy->nf_dst); 1341 + } 1342 + 1343 + static void 1344 + nfsd4_cleanup_intra_ssc(struct nfsd_file *src, struct nfsd_file *dst) 1345 + { 1346 + nfsd_file_put(src); 1347 + nfsd_file_put(dst); 1348 + } 1147 1349 1148 1350 static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) 1149 1351 { ··· 1411 1209 status = nfs_ok; 1412 1210 } 1413 1211 1414 - nfsd_file_put(copy->nf_src); 1415 - nfsd_file_put(copy->nf_dst); 1212 + if (!copy->cp_intra) /* Inter server SSC */ 1213 + nfsd4_cleanup_inter_ssc(copy->ss_mnt, copy->nf_src, 1214 + copy->nf_dst); 1215 + else 1216 + nfsd4_cleanup_intra_ssc(copy->nf_src, copy->nf_dst); 1217 + 1416 1218 return status; 1417 1219 } 1418 1220 1419 - static void dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) 1221 + static int dup_copy_fields(struct nfsd4_copy *src, struct nfsd4_copy *dst) 1420 1222 { 1421 1223 dst->cp_src_pos = src->cp_src_pos; 1422 1224 dst->cp_dst_pos = src->cp_dst_pos; ··· 1430 1224 memcpy(&dst->fh, &src->fh, sizeof(src->fh)); 1431 1225 dst->cp_clp = src->cp_clp; 1432 1226 dst->nf_dst = nfsd_file_get(src->nf_dst); 1433 - dst->nf_src = nfsd_file_get(src->nf_src); 1227 + dst->cp_intra = src->cp_intra; 1228 + if (src->cp_intra) /* for inter, file_src doesn't exist yet */ 1229 + dst->nf_src = nfsd_file_get(src->nf_src); 1230 + 1434 1231 memcpy(&dst->cp_stateid, &src->cp_stateid, sizeof(src->cp_stateid)); 1232 + memcpy(&dst->cp_src, &src->cp_src, sizeof(struct nl4_server)); 1233 + memcpy(&dst->stateid, &src->stateid, sizeof(src->stateid)); 1234 + memcpy(&dst->c_fh, &src->c_fh, sizeof(src->c_fh)); 1235 + dst->ss_mnt = src->ss_mnt; 1236 + 1237 + return 0; 1435 1238 } 1436 1239 1437 1240 static void cleanup_async_copy(struct nfsd4_copy *copy) ··· 1459 1244 struct nfsd4_copy *copy = (struct nfsd4_copy *)data; 1460 1245 struct nfsd4_copy *cb_copy; 1461 1246 1247 + if (!copy->cp_intra) { /* Inter server SSC */ 1248 + copy->nf_src = kzalloc(sizeof(struct nfsd_file), GFP_KERNEL); 1249 + if (!copy->nf_src) { 1250 + copy->nfserr = nfserr_serverfault; 1251 + nfsd4_interssc_disconnect(copy->ss_mnt); 1252 + goto do_callback; 1253 + } 1254 + copy->nf_src->nf_file = nfs42_ssc_open(copy->ss_mnt, &copy->c_fh, 1255 + &copy->stateid); 1256 + if (IS_ERR(copy->nf_src->nf_file)) { 1257 + kfree(copy->nf_src); 1258 + copy->nfserr = nfserr_offload_denied; 1259 + nfsd4_interssc_disconnect(copy->ss_mnt); 1260 + goto do_callback; 1261 + } 1262 + } 1263 + 1462 1264 copy->nfserr = nfsd4_do_copy(copy, 0); 1265 + do_callback: 1463 1266 cb_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); 1464 1267 if (!cb_copy) 1465 1268 goto out; ··· 1489 1256 &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); 1490 1257 nfsd4_run_cb(&cb_copy->cp_cb); 1491 1258 out: 1259 + if (!copy->cp_intra) 1260 + kfree(copy->nf_src); 1492 1261 cleanup_async_copy(copy); 1493 1262 return 0; 1494 1263 } ··· 1503 1268 __be32 status; 1504 1269 struct nfsd4_copy *async_copy = NULL; 1505 1270 1506 - status = nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid, 1507 - &copy->nf_src, &copy->cp_dst_stateid, 1508 - &copy->nf_dst); 1509 - if (status) 1510 - goto out; 1271 + if (!copy->cp_intra) { /* Inter server SSC */ 1272 + if (!inter_copy_offload_enable || copy->cp_synchronous) { 1273 + status = nfserr_notsupp; 1274 + goto out; 1275 + } 1276 + status = nfsd4_setup_inter_ssc(rqstp, cstate, copy, 1277 + &copy->ss_mnt); 1278 + if (status) 1279 + return nfserr_offload_denied; 1280 + } else { 1281 + status = nfsd4_setup_intra_ssc(rqstp, cstate, copy); 1282 + if (status) 1283 + return status; 1284 + } 1511 1285 1512 1286 copy->cp_clp = cstate->clp; 1513 1287 memcpy(&copy->fh, &cstate->current_fh.fh_handle, ··· 1527 1283 status = nfserrno(-ENOMEM); 1528 1284 async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL); 1529 1285 if (!async_copy) 1530 - goto out; 1531 - if (!nfs4_init_copy_state(nn, copy)) { 1532 - kfree(async_copy); 1533 - goto out; 1534 - } 1286 + goto out_err; 1287 + if (!nfs4_init_copy_state(nn, copy)) 1288 + goto out_err; 1535 1289 refcount_set(&async_copy->refcount, 1); 1536 1290 memcpy(&copy->cp_res.cb_stateid, &copy->cp_stateid, 1537 1291 sizeof(copy->cp_stateid)); 1538 - dup_copy_fields(copy, async_copy); 1292 + status = dup_copy_fields(copy, async_copy); 1293 + if (status) 1294 + goto out_err; 1539 1295 async_copy->copy_task = kthread_create(nfsd4_do_async_copy, 1540 1296 async_copy, "%s", "copy thread"); 1541 1297 if (IS_ERR(async_copy->copy_task)) ··· 1546 1302 spin_unlock(&async_copy->cp_clp->async_lock); 1547 1303 wake_up_process(async_copy->copy_task); 1548 1304 status = nfs_ok; 1549 - } else 1305 + } else { 1550 1306 status = nfsd4_do_copy(copy, 1); 1307 + } 1551 1308 out: 1552 1309 return status; 1553 1310 out_err: 1554 1311 if (async_copy) 1555 1312 cleanup_async_copy(async_copy); 1313 + status = nfserrno(-ENOMEM); 1314 + if (!copy->cp_intra) 1315 + nfsd4_interssc_disconnect(copy->ss_mnt); 1556 1316 goto out; 1557 1317 } 1558 1318 ··· 1567 1319 1568 1320 spin_lock(&clp->async_lock); 1569 1321 list_for_each_entry(copy, &clp->async_copies, copies) { 1570 - if (memcmp(&copy->cp_stateid, stateid, NFS4_STATEID_SIZE)) 1322 + if (memcmp(&copy->cp_stateid.stid, stateid, NFS4_STATEID_SIZE)) 1571 1323 continue; 1572 1324 refcount_inc(&copy->refcount); 1573 1325 spin_unlock(&clp->async_lock); ··· 1583 1335 union nfsd4_op_u *u) 1584 1336 { 1585 1337 struct nfsd4_offload_status *os = &u->offload_status; 1586 - __be32 status = 0; 1587 1338 struct nfsd4_copy *copy; 1588 1339 struct nfs4_client *clp = cstate->clp; 1589 1340 1590 1341 copy = find_async_copy(clp, &os->stateid); 1591 - if (copy) 1592 - nfsd4_stop_copy(copy); 1593 - else 1594 - status = nfserr_bad_stateid; 1342 + if (!copy) { 1343 + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 1595 1344 1596 - return status; 1345 + return manage_cpntf_state(nn, &os->stateid, clp, NULL); 1346 + } else 1347 + nfsd4_stop_copy(copy); 1348 + 1349 + return nfs_ok; 1597 1350 } 1598 1351 1599 1352 static __be32
+10 -5
fs/nfsd/nfs4state.c
··· 5681 5681 * copy stateid. Look up the copy notify stateid from the 5682 5682 * idr structure and take a reference on it. 5683 5683 */ 5684 - static __be32 _find_cpntf_state(struct nfsd_net *nn, stateid_t *st, 5685 - struct nfs4_cpntf_state **cps) 5684 + __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st, 5685 + struct nfs4_client *clp, 5686 + struct nfs4_cpntf_state **cps) 5686 5687 { 5687 5688 copy_stateid_t *cps_t; 5688 5689 struct nfs4_cpntf_state *state = NULL; ··· 5697 5696 cp_stateid); 5698 5697 if (state->cp_stateid.sc_type != NFS4_COPYNOTIFY_STID) 5699 5698 return nfserr_bad_stateid; 5700 - refcount_inc(&state->cp_stateid.sc_count); 5699 + if (!clp) 5700 + refcount_inc(&state->cp_stateid.sc_count); 5701 + else 5702 + _free_cpntf_state_locked(nn, state); 5701 5703 } 5702 5704 spin_unlock(&nn->s2s_cp_lock); 5703 5705 if (!state) 5704 5706 return nfserr_bad_stateid; 5705 - *cps = state; 5707 + if (!clp && state) 5708 + *cps = state; 5706 5709 return 0; 5707 5710 } 5708 5711 ··· 5717 5712 struct nfs4_cpntf_state *cps = NULL; 5718 5713 struct nfsd4_compound_state cstate; 5719 5714 5720 - status = _find_cpntf_state(nn, st, &cps); 5715 + status = manage_cpntf_state(nn, st, NULL, &cps); 5721 5716 if (status) 5722 5717 return status; 5723 5718
+6
fs/nfsd/nfssvc.c
··· 31 31 32 32 #define NFSDDBG_FACILITY NFSDDBG_SVC 33 33 34 + bool inter_copy_offload_enable; 35 + EXPORT_SYMBOL_GPL(inter_copy_offload_enable); 36 + module_param(inter_copy_offload_enable, bool, 0644); 37 + MODULE_PARM_DESC(inter_copy_offload_enable, 38 + "Enable inter server to server copy offload. Default: false"); 39 + 34 40 extern struct svc_program nfsd_program; 35 41 static int nfsd(void *vrqstp); 36 42 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
+3
fs/nfsd/state.h
··· 680 680 find_async_copy(struct nfs4_client *clp, stateid_t *staetid); 681 681 extern void nfs4_put_cpntf_state(struct nfsd_net *nn, 682 682 struct nfs4_cpntf_state *cps); 683 + extern __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st, 684 + struct nfs4_client *clp, 685 + struct nfs4_cpntf_state **cps); 683 686 static inline void get_nfs4_file(struct nfs4_file *fi) 684 687 { 685 688 refcount_inc(&fi->fi_ref);
+5
fs/nfsd/xdr4.h
··· 549 549 struct task_struct *copy_task; 550 550 refcount_t refcount; 551 551 bool stopped; 552 + 553 + struct vfsmount *ss_mnt; 554 + struct nfs_fh c_fh; 555 + nfs4_stateid stateid; 552 556 }; 557 + extern bool inter_copy_offload_enable; 553 558 554 559 struct nfsd4_seek { 555 560 /* request */