]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - fs/lockd/host.c
lockd: Specify address family for source address
[linux-2.6-omap-h63xx.git] / fs / lockd / host.c
1 /*
2  * linux/fs/lockd/host.c
3  *
4  * Management for NLM peer hosts. The nlm_host struct is shared
5  * between client and server implementation. The only reason to
6  * do so is to reduce code bloat.
7  *
8  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
9  */
10
11 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <linux/in.h>
14 #include <linux/in6.h>
15 #include <linux/sunrpc/clnt.h>
16 #include <linux/sunrpc/svc.h>
17 #include <linux/lockd/lockd.h>
18 #include <linux/lockd/sm_inter.h>
19 #include <linux/mutex.h>
20
21 #include <net/ipv6.h>
22
23 #define NLMDBG_FACILITY         NLMDBG_HOSTCACHE
24 #define NLM_HOST_NRHASH         32
25 #define NLM_ADDRHASH(addr)      (ntohl(addr) & (NLM_HOST_NRHASH-1))
26 #define NLM_HOST_REBIND         (60 * HZ)
27 #define NLM_HOST_EXPIRE         (300 * HZ)
28 #define NLM_HOST_COLLECT        (120 * HZ)
29
30 static struct hlist_head        nlm_hosts[NLM_HOST_NRHASH];
31 static unsigned long            next_gc;
32 static int                      nrhosts;
33 static DEFINE_MUTEX(nlm_host_mutex);
34
35
36 static void                     nlm_gc_hosts(void);
37 static struct nsm_handle *      __nsm_find(const struct sockaddr_in *,
38                                         const char *, unsigned int, int);
39 static struct nsm_handle *      nsm_find(const struct sockaddr_in *sin,
40                                          const char *hostname,
41                                          unsigned int hostname_len);
42
43 static void nlm_display_address(const struct sockaddr *sap,
44                                 char *buf, const size_t len)
45 {
46         const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
47         const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
48
49         switch (sap->sa_family) {
50         case AF_UNSPEC:
51                 snprintf(buf, len, "unspecified");
52                 break;
53         case AF_INET:
54                 snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
55                 break;
56         case AF_INET6:
57                 if (ipv6_addr_v4mapped(&sin6->sin6_addr))
58                         snprintf(buf, len, NIPQUAD_FMT,
59                                  NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
60                 else
61                         snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
62                 break;
63         default:
64                 snprintf(buf, len, "unsupported address family");
65                 break;
66         }
67 }
68
69 /*
70  * Common host lookup routine for server & client
71  */
72 static struct nlm_host *nlm_lookup_host(int server,
73                                         const struct sockaddr_in *sin,
74                                         int proto, u32 version,
75                                         const char *hostname,
76                                         unsigned int hostname_len,
77                                         const struct sockaddr_in *ssin)
78 {
79         struct hlist_head *chain;
80         struct hlist_node *pos;
81         struct nlm_host *host;
82         struct nsm_handle *nsm = NULL;
83         int             hash;
84
85         dprintk("lockd: nlm_lookup_host(proto=%d, vers=%u,"
86                         " my role is %s, hostname=%.*s)\n",
87                         proto, version, server ? "server" : "client",
88                         hostname_len, hostname ? hostname : "<none>");
89
90         hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
91
92         /* Lock hash table */
93         mutex_lock(&nlm_host_mutex);
94
95         if (time_after_eq(jiffies, next_gc))
96                 nlm_gc_hosts();
97
98         /* We may keep several nlm_host objects for a peer, because each
99          * nlm_host is identified by
100          * (address, protocol, version, server/client)
101          * We could probably simplify this a little by putting all those
102          * different NLM rpc_clients into one single nlm_host object.
103          * This would allow us to have one nlm_host per address.
104          */
105         chain = &nlm_hosts[hash];
106         hlist_for_each_entry(host, pos, chain, h_hash) {
107                 if (!nlm_cmp_addr(&host->h_addr, sin))
108                         continue;
109
110                 /* See if we have an NSM handle for this client */
111                 if (!nsm)
112                         nsm = host->h_nsmhandle;
113
114                 if (host->h_proto != proto)
115                         continue;
116                 if (host->h_version != version)
117                         continue;
118                 if (host->h_server != server)
119                         continue;
120                 if (!nlm_cmp_addr(&host->h_saddr, ssin))
121                         continue;
122
123                 /* Move to head of hash chain. */
124                 hlist_del(&host->h_hash);
125                 hlist_add_head(&host->h_hash, chain);
126
127                 nlm_get_host(host);
128                 dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
129                                 host->h_name, host->h_addrbuf);
130                 goto out;
131         }
132
133         /*
134          * The host wasn't in our hash table.  If we don't
135          * have an NSM handle for it yet, create one.
136          */
137         if (nsm)
138                 atomic_inc(&nsm->sm_count);
139         else {
140                 host = NULL;
141                 nsm = nsm_find(sin, hostname, hostname_len);
142                 if (!nsm) {
143                         dprintk("lockd: nlm_lookup_host failed; "
144                                 "no nsm handle\n");
145                         goto out;
146                 }
147         }
148
149         host = kzalloc(sizeof(*host), GFP_KERNEL);
150         if (!host) {
151                 nsm_release(nsm);
152                 dprintk("lockd: nlm_lookup_host failed; no memory\n");
153                 goto out;
154         }
155         host->h_name       = nsm->sm_name;
156         host->h_addr       = *sin;
157         host->h_addr.sin_port = 0;      /* ouch! */
158         host->h_saddr      = *ssin;
159         host->h_version    = version;
160         host->h_proto      = proto;
161         host->h_rpcclnt    = NULL;
162         mutex_init(&host->h_mutex);
163         host->h_nextrebind = jiffies + NLM_HOST_REBIND;
164         host->h_expires    = jiffies + NLM_HOST_EXPIRE;
165         atomic_set(&host->h_count, 1);
166         init_waitqueue_head(&host->h_gracewait);
167         init_rwsem(&host->h_rwsem);
168         host->h_state      = 0;                 /* pseudo NSM state */
169         host->h_nsmstate   = 0;                 /* real NSM state */
170         host->h_nsmhandle  = nsm;
171         host->h_server     = server;
172         hlist_add_head(&host->h_hash, chain);
173         INIT_LIST_HEAD(&host->h_lockowners);
174         spin_lock_init(&host->h_lock);
175         INIT_LIST_HEAD(&host->h_granted);
176         INIT_LIST_HEAD(&host->h_reclaim);
177
178         nrhosts++;
179
180         nlm_display_address((struct sockaddr *)&host->h_addr,
181                                 host->h_addrbuf, sizeof(host->h_addrbuf));
182         nlm_display_address((struct sockaddr *)&host->h_saddr,
183                                 host->h_saddrbuf, sizeof(host->h_saddrbuf));
184
185         dprintk("lockd: nlm_lookup_host created host %s\n",
186                         host->h_name);
187
188 out:
189         mutex_unlock(&nlm_host_mutex);
190         return host;
191 }
192
193 /*
194  * Destroy a host
195  */
196 static void
197 nlm_destroy_host(struct nlm_host *host)
198 {
199         struct rpc_clnt *clnt;
200
201         BUG_ON(!list_empty(&host->h_lockowners));
202         BUG_ON(atomic_read(&host->h_count));
203
204         /*
205          * Release NSM handle and unmonitor host.
206          */
207         nsm_unmonitor(host);
208
209         clnt = host->h_rpcclnt;
210         if (clnt != NULL)
211                 rpc_shutdown_client(clnt);
212         kfree(host);
213 }
214
215 /*
216  * Find an NLM server handle in the cache. If there is none, create it.
217  */
218 struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin,
219                                      int proto, u32 version,
220                                      const char *hostname,
221                                      unsigned int hostname_len)
222 {
223         const struct sockaddr_in source = {
224                 .sin_family     = AF_UNSPEC,
225         };
226
227         return nlm_lookup_host(0, sin, proto, version,
228                                hostname, hostname_len, &source);
229 }
230
231 /*
232  * Find an NLM client handle in the cache. If there is none, create it.
233  */
234 struct nlm_host *
235 nlmsvc_lookup_host(struct svc_rqst *rqstp,
236                         const char *hostname, unsigned int hostname_len)
237 {
238         const struct sockaddr_in source = {
239                 .sin_family     = AF_INET,
240                 .sin_addr       = rqstp->rq_daddr.addr,
241         };
242
243         return nlm_lookup_host(1, svc_addr_in(rqstp),
244                                rqstp->rq_prot, rqstp->rq_vers,
245                                hostname, hostname_len, &source);
246 }
247
248 /*
249  * Create the NLM RPC client for an NLM peer
250  */
251 struct rpc_clnt *
252 nlm_bind_host(struct nlm_host *host)
253 {
254         struct rpc_clnt *clnt;
255
256         dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n",
257                         host->h_name, host->h_addrbuf, host->h_saddrbuf);
258
259         /* Lock host handle */
260         mutex_lock(&host->h_mutex);
261
262         /* If we've already created an RPC client, check whether
263          * RPC rebind is required
264          */
265         if ((clnt = host->h_rpcclnt) != NULL) {
266                 if (time_after_eq(jiffies, host->h_nextrebind)) {
267                         rpc_force_rebind(clnt);
268                         host->h_nextrebind = jiffies + NLM_HOST_REBIND;
269                         dprintk("lockd: next rebind in %lu jiffies\n",
270                                         host->h_nextrebind - jiffies);
271                 }
272         } else {
273                 unsigned long increment = nlmsvc_timeout;
274                 struct rpc_timeout timeparms = {
275                         .to_initval     = increment,
276                         .to_increment   = increment,
277                         .to_maxval      = increment * 6UL,
278                         .to_retries     = 5U,
279                 };
280                 struct rpc_create_args args = {
281                         .protocol       = host->h_proto,
282                         .address        = (struct sockaddr *)&host->h_addr,
283                         .addrsize       = sizeof(host->h_addr),
284                         .saddress       = (struct sockaddr *)&host->h_saddr,
285                         .timeout        = &timeparms,
286                         .servername     = host->h_name,
287                         .program        = &nlm_program,
288                         .version        = host->h_version,
289                         .authflavor     = RPC_AUTH_UNIX,
290                         .flags          = (RPC_CLNT_CREATE_NOPING |
291                                            RPC_CLNT_CREATE_AUTOBIND),
292                 };
293
294                 /*
295                  * lockd retries server side blocks automatically so we want
296                  * those to be soft RPC calls. Client side calls need to be
297                  * hard RPC tasks.
298                  */
299                 if (!host->h_server)
300                         args.flags |= RPC_CLNT_CREATE_HARDRTRY;
301
302                 clnt = rpc_create(&args);
303                 if (!IS_ERR(clnt))
304                         host->h_rpcclnt = clnt;
305                 else {
306                         printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
307                         clnt = NULL;
308                 }
309         }
310
311         mutex_unlock(&host->h_mutex);
312         return clnt;
313 }
314
315 /*
316  * Force a portmap lookup of the remote lockd port
317  */
318 void
319 nlm_rebind_host(struct nlm_host *host)
320 {
321         dprintk("lockd: rebind host %s\n", host->h_name);
322         if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
323                 rpc_force_rebind(host->h_rpcclnt);
324                 host->h_nextrebind = jiffies + NLM_HOST_REBIND;
325         }
326 }
327
328 /*
329  * Increment NLM host count
330  */
331 struct nlm_host * nlm_get_host(struct nlm_host *host)
332 {
333         if (host) {
334                 dprintk("lockd: get host %s\n", host->h_name);
335                 atomic_inc(&host->h_count);
336                 host->h_expires = jiffies + NLM_HOST_EXPIRE;
337         }
338         return host;
339 }
340
341 /*
342  * Release NLM host after use
343  */
344 void nlm_release_host(struct nlm_host *host)
345 {
346         if (host != NULL) {
347                 dprintk("lockd: release host %s\n", host->h_name);
348                 BUG_ON(atomic_read(&host->h_count) < 0);
349                 if (atomic_dec_and_test(&host->h_count)) {
350                         BUG_ON(!list_empty(&host->h_lockowners));
351                         BUG_ON(!list_empty(&host->h_granted));
352                         BUG_ON(!list_empty(&host->h_reclaim));
353                 }
354         }
355 }
356
357 /*
358  * We were notified that the host indicated by address &sin
359  * has rebooted.
360  * Release all resources held by that peer.
361  */
362 void nlm_host_rebooted(const struct sockaddr_in *sin,
363                                 const char *hostname,
364                                 unsigned int hostname_len,
365                                 u32 new_state)
366 {
367         struct hlist_head *chain;
368         struct hlist_node *pos;
369         struct nsm_handle *nsm;
370         struct nlm_host *host;
371
372         /* Find the NSM handle for this peer */
373         nsm = __nsm_find(sin, hostname, hostname_len, 0);
374         if (nsm == NULL) {
375                 dprintk("lockd: never saw rebooted peer '%.*s' before\n",
376                                 hostname_len, hostname);
377                 return;
378         }
379
380         dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
381                         hostname_len, hostname, nsm->sm_addrbuf);
382
383         /* When reclaiming locks on this peer, make sure that
384          * we set up a new notification */
385         nsm->sm_monitored = 0;
386
387         /* Mark all hosts tied to this NSM state as having rebooted.
388          * We run the loop repeatedly, because we drop the host table
389          * lock for this.
390          * To avoid processing a host several times, we match the nsmstate.
391          */
392 again:  mutex_lock(&nlm_host_mutex);
393         for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
394                 hlist_for_each_entry(host, pos, chain, h_hash) {
395                         if (host->h_nsmhandle == nsm
396                          && host->h_nsmstate != new_state) {
397                                 host->h_nsmstate = new_state;
398                                 host->h_state++;
399
400                                 nlm_get_host(host);
401                                 mutex_unlock(&nlm_host_mutex);
402
403                                 if (host->h_server) {
404                                         /* We're server for this guy, just ditch
405                                          * all the locks he held. */
406                                         nlmsvc_free_host_resources(host);
407                                 } else {
408                                         /* He's the server, initiate lock recovery. */
409                                         nlmclnt_recovery(host);
410                                 }
411
412                                 nlm_release_host(host);
413                                 goto again;
414                         }
415                 }
416         }
417
418         mutex_unlock(&nlm_host_mutex);
419 }
420
421 /*
422  * Shut down the hosts module.
423  * Note that this routine is called only at server shutdown time.
424  */
425 void
426 nlm_shutdown_hosts(void)
427 {
428         struct hlist_head *chain;
429         struct hlist_node *pos;
430         struct nlm_host *host;
431
432         dprintk("lockd: shutting down host module\n");
433         mutex_lock(&nlm_host_mutex);
434
435         /* First, make all hosts eligible for gc */
436         dprintk("lockd: nuking all hosts...\n");
437         for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
438                 hlist_for_each_entry(host, pos, chain, h_hash) {
439                         host->h_expires = jiffies - 1;
440                         if (host->h_rpcclnt) {
441                                 rpc_shutdown_client(host->h_rpcclnt);
442                                 host->h_rpcclnt = NULL;
443                         }
444                 }
445         }
446
447         /* Then, perform a garbage collection pass */
448         nlm_gc_hosts();
449         mutex_unlock(&nlm_host_mutex);
450
451         /* complain if any hosts are left */
452         if (nrhosts) {
453                 printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
454                 dprintk("lockd: %d hosts left:\n", nrhosts);
455                 for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
456                         hlist_for_each_entry(host, pos, chain, h_hash) {
457                                 dprintk("       %s (cnt %d use %d exp %ld)\n",
458                                         host->h_name, atomic_read(&host->h_count),
459                                         host->h_inuse, host->h_expires);
460                         }
461                 }
462         }
463 }
464
465 /*
466  * Garbage collect any unused NLM hosts.
467  * This GC combines reference counting for async operations with
468  * mark & sweep for resources held by remote clients.
469  */
470 static void
471 nlm_gc_hosts(void)
472 {
473         struct hlist_head *chain;
474         struct hlist_node *pos, *next;
475         struct nlm_host *host;
476
477         dprintk("lockd: host garbage collection\n");
478         for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
479                 hlist_for_each_entry(host, pos, chain, h_hash)
480                         host->h_inuse = 0;
481         }
482
483         /* Mark all hosts that hold locks, blocks or shares */
484         nlmsvc_mark_resources();
485
486         for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
487                 hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
488                         if (atomic_read(&host->h_count) || host->h_inuse
489                          || time_before(jiffies, host->h_expires)) {
490                                 dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
491                                         host->h_name, atomic_read(&host->h_count),
492                                         host->h_inuse, host->h_expires);
493                                 continue;
494                         }
495                         dprintk("lockd: delete host %s\n", host->h_name);
496                         hlist_del_init(&host->h_hash);
497
498                         nlm_destroy_host(host);
499                         nrhosts--;
500                 }
501         }
502
503         next_gc = jiffies + NLM_HOST_COLLECT;
504 }
505
506
507 /*
508  * Manage NSM handles
509  */
510 static LIST_HEAD(nsm_handles);
511 static DEFINE_SPINLOCK(nsm_lock);
512
513 static struct nsm_handle *
514 __nsm_find(const struct sockaddr_in *sin,
515                 const char *hostname, unsigned int hostname_len,
516                 int create)
517 {
518         struct nsm_handle *nsm = NULL;
519         struct nsm_handle *pos;
520
521         if (!sin)
522                 return NULL;
523
524         if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
525                 if (printk_ratelimit()) {
526                         printk(KERN_WARNING "Invalid hostname \"%.*s\" "
527                                             "in NFS lock request\n",
528                                 hostname_len, hostname);
529                 }
530                 return NULL;
531         }
532
533 retry:
534         spin_lock(&nsm_lock);
535         list_for_each_entry(pos, &nsm_handles, sm_link) {
536
537                 if (hostname && nsm_use_hostnames) {
538                         if (strlen(pos->sm_name) != hostname_len
539                          || memcmp(pos->sm_name, hostname, hostname_len))
540                                 continue;
541                 } else if (!nlm_cmp_addr(&pos->sm_addr, sin))
542                         continue;
543                 atomic_inc(&pos->sm_count);
544                 kfree(nsm);
545                 nsm = pos;
546                 goto found;
547         }
548         if (nsm) {
549                 list_add(&nsm->sm_link, &nsm_handles);
550                 goto found;
551         }
552         spin_unlock(&nsm_lock);
553
554         if (!create)
555                 return NULL;
556
557         nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
558         if (nsm == NULL)
559                 return NULL;
560
561         nsm->sm_addr = *sin;
562         nsm->sm_name = (char *) (nsm + 1);
563         memcpy(nsm->sm_name, hostname, hostname_len);
564         nsm->sm_name[hostname_len] = '\0';
565         nlm_display_address((struct sockaddr *)&nsm->sm_addr,
566                                 nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
567         atomic_set(&nsm->sm_count, 1);
568         goto retry;
569
570 found:
571         spin_unlock(&nsm_lock);
572         return nsm;
573 }
574
575 static struct nsm_handle *
576 nsm_find(const struct sockaddr_in *sin, const char *hostname,
577          unsigned int hostname_len)
578 {
579         return __nsm_find(sin, hostname, hostname_len, 1);
580 }
581
582 /*
583  * Release an NSM handle
584  */
585 void
586 nsm_release(struct nsm_handle *nsm)
587 {
588         if (!nsm)
589                 return;
590         if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
591                 list_del(&nsm->sm_link);
592                 spin_unlock(&nsm_lock);
593                 kfree(nsm);
594         }
595 }