/* * Reclaim all locks on server host. We do this by spawning a separate * reclaimer thread. * FIXME: should bump MOD_USE_COUNT while reclaiming */ void nlmclnt_recovery(struct nlm_host *host, u32 newstate) { if (!host->h_reclaiming++) { if (host->h_nsmstate == newstate) return; printk(KERN_WARNING "lockd: Uh-oh! Interfering reclaims for host %s", host->h_name); host->h_monitored = 0; host->h_nsmstate = newstate; host->h_state++; nlm_release_host(host); } else { host->h_monitored = 0; host->h_nsmstate = newstate; host->h_state++; nlm_get_host(host); kernel_thread(reclaimer, host, 0); } }
struct nlm_host * nlm_find_client(void) { /* find a nlm_host for a client for which h_killed == 0. * and return it */ int hash; down(&nlm_host_sema); for (hash = 0 ; hash < NLM_HOST_NRHASH; hash++) { struct nlm_host *host, **hp; for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { if (host->h_server && host->h_killed == 0) { nlm_get_host(host); up(&nlm_host_sema); return host; } } } up(&nlm_host_sema); return NULL; }
/* * Common host lookup routine for server & client */ struct nlm_host * nlm_lookup_host(int server, struct sockaddr_in *sin, int proto, int version) { struct nlm_host *host, **hp; u32 addr; int hash; dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n", (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version); hash = NLM_ADDRHASH(sin->sin_addr.s_addr); /* Lock hash table */ down(&nlm_host_sema); if (time_after_eq(jiffies, next_gc)) nlm_gc_hosts(); for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { if (host->h_proto != proto) continue; if (host->h_version != version) continue; if (host->h_server != server) continue; if (nlm_cmp_addr(&host->h_addr, sin)) { if (hp != nlm_hosts + hash) { *hp = host->h_next; host->h_next = nlm_hosts[hash]; nlm_hosts[hash] = host; } nlm_get_host(host); up(&nlm_host_sema); return host; } } /* Ooops, no host found, create it */ dprintk("lockd: creating host entry\n"); if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL))) goto nohost; memset(host, 0, sizeof(*host)); addr = sin->sin_addr.s_addr; sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr)); host->h_addr = *sin; host->h_addr.sin_port = 0; /* ouch! */ host->h_version = version; host->h_proto = proto; host->h_authflavor = RPC_AUTH_UNIX; host->h_rpcclnt = NULL; init_MUTEX(&host->h_sema); host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; atomic_set(&host->h_count, 1); init_waitqueue_head(&host->h_gracewait); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_server = server; host->h_next = nlm_hosts[hash]; nlm_hosts[hash] = host; INIT_LIST_HEAD(&host->h_lockowners); spin_lock_init(&host->h_lock); if (++nrhosts > NLM_HOST_MAX) next_gc = 0; nohost: up(&nlm_host_sema); return host; }
/* * Try to claim a lock that was previously blocked. * * Note that we use both the RPC_GRANTED_MSG call _and_ an async * RPC thread when notifying the client. This seems like overkill... * Here's why: * - we don't want to use a synchronous RPC thread, otherwise * we might find ourselves hanging on a dead portmapper. * - Some lockd implementations (e.g. HP) don't react to * RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls. */ static void nlmsvc_grant_blocked(struct nlm_block *block) { struct nlm_file *file = block->b_file; struct nlm_lock *lock = &block->b_call.a_args.lock; struct file_lock *conflock; int error; dprintk("lockd: grant blocked lock %p\n", block); /* First thing is lock the file */ down(&file->f_sema); /* Unlink block request from list */ nlmsvc_remove_block(block); /* If b_granted is true this means we've been here before. * Just retry the grant callback, possibly refreshing the RPC * binding */ if (block->b_granted) { nlm_rebind_host(block->b_call.a_host); goto callback; } /* Try the lock operation again */ if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { /* Bummer, we blocked again */ dprintk("lockd: lock still blocked\n"); nlmsvc_insert_block(block, NLM_NEVER); posix_block_lock(conflock, &lock->fl); up(&file->f_sema); return; } /* Alright, no conflicting lock. Now lock it for real. If the * following yields an error, this is most probably due to low * memory. Retry the lock in a few seconds. */ if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) { printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", -error, __FUNCTION__); nlmsvc_insert_block(block, 10 * HZ); up(&file->f_sema); return; } callback: /* Lock was granted by VFS. */ dprintk("lockd: GRANTing blocked lock.\n"); block->b_granted = 1; block->b_incall = 1; /* Schedule next grant callback in 30 seconds */ nlmsvc_insert_block(block, 30 * HZ); /* Call the client */ nlm_get_host(block->b_call.a_host); if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, nlmsvc_grant_callback) < 0) nlm_release_host(block->b_call.a_host); up(&file->f_sema); }
static void nlmclnt_insert_lock_callback(struct file_lock *fl) { nlm_get_host(fl->fl_u.nfs_fl.host); }
/* * Generic NLM call, async version. */ int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; struct rpc_message msg = { .rpc_argp = &req->a_args, .rpc_resp = &req->a_res, }; int status; dprintk("lockd: call procedure %d on %s (async)\n", (int)proc, host->h_name); /* If we have no RPC client yet, create one. */ if ((clnt = nlm_bind_host(host)) == NULL) return -ENOLCK; msg.rpc_proc = &clnt->cl_procinfo[proc]; /* bootstrap and kick off the async RPC call */ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); return status; } int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; struct file *file = argp->lock.fl.fl_file; struct rpc_message msg = { .rpc_argp = argp, .rpc_resp = resp, }; int status; dprintk("lockd: call procedure %d on %s (async)\n", (int)proc, host->h_name); /* If we have no RPC client yet, create one. */ if ((clnt = nlm_bind_host(host)) == NULL) return -ENOLCK; msg.rpc_proc = &clnt->cl_procinfo[proc]; /* bootstrap and kick off the async RPC call */ if (file) msg.rpc_cred = nfs_file_cred(file); /* Increment host refcount */ nlm_get_host(host); status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); if (status < 0) nlm_release_host(host); return status; } /* * TEST for the presence of a conflicting lock */ static int nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) { int status; if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0) return status; status = req->a_res.status; if (status == NLM_LCK_GRANTED) { fl->fl_type = F_UNLCK; } if (status == NLM_LCK_DENIED) { /* * Report the conflicting lock back to the application. * FIXME: Is it OK to report the pid back as well? */ locks_copy_lock(fl, &req->a_res.lock.fl); /* fl->fl_pid = 0; */ } else { return nlm_stat_to_errno(req->a_res.status); } return 0; }