linux/fs/lockd/host.c
<<
>>
Prefs
   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_HOST_REBIND         (60 * HZ)
  26#define NLM_HOST_EXPIRE         (300 * HZ)
  27#define NLM_HOST_COLLECT        (120 * HZ)
  28
  29static struct hlist_head        nlm_hosts[NLM_HOST_NRHASH];
  30static unsigned long            next_gc;
  31static int                      nrhosts;
  32static DEFINE_MUTEX(nlm_host_mutex);
  33
  34static void                     nlm_gc_hosts(void);
  35static struct nsm_handle        *nsm_find(const struct sockaddr *sap,
  36                                                const size_t salen,
  37                                                const char *hostname,
  38                                                const size_t hostname_len,
  39                                                const int create);
  40
  41struct nlm_lookup_host_info {
  42        const int               server;         /* search for server|client */
  43        const struct sockaddr   *sap;           /* address to search for */
  44        const size_t            salen;          /* it's length */
  45        const unsigned short    protocol;       /* transport to search for*/
  46        const u32               version;        /* NLM version to search for */
  47        const char              *hostname;      /* remote's hostname */
  48        const size_t            hostname_len;   /* it's length */
  49        const struct sockaddr   *src_sap;       /* our address (optional) */
  50        const size_t            src_len;        /* it's length */
  51};
  52
  53/*
  54 * Hash function must work well on big- and little-endian platforms
  55 */
  56static unsigned int __nlm_hash32(const __be32 n)
  57{
  58        unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
  59        return hash ^ (hash >> 8);
  60}
  61
  62static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
  63{
  64        const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
  65        return __nlm_hash32(sin->sin_addr.s_addr);
  66}
  67
  68static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
  69{
  70        const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
  71        const struct in6_addr addr = sin6->sin6_addr;
  72        return __nlm_hash32(addr.s6_addr32[0]) ^
  73               __nlm_hash32(addr.s6_addr32[1]) ^
  74               __nlm_hash32(addr.s6_addr32[2]) ^
  75               __nlm_hash32(addr.s6_addr32[3]);
  76}
  77
  78static unsigned int nlm_hash_address(const struct sockaddr *sap)
  79{
  80        unsigned int hash;
  81
  82        switch (sap->sa_family) {
  83        case AF_INET:
  84                hash = __nlm_hash_addr4(sap);
  85                break;
  86        case AF_INET6:
  87                hash = __nlm_hash_addr6(sap);
  88                break;
  89        default:
  90                hash = 0;
  91        }
  92        return hash & (NLM_HOST_NRHASH - 1);
  93}
  94
  95static void nlm_clear_port(struct sockaddr *sap)
  96{
  97        switch (sap->sa_family) {
  98        case AF_INET:
  99                ((struct sockaddr_in *)sap)->sin_port = 0;
 100                break;
 101        case AF_INET6:
 102                ((struct sockaddr_in6 *)sap)->sin6_port = 0;
 103                break;
 104        }
 105}
 106
 107static void nlm_display_address(const struct sockaddr *sap,
 108                                char *buf, const size_t len)
 109{
 110        const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
 111        const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
 112
 113        switch (sap->sa_family) {
 114        case AF_UNSPEC:
 115                snprintf(buf, len, "unspecified");
 116                break;
 117        case AF_INET:
 118                snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
 119                break;
 120        case AF_INET6:
 121                if (ipv6_addr_v4mapped(&sin6->sin6_addr))
 122                        snprintf(buf, len, NIPQUAD_FMT,
 123                                 NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
 124                else
 125                        snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
 126                break;
 127        default:
 128                snprintf(buf, len, "unsupported address family");
 129                break;
 130        }
 131}
 132
 133/*
 134 * Common host lookup routine for server & client
 135 */
 136static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
 137{
 138        struct hlist_head *chain;
 139        struct hlist_node *pos;
 140        struct nlm_host *host;
 141        struct nsm_handle *nsm = NULL;
 142
 143        mutex_lock(&nlm_host_mutex);
 144
 145        if (time_after_eq(jiffies, next_gc))
 146                nlm_gc_hosts();
 147
 148        /* We may keep several nlm_host objects for a peer, because each
 149         * nlm_host is identified by
 150         * (address, protocol, version, server/client)
 151         * We could probably simplify this a little by putting all those
 152         * different NLM rpc_clients into one single nlm_host object.
 153         * This would allow us to have one nlm_host per address.
 154         */
 155        chain = &nlm_hosts[nlm_hash_address(ni->sap)];
 156        hlist_for_each_entry(host, pos, chain, h_hash) {
 157                if (!nlm_cmp_addr(nlm_addr(host), ni->sap))
 158                        continue;
 159
 160                /* See if we have an NSM handle for this client */
 161                if (!nsm)
 162                        nsm = host->h_nsmhandle;
 163
 164                if (host->h_proto != ni->protocol)
 165                        continue;
 166                if (host->h_version != ni->version)
 167                        continue;
 168                if (host->h_server != ni->server)
 169                        continue;
 170                if (ni->server &&
 171                    !nlm_cmp_addr(nlm_srcaddr(host), ni->src_sap))
 172                        continue;
 173
 174                /* Move to head of hash chain. */
 175                hlist_del(&host->h_hash);
 176                hlist_add_head(&host->h_hash, chain);
 177
 178                nlm_get_host(host);
 179                dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
 180                                host->h_name, host->h_addrbuf);
 181                goto out;
 182        }
 183
 184        /*
 185         * The host wasn't in our hash table.  If we don't
 186         * have an NSM handle for it yet, create one.
 187         */
 188        if (nsm)
 189                atomic_inc(&nsm->sm_count);
 190        else {
 191                host = NULL;
 192                nsm = nsm_find(ni->sap, ni->salen,
 193                                ni->hostname, ni->hostname_len, 1);
 194                if (!nsm) {
 195                        dprintk("lockd: nlm_lookup_host failed; "
 196                                "no nsm handle\n");
 197                        goto out;
 198                }
 199        }
 200
 201        host = kzalloc(sizeof(*host), GFP_KERNEL);
 202        if (!host) {
 203                nsm_release(nsm);
 204                dprintk("lockd: nlm_lookup_host failed; no memory\n");
 205                goto out;
 206        }
 207        host->h_name       = nsm->sm_name;
 208        memcpy(nlm_addr(host), ni->sap, ni->salen);
 209        host->h_addrlen = ni->salen;
 210        nlm_clear_port(nlm_addr(host));
 211        memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
 212        host->h_version    = ni->version;
 213        host->h_proto      = ni->protocol;
 214        host->h_rpcclnt    = NULL;
 215        mutex_init(&host->h_mutex);
 216        host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 217        host->h_expires    = jiffies + NLM_HOST_EXPIRE;
 218        atomic_set(&host->h_count, 1);
 219        init_waitqueue_head(&host->h_gracewait);
 220        init_rwsem(&host->h_rwsem);
 221        host->h_state      = 0;                 /* pseudo NSM state */
 222        host->h_nsmstate   = 0;                 /* real NSM state */
 223        host->h_nsmhandle  = nsm;
 224        host->h_server     = ni->server;
 225        hlist_add_head(&host->h_hash, chain);
 226        INIT_LIST_HEAD(&host->h_lockowners);
 227        spin_lock_init(&host->h_lock);
 228        INIT_LIST_HEAD(&host->h_granted);
 229        INIT_LIST_HEAD(&host->h_reclaim);
 230
 231        nrhosts++;
 232
 233        nlm_display_address((struct sockaddr *)&host->h_addr,
 234                                host->h_addrbuf, sizeof(host->h_addrbuf));
 235        nlm_display_address((struct sockaddr *)&host->h_srcaddr,
 236                                host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf));
 237
 238        dprintk("lockd: nlm_lookup_host created host %s\n",
 239                        host->h_name);
 240
 241out:
 242        mutex_unlock(&nlm_host_mutex);
 243        return host;
 244}
 245
 246/*
 247 * Destroy a host
 248 */
 249static void
 250nlm_destroy_host(struct nlm_host *host)
 251{
 252        struct rpc_clnt *clnt;
 253
 254        BUG_ON(!list_empty(&host->h_lockowners));
 255        BUG_ON(atomic_read(&host->h_count));
 256
 257        /*
 258         * Release NSM handle and unmonitor host.
 259         */
 260        nsm_unmonitor(host);
 261
 262        clnt = host->h_rpcclnt;
 263        if (clnt != NULL)
 264                rpc_shutdown_client(clnt);
 265        kfree(host);
 266}
 267
 268/**
 269 * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
 270 * @sap: network address of server
 271 * @salen: length of server address
 272 * @protocol: transport protocol to use
 273 * @version: NLM protocol version
 274 * @hostname: '\0'-terminated hostname of server
 275 *
 276 * Returns an nlm_host structure that matches the passed-in
 277 * [server address, transport protocol, NLM version, server hostname].
 278 * If one doesn't already exist in the host cache, a new handle is
 279 * created and returned.
 280 */
 281struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
 282                                     const size_t salen,
 283                                     const unsigned short protocol,
 284                                     const u32 version, const char *hostname)
 285{
 286        const struct sockaddr source = {
 287                .sa_family      = AF_UNSPEC,
 288        };
 289        struct nlm_lookup_host_info ni = {
 290                .server         = 0,
 291                .sap            = sap,
 292                .salen          = salen,
 293                .protocol       = protocol,
 294                .version        = version,
 295                .hostname       = hostname,
 296                .hostname_len   = strlen(hostname),
 297                .src_sap        = &source,
 298                .src_len        = sizeof(source),
 299        };
 300
 301        dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
 302                        (hostname ? hostname : "<none>"), version,
 303                        (protocol == IPPROTO_UDP ? "udp" : "tcp"));
 304
 305        return nlm_lookup_host(&ni);
 306}
 307
 308/**
 309 * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
 310 * @rqstp: incoming NLM request
 311 * @hostname: name of client host
 312 * @hostname_len: length of client hostname
 313 *
 314 * Returns an nlm_host structure that matches the [client address,
 315 * transport protocol, NLM version, client hostname] of the passed-in
 316 * NLM request.  If one doesn't already exist in the host cache, a
 317 * new handle is created and returned.
 318 *
 319 * Before possibly creating a new nlm_host, construct a sockaddr
 320 * for a specific source address in case the local system has
 321 * multiple network addresses.  The family of the address in
 322 * rq_daddr is guaranteed to be the same as the family of the
 323 * address in rq_addr, so it's safe to use the same family for
 324 * the source address.
 325 */
 326struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
 327                                    const char *hostname,
 328                                    const size_t hostname_len)
 329{
 330        struct sockaddr_in sin = {
 331                .sin_family     = AF_INET,
 332        };
 333        struct sockaddr_in6 sin6 = {
 334                .sin6_family    = AF_INET6,
 335        };
 336        struct nlm_lookup_host_info ni = {
 337                .server         = 1,
 338                .sap            = svc_addr(rqstp),
 339                .salen          = rqstp->rq_addrlen,
 340                .protocol       = rqstp->rq_prot,
 341                .version        = rqstp->rq_vers,
 342                .hostname       = hostname,
 343                .hostname_len   = hostname_len,
 344                .src_len        = rqstp->rq_addrlen,
 345        };
 346
 347        dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
 348                        (int)hostname_len, hostname, rqstp->rq_vers,
 349                        (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
 350
 351        switch (ni.sap->sa_family) {
 352        case AF_INET:
 353                sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
 354                ni.src_sap = (struct sockaddr *)&sin;
 355                break;
 356        case AF_INET6:
 357                ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
 358                ni.src_sap = (struct sockaddr *)&sin6;
 359                break;
 360        default:
 361                return NULL;
 362        }
 363
 364        return nlm_lookup_host(&ni);
 365}
 366
 367/*
 368 * Create the NLM RPC client for an NLM peer
 369 */
 370struct rpc_clnt *
 371nlm_bind_host(struct nlm_host *host)
 372{
 373        struct rpc_clnt *clnt;
 374
 375        dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n",
 376                        host->h_name, host->h_addrbuf, host->h_srcaddrbuf);
 377
 378        /* Lock host handle */
 379        mutex_lock(&host->h_mutex);
 380
 381        /* If we've already created an RPC client, check whether
 382         * RPC rebind is required
 383         */
 384        if ((clnt = host->h_rpcclnt) != NULL) {
 385                if (time_after_eq(jiffies, host->h_nextrebind)) {
 386                        rpc_force_rebind(clnt);
 387                        host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 388                        dprintk("lockd: next rebind in %lu jiffies\n",
 389                                        host->h_nextrebind - jiffies);
 390                }
 391        } else {
 392                unsigned long increment = nlmsvc_timeout;
 393                struct rpc_timeout timeparms = {
 394                        .to_initval     = increment,
 395                        .to_increment   = increment,
 396                        .to_maxval      = increment * 6UL,
 397                        .to_retries     = 5U,
 398                };
 399                struct rpc_create_args args = {
 400                        .protocol       = host->h_proto,
 401                        .address        = nlm_addr(host),
 402                        .addrsize       = host->h_addrlen,
 403                        .saddress       = nlm_srcaddr(host),
 404                        .timeout        = &timeparms,
 405                        .servername     = host->h_name,
 406                        .program        = &nlm_program,
 407                        .version        = host->h_version,
 408                        .authflavor     = RPC_AUTH_UNIX,
 409                        .flags          = (RPC_CLNT_CREATE_NOPING |
 410                                           RPC_CLNT_CREATE_AUTOBIND),
 411                };
 412
 413                /*
 414                 * lockd retries server side blocks automatically so we want
 415                 * those to be soft RPC calls. Client side calls need to be
 416                 * hard RPC tasks.
 417                 */
 418                if (!host->h_server)
 419                        args.flags |= RPC_CLNT_CREATE_HARDRTRY;
 420
 421                clnt = rpc_create(&args);
 422                if (!IS_ERR(clnt))
 423                        host->h_rpcclnt = clnt;
 424                else {
 425                        printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
 426                        clnt = NULL;
 427                }
 428        }
 429
 430        mutex_unlock(&host->h_mutex);
 431        return clnt;
 432}
 433
 434/*
 435 * Force a portmap lookup of the remote lockd port
 436 */
 437void
 438nlm_rebind_host(struct nlm_host *host)
 439{
 440        dprintk("lockd: rebind host %s\n", host->h_name);
 441        if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
 442                rpc_force_rebind(host->h_rpcclnt);
 443                host->h_nextrebind = jiffies + NLM_HOST_REBIND;
 444        }
 445}
 446
 447/*
 448 * Increment NLM host count
 449 */
 450struct nlm_host * nlm_get_host(struct nlm_host *host)
 451{
 452        if (host) {
 453                dprintk("lockd: get host %s\n", host->h_name);
 454                atomic_inc(&host->h_count);
 455                host->h_expires = jiffies + NLM_HOST_EXPIRE;
 456        }
 457        return host;
 458}
 459
 460/*
 461 * Release NLM host after use
 462 */
 463void nlm_release_host(struct nlm_host *host)
 464{
 465        if (host != NULL) {
 466                dprintk("lockd: release host %s\n", host->h_name);
 467                BUG_ON(atomic_read(&host->h_count) < 0);
 468                if (atomic_dec_and_test(&host->h_count)) {
 469                        BUG_ON(!list_empty(&host->h_lockowners));
 470                        BUG_ON(!list_empty(&host->h_granted));
 471                        BUG_ON(!list_empty(&host->h_reclaim));
 472                }
 473        }
 474}
 475
 476/*
 477 * We were notified that the host indicated by address &sin
 478 * has rebooted.
 479 * Release all resources held by that peer.
 480 */
 481void nlm_host_rebooted(const struct sockaddr_in *sin,
 482                                const char *hostname,
 483                                unsigned int hostname_len,
 484                                u32 new_state)
 485{
 486        struct hlist_head *chain;
 487        struct hlist_node *pos;
 488        struct nsm_handle *nsm;
 489        struct nlm_host *host;
 490
 491        nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin),
 492                        hostname, hostname_len, 0);
 493        if (nsm == NULL) {
 494                dprintk("lockd: never saw rebooted peer '%.*s' before\n",
 495                                hostname_len, hostname);
 496                return;
 497        }
 498
 499        dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
 500                        hostname_len, hostname, nsm->sm_addrbuf);
 501
 502        /* When reclaiming locks on this peer, make sure that
 503         * we set up a new notification */
 504        nsm->sm_monitored = 0;
 505
 506        /* Mark all hosts tied to this NSM state as having rebooted.
 507         * We run the loop repeatedly, because we drop the host table
 508         * lock for this.
 509         * To avoid processing a host several times, we match the nsmstate.
 510         */
 511again:  mutex_lock(&nlm_host_mutex);
 512        for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
 513                hlist_for_each_entry(host, pos, chain, h_hash) {
 514                        if (host->h_nsmhandle == nsm
 515                         && host->h_nsmstate != new_state) {
 516                                host->h_nsmstate = new_state;
 517                                host->h_state++;
 518
 519                                nlm_get_host(host);
 520                                mutex_unlock(&nlm_host_mutex);
 521
 522                                if (host->h_server) {
 523                                        /* We're server for this guy, just ditch
 524                                         * all the locks he held. */
 525                                        nlmsvc_free_host_resources(host);
 526                                } else {
 527                                        /* He's the server, initiate lock recovery. */
 528                                        nlmclnt_recovery(host);
 529                                }
 530
 531                                nlm_release_host(host);
 532                                goto again;
 533                        }
 534                }
 535        }
 536
 537        mutex_unlock(&nlm_host_mutex);
 538}
 539
 540/*
 541 * Shut down the hosts module.
 542 * Note that this routine is called only at server shutdown time.
 543 */
 544void
 545nlm_shutdown_hosts(void)
 546{
 547        struct hlist_head *chain;
 548        struct hlist_node *pos;
 549        struct nlm_host *host;
 550
 551        dprintk("lockd: shutting down host module\n");
 552        mutex_lock(&nlm_host_mutex);
 553
 554        /* First, make all hosts eligible for gc */
 555        dprintk("lockd: nuking all hosts...\n");
 556        for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
 557                hlist_for_each_entry(host, pos, chain, h_hash) {
 558                        host->h_expires = jiffies - 1;
 559                        if (host->h_rpcclnt) {
 560                                rpc_shutdown_client(host->h_rpcclnt);
 561                                host->h_rpcclnt = NULL;
 562                        }
 563                }
 564        }
 565
 566        /* Then, perform a garbage collection pass */
 567        nlm_gc_hosts();
 568        mutex_unlock(&nlm_host_mutex);
 569
 570        /* complain if any hosts are left */
 571        if (nrhosts) {
 572                printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
 573                dprintk("lockd: %d hosts left:\n", nrhosts);
 574                for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
 575                        hlist_for_each_entry(host, pos, chain, h_hash) {
 576                                dprintk("       %s (cnt %d use %d exp %ld)\n",
 577                                        host->h_name, atomic_read(&host->h_count),
 578                                        host->h_inuse, host->h_expires);
 579                        }
 580                }
 581        }
 582}
 583
 584/*
 585 * Garbage collect any unused NLM hosts.
 586 * This GC combines reference counting for async operations with
 587 * mark & sweep for resources held by remote clients.
 588 */
 589static void
 590nlm_gc_hosts(void)
 591{
 592        struct hlist_head *chain;
 593        struct hlist_node *pos, *next;
 594        struct nlm_host *host;
 595
 596        dprintk("lockd: host garbage collection\n");
 597        for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
 598                hlist_for_each_entry(host, pos, chain, h_hash)
 599                        host->h_inuse = 0;
 600        }
 601
 602        /* Mark all hosts that hold locks, blocks or shares */
 603        nlmsvc_mark_resources();
 604
 605        for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
 606                hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
 607                        if (atomic_read(&host->h_count) || host->h_inuse
 608                         || time_before(jiffies, host->h_expires)) {
 609                                dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
 610                                        host->h_name, atomic_read(&host->h_count),
 611                                        host->h_inuse, host->h_expires);
 612                                continue;
 613                        }
 614                        dprintk("lockd: delete host %s\n", host->h_name);
 615                        hlist_del_init(&host->h_hash);
 616
 617                        nlm_destroy_host(host);
 618                        nrhosts--;
 619                }
 620        }
 621
 622        next_gc = jiffies + NLM_HOST_COLLECT;
 623}
 624
 625
 626/*
 627 * Manage NSM handles
 628 */
 629static LIST_HEAD(nsm_handles);
 630static DEFINE_SPINLOCK(nsm_lock);
 631
 632static struct nsm_handle *nsm_find(const struct sockaddr *sap,
 633                                   const size_t salen,
 634                                   const char *hostname,
 635                                   const size_t hostname_len,
 636                                   const int create)
 637{
 638        struct nsm_handle *nsm = NULL;
 639        struct nsm_handle *pos;
 640
 641        if (!sap)
 642                return NULL;
 643
 644        if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
 645                if (printk_ratelimit()) {
 646                        printk(KERN_WARNING "Invalid hostname \"%.*s\" "
 647                                            "in NFS lock request\n",
 648                                (int)hostname_len, hostname);
 649                }
 650                return NULL;
 651        }
 652
 653retry:
 654        spin_lock(&nsm_lock);
 655        list_for_each_entry(pos, &nsm_handles, sm_link) {
 656
 657                if (hostname && nsm_use_hostnames) {
 658                        if (strlen(pos->sm_name) != hostname_len
 659                         || memcmp(pos->sm_name, hostname, hostname_len))
 660                                continue;
 661                } else if (!nlm_cmp_addr(nsm_addr(pos), sap))
 662                        continue;
 663                atomic_inc(&pos->sm_count);
 664                kfree(nsm);
 665                nsm = pos;
 666                goto found;
 667        }
 668        if (nsm) {
 669                list_add(&nsm->sm_link, &nsm_handles);
 670                goto found;
 671        }
 672        spin_unlock(&nsm_lock);
 673
 674        if (!create)
 675                return NULL;
 676
 677        nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
 678        if (nsm == NULL)
 679                return NULL;
 680
 681        memcpy(nsm_addr(nsm), sap, salen);
 682        nsm->sm_addrlen = salen;
 683        nsm->sm_name = (char *) (nsm + 1);
 684        memcpy(nsm->sm_name, hostname, hostname_len);
 685        nsm->sm_name[hostname_len] = '\0';
 686        nlm_display_address((struct sockaddr *)&nsm->sm_addr,
 687                                nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
 688        atomic_set(&nsm->sm_count, 1);
 689        goto retry;
 690
 691found:
 692        spin_unlock(&nsm_lock);
 693        return nsm;
 694}
 695
 696/*
 697 * Release an NSM handle
 698 */
 699void
 700nsm_release(struct nsm_handle *nsm)
 701{
 702        if (!nsm)
 703                return;
 704        if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
 705                list_del(&nsm->sm_link);
 706                spin_unlock(&nsm_lock);
 707                kfree(nsm);
 708        }
 709}
 710
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.