Commit 56463e50d1fc3f070492434cea6303b35ea000de
Committed by
Trond Myklebust
1 parent
60ac03685b
Exists in
master
and in
7 other branches
NFS: Use super.c for NFSROOT mount option parsing
Replace duplicate code in NFSROOT for mounting an NFS server on '/' with logic that uses the existing mainline text-based logic in the NFS client. Add documenting comments where appropriate. Note that this means NFSROOT mounts now use the same default settings as v2/v3 mounts done via mount(2) from user space. vers=3,tcp,rsize=<negotiated default>,wsize=<negotiated default> As before, however, no version/protocol negotiation with the server is done. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Showing 3 changed files with 187 additions and 17 deletions Side-by-side Diff
fs/nfs/nfsroot.c
... | ... | @@ -3,9 +3,10 @@ |
3 | 3 | * |
4 | 4 | * Allow an NFS filesystem to be mounted as root. The way this works is: |
5 | 5 | * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. |
6 | - * (2) Handle RPC negotiation with the system which replied to RARP or | |
7 | - * was reported as a boot server by BOOTP or manually. | |
8 | - * (3) The actual mounting is done later, when init() is running. | |
6 | + * (2) Construct the device string and the options string using DHCP | |
7 | + * option 17 and/or kernel command line options. | |
8 | + * (3) When mount_root() sets up the root file system, pass these strings | |
9 | + * to the NFS client's regular mount interface via sys_mount(). | |
9 | 10 | * |
10 | 11 | * |
11 | 12 | * Changes: |
... | ... | @@ -65,7 +66,8 @@ |
65 | 66 | * Hua Qin : Support for mounting root file system via |
66 | 67 | * NFS over TCP. |
67 | 68 | * Fabian Frederick: Option parser rebuilt (using parser lib) |
68 | -*/ | |
69 | + * Chuck Lever : Use super.c's text-based mount option parsing | |
70 | + */ | |
69 | 71 | |
70 | 72 | #include <linux/types.h> |
71 | 73 | #include <linux/string.h> |
72 | 74 | |
73 | 75 | |
... | ... | @@ -101,12 +103,18 @@ |
101 | 103 | /* Parameters passed from the kernel command line */ |
102 | 104 | static char nfs_root_parms[256] __initdata = ""; |
103 | 105 | |
106 | +/* Text-based mount options passed to super.c */ | |
107 | +static char nfs_root_options[256] __initdata = ""; | |
108 | + | |
104 | 109 | /* Address of NFS server */ |
105 | 110 | static __be32 servaddr __initdata = 0; |
106 | 111 | |
107 | 112 | /* Name of directory to mount */ |
108 | -static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; | |
113 | +static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; | |
109 | 114 | |
115 | +/* server:export path string passed to super.c */ | |
116 | +static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; | |
117 | + | |
110 | 118 | /* NFS-related data */ |
111 | 119 | static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ |
112 | 120 | static int nfs_port __initdata = 0; /* Port to connect to for NFS */ |
... | ... | @@ -537,7 +545,7 @@ |
537 | 545 | * Get the NFS port numbers and file handle, and return the prepared 'data' |
538 | 546 | * argument for mount() if everything went OK. Return NULL otherwise. |
539 | 547 | */ |
540 | -void * __init nfs_root_data(void) | |
548 | +void * __init old_nfs_root_data(void) | |
541 | 549 | { |
542 | 550 | if (root_nfs_init() < 0 |
543 | 551 | || root_nfs_ports() < 0 |
... | ... | @@ -545,5 +553,167 @@ |
545 | 553 | return NULL; |
546 | 554 | set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port)); |
547 | 555 | return (void*)&nfs_data; |
556 | +} | |
557 | + | |
558 | +static int __init root_nfs_copy(char *dest, const char *src, | |
559 | + const size_t destlen) | |
560 | +{ | |
561 | + if (strlcpy(dest, src, destlen) > destlen) | |
562 | + return -1; | |
563 | + return 0; | |
564 | +} | |
565 | + | |
566 | +static int __init root_nfs_cat(char *dest, const char *src, | |
567 | + const size_t destlen) | |
568 | +{ | |
569 | + if (strlcat(dest, src, destlen) > destlen) | |
570 | + return -1; | |
571 | + return 0; | |
572 | +} | |
573 | + | |
574 | +/* | |
575 | + * Parse out root export path and mount options from | |
576 | + * passed-in string @incoming. | |
577 | + * | |
578 | + * Copy the export path into @exppath. | |
579 | + */ | |
580 | +static int __init root_nfs_parse_options(char *incoming, char *exppath, | |
581 | + const size_t exppathlen) | |
582 | +{ | |
583 | + char *p; | |
584 | + | |
585 | + /* | |
586 | + * Set the NFS remote path | |
587 | + */ | |
588 | + p = strsep(&incoming, ","); | |
589 | + if (*p != '\0' && strcmp(p, "default") != 0) | |
590 | + if (root_nfs_copy(exppath, p, exppathlen)) | |
591 | + return -1; | |
592 | + | |
593 | + /* | |
594 | + * @incoming now points to the rest of the string; if it | |
595 | + * contains something, append it to our root options buffer | |
596 | + */ | |
597 | + if (incoming != NULL && *incoming != '\0') | |
598 | + if (root_nfs_cat(nfs_root_options, incoming, | |
599 | + sizeof(nfs_root_options))) | |
600 | + return -1; | |
601 | + | |
602 | + /* | |
603 | + * Possibly prepare for more options to be appended | |
604 | + */ | |
605 | + if (nfs_root_options[0] != '\0' && | |
606 | + nfs_root_options[strlen(nfs_root_options)] != ',') | |
607 | + if (root_nfs_cat(nfs_root_options, ",", | |
608 | + sizeof(nfs_root_options))) | |
609 | + return -1; | |
610 | + | |
611 | + return 0; | |
612 | +} | |
613 | + | |
614 | +/* | |
615 | + * Decode the export directory path name and NFS options from | |
616 | + * the kernel command line. This has to be done late in order to | |
617 | + * use a dynamically acquired client IP address for the remote | |
618 | + * root directory path. | |
619 | + * | |
620 | + * Returns zero if successful; otherwise -1 is returned. | |
621 | + */ | |
622 | +static int __init root_nfs_data(char *cmdline) | |
623 | +{ | |
624 | + char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1]; | |
625 | + int len, retval = -1; | |
626 | + char *tmp = NULL; | |
627 | + const size_t tmplen = sizeof(nfs_export_path); | |
628 | + | |
629 | + tmp = kzalloc(tmplen, GFP_KERNEL); | |
630 | + if (tmp == NULL) | |
631 | + goto out_nomem; | |
632 | + strcpy(tmp, NFS_ROOT); | |
633 | + | |
634 | + if (root_server_path[0] != '\0') { | |
635 | + dprintk("Root-NFS: DHCPv4 option 17: %s\n", | |
636 | + root_server_path); | |
637 | + if (root_nfs_parse_options(root_server_path, tmp, tmplen)) | |
638 | + goto out_optionstoolong; | |
639 | + } | |
640 | + | |
641 | + if (cmdline[0] != '\0') { | |
642 | + dprintk("Root-NFS: nfsroot=%s\n", cmdline); | |
643 | + if (root_nfs_parse_options(cmdline, tmp, tmplen)) | |
644 | + goto out_optionstoolong; | |
645 | + } | |
646 | + | |
647 | + /* | |
648 | + * Append mandatory options for nfsroot so they override | |
649 | + * what has come before | |
650 | + */ | |
651 | + snprintf(addr_option, sizeof(addr_option), "nolock,addr=%pI4", | |
652 | + &servaddr); | |
653 | + if (root_nfs_cat(nfs_root_options, addr_option, | |
654 | + sizeof(nfs_root_options))) | |
655 | + goto out_optionstoolong; | |
656 | + | |
657 | + /* | |
658 | + * Set up nfs_root_device. For NFS mounts, this looks like | |
659 | + * | |
660 | + * server:/path | |
661 | + * | |
662 | + * At this point, utsname()->nodename contains our local | |
663 | + * IP address or hostname, set by ipconfig. If "%s" exists | |
664 | + * in tmp, substitute the nodename, then shovel the whole | |
665 | + * mess into nfs_root_device. | |
666 | + */ | |
667 | + len = snprintf(nfs_export_path, sizeof(nfs_export_path), | |
668 | + tmp, utsname()->nodename); | |
669 | + if (len > (int)sizeof(nfs_export_path)) | |
670 | + goto out_devnametoolong; | |
671 | + len = snprintf(nfs_root_device, sizeof(nfs_root_device), | |
672 | + "%pI4:%s", &servaddr, nfs_export_path); | |
673 | + if (len > (int)sizeof(nfs_root_device)) | |
674 | + goto out_devnametoolong; | |
675 | + | |
676 | + retval = 0; | |
677 | + | |
678 | +out: | |
679 | + kfree(tmp); | |
680 | + return retval; | |
681 | +out_nomem: | |
682 | + printk(KERN_ERR "Root-NFS: could not allocate memory\n"); | |
683 | + goto out; | |
684 | +out_optionstoolong: | |
685 | + printk(KERN_ERR "Root-NFS: mount options string too long\n"); | |
686 | + goto out; | |
687 | +out_devnametoolong: | |
688 | + printk(KERN_ERR "Root-NFS: root device name too long.\n"); | |
689 | + goto out; | |
690 | +} | |
691 | + | |
692 | +/** | |
693 | + * nfs_root_data - Return prepared 'data' for NFSROOT mount | |
694 | + * @root_device: OUT: address of string containing NFSROOT device | |
695 | + * @root_data: OUT: address of string containing NFSROOT mount options | |
696 | + * | |
697 | + * Returns zero and sets @root_device and @root_data if successful, | |
698 | + * otherwise -1 is returned. | |
699 | + */ | |
700 | +int __init nfs_root_data(char **root_device, char **root_data) | |
701 | +{ | |
702 | +#ifdef NFSROOT_DEBUG | |
703 | + nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; | |
704 | +#endif /* NFSROOT_DEBUG */ | |
705 | + | |
706 | + servaddr = root_server_addr; | |
707 | + if (servaddr == htonl(INADDR_NONE)) { | |
708 | + printk(KERN_ERR "Root-NFS: no NFS server address\n"); | |
709 | + return -1; | |
710 | + } | |
711 | + | |
712 | + if (root_nfs_data(nfs_root_parms) < 0) | |
713 | + return -1; | |
714 | + | |
715 | + *root_device = nfs_root_device; | |
716 | + *root_data = nfs_root_options; | |
717 | + return 0; | |
548 | 718 | } |
include/linux/nfs_fs.h
... | ... | @@ -364,6 +364,7 @@ |
364 | 364 | extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx); |
365 | 365 | extern u64 nfs_compat_user_ino64(u64 fileid); |
366 | 366 | extern void nfs_fattr_init(struct nfs_fattr *fattr); |
367 | +extern unsigned long nfs_inc_attr_generation_counter(void); | |
367 | 368 | |
368 | 369 | extern struct nfs_fattr *nfs_alloc_fattr(void); |
369 | 370 | |
370 | 371 | |
... | ... | @@ -379,9 +380,12 @@ |
379 | 380 | kfree(fh); |
380 | 381 | } |
381 | 382 | |
383 | +/* | |
384 | + * linux/fs/nfs/nfsroot.c | |
385 | + */ | |
386 | +extern int nfs_root_data(char **root_device, char **root_data); /*__init*/ | |
382 | 387 | /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ |
383 | 388 | extern __be32 root_nfs_parse_addr(char *name); /*__init*/ |
384 | -extern unsigned long nfs_inc_attr_generation_counter(void); | |
385 | 389 | |
386 | 390 | /* |
387 | 391 | * linux/fs/nfs/file.c |
... | ... | @@ -583,10 +587,6 @@ |
583 | 587 | ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8; |
584 | 588 | return ino; |
585 | 589 | } |
586 | - | |
587 | -/* NFS root */ | |
588 | - | |
589 | -extern void * nfs_root_data(void); | |
590 | 590 | |
591 | 591 | #define nfs_wait_event(clnt, wq, condition) \ |
592 | 592 | ({ \ |
init/do_mounts.c
... | ... | @@ -291,13 +291,13 @@ |
291 | 291 | #ifdef CONFIG_ROOT_NFS |
292 | 292 | static int __init mount_nfs_root(void) |
293 | 293 | { |
294 | - void *data = nfs_root_data(); | |
294 | + char *root_dev, *root_data; | |
295 | 295 | |
296 | - create_dev("/dev/root", ROOT_DEV); | |
297 | - if (data && | |
298 | - do_mount_root("/dev/root", "nfs", root_mountflags, data) == 0) | |
299 | - return 1; | |
300 | - return 0; | |
296 | + if (nfs_root_data(&root_dev, &root_data) != 0) | |
297 | + return 0; | |
298 | + if (do_mount_root(root_dev, "nfs", root_mountflags, root_data) != 0) | |
299 | + return 0; | |
300 | + return 1; | |
301 | 301 | } |
302 | 302 | #endif |
303 | 303 |