162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* AFS file locking support 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "internal.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define AFS_LOCK_GRANTED 0 1162306a36Sopenharmony_ci#define AFS_LOCK_PENDING 1 1262306a36Sopenharmony_ci#define AFS_LOCK_YOUR_TRY 2 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistruct workqueue_struct *afs_lock_manager; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void afs_next_locker(struct afs_vnode *vnode, int error); 1762306a36Sopenharmony_cistatic void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl); 1862306a36Sopenharmony_cistatic void afs_fl_release_private(struct file_lock *fl); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const struct file_lock_operations afs_lock_ops = { 2162306a36Sopenharmony_ci .fl_copy_lock = afs_fl_copy_lock, 2262306a36Sopenharmony_ci .fl_release_private = afs_fl_release_private, 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic inline void afs_set_lock_state(struct afs_vnode *vnode, enum afs_lock_state state) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci _debug("STATE %u -> %u", vnode->lock_state, state); 2862306a36Sopenharmony_ci vnode->lock_state = state; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic atomic_t afs_file_lock_debug_id; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * if the callback is broken on this vnode, then the lock may now be available 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_civoid afs_lock_may_be_available(struct afs_vnode *vnode) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci spin_lock(&vnode->lock); 4162306a36Sopenharmony_ci if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) 4262306a36Sopenharmony_ci afs_next_locker(vnode, 0); 4362306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_callback_break, 0); 4462306a36Sopenharmony_ci spin_unlock(&vnode->lock); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * the lock will time out in 5 minutes unless we extend it, so schedule 4962306a36Sopenharmony_ci * extension in a bit less than that time 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic void afs_schedule_lock_extension(struct afs_vnode *vnode) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci ktime_t expires_at, now, duration; 5462306a36Sopenharmony_ci u64 duration_j; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci expires_at = ktime_add_ms(vnode->locked_at, AFS_LOCKWAIT * 1000 / 2); 5762306a36Sopenharmony_ci now = ktime_get_real(); 5862306a36Sopenharmony_ci duration = ktime_sub(expires_at, now); 5962306a36Sopenharmony_ci if (duration <= 0) 6062306a36Sopenharmony_ci duration_j = 0; 6162306a36Sopenharmony_ci else 6262306a36Sopenharmony_ci duration_j = nsecs_to_jiffies(ktime_to_ns(duration)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci queue_delayed_work(afs_lock_manager, &vnode->lock_work, duration_j); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * In the case of successful completion of a lock operation, record the time 6962306a36Sopenharmony_ci * the reply appeared and start the lock extension timer. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_civoid afs_lock_op_done(struct afs_call *call) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct afs_operation *op = call->op; 7462306a36Sopenharmony_ci struct afs_vnode *vnode = op->file[0].vnode; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (call->error == 0) { 7762306a36Sopenharmony_ci spin_lock(&vnode->lock); 7862306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_timestamp, 0); 7962306a36Sopenharmony_ci vnode->locked_at = call->issue_time; 8062306a36Sopenharmony_ci afs_schedule_lock_extension(vnode); 8162306a36Sopenharmony_ci spin_unlock(&vnode->lock); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * grant one or more locks (readlocks are allowed to jump the queue if the 8762306a36Sopenharmony_ci * first lock in the queue is itself a readlock) 8862306a36Sopenharmony_ci * - the caller must hold the vnode lock 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic void afs_grant_locks(struct afs_vnode *vnode) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct file_lock *p, *_p; 9362306a36Sopenharmony_ci bool exclusive = (vnode->lock_type == AFS_LOCK_WRITE); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { 9662306a36Sopenharmony_ci if (!exclusive && p->fl_type == F_WRLCK) 9762306a36Sopenharmony_ci continue; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci list_move_tail(&p->fl_u.afs.link, &vnode->granted_locks); 10062306a36Sopenharmony_ci p->fl_u.afs.state = AFS_LOCK_GRANTED; 10162306a36Sopenharmony_ci trace_afs_flock_op(vnode, p, afs_flock_op_grant); 10262306a36Sopenharmony_ci wake_up(&p->fl_wait); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * If an error is specified, reject every pending lock that matches the 10862306a36Sopenharmony_ci * authentication and type of the lock we failed to get. If there are any 10962306a36Sopenharmony_ci * remaining lockers, try to wake up one of them to have a go. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic void afs_next_locker(struct afs_vnode *vnode, int error) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct file_lock *p, *_p, *next = NULL; 11462306a36Sopenharmony_ci struct key *key = vnode->lock_key; 11562306a36Sopenharmony_ci unsigned int fl_type = F_RDLCK; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci _enter(""); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (vnode->lock_type == AFS_LOCK_WRITE) 12062306a36Sopenharmony_ci fl_type = F_WRLCK; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { 12362306a36Sopenharmony_ci if (error && 12462306a36Sopenharmony_ci p->fl_type == fl_type && 12562306a36Sopenharmony_ci afs_file_key(p->fl_file) == key) { 12662306a36Sopenharmony_ci list_del_init(&p->fl_u.afs.link); 12762306a36Sopenharmony_ci p->fl_u.afs.state = error; 12862306a36Sopenharmony_ci wake_up(&p->fl_wait); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Select the next locker to hand off to. */ 13262306a36Sopenharmony_ci if (next && 13362306a36Sopenharmony_ci (next->fl_type == F_WRLCK || p->fl_type == F_RDLCK)) 13462306a36Sopenharmony_ci continue; 13562306a36Sopenharmony_ci next = p; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci vnode->lock_key = NULL; 13962306a36Sopenharmony_ci key_put(key); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (next) { 14262306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); 14362306a36Sopenharmony_ci next->fl_u.afs.state = AFS_LOCK_YOUR_TRY; 14462306a36Sopenharmony_ci trace_afs_flock_op(vnode, next, afs_flock_op_wake); 14562306a36Sopenharmony_ci wake_up(&next->fl_wait); 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_NONE); 14862306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_no_lockers, 0); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci _leave(""); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * Kill off all waiters in the the pending lock queue due to the vnode being 15662306a36Sopenharmony_ci * deleted. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic void afs_kill_lockers_enoent(struct afs_vnode *vnode) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct file_lock *p; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_DELETED); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci while (!list_empty(&vnode->pending_locks)) { 16562306a36Sopenharmony_ci p = list_entry(vnode->pending_locks.next, 16662306a36Sopenharmony_ci struct file_lock, fl_u.afs.link); 16762306a36Sopenharmony_ci list_del_init(&p->fl_u.afs.link); 16862306a36Sopenharmony_ci p->fl_u.afs.state = -ENOENT; 16962306a36Sopenharmony_ci wake_up(&p->fl_wait); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci key_put(vnode->lock_key); 17362306a36Sopenharmony_ci vnode->lock_key = NULL; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void afs_lock_success(struct afs_operation *op) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci _enter("op=%08x", op->debug_id); 17962306a36Sopenharmony_ci afs_vnode_commit_status(op, &op->file[0]); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct afs_operation_ops afs_set_lock_operation = { 18362306a36Sopenharmony_ci .issue_afs_rpc = afs_fs_set_lock, 18462306a36Sopenharmony_ci .issue_yfs_rpc = yfs_fs_set_lock, 18562306a36Sopenharmony_ci .success = afs_lock_success, 18662306a36Sopenharmony_ci .aborted = afs_check_for_remote_deletion, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * Get a lock on a file 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int afs_set_lock(struct afs_vnode *vnode, struct key *key, 19362306a36Sopenharmony_ci afs_lock_type_t type) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct afs_operation *op; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci _enter("%s{%llx:%llu.%u},%x,%u", 19862306a36Sopenharmony_ci vnode->volume->name, 19962306a36Sopenharmony_ci vnode->fid.vid, 20062306a36Sopenharmony_ci vnode->fid.vnode, 20162306a36Sopenharmony_ci vnode->fid.unique, 20262306a36Sopenharmony_ci key_serial(key), type); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci op = afs_alloc_operation(key, vnode->volume); 20562306a36Sopenharmony_ci if (IS_ERR(op)) 20662306a36Sopenharmony_ci return PTR_ERR(op); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci op->lock.type = type; 21162306a36Sopenharmony_ci op->ops = &afs_set_lock_operation; 21262306a36Sopenharmony_ci return afs_do_sync_operation(op); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic const struct afs_operation_ops afs_extend_lock_operation = { 21662306a36Sopenharmony_ci .issue_afs_rpc = afs_fs_extend_lock, 21762306a36Sopenharmony_ci .issue_yfs_rpc = yfs_fs_extend_lock, 21862306a36Sopenharmony_ci .success = afs_lock_success, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * Extend a lock on a file 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic int afs_extend_lock(struct afs_vnode *vnode, struct key *key) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct afs_operation *op; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci _enter("%s{%llx:%llu.%u},%x", 22962306a36Sopenharmony_ci vnode->volume->name, 23062306a36Sopenharmony_ci vnode->fid.vid, 23162306a36Sopenharmony_ci vnode->fid.vnode, 23262306a36Sopenharmony_ci vnode->fid.unique, 23362306a36Sopenharmony_ci key_serial(key)); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci op = afs_alloc_operation(key, vnode->volume); 23662306a36Sopenharmony_ci if (IS_ERR(op)) 23762306a36Sopenharmony_ci return PTR_ERR(op); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci op->flags |= AFS_OPERATION_UNINTR; 24262306a36Sopenharmony_ci op->ops = &afs_extend_lock_operation; 24362306a36Sopenharmony_ci return afs_do_sync_operation(op); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct afs_operation_ops afs_release_lock_operation = { 24762306a36Sopenharmony_ci .issue_afs_rpc = afs_fs_release_lock, 24862306a36Sopenharmony_ci .issue_yfs_rpc = yfs_fs_release_lock, 24962306a36Sopenharmony_ci .success = afs_lock_success, 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Release a lock on a file 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistatic int afs_release_lock(struct afs_vnode *vnode, struct key *key) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct afs_operation *op; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci _enter("%s{%llx:%llu.%u},%x", 26062306a36Sopenharmony_ci vnode->volume->name, 26162306a36Sopenharmony_ci vnode->fid.vid, 26262306a36Sopenharmony_ci vnode->fid.vnode, 26362306a36Sopenharmony_ci vnode->fid.unique, 26462306a36Sopenharmony_ci key_serial(key)); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci op = afs_alloc_operation(key, vnode->volume); 26762306a36Sopenharmony_ci if (IS_ERR(op)) 26862306a36Sopenharmony_ci return PTR_ERR(op); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci afs_op_set_vnode(op, 0, vnode); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci op->flags |= AFS_OPERATION_UNINTR; 27362306a36Sopenharmony_ci op->ops = &afs_release_lock_operation; 27462306a36Sopenharmony_ci return afs_do_sync_operation(op); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * do work for a lock, including: 27962306a36Sopenharmony_ci * - probing for a lock we're waiting on but didn't get immediately 28062306a36Sopenharmony_ci * - extending a lock that's close to timing out 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_civoid afs_lock_work(struct work_struct *work) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct afs_vnode *vnode = 28562306a36Sopenharmony_ci container_of(work, struct afs_vnode, lock_work.work); 28662306a36Sopenharmony_ci struct key *key; 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci spin_lock(&vnode->lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciagain: 29462306a36Sopenharmony_ci _debug("wstate %u for %p", vnode->lock_state, vnode); 29562306a36Sopenharmony_ci switch (vnode->lock_state) { 29662306a36Sopenharmony_ci case AFS_VNODE_LOCK_NEED_UNLOCK: 29762306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_UNLOCKING); 29862306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_work_unlocking, 0); 29962306a36Sopenharmony_ci spin_unlock(&vnode->lock); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* attempt to release the server lock; if it fails, we just 30262306a36Sopenharmony_ci * wait 5 minutes and it'll expire anyway */ 30362306a36Sopenharmony_ci ret = afs_release_lock(vnode, vnode->lock_key); 30462306a36Sopenharmony_ci if (ret < 0 && vnode->lock_state != AFS_VNODE_LOCK_DELETED) { 30562306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail, 30662306a36Sopenharmony_ci ret); 30762306a36Sopenharmony_ci printk(KERN_WARNING "AFS:" 30862306a36Sopenharmony_ci " Failed to release lock on {%llx:%llx} error %d\n", 30962306a36Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, ret); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci spin_lock(&vnode->lock); 31362306a36Sopenharmony_ci if (ret == -ENOENT) 31462306a36Sopenharmony_ci afs_kill_lockers_enoent(vnode); 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci afs_next_locker(vnode, 0); 31762306a36Sopenharmony_ci spin_unlock(&vnode->lock); 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* If we've already got a lock, then it must be time to extend that 32162306a36Sopenharmony_ci * lock as AFS locks time out after 5 minutes. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci case AFS_VNODE_LOCK_GRANTED: 32462306a36Sopenharmony_ci _debug("extend"); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ASSERT(!list_empty(&vnode->granted_locks)); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci key = key_get(vnode->lock_key); 32962306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_EXTENDING); 33062306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_work_extending, 0); 33162306a36Sopenharmony_ci spin_unlock(&vnode->lock); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = afs_extend_lock(vnode, key); /* RPC */ 33462306a36Sopenharmony_ci key_put(key); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (ret < 0) { 33762306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_extend_fail, 33862306a36Sopenharmony_ci ret); 33962306a36Sopenharmony_ci pr_warn("AFS: Failed to extend lock on {%llx:%llx} error %d\n", 34062306a36Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, ret); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci spin_lock(&vnode->lock); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (ret == -ENOENT) { 34662306a36Sopenharmony_ci afs_kill_lockers_enoent(vnode); 34762306a36Sopenharmony_ci spin_unlock(&vnode->lock); 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING) 35262306a36Sopenharmony_ci goto again; 35362306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (ret != 0) 35662306a36Sopenharmony_ci queue_delayed_work(afs_lock_manager, &vnode->lock_work, 35762306a36Sopenharmony_ci HZ * 10); 35862306a36Sopenharmony_ci spin_unlock(&vnode->lock); 35962306a36Sopenharmony_ci _leave(" [ext]"); 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* If we're waiting for a callback to indicate lock release, we can't 36362306a36Sopenharmony_ci * actually rely on this, so need to recheck at regular intervals. The 36462306a36Sopenharmony_ci * problem is that the server might not notify us if the lock just 36562306a36Sopenharmony_ci * expires (say because a client died) rather than being explicitly 36662306a36Sopenharmony_ci * released. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci case AFS_VNODE_LOCK_WAITING_FOR_CB: 36962306a36Sopenharmony_ci _debug("retry"); 37062306a36Sopenharmony_ci afs_next_locker(vnode, 0); 37162306a36Sopenharmony_ci spin_unlock(&vnode->lock); 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci case AFS_VNODE_LOCK_DELETED: 37562306a36Sopenharmony_ci afs_kill_lockers_enoent(vnode); 37662306a36Sopenharmony_ci spin_unlock(&vnode->lock); 37762306a36Sopenharmony_ci return; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci /* Looks like a lock request was withdrawn. */ 38162306a36Sopenharmony_ci spin_unlock(&vnode->lock); 38262306a36Sopenharmony_ci _leave(" [no]"); 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * pass responsibility for the unlocking of a vnode on the server to the 38962306a36Sopenharmony_ci * manager thread, lest a pending signal in the calling thread interrupt 39062306a36Sopenharmony_ci * AF_RXRPC 39162306a36Sopenharmony_ci * - the caller must hold the vnode lock 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_cistatic void afs_defer_unlock(struct afs_vnode *vnode) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci _enter("%u", vnode->lock_state); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (list_empty(&vnode->granted_locks) && 39862306a36Sopenharmony_ci (vnode->lock_state == AFS_VNODE_LOCK_GRANTED || 39962306a36Sopenharmony_ci vnode->lock_state == AFS_VNODE_LOCK_EXTENDING)) { 40062306a36Sopenharmony_ci cancel_delayed_work(&vnode->lock_work); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_NEED_UNLOCK); 40362306a36Sopenharmony_ci trace_afs_flock_ev(vnode, NULL, afs_flock_defer_unlock, 0); 40462306a36Sopenharmony_ci queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* 40962306a36Sopenharmony_ci * Check that our view of the file metadata is up to date and check to see 41062306a36Sopenharmony_ci * whether we think that we have a locking permit. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_cistatic int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, 41362306a36Sopenharmony_ci enum afs_flock_mode mode, afs_lock_type_t type) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci afs_access_t access; 41662306a36Sopenharmony_ci int ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Make sure we've got a callback on this file and that our view of the 41962306a36Sopenharmony_ci * data version is up to date. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci ret = afs_validate(vnode, key); 42262306a36Sopenharmony_ci if (ret < 0) 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Check the permission set to see if we're actually going to be 42662306a36Sopenharmony_ci * allowed to get a lock on this file. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci ret = afs_check_permit(vnode, key, &access); 42962306a36Sopenharmony_ci if (ret < 0) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* At a rough estimation, you need LOCK, WRITE or INSERT perm to 43362306a36Sopenharmony_ci * read-lock a file and WRITE or INSERT perm to write-lock a file. 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * We can't rely on the server to do this for us since if we want to 43662306a36Sopenharmony_ci * share a read lock that we already have, we won't go the server. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci if (type == AFS_LOCK_READ) { 43962306a36Sopenharmony_ci if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK))) 44062306a36Sopenharmony_ci return -EACCES; 44162306a36Sopenharmony_ci } else { 44262306a36Sopenharmony_ci if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE))) 44362306a36Sopenharmony_ci return -EACCES; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * request a lock on a file on the server 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic int afs_do_setlk(struct file *file, struct file_lock *fl) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct inode *inode = file_inode(file); 45562306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(inode); 45662306a36Sopenharmony_ci enum afs_flock_mode mode = AFS_FS_S(inode->i_sb)->flock_mode; 45762306a36Sopenharmony_ci afs_lock_type_t type; 45862306a36Sopenharmony_ci struct key *key = afs_file_key(file); 45962306a36Sopenharmony_ci bool partial, no_server_lock = false; 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (mode == afs_flock_mode_unset) 46362306a36Sopenharmony_ci mode = afs_flock_mode_openafs; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci _enter("{%llx:%llu},%llu-%llu,%u,%u", 46662306a36Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, 46762306a36Sopenharmony_ci fl->fl_start, fl->fl_end, fl->fl_type, mode); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci fl->fl_ops = &afs_lock_ops; 47062306a36Sopenharmony_ci INIT_LIST_HEAD(&fl->fl_u.afs.link); 47162306a36Sopenharmony_ci fl->fl_u.afs.state = AFS_LOCK_PENDING; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci partial = (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX); 47462306a36Sopenharmony_ci type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; 47562306a36Sopenharmony_ci if (mode == afs_flock_mode_write && partial) 47662306a36Sopenharmony_ci type = AFS_LOCK_WRITE; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = afs_do_setlk_check(vnode, key, mode, type); 47962306a36Sopenharmony_ci if (ret < 0) 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, afs_flock_op_set_lock); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* AFS3 protocol only supports full-file locks and doesn't provide any 48562306a36Sopenharmony_ci * method of upgrade/downgrade, so we need to emulate for partial-file 48662306a36Sopenharmony_ci * locks. 48762306a36Sopenharmony_ci * 48862306a36Sopenharmony_ci * The OpenAFS client only gets a server lock for a full-file lock and 48962306a36Sopenharmony_ci * keeps partial-file locks local. Allow this behaviour to be emulated 49062306a36Sopenharmony_ci * (as the default). 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci if (mode == afs_flock_mode_local || 49362306a36Sopenharmony_ci (partial && mode == afs_flock_mode_openafs)) { 49462306a36Sopenharmony_ci no_server_lock = true; 49562306a36Sopenharmony_ci goto skip_server_lock; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci spin_lock(&vnode->lock); 49962306a36Sopenharmony_ci list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ret = -ENOENT; 50262306a36Sopenharmony_ci if (vnode->lock_state == AFS_VNODE_LOCK_DELETED) 50362306a36Sopenharmony_ci goto error_unlock; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* If we've already got a lock on the server then try to move to having 50662306a36Sopenharmony_ci * the VFS grant the requested lock. Note that this means that other 50762306a36Sopenharmony_ci * clients may get starved out. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci _debug("try %u", vnode->lock_state); 51062306a36Sopenharmony_ci if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED) { 51162306a36Sopenharmony_ci if (type == AFS_LOCK_READ) { 51262306a36Sopenharmony_ci _debug("instant readlock"); 51362306a36Sopenharmony_ci list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); 51462306a36Sopenharmony_ci fl->fl_u.afs.state = AFS_LOCK_GRANTED; 51562306a36Sopenharmony_ci goto vnode_is_locked_u; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (vnode->lock_type == AFS_LOCK_WRITE) { 51962306a36Sopenharmony_ci _debug("instant writelock"); 52062306a36Sopenharmony_ci list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); 52162306a36Sopenharmony_ci fl->fl_u.afs.state = AFS_LOCK_GRANTED; 52262306a36Sopenharmony_ci goto vnode_is_locked_u; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (vnode->lock_state == AFS_VNODE_LOCK_NONE && 52762306a36Sopenharmony_ci !(fl->fl_flags & FL_SLEEP)) { 52862306a36Sopenharmony_ci ret = -EAGAIN; 52962306a36Sopenharmony_ci if (type == AFS_LOCK_READ) { 53062306a36Sopenharmony_ci if (vnode->status.lock_count == -1) 53162306a36Sopenharmony_ci goto lock_is_contended; /* Write locked */ 53262306a36Sopenharmony_ci } else { 53362306a36Sopenharmony_ci if (vnode->status.lock_count != 0) 53462306a36Sopenharmony_ci goto lock_is_contended; /* Locked */ 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (vnode->lock_state != AFS_VNODE_LOCK_NONE) 53962306a36Sopenharmony_ci goto need_to_wait; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_citry_to_lock: 54262306a36Sopenharmony_ci /* We don't have a lock on this vnode and we aren't currently waiting 54362306a36Sopenharmony_ci * for one either, so ask the server for a lock. 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * Note that we need to be careful if we get interrupted by a signal 54662306a36Sopenharmony_ci * after dispatching the request as we may still get the lock, even 54762306a36Sopenharmony_ci * though we don't wait for the reply (it's not too bad a problem - the 54862306a36Sopenharmony_ci * lock will expire in 5 mins anyway). 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_try_to_lock, 0); 55162306a36Sopenharmony_ci vnode->lock_key = key_get(key); 55262306a36Sopenharmony_ci vnode->lock_type = type; 55362306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); 55462306a36Sopenharmony_ci spin_unlock(&vnode->lock); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci ret = afs_set_lock(vnode, key, type); /* RPC */ 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci spin_lock(&vnode->lock); 55962306a36Sopenharmony_ci switch (ret) { 56062306a36Sopenharmony_ci case -EKEYREJECTED: 56162306a36Sopenharmony_ci case -EKEYEXPIRED: 56262306a36Sopenharmony_ci case -EKEYREVOKED: 56362306a36Sopenharmony_ci case -EPERM: 56462306a36Sopenharmony_ci case -EACCES: 56562306a36Sopenharmony_ci fl->fl_u.afs.state = ret; 56662306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_fail_perm, ret); 56762306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 56862306a36Sopenharmony_ci afs_next_locker(vnode, ret); 56962306a36Sopenharmony_ci goto error_unlock; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci case -ENOENT: 57262306a36Sopenharmony_ci fl->fl_u.afs.state = ret; 57362306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret); 57462306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 57562306a36Sopenharmony_ci afs_kill_lockers_enoent(vnode); 57662306a36Sopenharmony_ci goto error_unlock; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci default: 57962306a36Sopenharmony_ci fl->fl_u.afs.state = ret; 58062306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret); 58162306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 58262306a36Sopenharmony_ci afs_next_locker(vnode, 0); 58362306a36Sopenharmony_ci goto error_unlock; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci case -EWOULDBLOCK: 58662306a36Sopenharmony_ci /* The server doesn't have a lock-waiting queue, so the client 58762306a36Sopenharmony_ci * will have to retry. The server will break the outstanding 58862306a36Sopenharmony_ci * callbacks on a file when a lock is released. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci ASSERT(list_empty(&vnode->granted_locks)); 59162306a36Sopenharmony_ci ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link); 59262306a36Sopenharmony_ci goto lock_is_contended; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci case 0: 59562306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); 59662306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_acquired, type); 59762306a36Sopenharmony_ci afs_grant_locks(vnode); 59862306a36Sopenharmony_ci goto vnode_is_locked_u; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_civnode_is_locked_u: 60262306a36Sopenharmony_ci spin_unlock(&vnode->lock); 60362306a36Sopenharmony_civnode_is_locked: 60462306a36Sopenharmony_ci /* the lock has been granted by the server... */ 60562306a36Sopenharmony_ci ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciskip_server_lock: 60862306a36Sopenharmony_ci /* ... but the VFS still needs to distribute access on this client. */ 60962306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_vfs_locking, 0); 61062306a36Sopenharmony_ci ret = locks_lock_file_wait(file, fl); 61162306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_vfs_lock, ret); 61262306a36Sopenharmony_ci if (ret < 0) 61362306a36Sopenharmony_ci goto vfs_rejected_lock; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Again, make sure we've got a callback on this file and, again, make 61662306a36Sopenharmony_ci * sure that our view of the data version is up to date (we ignore 61762306a36Sopenharmony_ci * errors incurred here and deal with the consequences elsewhere). 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci afs_validate(vnode, key); 62062306a36Sopenharmony_ci _leave(" = 0"); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cilock_is_contended: 62462306a36Sopenharmony_ci if (!(fl->fl_flags & FL_SLEEP)) { 62562306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 62662306a36Sopenharmony_ci afs_next_locker(vnode, 0); 62762306a36Sopenharmony_ci ret = -EAGAIN; 62862306a36Sopenharmony_ci goto error_unlock; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_WAITING_FOR_CB); 63262306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_would_block, ret); 63362306a36Sopenharmony_ci queue_delayed_work(afs_lock_manager, &vnode->lock_work, HZ * 5); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cineed_to_wait: 63662306a36Sopenharmony_ci /* We're going to have to wait. Either this client doesn't have a lock 63762306a36Sopenharmony_ci * on the server yet and we need to wait for a callback to occur, or 63862306a36Sopenharmony_ci * the client does have a lock on the server, but it's shared and we 63962306a36Sopenharmony_ci * need an exclusive lock. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci spin_unlock(&vnode->lock); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_waiting, 0); 64462306a36Sopenharmony_ci ret = wait_event_interruptible(fl->fl_wait, 64562306a36Sopenharmony_ci fl->fl_u.afs.state != AFS_LOCK_PENDING); 64662306a36Sopenharmony_ci trace_afs_flock_ev(vnode, fl, afs_flock_waited, ret); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (fl->fl_u.afs.state >= 0 && fl->fl_u.afs.state != AFS_LOCK_GRANTED) { 64962306a36Sopenharmony_ci spin_lock(&vnode->lock); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci switch (fl->fl_u.afs.state) { 65262306a36Sopenharmony_ci case AFS_LOCK_YOUR_TRY: 65362306a36Sopenharmony_ci fl->fl_u.afs.state = AFS_LOCK_PENDING; 65462306a36Sopenharmony_ci goto try_to_lock; 65562306a36Sopenharmony_ci case AFS_LOCK_PENDING: 65662306a36Sopenharmony_ci if (ret > 0) { 65762306a36Sopenharmony_ci /* We need to retry the lock. We may not be 65862306a36Sopenharmony_ci * notified by the server if it just expired 65962306a36Sopenharmony_ci * rather than being released. 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci ASSERTCMP(vnode->lock_state, ==, AFS_VNODE_LOCK_WAITING_FOR_CB); 66262306a36Sopenharmony_ci afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); 66362306a36Sopenharmony_ci fl->fl_u.afs.state = AFS_LOCK_PENDING; 66462306a36Sopenharmony_ci goto try_to_lock; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci goto error_unlock; 66762306a36Sopenharmony_ci case AFS_LOCK_GRANTED: 66862306a36Sopenharmony_ci default: 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci spin_unlock(&vnode->lock); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (fl->fl_u.afs.state == AFS_LOCK_GRANTED) 67662306a36Sopenharmony_ci goto vnode_is_locked; 67762306a36Sopenharmony_ci ret = fl->fl_u.afs.state; 67862306a36Sopenharmony_ci goto error; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_civfs_rejected_lock: 68162306a36Sopenharmony_ci /* The VFS rejected the lock we just obtained, so we have to discard 68262306a36Sopenharmony_ci * what we just got. We defer this to the lock manager work item to 68362306a36Sopenharmony_ci * deal with. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci _debug("vfs refused %d", ret); 68662306a36Sopenharmony_ci if (no_server_lock) 68762306a36Sopenharmony_ci goto error; 68862306a36Sopenharmony_ci spin_lock(&vnode->lock); 68962306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 69062306a36Sopenharmony_ci afs_defer_unlock(vnode); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cierror_unlock: 69362306a36Sopenharmony_ci spin_unlock(&vnode->lock); 69462306a36Sopenharmony_cierror: 69562306a36Sopenharmony_ci _leave(" = %d", ret); 69662306a36Sopenharmony_ci return ret; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * unlock on a file on the server 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_cistatic int afs_do_unlk(struct file *file, struct file_lock *fl) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); 70562306a36Sopenharmony_ci int ret; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci _enter("{%llx:%llu},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, afs_flock_op_unlock); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Flush all pending writes before doing anything with locks. */ 71262306a36Sopenharmony_ci vfs_fsync(file, 0); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ret = locks_lock_file_wait(file, fl); 71562306a36Sopenharmony_ci _leave(" = %d [%u]", ret, vnode->lock_state); 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/* 72062306a36Sopenharmony_ci * return information about a lock we currently hold, if indeed we hold one 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistatic int afs_do_getlk(struct file *file, struct file_lock *fl) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); 72562306a36Sopenharmony_ci struct key *key = afs_file_key(file); 72662306a36Sopenharmony_ci int ret, lock_count; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci _enter(""); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (vnode->lock_state == AFS_VNODE_LOCK_DELETED) 73162306a36Sopenharmony_ci return -ENOENT; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* check local lock records first */ 73662306a36Sopenharmony_ci posix_test_lock(file, fl); 73762306a36Sopenharmony_ci if (fl->fl_type == F_UNLCK) { 73862306a36Sopenharmony_ci /* no local locks; consult the server */ 73962306a36Sopenharmony_ci ret = afs_fetch_status(vnode, key, false, NULL); 74062306a36Sopenharmony_ci if (ret < 0) 74162306a36Sopenharmony_ci goto error; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci lock_count = READ_ONCE(vnode->status.lock_count); 74462306a36Sopenharmony_ci if (lock_count != 0) { 74562306a36Sopenharmony_ci if (lock_count > 0) 74662306a36Sopenharmony_ci fl->fl_type = F_RDLCK; 74762306a36Sopenharmony_ci else 74862306a36Sopenharmony_ci fl->fl_type = F_WRLCK; 74962306a36Sopenharmony_ci fl->fl_start = 0; 75062306a36Sopenharmony_ci fl->fl_end = OFFSET_MAX; 75162306a36Sopenharmony_ci fl->fl_pid = 0; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = 0; 75662306a36Sopenharmony_cierror: 75762306a36Sopenharmony_ci _leave(" = %d [%hd]", ret, fl->fl_type); 75862306a36Sopenharmony_ci return ret; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/* 76262306a36Sopenharmony_ci * manage POSIX locks on a file 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ciint afs_lock(struct file *file, int cmd, struct file_lock *fl) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); 76762306a36Sopenharmony_ci enum afs_flock_operation op; 76862306a36Sopenharmony_ci int ret; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci _enter("{%llx:%llu},%d,{t=%x,fl=%x,r=%Ld:%Ld}", 77162306a36Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, cmd, 77262306a36Sopenharmony_ci fl->fl_type, fl->fl_flags, 77362306a36Sopenharmony_ci (long long) fl->fl_start, (long long) fl->fl_end); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (IS_GETLK(cmd)) 77662306a36Sopenharmony_ci return afs_do_getlk(file, fl); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); 77962306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, afs_flock_op_lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (fl->fl_type == F_UNLCK) 78262306a36Sopenharmony_ci ret = afs_do_unlk(file, fl); 78362306a36Sopenharmony_ci else 78462306a36Sopenharmony_ci ret = afs_do_setlk(file, fl); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci switch (ret) { 78762306a36Sopenharmony_ci case 0: op = afs_flock_op_return_ok; break; 78862306a36Sopenharmony_ci case -EAGAIN: op = afs_flock_op_return_eagain; break; 78962306a36Sopenharmony_ci case -EDEADLK: op = afs_flock_op_return_edeadlk; break; 79062306a36Sopenharmony_ci default: op = afs_flock_op_return_error; break; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, op); 79362306a36Sopenharmony_ci return ret; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci/* 79762306a36Sopenharmony_ci * manage FLOCK locks on a file 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ciint afs_flock(struct file *file, int cmd, struct file_lock *fl) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); 80262306a36Sopenharmony_ci enum afs_flock_operation op; 80362306a36Sopenharmony_ci int ret; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci _enter("{%llx:%llu},%d,{t=%x,fl=%x}", 80662306a36Sopenharmony_ci vnode->fid.vid, vnode->fid.vnode, cmd, 80762306a36Sopenharmony_ci fl->fl_type, fl->fl_flags); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * No BSD flocks over NFS allowed. 81162306a36Sopenharmony_ci * Note: we could try to fake a POSIX lock request here by 81262306a36Sopenharmony_ci * using ((u32) filp | 0x80000000) or some such as the pid. 81362306a36Sopenharmony_ci * Not sure whether that would be unique, though, or whether 81462306a36Sopenharmony_ci * that would break in other places. 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci if (!(fl->fl_flags & FL_FLOCK)) 81762306a36Sopenharmony_ci return -ENOLCK; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci fl->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); 82062306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, afs_flock_op_flock); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* we're simulating flock() locks using posix locks on the server */ 82362306a36Sopenharmony_ci if (fl->fl_type == F_UNLCK) 82462306a36Sopenharmony_ci ret = afs_do_unlk(file, fl); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci ret = afs_do_setlk(file, fl); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci switch (ret) { 82962306a36Sopenharmony_ci case 0: op = afs_flock_op_return_ok; break; 83062306a36Sopenharmony_ci case -EAGAIN: op = afs_flock_op_return_eagain; break; 83162306a36Sopenharmony_ci case -EDEADLK: op = afs_flock_op_return_edeadlk; break; 83262306a36Sopenharmony_ci default: op = afs_flock_op_return_error; break; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, op); 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/* 83962306a36Sopenharmony_ci * the POSIX lock management core VFS code copies the lock record and adds the 84062306a36Sopenharmony_ci * copy into its own list, so we need to add that copy to the vnode's lock 84162306a36Sopenharmony_ci * queue in the same place as the original (which will be deleted shortly 84262306a36Sopenharmony_ci * after) 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_cistatic void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(fl->fl_file)); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci _enter(""); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci new->fl_u.afs.debug_id = atomic_inc_return(&afs_file_lock_debug_id); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci spin_lock(&vnode->lock); 85362306a36Sopenharmony_ci trace_afs_flock_op(vnode, new, afs_flock_op_copy_lock); 85462306a36Sopenharmony_ci list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); 85562306a36Sopenharmony_ci spin_unlock(&vnode->lock); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci/* 85962306a36Sopenharmony_ci * need to remove this lock from the vnode queue when it's removed from the 86062306a36Sopenharmony_ci * VFS's list 86162306a36Sopenharmony_ci */ 86262306a36Sopenharmony_cistatic void afs_fl_release_private(struct file_lock *fl) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(file_inode(fl->fl_file)); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci _enter(""); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci spin_lock(&vnode->lock); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci trace_afs_flock_op(vnode, fl, afs_flock_op_release_lock); 87162306a36Sopenharmony_ci list_del_init(&fl->fl_u.afs.link); 87262306a36Sopenharmony_ci if (list_empty(&vnode->granted_locks)) 87362306a36Sopenharmony_ci afs_defer_unlock(vnode); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci _debug("state %u for %p", vnode->lock_state, vnode); 87662306a36Sopenharmony_ci spin_unlock(&vnode->lock); 87762306a36Sopenharmony_ci} 878