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