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

nfsd: Allow containers to set supported nfs versions

Support use of the --nfs-version/--no-nfs-version arguments to rpc.nfsd
in containers.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Trond Myklebust and committed by
J. Bruce Fields
e333f3bb 029be5d0

+197 -61
+8
fs/nfsd/netns.h
··· 134 134 u32 s2s_cp_cl_id; 135 135 struct idr s2s_cp_stateids; 136 136 spinlock_t s2s_cp_lock; 137 + 138 + /* 139 + * Version information 140 + */ 141 + bool *nfsd_versions; 142 + bool *nfsd4_minorversions; 137 143 }; 138 144 139 145 /* Simple check to find out if a given net was properly initialized */ 140 146 #define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl) 147 + 148 + extern void nfsd_netns_free_versions(struct nfsd_net *nn); 141 149 142 150 extern unsigned int nfsd_net_id; 143 151 #endif /* __NFSD_NETNS_H__ */
+2 -1
fs/nfsd/nfs4proc.c
··· 1926 1926 struct nfsd4_compound_state *cstate = &resp->cstate; 1927 1927 struct svc_fh *current_fh = &cstate->current_fh; 1928 1928 struct svc_fh *save_fh = &cstate->save_fh; 1929 + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 1929 1930 __be32 status; 1930 1931 1931 1932 svcxdr_init_encode(rqstp, resp); ··· 1949 1948 * According to RFC3010, this takes precedence over all other errors. 1950 1949 */ 1951 1950 status = nfserr_minor_vers_mismatch; 1952 - if (nfsd_minorversion(args->minorversion, NFSD_TEST) <= 0) 1951 + if (nfsd_minorversion(nn, args->minorversion, NFSD_TEST) <= 0) 1953 1952 goto out; 1954 1953 status = nfserr_resource; 1955 1954 if (args->opcnt > NFSD_MAX_OPS_PER_COMPOUND)
+14 -11
fs/nfsd/nfsctl.c
··· 537 537 } 538 538 539 539 static ssize_t 540 - nfsd_print_version_support(char *buf, int remaining, const char *sep, 541 - unsigned vers, int minor) 540 + nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining, 541 + const char *sep, unsigned vers, int minor) 542 542 { 543 543 const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; 544 - bool supported = !!nfsd_vers(vers, NFSD_TEST); 544 + bool supported = !!nfsd_vers(nn, vers, NFSD_TEST); 545 545 546 546 if (vers == 4 && minor >= 0 && 547 - !nfsd_minorversion(minor, NFSD_TEST)) 547 + !nfsd_minorversion(nn, minor, NFSD_TEST)) 548 548 supported = false; 549 549 if (minor == 0 && supported) 550 550 /* ··· 599 599 switch(num) { 600 600 case 2: 601 601 case 3: 602 - nfsd_vers(num, cmd); 602 + nfsd_vers(nn, num, cmd); 603 603 break; 604 604 case 4: 605 605 if (*minorp == '.') { 606 - if (nfsd_minorversion(minor, cmd) < 0) 606 + if (nfsd_minorversion(nn, minor, cmd) < 0) 607 607 return -EINVAL; 608 - } else if ((cmd == NFSD_SET) != nfsd_vers(num, NFSD_TEST)) { 608 + } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { 609 609 /* 610 610 * Either we have +4 and no minors are enabled, 611 611 * or we have -4 and at least one minor is enabled. 612 612 * In either case, propagate 'cmd' to all minors. 613 613 */ 614 614 minor = 0; 615 - while (nfsd_minorversion(minor, cmd) >= 0) 615 + while (nfsd_minorversion(nn, minor, cmd) >= 0) 616 616 minor++; 617 617 } 618 618 break; ··· 624 624 /* If all get turned off, turn them back on, as 625 625 * having no versions is BAD 626 626 */ 627 - nfsd_reset_versions(); 627 + nfsd_reset_versions(nn); 628 628 } 629 629 630 630 /* Now write current state into reply buffer */ ··· 633 633 remaining = SIMPLE_TRANSACTION_LIMIT; 634 634 for (num=2 ; num <= 4 ; num++) { 635 635 int minor; 636 - if (!nfsd_vers(num, NFSD_AVAIL)) 636 + if (!nfsd_vers(nn, num, NFSD_AVAIL)) 637 637 continue; 638 638 639 639 minor = -1; 640 640 do { 641 - len = nfsd_print_version_support(buf, remaining, 641 + len = nfsd_print_version_support(nn, buf, remaining, 642 642 sep, num, minor); 643 643 if (len >= remaining) 644 644 goto out; ··· 1239 1239 retval = nfsd_idmap_init(net); 1240 1240 if (retval) 1241 1241 goto out_idmap_error; 1242 + nn->nfsd_versions = NULL; 1243 + nn->nfsd4_minorversions = NULL; 1242 1244 nn->nfsd4_lease = 90; /* default lease time */ 1243 1245 nn->nfsd4_grace = 90; 1244 1246 nn->somebody_reclaimed = false; ··· 1263 1261 { 1264 1262 nfsd_idmap_shutdown(net); 1265 1263 nfsd_export_shutdown(net); 1264 + nfsd_netns_free_versions(net_generic(net, nfsd_net_id)); 1266 1265 } 1267 1266 1268 1267 static struct pernet_operations nfsd_net_ops = {
+5 -3
fs/nfsd/nfsd.h
··· 98 98 #endif 99 99 #endif 100 100 101 + struct nfsd_net; 102 + 101 103 enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; 102 - int nfsd_vers(int vers, enum vers_op change); 103 - int nfsd_minorversion(u32 minorversion, enum vers_op change); 104 - void nfsd_reset_versions(void); 104 + int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); 105 + int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change); 106 + void nfsd_reset_versions(struct nfsd_net *nn); 105 107 int nfsd_create_serv(struct net *net); 106 108 107 109 extern int nfsd_max_blksize;
+168 -46
fs/nfsd/nfssvc.c
··· 38 38 u32, int, 39 39 unsigned short, 40 40 unsigned short); 41 + static __be32 nfsd_acl_init_request(struct svc_rqst *, 42 + const struct svc_program *, 43 + struct svc_process_info *); 41 44 #endif 42 45 static int nfsd_rpcbind_set(struct net *, 43 46 const struct svc_program *, 44 47 u32, int, 45 48 unsigned short, 46 49 unsigned short); 50 + static __be32 nfsd_init_request(struct svc_rqst *, 51 + const struct svc_program *, 52 + struct svc_process_info *); 47 53 48 54 /* 49 55 * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members ··· 104 98 .pg_class = "nfsd", 105 99 .pg_stats = &nfsd_acl_svcstats, 106 100 .pg_authenticate = &svc_set_client, 107 - .pg_init_request = svc_generic_init_request, 101 + .pg_init_request = nfsd_acl_init_request, 108 102 .pg_rpcbind_set = nfsd_acl_rpcbind_set, 109 103 }; 110 104 ··· 125 119 126 120 #define NFSD_MINVERS 2 127 121 #define NFSD_NRVERS ARRAY_SIZE(nfsd_version) 128 - static const struct svc_version *nfsd_versions[NFSD_NRVERS]; 129 122 130 123 struct svc_program nfsd_program = { 131 124 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) ··· 132 127 #endif 133 128 .pg_prog = NFS_PROGRAM, /* program number */ 134 129 .pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */ 135 - .pg_vers = nfsd_versions, /* version table */ 130 + .pg_vers = nfsd_version, /* version table */ 136 131 .pg_name = "nfsd", /* program name */ 137 132 .pg_class = "nfsd", /* authentication class */ 138 133 .pg_stats = &nfsd_svcstats, /* version table */ 139 134 .pg_authenticate = &svc_set_client, /* export authentication */ 140 - .pg_init_request = svc_generic_init_request, 135 + .pg_init_request = nfsd_init_request, 141 136 .pg_rpcbind_set = nfsd_rpcbind_set, 142 137 }; 143 138 144 - static bool nfsd_supported_minorversions[NFSD_SUPPORTED_MINOR_VERSION + 1] = { 145 - [0] = 1, 146 - [1] = 1, 147 - [2] = 1, 148 - }; 139 + static bool 140 + nfsd_support_version(int vers) 141 + { 142 + if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS) 143 + return nfsd_version[vers] != NULL; 144 + return false; 145 + } 149 146 150 - int nfsd_vers(int vers, enum vers_op change) 147 + static bool * 148 + nfsd_alloc_versions(void) 149 + { 150 + bool *vers = kmalloc_array(NFSD_NRVERS, sizeof(bool), GFP_KERNEL); 151 + unsigned i; 152 + 153 + if (vers) { 154 + /* All compiled versions are enabled by default */ 155 + for (i = 0; i < NFSD_NRVERS; i++) 156 + vers[i] = nfsd_support_version(i); 157 + } 158 + return vers; 159 + } 160 + 161 + static bool * 162 + nfsd_alloc_minorversions(void) 163 + { 164 + bool *vers = kmalloc_array(NFSD_SUPPORTED_MINOR_VERSION + 1, 165 + sizeof(bool), GFP_KERNEL); 166 + unsigned i; 167 + 168 + if (vers) { 169 + /* All minor versions are enabled by default */ 170 + for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) 171 + vers[i] = nfsd_support_version(4); 172 + } 173 + return vers; 174 + } 175 + 176 + void 177 + nfsd_netns_free_versions(struct nfsd_net *nn) 178 + { 179 + kfree(nn->nfsd_versions); 180 + kfree(nn->nfsd4_minorversions); 181 + nn->nfsd_versions = NULL; 182 + nn->nfsd4_minorversions = NULL; 183 + } 184 + 185 + static void 186 + nfsd_netns_init_versions(struct nfsd_net *nn) 187 + { 188 + if (!nn->nfsd_versions) { 189 + nn->nfsd_versions = nfsd_alloc_versions(); 190 + nn->nfsd4_minorversions = nfsd_alloc_minorversions(); 191 + if (!nn->nfsd_versions || !nn->nfsd4_minorversions) 192 + nfsd_netns_free_versions(nn); 193 + } 194 + } 195 + 196 + int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change) 151 197 { 152 198 if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) 153 199 return 0; 154 200 switch(change) { 155 201 case NFSD_SET: 156 - nfsd_versions[vers] = nfsd_version[vers]; 157 - #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) 158 - if (vers < NFSD_ACL_NRVERS) 159 - nfsd_acl_versions[vers] = nfsd_acl_version[vers]; 160 - #endif 202 + if (nn->nfsd_versions) 203 + nn->nfsd_versions[vers] = nfsd_support_version(vers); 161 204 break; 162 205 case NFSD_CLEAR: 163 - nfsd_versions[vers] = NULL; 164 - #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) 165 - if (vers < NFSD_ACL_NRVERS) 166 - nfsd_acl_versions[vers] = NULL; 167 - #endif 206 + nfsd_netns_init_versions(nn); 207 + if (nn->nfsd_versions) 208 + nn->nfsd_versions[vers] = false; 168 209 break; 169 210 case NFSD_TEST: 170 - return nfsd_versions[vers] != NULL; 211 + if (nn->nfsd_versions) 212 + return nn->nfsd_versions[vers]; 213 + /* Fallthrough */ 171 214 case NFSD_AVAIL: 172 - return nfsd_version[vers] != NULL; 215 + return nfsd_support_version(vers); 173 216 } 174 217 return 0; 175 218 } 176 219 177 220 static void 178 - nfsd_adjust_nfsd_versions4(void) 221 + nfsd_adjust_nfsd_versions4(struct nfsd_net *nn) 179 222 { 180 223 unsigned i; 181 224 182 225 for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) { 183 - if (nfsd_supported_minorversions[i]) 226 + if (nn->nfsd4_minorversions[i]) 184 227 return; 185 228 } 186 - nfsd_vers(4, NFSD_CLEAR); 229 + nfsd_vers(nn, 4, NFSD_CLEAR); 187 230 } 188 231 189 - int nfsd_minorversion(u32 minorversion, enum vers_op change) 232 + int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change) 190 233 { 191 234 if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && 192 235 change != NFSD_AVAIL) 193 236 return -1; 237 + 194 238 switch(change) { 195 239 case NFSD_SET: 196 - nfsd_supported_minorversions[minorversion] = true; 197 - nfsd_vers(4, NFSD_SET); 240 + if (nn->nfsd4_minorversions) { 241 + nfsd_vers(nn, 4, NFSD_SET); 242 + nn->nfsd4_minorversions[minorversion] = 243 + nfsd_vers(nn, 4, NFSD_TEST); 244 + } 198 245 break; 199 246 case NFSD_CLEAR: 200 - nfsd_supported_minorversions[minorversion] = false; 201 - nfsd_adjust_nfsd_versions4(); 247 + nfsd_netns_init_versions(nn); 248 + if (nn->nfsd4_minorversions) { 249 + nn->nfsd4_minorversions[minorversion] = false; 250 + nfsd_adjust_nfsd_versions4(nn); 251 + } 202 252 break; 203 253 case NFSD_TEST: 204 - return nfsd_supported_minorversions[minorversion]; 254 + if (nn->nfsd4_minorversions) 255 + return nn->nfsd4_minorversions[minorversion]; 256 + return nfsd_vers(nn, 4, NFSD_TEST); 205 257 case NFSD_AVAIL: 206 - return minorversion <= NFSD_SUPPORTED_MINOR_VERSION; 258 + return minorversion <= NFSD_SUPPORTED_MINOR_VERSION && 259 + nfsd_vers(nn, 4, NFSD_AVAIL); 207 260 } 208 261 return 0; 209 262 } ··· 343 280 nfsd_racache_shutdown(); 344 281 } 345 282 346 - static bool nfsd_needs_lockd(void) 283 + static bool nfsd_needs_lockd(struct nfsd_net *nn) 347 284 { 348 - #if defined(CONFIG_NFSD_V3) 349 - return (nfsd_versions[2] != NULL) || (nfsd_versions[3] != NULL); 350 - #else 351 - return (nfsd_versions[2] != NULL); 352 - #endif 285 + return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST); 353 286 } 354 287 355 288 static int nfsd_startup_net(int nrservs, struct net *net) ··· 363 304 if (ret) 364 305 goto out_socks; 365 306 366 - if (nfsd_needs_lockd() && !nn->lockd_up) { 307 + if (nfsd_needs_lockd(nn) && !nn->lockd_up) { 367 308 ret = lockd_up(net); 368 309 if (ret) 369 310 goto out_socks; ··· 496 437 nfsd_export_flush(net); 497 438 } 498 439 499 - void nfsd_reset_versions(void) 440 + void nfsd_reset_versions(struct nfsd_net *nn) 500 441 { 501 442 int i; 502 443 503 444 for (i = 0; i < NFSD_NRVERS; i++) 504 - if (nfsd_vers(i, NFSD_TEST)) 445 + if (nfsd_vers(nn, i, NFSD_TEST)) 505 446 return; 506 447 507 448 for (i = 0; i < NFSD_NRVERS; i++) 508 449 if (i != 4) 509 - nfsd_vers(i, NFSD_SET); 450 + nfsd_vers(nn, i, NFSD_SET); 510 451 else { 511 452 int minor = 0; 512 - while (nfsd_minorversion(minor, NFSD_SET) >= 0) 453 + while (nfsd_minorversion(nn, minor, NFSD_SET) >= 0) 513 454 minor++; 514 455 } 515 456 } ··· 577 518 } 578 519 if (nfsd_max_blksize == 0) 579 520 nfsd_max_blksize = nfsd_get_default_max_blksize(); 580 - nfsd_reset_versions(); 521 + nfsd_reset_versions(nn); 581 522 nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, 582 523 &nfsd_thread_sv_ops); 583 524 if (nn->nfsd_serv == NULL) ··· 756 697 unsigned short port) 757 698 { 758 699 if (!nfsd_support_acl_version(version) || 759 - !nfsd_vers(version, NFSD_TEST)) 700 + !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) 760 701 return 0; 761 702 return svc_generic_rpcbind_set(net, progp, version, family, 762 703 proto, port); 704 + } 705 + 706 + static __be32 707 + nfsd_acl_init_request(struct svc_rqst *rqstp, 708 + const struct svc_program *progp, 709 + struct svc_process_info *ret) 710 + { 711 + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 712 + int i; 713 + 714 + if (likely(nfsd_support_acl_version(rqstp->rq_vers) && 715 + nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) 716 + return svc_generic_init_request(rqstp, progp, ret); 717 + 718 + ret->mismatch.lovers = NFSD_ACL_NRVERS; 719 + for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) { 720 + if (nfsd_support_acl_version(rqstp->rq_vers) && 721 + nfsd_vers(nn, i, NFSD_TEST)) { 722 + ret->mismatch.lovers = i; 723 + break; 724 + } 725 + } 726 + if (ret->mismatch.lovers == NFSD_ACL_NRVERS) 727 + return rpc_prog_unavail; 728 + ret->mismatch.hivers = NFSD_ACL_MINVERS; 729 + for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) { 730 + if (nfsd_support_acl_version(rqstp->rq_vers) && 731 + nfsd_vers(nn, i, NFSD_TEST)) { 732 + ret->mismatch.hivers = i; 733 + break; 734 + } 735 + } 736 + return rpc_prog_mismatch; 763 737 } 764 738 #endif 765 739 ··· 801 709 u32 version, int family, unsigned short proto, 802 710 unsigned short port) 803 711 { 804 - if (!nfsd_vers(version, NFSD_TEST)) 712 + if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) 805 713 return 0; 806 714 return svc_generic_rpcbind_set(net, progp, version, family, 807 715 proto, port); 716 + } 717 + 718 + static __be32 719 + nfsd_init_request(struct svc_rqst *rqstp, 720 + const struct svc_program *progp, 721 + struct svc_process_info *ret) 722 + { 723 + struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 724 + int i; 725 + 726 + if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) 727 + return svc_generic_init_request(rqstp, progp, ret); 728 + 729 + ret->mismatch.lovers = NFSD_NRVERS; 730 + for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) { 731 + if (nfsd_vers(nn, i, NFSD_TEST)) { 732 + ret->mismatch.lovers = i; 733 + break; 734 + } 735 + } 736 + if (ret->mismatch.lovers == NFSD_NRVERS) 737 + return rpc_prog_unavail; 738 + ret->mismatch.hivers = NFSD_MINVERS; 739 + for (i = NFSD_NRVERS - 1; i >= NFSD_MINVERS; i--) { 740 + if (nfsd_vers(nn, i, NFSD_TEST)) { 741 + ret->mismatch.hivers = i; 742 + break; 743 + } 744 + } 745 + return rpc_prog_mismatch; 808 746 } 809 747 810 748 /*