diff options
Diffstat (limited to 'net/sunrpc/svcauth_unix.c')
-rw-r--r-- | net/sunrpc/svcauth_unix.c | 194 |
1 files changed, 139 insertions, 55 deletions
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 207311610988..560677d187f1 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c | |||
@@ -18,6 +18,8 @@ | |||
18 | 18 | ||
19 | #include <linux/sunrpc/clnt.h> | 19 | #include <linux/sunrpc/clnt.h> |
20 | 20 | ||
21 | #include "netns.h" | ||
22 | |||
21 | /* | 23 | /* |
22 | * AUTHUNIX and AUTHNULL credentials are both handled here. | 24 | * AUTHUNIX and AUTHNULL credentials are both handled here. |
23 | * AUTHNULL is treated just like AUTHUNIX except that the uid/gid | 25 | * AUTHNULL is treated just like AUTHUNIX except that the uid/gid |
@@ -92,7 +94,6 @@ struct ip_map { | |||
92 | struct unix_domain *m_client; | 94 | struct unix_domain *m_client; |
93 | int m_add_change; | 95 | int m_add_change; |
94 | }; | 96 | }; |
95 | static struct cache_head *ip_table[IP_HASHMAX]; | ||
96 | 97 | ||
97 | static void ip_map_put(struct kref *kref) | 98 | static void ip_map_put(struct kref *kref) |
98 | { | 99 | { |
@@ -178,8 +179,8 @@ static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h) | |||
178 | return sunrpc_cache_pipe_upcall(cd, h, ip_map_request); | 179 | return sunrpc_cache_pipe_upcall(cd, h, ip_map_request); |
179 | } | 180 | } |
180 | 181 | ||
181 | static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr); | 182 | static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr); |
182 | static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry); | 183 | static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry); |
183 | 184 | ||
184 | static int ip_map_parse(struct cache_detail *cd, | 185 | static int ip_map_parse(struct cache_detail *cd, |
185 | char *mesg, int mlen) | 186 | char *mesg, int mlen) |
@@ -219,10 +220,9 @@ static int ip_map_parse(struct cache_detail *cd, | |||
219 | switch (address.sa.sa_family) { | 220 | switch (address.sa.sa_family) { |
220 | case AF_INET: | 221 | case AF_INET: |
221 | /* Form a mapped IPv4 address in sin6 */ | 222 | /* Form a mapped IPv4 address in sin6 */ |
222 | memset(&sin6, 0, sizeof(sin6)); | ||
223 | sin6.sin6_family = AF_INET6; | 223 | sin6.sin6_family = AF_INET6; |
224 | sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); | 224 | ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr, |
225 | sin6.sin6_addr.s6_addr32[3] = address.s4.sin_addr.s_addr; | 225 | &sin6.sin6_addr); |
226 | break; | 226 | break; |
227 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 227 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
228 | case AF_INET6: | 228 | case AF_INET6: |
@@ -249,9 +249,9 @@ static int ip_map_parse(struct cache_detail *cd, | |||
249 | dom = NULL; | 249 | dom = NULL; |
250 | 250 | ||
251 | /* IPv6 scope IDs are ignored for now */ | 251 | /* IPv6 scope IDs are ignored for now */ |
252 | ipmp = ip_map_lookup(class, &sin6.sin6_addr); | 252 | ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr); |
253 | if (ipmp) { | 253 | if (ipmp) { |
254 | err = ip_map_update(ipmp, | 254 | err = __ip_map_update(cd, ipmp, |
255 | container_of(dom, struct unix_domain, h), | 255 | container_of(dom, struct unix_domain, h), |
256 | expiry); | 256 | expiry); |
257 | } else | 257 | } else |
@@ -294,29 +294,15 @@ static int ip_map_show(struct seq_file *m, | |||
294 | } | 294 | } |
295 | 295 | ||
296 | 296 | ||
297 | struct cache_detail ip_map_cache = { | 297 | static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, |
298 | .owner = THIS_MODULE, | 298 | struct in6_addr *addr) |
299 | .hash_size = IP_HASHMAX, | ||
300 | .hash_table = ip_table, | ||
301 | .name = "auth.unix.ip", | ||
302 | .cache_put = ip_map_put, | ||
303 | .cache_upcall = ip_map_upcall, | ||
304 | .cache_parse = ip_map_parse, | ||
305 | .cache_show = ip_map_show, | ||
306 | .match = ip_map_match, | ||
307 | .init = ip_map_init, | ||
308 | .update = update, | ||
309 | .alloc = ip_map_alloc, | ||
310 | }; | ||
311 | |||
312 | static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr) | ||
313 | { | 299 | { |
314 | struct ip_map ip; | 300 | struct ip_map ip; |
315 | struct cache_head *ch; | 301 | struct cache_head *ch; |
316 | 302 | ||
317 | strcpy(ip.m_class, class); | 303 | strcpy(ip.m_class, class); |
318 | ipv6_addr_copy(&ip.m_addr, addr); | 304 | ipv6_addr_copy(&ip.m_addr, addr); |
319 | ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h, | 305 | ch = sunrpc_cache_lookup(cd, &ip.h, |
320 | hash_str(class, IP_HASHBITS) ^ | 306 | hash_str(class, IP_HASHBITS) ^ |
321 | hash_ip6(*addr)); | 307 | hash_ip6(*addr)); |
322 | 308 | ||
@@ -326,7 +312,17 @@ static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr) | |||
326 | return NULL; | 312 | return NULL; |
327 | } | 313 | } |
328 | 314 | ||
329 | static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry) | 315 | static inline struct ip_map *ip_map_lookup(struct net *net, char *class, |
316 | struct in6_addr *addr) | ||
317 | { | ||
318 | struct sunrpc_net *sn; | ||
319 | |||
320 | sn = net_generic(net, sunrpc_net_id); | ||
321 | return __ip_map_lookup(sn->ip_map_cache, class, addr); | ||
322 | } | ||
323 | |||
324 | static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, | ||
325 | struct unix_domain *udom, time_t expiry) | ||
330 | { | 326 | { |
331 | struct ip_map ip; | 327 | struct ip_map ip; |
332 | struct cache_head *ch; | 328 | struct cache_head *ch; |
@@ -344,17 +340,25 @@ static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t ex | |||
344 | ip.m_add_change++; | 340 | ip.m_add_change++; |
345 | } | 341 | } |
346 | ip.h.expiry_time = expiry; | 342 | ip.h.expiry_time = expiry; |
347 | ch = sunrpc_cache_update(&ip_map_cache, | 343 | ch = sunrpc_cache_update(cd, &ip.h, &ipm->h, |
348 | &ip.h, &ipm->h, | ||
349 | hash_str(ipm->m_class, IP_HASHBITS) ^ | 344 | hash_str(ipm->m_class, IP_HASHBITS) ^ |
350 | hash_ip6(ipm->m_addr)); | 345 | hash_ip6(ipm->m_addr)); |
351 | if (!ch) | 346 | if (!ch) |
352 | return -ENOMEM; | 347 | return -ENOMEM; |
353 | cache_put(ch, &ip_map_cache); | 348 | cache_put(ch, cd); |
354 | return 0; | 349 | return 0; |
355 | } | 350 | } |
356 | 351 | ||
357 | int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) | 352 | static inline int ip_map_update(struct net *net, struct ip_map *ipm, |
353 | struct unix_domain *udom, time_t expiry) | ||
354 | { | ||
355 | struct sunrpc_net *sn; | ||
356 | |||
357 | sn = net_generic(net, sunrpc_net_id); | ||
358 | return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry); | ||
359 | } | ||
360 | |||
361 | int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom) | ||
358 | { | 362 | { |
359 | struct unix_domain *udom; | 363 | struct unix_domain *udom; |
360 | struct ip_map *ipmp; | 364 | struct ip_map *ipmp; |
@@ -362,10 +366,10 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) | |||
362 | if (dom->flavour != &svcauth_unix) | 366 | if (dom->flavour != &svcauth_unix) |
363 | return -EINVAL; | 367 | return -EINVAL; |
364 | udom = container_of(dom, struct unix_domain, h); | 368 | udom = container_of(dom, struct unix_domain, h); |
365 | ipmp = ip_map_lookup("nfsd", addr); | 369 | ipmp = ip_map_lookup(net, "nfsd", addr); |
366 | 370 | ||
367 | if (ipmp) | 371 | if (ipmp) |
368 | return ip_map_update(ipmp, udom, NEVER); | 372 | return ip_map_update(net, ipmp, udom, NEVER); |
369 | else | 373 | else |
370 | return -ENOMEM; | 374 | return -ENOMEM; |
371 | } | 375 | } |
@@ -383,16 +387,18 @@ int auth_unix_forget_old(struct auth_domain *dom) | |||
383 | } | 387 | } |
384 | EXPORT_SYMBOL_GPL(auth_unix_forget_old); | 388 | EXPORT_SYMBOL_GPL(auth_unix_forget_old); |
385 | 389 | ||
386 | struct auth_domain *auth_unix_lookup(struct in6_addr *addr) | 390 | struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr) |
387 | { | 391 | { |
388 | struct ip_map *ipm; | 392 | struct ip_map *ipm; |
389 | struct auth_domain *rv; | 393 | struct auth_domain *rv; |
394 | struct sunrpc_net *sn; | ||
390 | 395 | ||
391 | ipm = ip_map_lookup("nfsd", addr); | 396 | sn = net_generic(net, sunrpc_net_id); |
397 | ipm = ip_map_lookup(net, "nfsd", addr); | ||
392 | 398 | ||
393 | if (!ipm) | 399 | if (!ipm) |
394 | return NULL; | 400 | return NULL; |
395 | if (cache_check(&ip_map_cache, &ipm->h, NULL)) | 401 | if (cache_check(sn->ip_map_cache, &ipm->h, NULL)) |
396 | return NULL; | 402 | return NULL; |
397 | 403 | ||
398 | if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) { | 404 | if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) { |
@@ -403,22 +409,29 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr) | |||
403 | rv = &ipm->m_client->h; | 409 | rv = &ipm->m_client->h; |
404 | kref_get(&rv->ref); | 410 | kref_get(&rv->ref); |
405 | } | 411 | } |
406 | cache_put(&ipm->h, &ip_map_cache); | 412 | cache_put(&ipm->h, sn->ip_map_cache); |
407 | return rv; | 413 | return rv; |
408 | } | 414 | } |
409 | EXPORT_SYMBOL_GPL(auth_unix_lookup); | 415 | EXPORT_SYMBOL_GPL(auth_unix_lookup); |
410 | 416 | ||
411 | void svcauth_unix_purge(void) | 417 | void svcauth_unix_purge(void) |
412 | { | 418 | { |
413 | cache_purge(&ip_map_cache); | 419 | struct net *net; |
420 | |||
421 | for_each_net(net) { | ||
422 | struct sunrpc_net *sn; | ||
423 | |||
424 | sn = net_generic(net, sunrpc_net_id); | ||
425 | cache_purge(sn->ip_map_cache); | ||
426 | } | ||
414 | } | 427 | } |
415 | EXPORT_SYMBOL_GPL(svcauth_unix_purge); | 428 | EXPORT_SYMBOL_GPL(svcauth_unix_purge); |
416 | 429 | ||
417 | static inline struct ip_map * | 430 | static inline struct ip_map * |
418 | ip_map_cached_get(struct svc_rqst *rqstp) | 431 | ip_map_cached_get(struct svc_xprt *xprt) |
419 | { | 432 | { |
420 | struct ip_map *ipm = NULL; | 433 | struct ip_map *ipm = NULL; |
421 | struct svc_xprt *xprt = rqstp->rq_xprt; | 434 | struct sunrpc_net *sn; |
422 | 435 | ||
423 | if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) { | 436 | if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) { |
424 | spin_lock(&xprt->xpt_lock); | 437 | spin_lock(&xprt->xpt_lock); |
@@ -430,9 +443,10 @@ ip_map_cached_get(struct svc_rqst *rqstp) | |||
430 | * remembered, e.g. by a second mount from the | 443 | * remembered, e.g. by a second mount from the |
431 | * same IP address. | 444 | * same IP address. |
432 | */ | 445 | */ |
446 | sn = net_generic(xprt->xpt_net, sunrpc_net_id); | ||
433 | xprt->xpt_auth_cache = NULL; | 447 | xprt->xpt_auth_cache = NULL; |
434 | spin_unlock(&xprt->xpt_lock); | 448 | spin_unlock(&xprt->xpt_lock); |
435 | cache_put(&ipm->h, &ip_map_cache); | 449 | cache_put(&ipm->h, sn->ip_map_cache); |
436 | return NULL; | 450 | return NULL; |
437 | } | 451 | } |
438 | cache_get(&ipm->h); | 452 | cache_get(&ipm->h); |
@@ -443,10 +457,8 @@ ip_map_cached_get(struct svc_rqst *rqstp) | |||
443 | } | 457 | } |
444 | 458 | ||
445 | static inline void | 459 | static inline void |
446 | ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm) | 460 | ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm) |
447 | { | 461 | { |
448 | struct svc_xprt *xprt = rqstp->rq_xprt; | ||
449 | |||
450 | if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) { | 462 | if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) { |
451 | spin_lock(&xprt->xpt_lock); | 463 | spin_lock(&xprt->xpt_lock); |
452 | if (xprt->xpt_auth_cache == NULL) { | 464 | if (xprt->xpt_auth_cache == NULL) { |
@@ -456,15 +468,26 @@ ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm) | |||
456 | } | 468 | } |
457 | spin_unlock(&xprt->xpt_lock); | 469 | spin_unlock(&xprt->xpt_lock); |
458 | } | 470 | } |
459 | if (ipm) | 471 | if (ipm) { |
460 | cache_put(&ipm->h, &ip_map_cache); | 472 | struct sunrpc_net *sn; |
473 | |||
474 | sn = net_generic(xprt->xpt_net, sunrpc_net_id); | ||
475 | cache_put(&ipm->h, sn->ip_map_cache); | ||
476 | } | ||
461 | } | 477 | } |
462 | 478 | ||
463 | void | 479 | void |
464 | svcauth_unix_info_release(void *info) | 480 | svcauth_unix_info_release(struct svc_xprt *xpt) |
465 | { | 481 | { |
466 | struct ip_map *ipm = info; | 482 | struct ip_map *ipm; |
467 | cache_put(&ipm->h, &ip_map_cache); | 483 | |
484 | ipm = xpt->xpt_auth_cache; | ||
485 | if (ipm != NULL) { | ||
486 | struct sunrpc_net *sn; | ||
487 | |||
488 | sn = net_generic(xpt->xpt_net, sunrpc_net_id); | ||
489 | cache_put(&ipm->h, sn->ip_map_cache); | ||
490 | } | ||
468 | } | 491 | } |
469 | 492 | ||
470 | /**************************************************************************** | 493 | /**************************************************************************** |
@@ -674,6 +697,8 @@ static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp) | |||
674 | switch (ret) { | 697 | switch (ret) { |
675 | case -ENOENT: | 698 | case -ENOENT: |
676 | return ERR_PTR(-ENOENT); | 699 | return ERR_PTR(-ENOENT); |
700 | case -ETIMEDOUT: | ||
701 | return ERR_PTR(-ESHUTDOWN); | ||
677 | case 0: | 702 | case 0: |
678 | gi = get_group_info(ug->gi); | 703 | gi = get_group_info(ug->gi); |
679 | cache_put(&ug->h, &unix_gid_cache); | 704 | cache_put(&ug->h, &unix_gid_cache); |
@@ -691,6 +716,9 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) | |||
691 | struct ip_map *ipm; | 716 | struct ip_map *ipm; |
692 | struct group_info *gi; | 717 | struct group_info *gi; |
693 | struct svc_cred *cred = &rqstp->rq_cred; | 718 | struct svc_cred *cred = &rqstp->rq_cred; |
719 | struct svc_xprt *xprt = rqstp->rq_xprt; | ||
720 | struct net *net = xprt->xpt_net; | ||
721 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
694 | 722 | ||
695 | switch (rqstp->rq_addr.ss_family) { | 723 | switch (rqstp->rq_addr.ss_family) { |
696 | case AF_INET: | 724 | case AF_INET: |
@@ -709,26 +737,27 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) | |||
709 | if (rqstp->rq_proc == 0) | 737 | if (rqstp->rq_proc == 0) |
710 | return SVC_OK; | 738 | return SVC_OK; |
711 | 739 | ||
712 | ipm = ip_map_cached_get(rqstp); | 740 | ipm = ip_map_cached_get(xprt); |
713 | if (ipm == NULL) | 741 | if (ipm == NULL) |
714 | ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, | 742 | ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class, |
715 | &sin6->sin6_addr); | 743 | &sin6->sin6_addr); |
716 | 744 | ||
717 | if (ipm == NULL) | 745 | if (ipm == NULL) |
718 | return SVC_DENIED; | 746 | return SVC_DENIED; |
719 | 747 | ||
720 | switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) { | 748 | switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) { |
721 | default: | 749 | default: |
722 | BUG(); | 750 | BUG(); |
723 | case -EAGAIN: | ||
724 | case -ETIMEDOUT: | 751 | case -ETIMEDOUT: |
752 | return SVC_CLOSE; | ||
753 | case -EAGAIN: | ||
725 | return SVC_DROP; | 754 | return SVC_DROP; |
726 | case -ENOENT: | 755 | case -ENOENT: |
727 | return SVC_DENIED; | 756 | return SVC_DENIED; |
728 | case 0: | 757 | case 0: |
729 | rqstp->rq_client = &ipm->m_client->h; | 758 | rqstp->rq_client = &ipm->m_client->h; |
730 | kref_get(&rqstp->rq_client->ref); | 759 | kref_get(&rqstp->rq_client->ref); |
731 | ip_map_cached_put(rqstp, ipm); | 760 | ip_map_cached_put(xprt, ipm); |
732 | break; | 761 | break; |
733 | } | 762 | } |
734 | 763 | ||
@@ -736,6 +765,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) | |||
736 | switch (PTR_ERR(gi)) { | 765 | switch (PTR_ERR(gi)) { |
737 | case -EAGAIN: | 766 | case -EAGAIN: |
738 | return SVC_DROP; | 767 | return SVC_DROP; |
768 | case -ESHUTDOWN: | ||
769 | return SVC_CLOSE; | ||
739 | case -ENOENT: | 770 | case -ENOENT: |
740 | break; | 771 | break; |
741 | default: | 772 | default: |
@@ -776,7 +807,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) | |||
776 | cred->cr_gid = (gid_t) -1; | 807 | cred->cr_gid = (gid_t) -1; |
777 | cred->cr_group_info = groups_alloc(0); | 808 | cred->cr_group_info = groups_alloc(0); |
778 | if (cred->cr_group_info == NULL) | 809 | if (cred->cr_group_info == NULL) |
779 | return SVC_DROP; /* kmalloc failure - client must retry */ | 810 | return SVC_CLOSE; /* kmalloc failure - client must retry */ |
780 | 811 | ||
781 | /* Put NULL verifier */ | 812 | /* Put NULL verifier */ |
782 | svc_putnl(resv, RPC_AUTH_NULL); | 813 | svc_putnl(resv, RPC_AUTH_NULL); |
@@ -840,7 +871,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) | |||
840 | goto badcred; | 871 | goto badcred; |
841 | cred->cr_group_info = groups_alloc(slen); | 872 | cred->cr_group_info = groups_alloc(slen); |
842 | if (cred->cr_group_info == NULL) | 873 | if (cred->cr_group_info == NULL) |
843 | return SVC_DROP; | 874 | return SVC_CLOSE; |
844 | for (i = 0; i < slen; i++) | 875 | for (i = 0; i < slen; i++) |
845 | GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv); | 876 | GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv); |
846 | if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { | 877 | if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { |
@@ -886,3 +917,56 @@ struct auth_ops svcauth_unix = { | |||
886 | .set_client = svcauth_unix_set_client, | 917 | .set_client = svcauth_unix_set_client, |
887 | }; | 918 | }; |
888 | 919 | ||
920 | int ip_map_cache_create(struct net *net) | ||
921 | { | ||
922 | int err = -ENOMEM; | ||
923 | struct cache_detail *cd; | ||
924 | struct cache_head **tbl; | ||
925 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
926 | |||
927 | cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL); | ||
928 | if (cd == NULL) | ||
929 | goto err_cd; | ||
930 | |||
931 | tbl = kzalloc(IP_HASHMAX * sizeof(struct cache_head *), GFP_KERNEL); | ||
932 | if (tbl == NULL) | ||
933 | goto err_tbl; | ||
934 | |||
935 | cd->owner = THIS_MODULE, | ||
936 | cd->hash_size = IP_HASHMAX, | ||
937 | cd->hash_table = tbl, | ||
938 | cd->name = "auth.unix.ip", | ||
939 | cd->cache_put = ip_map_put, | ||
940 | cd->cache_upcall = ip_map_upcall, | ||
941 | cd->cache_parse = ip_map_parse, | ||
942 | cd->cache_show = ip_map_show, | ||
943 | cd->match = ip_map_match, | ||
944 | cd->init = ip_map_init, | ||
945 | cd->update = update, | ||
946 | cd->alloc = ip_map_alloc, | ||
947 | |||
948 | err = cache_register_net(cd, net); | ||
949 | if (err) | ||
950 | goto err_reg; | ||
951 | |||
952 | sn->ip_map_cache = cd; | ||
953 | return 0; | ||
954 | |||
955 | err_reg: | ||
956 | kfree(tbl); | ||
957 | err_tbl: | ||
958 | kfree(cd); | ||
959 | err_cd: | ||
960 | return err; | ||
961 | } | ||
962 | |||
963 | void ip_map_cache_destroy(struct net *net) | ||
964 | { | ||
965 | struct sunrpc_net *sn; | ||
966 | |||
967 | sn = net_generic(net, sunrpc_net_id); | ||
968 | cache_purge(sn->ip_map_cache); | ||
969 | cache_unregister_net(sn->ip_map_cache, net); | ||
970 | kfree(sn->ip_map_cache->hash_table); | ||
971 | kfree(sn->ip_map_cache); | ||
972 | } | ||