Commit e571cbf1a4f8d8b6cfd4898df718dae84c75a8e1
1 parent
96c61cbd0f
Exists in
master
and in
7 other branches
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. |
fs/nfs/Makefile
... | ... | @@ -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 |
fs/nfs/cache_lib.c
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 | +} |
fs/nfs/cache_lib.h
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 |
fs/nfs/inode.c
... | ... | @@ -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 | }; |