Commit e571cbf1a4f8d8b6cfd4898df718dae84c75a8e1

Authored by Trond Myklebust
1 parent 96c61cbd0f

NFS: Add a dns resolver for use with NFSv4 referrals and migration

The NFSv4 and NFSv4.1 protocols both allow for the redirection of a client
from one server to another in order to support filesystem migration and
replication. For full protocol support, we need to add the ability to
convert a DNS host name into an IP address that we can feed to the RPC
client.

We'll reuse the sunrpc cache, now that it has been converted to work with
rpc_pipefs.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

Showing 9 changed files with 639 additions and 1 deletions Side-by-side Diff

Documentation/filesystems/nfs.txt
  1 +
  2 +The NFS client
  3 +==============
  4 +
  5 +The NFS version 2 protocol was first documented in RFC1094 (March 1989).
  6 +Since then two more major releases of NFS have been published, with NFSv3
  7 +being documented in RFC1813 (June 1995), and NFSv4 in RFC3530 (April
  8 +2003).
  9 +
  10 +The Linux NFS client currently supports all the above published versions,
  11 +and work is in progress on adding support for minor version 1 of the NFSv4
  12 +protocol.
  13 +
  14 +The purpose of this document is to provide information on some of the
  15 +upcall interfaces that are used in order to provide the NFS client with
  16 +some of the information that it requires in order to fully comply with
  17 +the NFS spec.
  18 +
  19 +The DNS resolver
  20 +================
  21 +
  22 +NFSv4 allows for one server to refer the NFS client to data that has been
  23 +migrated onto another server by means of the special "fs_locations"
  24 +attribute. See
  25 + http://tools.ietf.org/html/rfc3530#section-6
  26 +and
  27 + http://tools.ietf.org/html/draft-ietf-nfsv4-referrals-00
  28 +
  29 +The fs_locations information can take the form of either an ip address and
  30 +a path, or a DNS hostname and a path. The latter requires the NFS client to
  31 +do a DNS lookup in order to mount the new volume, and hence the need for an
  32 +upcall to allow userland to provide this service.
  33 +
  34 +Assuming that the user has the 'rpc_pipefs' filesystem mounted in the usual
  35 +/var/lib/nfs/rpc_pipefs, the upcall consists of the following steps:
  36 +
  37 + (1) The process checks the dns_resolve cache to see if it contains a
  38 + valid entry. If so, it returns that entry and exits.
  39 +
  40 + (2) If no valid entry exists, the helper script '/sbin/nfs_cache_getent'
  41 + (may be changed using the 'nfs.cache_getent' kernel boot parameter)
  42 + is run, with two arguments:
  43 + - the cache name, "dns_resolve"
  44 + - the hostname to resolve
  45 +
  46 + (3) After looking up the corresponding ip address, the helper script
  47 + writes the result into the rpc_pipefs pseudo-file
  48 + '/var/lib/nfs/rpc_pipefs/cache/dns_resolve/channel'
  49 + in the following (text) format:
  50 +
  51 + "<ip address> <hostname> <ttl>\n"
  52 +
  53 + Where <ip address> is in the usual IPv4 (123.456.78.90) or IPv6
  54 + (ffee:ddcc:bbaa:9988:7766:5544:3322:1100, ffee::1100, ...) format.
  55 + <hostname> is identical to the second argument of the helper
  56 + script, and <ttl> is the 'time to live' of this cache entry (in
  57 + units of seconds).
  58 +
  59 + Note: If <ip address> is invalid, say the string "0", then a negative
  60 + entry is created, which will cause the kernel to treat the hostname
  61 + as having no valid DNS translation.
  62 +
  63 +
  64 +
  65 +
  66 +A basic sample /sbin/nfs_cache_getent
  67 +=====================================
  68 +
  69 +#!/bin/bash
  70 +#
  71 +ttl=600
  72 +#
  73 +cut=/usr/bin/cut
  74 +getent=/usr/bin/getent
  75 +rpc_pipefs=/var/lib/nfs/rpc_pipefs
  76 +#
  77 +die()
  78 +{
  79 + echo "Usage: $0 cache_name entry_name"
  80 + exit 1
  81 +}
  82 +
  83 +[ $# -lt 2 ] && die
  84 +cachename="$1"
  85 +cache_path=${rpc_pipefs}/cache/${cachename}/channel
  86 +
  87 +case "${cachename}" in
  88 + dns_resolve)
  89 + name="$2"
  90 + result="$(${getent} hosts ${name} | ${cut} -f1 -d\ )"
  91 + [ -z "${result}" ] && result="0"
  92 + ;;
  93 + *)
  94 + die
  95 + ;;
  96 +esac
  97 +echo "${result} ${name} ${ttl}" >${cache_path}
Documentation/kernel-parameters.txt
... ... @@ -1503,6 +1503,14 @@
1503 1503 [NFS] set the TCP port on which the NFSv4 callback
1504 1504 channel should listen.
1505 1505  
  1506 + nfs.cache_getent=
  1507 + [NFS] sets the pathname to the program which is used
  1508 + to update the NFS client cache entries.
  1509 +
  1510 + nfs.cache_getent_timeout=
  1511 + [NFS] sets the timeout after which an attempt to
  1512 + update a cache entry is deemed to have failed.
  1513 +
1506 1514 nfs.idmap_cache_timeout=
1507 1515 [NFS] set the maximum lifetime for idmapper cache
1508 1516 entries.
... ... @@ -6,7 +6,8 @@
6 6  
7 7 nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
8 8 direct.o pagelist.o proc.o read.o symlink.o unlink.o \
9   - write.o namespace.o mount_clnt.o
  9 + write.o namespace.o mount_clnt.o \
  10 + dns_resolve.o cache_lib.o
10 11 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
11 12 nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
12 13 nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
  1 +/*
  2 + * linux/fs/nfs/cache_lib.c
  3 + *
  4 + * Helper routines for the NFS client caches
  5 + *
  6 + * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
  7 + */
  8 +#include <linux/kmod.h>
  9 +#include <linux/module.h>
  10 +#include <linux/moduleparam.h>
  11 +#include <linux/mount.h>
  12 +#include <linux/namei.h>
  13 +#include <linux/sunrpc/cache.h>
  14 +#include <linux/sunrpc/rpc_pipe_fs.h>
  15 +
  16 +#include "cache_lib.h"
  17 +
  18 +#define NFS_CACHE_UPCALL_PATHLEN 256
  19 +#define NFS_CACHE_UPCALL_TIMEOUT 15
  20 +
  21 +static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] =
  22 + "/sbin/nfs_cache_getent";
  23 +static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT;
  24 +
  25 +module_param_string(cache_getent, nfs_cache_getent_prog,
  26 + sizeof(nfs_cache_getent_prog), 0600);
  27 +MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program");
  28 +module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600);
  29 +MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which "
  30 + "the cache upcall is assumed to have failed");
  31 +
  32 +int nfs_cache_upcall(struct cache_detail *cd, char *entry_name)
  33 +{
  34 + static char *envp[] = { "HOME=/",
  35 + "TERM=linux",
  36 + "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
  37 + NULL
  38 + };
  39 + char *argv[] = {
  40 + nfs_cache_getent_prog,
  41 + cd->name,
  42 + entry_name,
  43 + NULL
  44 + };
  45 + int ret = -EACCES;
  46 +
  47 + if (nfs_cache_getent_prog[0] == '\0')
  48 + goto out;
  49 + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
  50 + /*
  51 + * Disable the upcall mechanism if we're getting an ENOENT or
  52 + * EACCES error. The admin can re-enable it on the fly by using
  53 + * sysfs to set the 'cache_getent' parameter once the problem
  54 + * has been fixed.
  55 + */
  56 + if (ret == -ENOENT || ret == -EACCES)
  57 + nfs_cache_getent_prog[0] = '\0';
  58 +out:
  59 + return ret > 0 ? 0 : ret;
  60 +}
  61 +
  62 +/*
  63 + * Deferred request handling
  64 + */
  65 +void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq)
  66 +{
  67 + if (atomic_dec_and_test(&dreq->count))
  68 + kfree(dreq);
  69 +}
  70 +
  71 +static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
  72 +{
  73 + struct nfs_cache_defer_req *dreq;
  74 +
  75 + dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
  76 +
  77 + complete_all(&dreq->completion);
  78 + nfs_cache_defer_req_put(dreq);
  79 +}
  80 +
  81 +static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req)
  82 +{
  83 + struct nfs_cache_defer_req *dreq;
  84 +
  85 + dreq = container_of(req, struct nfs_cache_defer_req, req);
  86 + dreq->deferred_req.revisit = nfs_dns_cache_revisit;
  87 + atomic_inc(&dreq->count);
  88 +
  89 + return &dreq->deferred_req;
  90 +}
  91 +
  92 +struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void)
  93 +{
  94 + struct nfs_cache_defer_req *dreq;
  95 +
  96 + dreq = kzalloc(sizeof(*dreq), GFP_KERNEL);
  97 + if (dreq) {
  98 + init_completion(&dreq->completion);
  99 + atomic_set(&dreq->count, 1);
  100 + dreq->req.defer = nfs_dns_cache_defer;
  101 + }
  102 + return dreq;
  103 +}
  104 +
  105 +int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
  106 +{
  107 + if (wait_for_completion_timeout(&dreq->completion,
  108 + nfs_cache_getent_timeout * HZ) == 0)
  109 + return -ETIMEDOUT;
  110 + return 0;
  111 +}
  112 +
  113 +int nfs_cache_register(struct cache_detail *cd)
  114 +{
  115 + struct nameidata nd;
  116 + struct vfsmount *mnt;
  117 + int ret;
  118 +
  119 + mnt = rpc_get_mount();
  120 + if (IS_ERR(mnt))
  121 + return PTR_ERR(mnt);
  122 + ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
  123 + if (ret)
  124 + goto err;
  125 + ret = sunrpc_cache_register_pipefs(nd.path.dentry,
  126 + cd->name, 0600, cd);
  127 + path_put(&nd.path);
  128 + if (!ret)
  129 + return ret;
  130 +err:
  131 + rpc_put_mount();
  132 + return ret;
  133 +}
  134 +
  135 +void nfs_cache_unregister(struct cache_detail *cd)
  136 +{
  137 + sunrpc_cache_unregister_pipefs(cd);
  138 + rpc_put_mount();
  139 +}
  1 +/*
  2 + * Helper routines for the NFS client caches
  3 + *
  4 + * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
  5 + */
  6 +
  7 +#include <linux/completion.h>
  8 +#include <linux/sunrpc/cache.h>
  9 +#include <asm/atomic.h>
  10 +
  11 +/*
  12 + * Deferred request handling
  13 + */
  14 +struct nfs_cache_defer_req {
  15 + struct cache_req req;
  16 + struct cache_deferred_req deferred_req;
  17 + struct completion completion;
  18 + atomic_t count;
  19 +};
  20 +
  21 +extern int nfs_cache_upcall(struct cache_detail *cd, char *entry_name);
  22 +extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void);
  23 +extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq);
  24 +extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq);
  25 +
  26 +extern int nfs_cache_register(struct cache_detail *cd);
  27 +extern void nfs_cache_unregister(struct cache_detail *cd);
fs/nfs/dns_resolve.c
  1 +/*
  2 + * linux/fs/nfs/dns_resolve.c
  3 + *
  4 + * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
  5 + *
  6 + * Resolves DNS hostnames into valid ip addresses
  7 + */
  8 +
  9 +#include <linux/hash.h>
  10 +#include <linux/string.h>
  11 +#include <linux/kmod.h>
  12 +#include <linux/module.h>
  13 +#include <linux/socket.h>
  14 +#include <linux/seq_file.h>
  15 +#include <linux/inet.h>
  16 +#include <linux/sunrpc/clnt.h>
  17 +#include <linux/sunrpc/cache.h>
  18 +#include <linux/sunrpc/svcauth.h>
  19 +
  20 +#include "dns_resolve.h"
  21 +#include "cache_lib.h"
  22 +
  23 +#define NFS_DNS_HASHBITS 4
  24 +#define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
  25 +
  26 +static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
  27 +
  28 +struct nfs_dns_ent {
  29 + struct cache_head h;
  30 +
  31 + char *hostname;
  32 + size_t namelen;
  33 +
  34 + struct sockaddr_storage addr;
  35 + size_t addrlen;
  36 +};
  37 +
  38 +
  39 +static void nfs_dns_ent_init(struct cache_head *cnew,
  40 + struct cache_head *ckey)
  41 +{
  42 + struct nfs_dns_ent *new;
  43 + struct nfs_dns_ent *key;
  44 +
  45 + new = container_of(cnew, struct nfs_dns_ent, h);
  46 + key = container_of(ckey, struct nfs_dns_ent, h);
  47 +
  48 + kfree(new->hostname);
  49 + new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
  50 + if (new->hostname) {
  51 + new->namelen = key->namelen;
  52 + memcpy(&new->addr, &key->addr, key->addrlen);
  53 + new->addrlen = key->addrlen;
  54 + } else {
  55 + new->namelen = 0;
  56 + new->addrlen = 0;
  57 + }
  58 +}
  59 +
  60 +static void nfs_dns_ent_put(struct kref *ref)
  61 +{
  62 + struct nfs_dns_ent *item;
  63 +
  64 + item = container_of(ref, struct nfs_dns_ent, h.ref);
  65 + kfree(item->hostname);
  66 + kfree(item);
  67 +}
  68 +
  69 +static struct cache_head *nfs_dns_ent_alloc(void)
  70 +{
  71 + struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
  72 +
  73 + if (item != NULL) {
  74 + item->hostname = NULL;
  75 + item->namelen = 0;
  76 + item->addrlen = 0;
  77 + return &item->h;
  78 + }
  79 + return NULL;
  80 +};
  81 +
  82 +static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
  83 +{
  84 + return hash_str(key->hostname, NFS_DNS_HASHBITS);
  85 +}
  86 +
  87 +static void nfs_dns_request(struct cache_detail *cd,
  88 + struct cache_head *ch,
  89 + char **bpp, int *blen)
  90 +{
  91 + struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
  92 +
  93 + qword_add(bpp, blen, key->hostname);
  94 + (*bpp)[-1] = '\n';
  95 +}
  96 +
  97 +static int nfs_dns_upcall(struct cache_detail *cd,
  98 + struct cache_head *ch)
  99 +{
  100 + struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
  101 + int ret;
  102 +
  103 + ret = nfs_cache_upcall(cd, key->hostname);
  104 + if (ret)
  105 + ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request);
  106 + return ret;
  107 +}
  108 +
  109 +static int nfs_dns_match(struct cache_head *ca,
  110 + struct cache_head *cb)
  111 +{
  112 + struct nfs_dns_ent *a;
  113 + struct nfs_dns_ent *b;
  114 +
  115 + a = container_of(ca, struct nfs_dns_ent, h);
  116 + b = container_of(cb, struct nfs_dns_ent, h);
  117 +
  118 + if (a->namelen == 0 || a->namelen != b->namelen)
  119 + return 0;
  120 + return memcmp(a->hostname, b->hostname, a->namelen) == 0;
  121 +}
  122 +
  123 +static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
  124 + struct cache_head *h)
  125 +{
  126 + struct nfs_dns_ent *item;
  127 + long ttl;
  128 +
  129 + if (h == NULL) {
  130 + seq_puts(m, "# ip address hostname ttl\n");
  131 + return 0;
  132 + }
  133 + item = container_of(h, struct nfs_dns_ent, h);
  134 + ttl = (long)item->h.expiry_time - (long)get_seconds();
  135 + if (ttl < 0)
  136 + ttl = 0;
  137 +
  138 + if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
  139 + char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
  140 +
  141 + rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf));
  142 + seq_printf(m, "%15s ", buf);
  143 + } else
  144 + seq_puts(m, "<none> ");
  145 + seq_printf(m, "%15s %ld\n", item->hostname, ttl);
  146 + return 0;
  147 +}
  148 +
  149 +struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
  150 + struct nfs_dns_ent *key)
  151 +{
  152 + struct cache_head *ch;
  153 +
  154 + ch = sunrpc_cache_lookup(cd,
  155 + &key->h,
  156 + nfs_dns_hash(key));
  157 + if (!ch)
  158 + return NULL;
  159 + return container_of(ch, struct nfs_dns_ent, h);
  160 +}
  161 +
  162 +struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
  163 + struct nfs_dns_ent *new,
  164 + struct nfs_dns_ent *key)
  165 +{
  166 + struct cache_head *ch;
  167 +
  168 + ch = sunrpc_cache_update(cd,
  169 + &new->h, &key->h,
  170 + nfs_dns_hash(key));
  171 + if (!ch)
  172 + return NULL;
  173 + return container_of(ch, struct nfs_dns_ent, h);
  174 +}
  175 +
  176 +static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
  177 +{
  178 + char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
  179 + struct nfs_dns_ent key, *item;
  180 + unsigned long ttl;
  181 + ssize_t len;
  182 + int ret = -EINVAL;
  183 +
  184 + if (buf[buflen-1] != '\n')
  185 + goto out;
  186 + buf[buflen-1] = '\0';
  187 +
  188 + len = qword_get(&buf, buf1, sizeof(buf1));
  189 + if (len <= 0)
  190 + goto out;
  191 + key.addrlen = rpc_pton(buf1, len,
  192 + (struct sockaddr *)&key.addr,
  193 + sizeof(key.addr));
  194 +
  195 + len = qword_get(&buf, buf1, sizeof(buf1));
  196 + if (len <= 0)
  197 + goto out;
  198 +
  199 + key.hostname = buf1;
  200 + key.namelen = len;
  201 + memset(&key.h, 0, sizeof(key.h));
  202 +
  203 + ttl = get_expiry(&buf);
  204 + if (ttl == 0)
  205 + goto out;
  206 + key.h.expiry_time = ttl + get_seconds();
  207 +
  208 + ret = -ENOMEM;
  209 + item = nfs_dns_lookup(cd, &key);
  210 + if (item == NULL)
  211 + goto out;
  212 +
  213 + if (key.addrlen == 0)
  214 + set_bit(CACHE_NEGATIVE, &key.h.flags);
  215 +
  216 + item = nfs_dns_update(cd, &key, item);
  217 + if (item == NULL)
  218 + goto out;
  219 +
  220 + ret = 0;
  221 + cache_put(&item->h, cd);
  222 +out:
  223 + return ret;
  224 +}
  225 +
  226 +static struct cache_detail nfs_dns_resolve = {
  227 + .owner = THIS_MODULE,
  228 + .hash_size = NFS_DNS_HASHTBL_SIZE,
  229 + .hash_table = nfs_dns_table,
  230 + .name = "dns_resolve",
  231 + .cache_put = nfs_dns_ent_put,
  232 + .cache_upcall = nfs_dns_upcall,
  233 + .cache_parse = nfs_dns_parse,
  234 + .cache_show = nfs_dns_show,
  235 + .match = nfs_dns_match,
  236 + .init = nfs_dns_ent_init,
  237 + .update = nfs_dns_ent_init,
  238 + .alloc = nfs_dns_ent_alloc,
  239 +};
  240 +
  241 +static int do_cache_lookup(struct cache_detail *cd,
  242 + struct nfs_dns_ent *key,
  243 + struct nfs_dns_ent **item,
  244 + struct nfs_cache_defer_req *dreq)
  245 +{
  246 + int ret = -ENOMEM;
  247 +
  248 + *item = nfs_dns_lookup(cd, key);
  249 + if (*item) {
  250 + ret = cache_check(cd, &(*item)->h, &dreq->req);
  251 + if (ret)
  252 + *item = NULL;
  253 + }
  254 + return ret;
  255 +}
  256 +
  257 +static int do_cache_lookup_nowait(struct cache_detail *cd,
  258 + struct nfs_dns_ent *key,
  259 + struct nfs_dns_ent **item)
  260 +{
  261 + int ret = -ENOMEM;
  262 +
  263 + *item = nfs_dns_lookup(cd, key);
  264 + if (!*item)
  265 + goto out_err;
  266 + ret = -ETIMEDOUT;
  267 + if (!test_bit(CACHE_VALID, &(*item)->h.flags)
  268 + || (*item)->h.expiry_time < get_seconds()
  269 + || cd->flush_time > (*item)->h.last_refresh)
  270 + goto out_put;
  271 + ret = -ENOENT;
  272 + if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
  273 + goto out_put;
  274 + return 0;
  275 +out_put:
  276 + cache_put(&(*item)->h, cd);
  277 +out_err:
  278 + *item = NULL;
  279 + return ret;
  280 +}
  281 +
  282 +static int do_cache_lookup_wait(struct cache_detail *cd,
  283 + struct nfs_dns_ent *key,
  284 + struct nfs_dns_ent **item)
  285 +{
  286 + struct nfs_cache_defer_req *dreq;
  287 + int ret = -ENOMEM;
  288 +
  289 + dreq = nfs_cache_defer_req_alloc();
  290 + if (!dreq)
  291 + goto out;
  292 + ret = do_cache_lookup(cd, key, item, dreq);
  293 + if (ret == -EAGAIN) {
  294 + ret = nfs_cache_wait_for_upcall(dreq);
  295 + if (!ret)
  296 + ret = do_cache_lookup_nowait(cd, key, item);
  297 + }
  298 + nfs_cache_defer_req_put(dreq);
  299 +out:
  300 + return ret;
  301 +}
  302 +
  303 +ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
  304 + struct sockaddr *sa, size_t salen)
  305 +{
  306 + struct nfs_dns_ent key = {
  307 + .hostname = name,
  308 + .namelen = namelen,
  309 + };
  310 + struct nfs_dns_ent *item = NULL;
  311 + ssize_t ret;
  312 +
  313 + ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item);
  314 + if (ret == 0) {
  315 + if (salen >= item->addrlen) {
  316 + memcpy(sa, &item->addr, item->addrlen);
  317 + ret = item->addrlen;
  318 + } else
  319 + ret = -EOVERFLOW;
  320 + cache_put(&item->h, &nfs_dns_resolve);
  321 + } else if (ret == -ENOENT)
  322 + ret = -ESRCH;
  323 + return ret;
  324 +}
  325 +
  326 +int nfs_dns_resolver_init(void)
  327 +{
  328 + return nfs_cache_register(&nfs_dns_resolve);
  329 +}
  330 +
  331 +void nfs_dns_resolver_destroy(void)
  332 +{
  333 + nfs_cache_unregister(&nfs_dns_resolve);
  334 +}
fs/nfs/dns_resolve.h
  1 +/*
  2 + * Resolve DNS hostnames into valid ip addresses
  3 + */
  4 +#ifndef __LINUX_FS_NFS_DNS_RESOLVE_H
  5 +#define __LINUX_FS_NFS_DNS_RESOLVE_H
  6 +
  7 +#define NFS_DNS_HOSTNAME_MAXLEN (128)
  8 +
  9 +extern int nfs_dns_resolver_init(void);
  10 +extern void nfs_dns_resolver_destroy(void);
  11 +extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
  12 + struct sockaddr *sa, size_t salen);
  13 +
  14 +#endif
... ... @@ -46,6 +46,7 @@
46 46 #include "iostat.h"
47 47 #include "internal.h"
48 48 #include "fscache.h"
  49 +#include "dns_resolve.h"
49 50  
50 51 #define NFSDBG_FACILITY NFSDBG_VFS
51 52  
... ... @@ -1506,6 +1507,10 @@
1506 1507 {
1507 1508 int err;
1508 1509  
  1510 + err = nfs_dns_resolver_init();
  1511 + if (err < 0)
  1512 + goto out8;
  1513 +
1509 1514 err = nfs_fscache_register();
1510 1515 if (err < 0)
1511 1516 goto out7;
... ... @@ -1564,6 +1569,8 @@
1564 1569 out6:
1565 1570 nfs_fscache_unregister();
1566 1571 out7:
  1572 + nfs_dns_resolver_destroy();
  1573 +out8:
1567 1574 return err;
1568 1575 }
1569 1576  
... ... @@ -1575,6 +1582,7 @@
1575 1582 nfs_destroy_inodecache();
1576 1583 nfs_destroy_nfspagecache();
1577 1584 nfs_fscache_unregister();
  1585 + nfs_dns_resolver_destroy();
1578 1586 #ifdef CONFIG_PROC_FS
1579 1587 rpc_proc_unregister("nfs");
1580 1588 #endif
net/sunrpc/rpc_pipe.c
... ... @@ -416,11 +416,13 @@
416 416 return ERR_PTR(err);
417 417 return rpc_mount;
418 418 }
  419 +EXPORT_SYMBOL_GPL(rpc_get_mount);
419 420  
420 421 void rpc_put_mount(void)
421 422 {
422 423 simple_release_fs(&rpc_mount, &rpc_mount_count);
423 424 }
  425 +EXPORT_SYMBOL_GPL(rpc_put_mount);
424 426  
425 427 static int rpc_delete_dentry(struct dentry *dentry)
426 428 {
... ... @@ -946,6 +948,7 @@
946 948 RPCAUTH_portmap,
947 949 RPCAUTH_statd,
948 950 RPCAUTH_nfsd4_cb,
  951 + RPCAUTH_cache,
949 952 RPCAUTH_RootEOF
950 953 };
951 954  
... ... @@ -972,6 +975,10 @@
972 975 },
973 976 [RPCAUTH_nfsd4_cb] = {
974 977 .name = "nfsd4_cb",
  978 + .mode = S_IFDIR | S_IRUGO | S_IXUGO,
  979 + },
  980 + [RPCAUTH_cache] = {
  981 + .name = "cache",
975 982 .mode = S_IFDIR | S_IRUGO | S_IXUGO,
976 983 },
977 984 };