Commit 56463e50d1fc3f070492434cea6303b35ea000de

Authored by Chuck Lever
Committed by Trond Myklebust
1 parent 60ac03685b

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

... ... @@ -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 ({ \
... ... @@ -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