diff options
Diffstat (limited to 'fs/lockd/host.c')
-rw-r--r-- | fs/lockd/host.c | 121 |
1 files changed, 110 insertions, 11 deletions
diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 481ce7ee1002..0bf4afb71d25 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c | |||
@@ -34,6 +34,8 @@ static DEFINE_MUTEX(nlm_host_mutex); | |||
34 | 34 | ||
35 | 35 | ||
36 | static void nlm_gc_hosts(void); | 36 | static void nlm_gc_hosts(void); |
37 | static struct nsm_handle * __nsm_find(const struct sockaddr_in *, | ||
38 | const char *, int, int); | ||
37 | 39 | ||
38 | /* | 40 | /* |
39 | * Find an NLM server handle in the cache. If there is none, create it. | 41 | * Find an NLM server handle in the cache. If there is none, create it. |
@@ -68,7 +70,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
68 | int hostname_len) | 70 | int hostname_len) |
69 | { | 71 | { |
70 | struct nlm_host *host, **hp; | 72 | struct nlm_host *host, **hp; |
71 | u32 addr; | 73 | struct nsm_handle *nsm = NULL; |
72 | int hash; | 74 | int hash; |
73 | 75 | ||
74 | dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", | 76 | dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", |
@@ -86,7 +88,21 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
86 | if (time_after_eq(jiffies, next_gc)) | 88 | if (time_after_eq(jiffies, next_gc)) |
87 | nlm_gc_hosts(); | 89 | nlm_gc_hosts(); |
88 | 90 | ||
91 | /* We may keep several nlm_host objects for a peer, because each | ||
92 | * nlm_host is identified by | ||
93 | * (address, protocol, version, server/client) | ||
94 | * We could probably simplify this a little by putting all those | ||
95 | * different NLM rpc_clients into one single nlm_host object. | ||
96 | * This would allow us to have one nlm_host per address. | ||
97 | */ | ||
89 | for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { | 98 | for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { |
99 | if (!nlm_cmp_addr(&host->h_addr, sin)) | ||
100 | continue; | ||
101 | |||
102 | /* See if we have an NSM handle for this client */ | ||
103 | if (!nsm && (nsm = host->h_nsmhandle) != 0) | ||
104 | atomic_inc(&nsm->sm_count); | ||
105 | |||
90 | if (host->h_proto != proto) | 106 | if (host->h_proto != proto) |
91 | continue; | 107 | continue; |
92 | if (host->h_version != version) | 108 | if (host->h_version != version) |
@@ -94,7 +110,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
94 | if (host->h_server != server) | 110 | if (host->h_server != server) |
95 | continue; | 111 | continue; |
96 | 112 | ||
97 | if (nlm_cmp_addr(&host->h_addr, sin)) { | 113 | { |
98 | if (hp != nlm_hosts + hash) { | 114 | if (hp != nlm_hosts + hash) { |
99 | *hp = host->h_next; | 115 | *hp = host->h_next; |
100 | host->h_next = nlm_hosts[hash]; | 116 | host->h_next = nlm_hosts[hash]; |
@@ -106,16 +122,18 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
106 | } | 122 | } |
107 | } | 123 | } |
108 | 124 | ||
109 | /* Ooops, no host found, create it */ | 125 | /* Sadly, the host isn't in our hash table yet. See if |
110 | dprintk("lockd: creating host entry\n"); | 126 | * we have an NSM handle for it. If not, create one. |
127 | */ | ||
128 | if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len))) | ||
129 | goto out; | ||
111 | 130 | ||
112 | host = kzalloc(sizeof(*host), GFP_KERNEL); | 131 | host = kzalloc(sizeof(*host), GFP_KERNEL); |
113 | if (!host) | 132 | if (!host) { |
114 | goto nohost; | 133 | nsm_release(nsm); |
115 | 134 | goto out; | |
116 | addr = sin->sin_addr.s_addr; | 135 | } |
117 | sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr)); | 136 | host->h_name = nsm->sm_name; |
118 | |||
119 | host->h_addr = *sin; | 137 | host->h_addr = *sin; |
120 | host->h_addr.sin_port = 0; /* ouch! */ | 138 | host->h_addr.sin_port = 0; /* ouch! */ |
121 | host->h_version = version; | 139 | host->h_version = version; |
@@ -129,6 +147,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
129 | init_rwsem(&host->h_rwsem); | 147 | init_rwsem(&host->h_rwsem); |
130 | host->h_state = 0; /* pseudo NSM state */ | 148 | host->h_state = 0; /* pseudo NSM state */ |
131 | host->h_nsmstate = 0; /* real NSM state */ | 149 | host->h_nsmstate = 0; /* real NSM state */ |
150 | host->h_nsmhandle = nsm; | ||
132 | host->h_server = server; | 151 | host->h_server = server; |
133 | host->h_next = nlm_hosts[hash]; | 152 | host->h_next = nlm_hosts[hash]; |
134 | nlm_hosts[hash] = host; | 153 | nlm_hosts[hash] = host; |
@@ -140,7 +159,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, | |||
140 | if (++nrhosts > NLM_HOST_MAX) | 159 | if (++nrhosts > NLM_HOST_MAX) |
141 | next_gc = 0; | 160 | next_gc = 0; |
142 | 161 | ||
143 | nohost: | 162 | out: |
144 | mutex_unlock(&nlm_host_mutex); | 163 | mutex_unlock(&nlm_host_mutex); |
145 | return host; | 164 | return host; |
146 | } | 165 | } |
@@ -393,3 +412,83 @@ nlm_gc_hosts(void) | |||
393 | next_gc = jiffies + NLM_HOST_COLLECT; | 412 | next_gc = jiffies + NLM_HOST_COLLECT; |
394 | } | 413 | } |
395 | 414 | ||
415 | |||
416 | /* | ||
417 | * Manage NSM handles | ||
418 | */ | ||
419 | static LIST_HEAD(nsm_handles); | ||
420 | static DECLARE_MUTEX(nsm_sema); | ||
421 | |||
422 | static struct nsm_handle * | ||
423 | __nsm_find(const struct sockaddr_in *sin, | ||
424 | const char *hostname, int hostname_len, | ||
425 | int create) | ||
426 | { | ||
427 | struct nsm_handle *nsm = NULL; | ||
428 | struct list_head *pos; | ||
429 | |||
430 | if (!sin) | ||
431 | return NULL; | ||
432 | |||
433 | if (hostname && memchr(hostname, '/', hostname_len) != NULL) { | ||
434 | if (printk_ratelimit()) { | ||
435 | printk(KERN_WARNING "Invalid hostname \"%.*s\" " | ||
436 | "in NFS lock request\n", | ||
437 | hostname_len, hostname); | ||
438 | } | ||
439 | return NULL; | ||
440 | } | ||
441 | |||
442 | down(&nsm_sema); | ||
443 | list_for_each(pos, &nsm_handles) { | ||
444 | nsm = list_entry(pos, struct nsm_handle, sm_link); | ||
445 | |||
446 | if (!nlm_cmp_addr(&nsm->sm_addr, sin)) | ||
447 | continue; | ||
448 | atomic_inc(&nsm->sm_count); | ||
449 | goto out; | ||
450 | } | ||
451 | |||
452 | if (!create) { | ||
453 | nsm = NULL; | ||
454 | goto out; | ||
455 | } | ||
456 | |||
457 | nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); | ||
458 | if (nsm != NULL) { | ||
459 | nsm->sm_addr = *sin; | ||
460 | nsm->sm_name = (char *) (nsm + 1); | ||
461 | memcpy(nsm->sm_name, hostname, hostname_len); | ||
462 | nsm->sm_name[hostname_len] = '\0'; | ||
463 | atomic_set(&nsm->sm_count, 1); | ||
464 | |||
465 | list_add(&nsm->sm_link, &nsm_handles); | ||
466 | } | ||
467 | |||
468 | out: up(&nsm_sema); | ||
469 | return nsm; | ||
470 | } | ||
471 | |||
472 | struct nsm_handle * | ||
473 | nsm_find(const struct sockaddr_in *sin, const char *hostname, int hostname_len) | ||
474 | { | ||
475 | return __nsm_find(sin, hostname, hostname_len, 1); | ||
476 | } | ||
477 | |||
478 | /* | ||
479 | * Release an NSM handle | ||
480 | */ | ||
481 | void | ||
482 | nsm_release(struct nsm_handle *nsm) | ||
483 | { | ||
484 | if (!nsm) | ||
485 | return; | ||
486 | if (atomic_dec_and_test(&nsm->sm_count)) { | ||
487 | down(&nsm_sema); | ||
488 | if (atomic_read(&nsm->sm_count) == 0) { | ||
489 | list_del(&nsm->sm_link); | ||
490 | kfree(nsm); | ||
491 | } | ||
492 | up(&nsm_sema); | ||
493 | } | ||
494 | } | ||