/* * Create a block and initialize it. * * Note: we explicitly set the cookie of the grant reply to that of * the blocked lock request. The spec explicitly mentions that the client * should _not_ rely on the callback containing the same cookie as the * request, but (as I found out later) that's because some implementations * do just this. Never mind the standards comittees, they support our * logging industries. */ static inline struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lock *lock, struct nlm_cookie *cookie) { struct nlm_block *block; struct nlm_host *host; struct nlm_rqst *call; /* Create host handle for callback */ host = nlmclnt_lookup_host(&rqstp->rq_addr, rqstp->rq_prot, rqstp->rq_vers); if (host == NULL) return NULL; /* Allocate memory for block, and initialize arguments */ if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL))) goto failed; memset(block, 0, sizeof(*block)); locks_init_lock(&block->b_call.a_args.lock.fl); locks_init_lock(&block->b_call.a_res.lock.fl); block->b_host = nlmsvc_lookup_host(rqstp); if (block->b_host == NULL) { goto failed_free; } if (!nlmclnt_setgrantargs(&block->b_call, lock)) goto failed_free; /* Set notifier function for VFS, and init args */ block->b_call.a_args.lock.fl.fl_notify = nlmsvc_notify_blocked; block->b_call.a_args.cookie = *cookie; /* see above */ dprintk("lockd: created block %p...\n", block); /* Create and initialize the block */ block->b_daemon = rqstp->rq_server; block->b_file = file; /* Add to file's list of blocks */ block->b_fnext = file->f_blocks; file->f_blocks = block; /* Set up RPC arguments for callback */ call = &block->b_call; call->a_host = host; call->a_flags = RPC_TASK_ASYNC; return block; failed_free: kfree(block); failed: nlm_release_host(host); return NULL; }
static __be32 * nlm_decode_lock(__be32 *p, struct nlm_lock *lock) { struct file_lock *fl = &lock->fl; s32 start, len, end; if (!(p = xdr_decode_string_inplace(p, &lock->caller, &lock->len, NLM_MAXSTRLEN)) || !(p = nlm_decode_fh(p, &lock->fh)) || !(p = nlm_decode_oh(p, &lock->oh))) return NULL; lock->svid = ntohl(*p++); locks_init_lock(fl); fl->fl_owner = current->files; fl->fl_pid = (pid_t)lock->svid; fl->fl_flags = FL_POSIX; fl->fl_type = F_RDLCK; /* as good as anything else */ start = ntohl(*p++); len = ntohl(*p++); end = start + len - 1; fl->fl_start = s32_to_loff_t(start); if (len == 0 || end < 0) fl->fl_end = OFFSET_MAX; else fl->fl_end = s32_to_loff_t(end); return p; }
static int nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) { if (!(p = nlm4_decode_cookie(p, &resp->cookie))) return -EIO; resp->status = *p++; if (resp->status == nlm_lck_denied) { struct file_lock *fl = &resp->lock.fl; u32 excl; __u64 start, len; __s64 end; memset(&resp->lock, 0, sizeof(resp->lock)); locks_init_lock(fl); excl = ntohl(*p++); resp->lock.svid = ntohl(*p++); fl->fl_pid = (pid_t)resp->lock.svid; if (!(p = nlm4_decode_oh(p, &resp->lock.oh))) return -EIO; fl->fl_flags = FL_POSIX; fl->fl_type = excl? F_WRLCK : F_RDLCK; p = xdr_decode_hyper(p, &start); p = xdr_decode_hyper(p, &len); end = start + len - 1; fl->fl_start = s64_to_loff_t(start); if (len == 0 || end < 0) fl->fl_end = OFFSET_MAX; else fl->fl_end = s64_to_loff_t(end); } return 0; }
/* * Initialises the fields of the file lock which are invariant for * free file_locks. */ static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags) { struct file_lock *lock = (struct file_lock *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) != SLAB_CTOR_CONSTRUCTOR) return; locks_init_lock(lock); }
/* * Allocate an NLM RPC call struct */ struct nlm_rqst * nlmclnt_alloc_call(void) { struct nlm_rqst *call; while (!signalled()) { call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL); if (call) { memset(call, 0, sizeof(*call)); locks_init_lock(&call->a_args.lock.fl); locks_init_lock(&call->a_res.lock.fl); return call; } printk("nlmclnt_alloc_call: failed, waiting for memory\n"); current->state = TASK_INTERRUPTIBLE; schedule_timeout(5*HZ); } return NULL; }
/* * RECLAIM: Try to reclaim a lock */ int nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) { struct nlm_rqst reqst, *req; int status; req = &reqst; memset(req, 0, sizeof(*req)); locks_init_lock(&req->a_args.lock.fl); locks_init_lock(&req->a_res.lock.fl); req->a_host = host; req->a_flags = 0; /* Set up the argument struct */ nlmclnt_setlockargs(req, fl); req->a_args.reclaim = 1; if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0 && req->a_res.status == NLM_LCK_GRANTED) return 0; printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d " "(errno %d, status %d)\n", fl->fl_pid, status, req->a_res.status); /* * FIXME: This is a serious failure. We can * * a. Ignore the problem * b. Send the owning process some signal (Linux doesn't have * SIGLOST, though...) * c. Retry the operation * * Until someone comes up with a simple implementation * for b or c, I'll choose option a. */ return -ENOLCK; }
static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) { struct nlm_lock *lock = &result->lock; struct file_lock *fl = &lock->fl; u64 l_offset, l_len; u32 exclusive; int error; __be32 *p; s32 end; memset(lock, 0, sizeof(*lock)); locks_init_lock(fl); p = xdr_inline_decode(xdr, 4 + 4); if (unlikely(p == NULL)) goto out_overflow; exclusive = be32_to_cpup(p++); lock->svid = be32_to_cpup(p); fl->fl_pid = (pid_t)lock->svid; error = decode_netobj(xdr, &lock->oh); if (unlikely(error)) goto out; p = xdr_inline_decode(xdr, 8 + 8); if (unlikely(p == NULL)) goto out_overflow; fl->fl_flags = FL_POSIX; fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK; p = xdr_decode_hyper(p, &l_offset); xdr_decode_hyper(p, &l_len); end = l_offset + l_len - 1; fl->fl_start = (loff_t)l_offset; if (l_len == 0 || end < 0) fl->fl_end = OFFSET_MAX; else fl->fl_end = (loff_t)end; error = 0; out: return error; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; }
int nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) { struct nlm_lock *lock = &argp->lock; memset(lock, 0, sizeof(*lock)); locks_init_lock(&lock->fl); lock->fl.fl_pid = ~(u32) 0; if (!(p = nlm4_decode_cookie(p, &argp->cookie)) || !(p = xdr_decode_string_inplace(p, &lock->caller, &lock->len, NLM_MAXSTRLEN)) || !(p = nlm4_decode_fh(p, &lock->fh)) || !(p = nlm4_decode_oh(p, &lock->oh))) return 0; argp->fsm_mode = ntohl(*p++); argp->fsm_access = ntohl(*p++); return xdr_argsize_check(rqstp, p); }
static int posix_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; struct file_lock *new_fl, *new_fl2; struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; int error; int added = 0; /* * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ new_fl = locks_empty_lock(); new_fl2 = locks_empty_lock(); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; if (caller->fl_type != F_UNLCK) { repeat: error = -EBUSY; if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_FLOCK)) goto out; while (fl != NULL) { if (!posix_locks_conflict(caller, fl)) { fl = fl->fl_next; continue; } error = -EAGAIN; if (!wait) goto out; error = -EDEADLK; if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner)) goto out; error = -ERESTARTSYS; if (current->signal & ~current->blocked) goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); locks_delete_block(fl, caller); goto repeat; } } /* * We've allocated the new locks in advance, so there are no * errors possible (and no blocking operations) from here on. * * Find the first old lock with the same owner as the new lock. */ before = &filp->f_inode->i_flock; error = -EBUSY; if ((*before != NULL) && ((*before)->fl_flags & FL_FLOCK)) goto out; /* First skip locks owned by other processes. */ while ((fl = *before) && (caller->fl_owner != fl->fl_owner)) { before = &fl->fl_next; } /* Process locks with this owner. */ while ((fl = *before) && (caller->fl_owner == fl->fl_owner)) { /* Detect adjacent or overlapping regions (if same lock type) */ if (caller->fl_type == fl->fl_type) { if (fl->fl_end < caller->fl_start - 1) goto next_lock; /* If the next lock in the list has entirely bigger * addresses than the new one, insert the lock here. */ if (fl->fl_start > caller->fl_end + 1) break; /* If we come here, the new and old lock are of the * same type and adjacent or overlapping. Make one * lock yielding from the lower start address of both * locks to the higher end address. */ if (fl->fl_start > caller->fl_start) fl->fl_start = caller->fl_start; else caller->fl_start = fl->fl_start; if (fl->fl_end < caller->fl_end) fl->fl_end = caller->fl_end; else caller->fl_end = fl->fl_end; if (added) { locks_delete_lock(before, 0); continue; } caller = fl; added = 1; } else { /* Processing for different lock types is a bit * more complex. */ if (fl->fl_end < caller->fl_start) goto next_lock; if (fl->fl_start > caller->fl_end) break; if (caller->fl_type == F_UNLCK) added = 1; if (fl->fl_start < caller->fl_start) left = fl; /* If the next lock in the list has a higher end * address than the new one, insert the new one here. */ if (fl->fl_end > caller->fl_end) { right = fl; break; } if (fl->fl_start >= caller->fl_start) { /* The new lock completely replaces an old * one (This may happen several times). */ if (added) { locks_delete_lock(before, 0); continue; } /* Replace the old lock with the new one. * Wake up anybody waiting for the old one, * as the change in lock type might satisfy * their needs. */ locks_wake_up_blocks(fl, 0); fl->fl_start = caller->fl_start; fl->fl_end = caller->fl_end; fl->fl_type = caller->fl_type; caller = fl; added = 1; } } /* Go on to next lock. */ next_lock: before = &fl->fl_next; } error = 0; if (!added) { if (caller->fl_type == F_UNLCK) goto out; locks_init_lock(new_fl, caller); locks_insert_lock(before, new_fl); new_fl = NULL; } if (right) { if (left == right) { /* The new lock breaks the old one in two pieces, * so we have to use the second new lock (in this * case, even F_UNLCK may fail!). */ left = locks_init_lock(new_fl2, right); locks_insert_lock(before, left); new_fl2 = NULL; } right->fl_start = caller->fl_end + 1; locks_wake_up_blocks(right, 0); } if (left) { left->fl_end = caller->fl_start - 1; locks_wake_up_blocks(left, 0); } out: /* * Free any unused locks. (They haven't * ever been used, so we use kfree().) */ if (new_fl) kfree(new_fl); if (new_fl2) kfree(new_fl2); return error; }
/* Allocate a new lock, and initialize its fields from fl. * The lock is not inserted into any lists until locks_insert_lock() or * locks_insert_block() are called. */ static inline struct file_lock *locks_alloc_lock(struct file_lock *fl) { return locks_init_lock(locks_empty_lock(), fl); }
int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file, int cmd, struct file_lock *fl) { struct dlm_ls *ls; struct plock_op *op; struct plock_xop *xop; int rv; ls = dlm_find_lockspace_local(lockspace); if (!ls) return -EINVAL; xop = kzalloc(sizeof(*xop), GFP_KERNEL); if (!xop) { rv = -ENOMEM; goto out; } op = &xop->xop; op->info.optype = DLM_PLOCK_OP_LOCK; op->info.pid = fl->fl_pid; op->info.ex = (fl->fl_type == F_WRLCK); op->info.wait = IS_SETLKW(cmd); op->info.fsid = ls->ls_global_id; op->info.number = number; op->info.start = fl->fl_start; op->info.end = fl->fl_end; if (fl->fl_lmops && fl->fl_lmops->fl_grant) { /* fl_owner is lockd which doesn't distinguish processes on the nfs client */ op->info.owner = (__u64) fl->fl_pid; xop->callback = fl->fl_lmops->fl_grant; locks_init_lock(&xop->flc); locks_copy_lock(&xop->flc, fl); xop->fl = fl; xop->file = file; } else { op->info.owner = (__u64)(long) fl->fl_owner; xop->callback = NULL; } send_op(op); if (xop->callback == NULL) wait_event(recv_wq, (op->done != 0)); else { rv = FILE_LOCK_DEFERRED; goto out; } spin_lock(&ops_lock); if (!list_empty(&op->list)) { log_error(ls, "dlm_posix_lock: op on list %llx", (unsigned long long)number); list_del(&op->list); } spin_unlock(&ops_lock); rv = op->info.rv; if (!rv) { if (posix_lock_file_wait(file, fl) < 0) log_error(ls, "dlm_posix_lock: vfs lock error %llx", (unsigned long long)number); } kfree(xop); out: dlm_put_lockspace(ls); return rv; }
int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file, struct file_lock *fl) { struct dlm_ls *ls; struct plock_op *op; int rv; ls = dlm_find_lockspace_local(lockspace); if (!ls) return -EINVAL; op = kzalloc(sizeof(*op), GFP_KERNEL); if (!op) { rv = -ENOMEM; goto out; } op->info.optype = DLM_PLOCK_OP_GET; op->info.pid = fl->fl_pid; op->info.ex = (fl->fl_type == F_WRLCK); op->info.fsid = ls->ls_global_id; op->info.number = number; op->info.start = fl->fl_start; op->info.end = fl->fl_end; if (fl->fl_lmops && fl->fl_lmops->fl_grant) op->info.owner = (__u64) fl->fl_pid; else op->info.owner = (__u64)(long) fl->fl_owner; send_op(op); wait_event(recv_wq, (op->done != 0)); spin_lock(&ops_lock); if (!list_empty(&op->list)) { log_error(ls, "dlm_posix_get: op on list %llx", (unsigned long long)number); list_del(&op->list); } spin_unlock(&ops_lock); /* info.rv from userspace is 1 for conflict, 0 for no-conflict, -ENOENT if there are no locks on the file */ rv = op->info.rv; fl->fl_type = F_UNLCK; if (rv == -ENOENT) rv = 0; else if (rv > 0) { locks_init_lock(fl); fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; fl->fl_flags = FL_POSIX; fl->fl_pid = op->info.pid; fl->fl_start = op->info.start; fl->fl_end = op->info.end; rv = 0; } kfree(op); out: dlm_put_lockspace(ls); return rv; }
/* * This is the main entry point for the NLM client. */ int nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) { struct nfs_server *nfssrv = NFS_SERVER(inode); struct nlm_host *host; struct nlm_rqst reqst, *call = &reqst; sigset_t oldset; unsigned long flags; int status, proto, vers; vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1; if (NFS_PROTO(inode)->version > 3) { printk(KERN_NOTICE "NFSv4 file locking not implemented!\n"); return -ENOLCK; } /* Retrieve transport protocol from NFS client */ proto = NFS_CLIENT(inode)->cl_xprt->prot; if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers))) return -ENOLCK; /* Create RPC client handle if not there, and copy soft * and intr flags from NFS client. */ if (host->h_rpcclnt == NULL) { struct rpc_clnt *clnt; /* Bind an rpc client to this host handle (does not * perform a portmapper lookup) */ if (!(clnt = nlm_bind_host(host))) { status = -ENOLCK; goto done; } clnt->cl_softrtry = nfssrv->client->cl_softrtry; clnt->cl_intr = nfssrv->client->cl_intr; clnt->cl_chatty = nfssrv->client->cl_chatty; } /* Keep the old signal mask */ spin_lock_irqsave(¤t->sighand->siglock, flags); oldset = current->blocked; /* If we're cleaning up locks because the process is exiting, * perform the RPC call asynchronously. */ if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type == F_UNLCK && (current->flags & PF_EXITING)) { sigfillset(¤t->blocked); /* Mask all signals */ recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); call = nlmclnt_alloc_call(); if (!call) { status = -ENOMEM; goto out_restore; } call->a_flags = RPC_TASK_ASYNC; } else { spin_unlock_irqrestore(¤t->sighand->siglock, flags); memset(call, 0, sizeof(*call)); locks_init_lock(&call->a_args.lock.fl); locks_init_lock(&call->a_res.lock.fl); } call->a_host = host; /* Set up the argument struct */ nlmclnt_setlockargs(call, fl); if (IS_SETLK(cmd) || IS_SETLKW(cmd)) { if (fl->fl_type != F_UNLCK) { call->a_args.block = IS_SETLKW(cmd) ? 1 : 0; status = nlmclnt_lock(call, fl); } else status = nlmclnt_unlock(call, fl); } else if (IS_GETLK(cmd)) status = nlmclnt_test(call, fl); else status = -EINVAL; if (status < 0 && (call->a_flags & RPC_TASK_ASYNC)) kfree(call); out_restore: spin_lock_irqsave(¤t->sighand->siglock, flags); current->blocked = oldset; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, flags); done: dprintk("lockd: clnt proc returns %d\n", status); nlm_release_host(host); return status; }
/* * Initialises the fields of the file lock which are invariant for * free file_locks. */ static void init_once(void *foo) { struct file_lock *lock = (struct file_lock *) foo; locks_init_lock(lock); }