162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h> 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/file.h> 562306a36Sopenharmony_ci#include <linux/namei.h> 662306a36Sopenharmony_ci#include <linux/random.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "super.h" 962306a36Sopenharmony_ci#include "mds_client.h" 1062306a36Sopenharmony_ci#include <linux/filelock.h> 1162306a36Sopenharmony_ci#include <linux/ceph/pagelist.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic u64 lock_secret; 1462306a36Sopenharmony_cistatic int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc, 1562306a36Sopenharmony_ci struct ceph_mds_request *req); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic inline u64 secure_addr(void *addr) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci u64 v = lock_secret ^ (u64)(unsigned long)addr; 2062306a36Sopenharmony_ci /* 2162306a36Sopenharmony_ci * Set the most significant bit, so that MDS knows the 'owner' 2262306a36Sopenharmony_ci * is sufficient to identify the owner of lock. (old code uses 2362306a36Sopenharmony_ci * both 'owner' and 'pid') 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci v |= (1ULL << 63); 2662306a36Sopenharmony_ci return v; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid __init ceph_flock_init(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci get_random_bytes(&lock_secret, sizeof(lock_secret)); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void ceph_fl_copy_lock(struct file_lock *dst, struct file_lock *src) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct inode *inode = file_inode(dst->fl_file); 3762306a36Sopenharmony_ci atomic_inc(&ceph_inode(inode)->i_filelock_ref); 3862306a36Sopenharmony_ci dst->fl_u.ceph.inode = igrab(inode); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Do not use the 'fl->fl_file' in release function, which 4362306a36Sopenharmony_ci * is possibly already released by another thread. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic void ceph_fl_release_lock(struct file_lock *fl) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct inode *inode = fl->fl_u.ceph.inode; 4862306a36Sopenharmony_ci struct ceph_inode_info *ci; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * If inode is NULL it should be a request file_lock, 5262306a36Sopenharmony_ci * nothing we can do. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (!inode) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ci = ceph_inode(inode); 5862306a36Sopenharmony_ci if (atomic_dec_and_test(&ci->i_filelock_ref)) { 5962306a36Sopenharmony_ci /* clear error when all locks are released */ 6062306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 6162306a36Sopenharmony_ci ci->i_ceph_flags &= ~CEPH_I_ERROR_FILELOCK; 6262306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci fl->fl_u.ceph.inode = NULL; 6562306a36Sopenharmony_ci iput(inode); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const struct file_lock_operations ceph_fl_lock_ops = { 6962306a36Sopenharmony_ci .fl_copy_lock = ceph_fl_copy_lock, 7062306a36Sopenharmony_ci .fl_release_private = ceph_fl_release_lock, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * Implement fcntl and flock locking functions. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic int ceph_lock_message(u8 lock_type, u16 operation, struct inode *inode, 7762306a36Sopenharmony_ci int cmd, u8 wait, struct file_lock *fl) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); 8062306a36Sopenharmony_ci struct ceph_mds_request *req; 8162306a36Sopenharmony_ci int err; 8262306a36Sopenharmony_ci u64 length = 0; 8362306a36Sopenharmony_ci u64 owner; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (operation == CEPH_MDS_OP_SETFILELOCK) { 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * increasing i_filelock_ref closes race window between 8862306a36Sopenharmony_ci * handling request reply and adding file_lock struct to 8962306a36Sopenharmony_ci * inode. Otherwise, auth caps may get trimmed in the 9062306a36Sopenharmony_ci * window. Caller function will decrease the counter. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci fl->fl_ops = &ceph_fl_lock_ops; 9362306a36Sopenharmony_ci fl->fl_ops->fl_copy_lock(fl, NULL); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (operation != CEPH_MDS_OP_SETFILELOCK || cmd == CEPH_LOCK_UNLOCK) 9762306a36Sopenharmony_ci wait = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci req = ceph_mdsc_create_request(mdsc, operation, USE_AUTH_MDS); 10062306a36Sopenharmony_ci if (IS_ERR(req)) 10162306a36Sopenharmony_ci return PTR_ERR(req); 10262306a36Sopenharmony_ci req->r_inode = inode; 10362306a36Sopenharmony_ci ihold(inode); 10462306a36Sopenharmony_ci req->r_num_caps = 1; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* mds requires start and length rather than start and end */ 10762306a36Sopenharmony_ci if (LLONG_MAX == fl->fl_end) 10862306a36Sopenharmony_ci length = 0; 10962306a36Sopenharmony_ci else 11062306a36Sopenharmony_ci length = fl->fl_end - fl->fl_start + 1; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci owner = secure_addr(fl->fl_owner); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci dout("ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, " 11562306a36Sopenharmony_ci "start: %llu, length: %llu, wait: %d, type: %d\n", (int)lock_type, 11662306a36Sopenharmony_ci (int)operation, owner, (u64)fl->fl_pid, fl->fl_start, length, 11762306a36Sopenharmony_ci wait, fl->fl_type); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci req->r_args.filelock_change.rule = lock_type; 12062306a36Sopenharmony_ci req->r_args.filelock_change.type = cmd; 12162306a36Sopenharmony_ci req->r_args.filelock_change.owner = cpu_to_le64(owner); 12262306a36Sopenharmony_ci req->r_args.filelock_change.pid = cpu_to_le64((u64)fl->fl_pid); 12362306a36Sopenharmony_ci req->r_args.filelock_change.start = cpu_to_le64(fl->fl_start); 12462306a36Sopenharmony_ci req->r_args.filelock_change.length = cpu_to_le64(length); 12562306a36Sopenharmony_ci req->r_args.filelock_change.wait = wait; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci err = ceph_mdsc_submit_request(mdsc, inode, req); 12862306a36Sopenharmony_ci if (!err) 12962306a36Sopenharmony_ci err = ceph_mdsc_wait_request(mdsc, req, wait ? 13062306a36Sopenharmony_ci ceph_lock_wait_for_completion : NULL); 13162306a36Sopenharmony_ci if (!err && operation == CEPH_MDS_OP_GETFILELOCK) { 13262306a36Sopenharmony_ci fl->fl_pid = -le64_to_cpu(req->r_reply_info.filelock_reply->pid); 13362306a36Sopenharmony_ci if (CEPH_LOCK_SHARED == req->r_reply_info.filelock_reply->type) 13462306a36Sopenharmony_ci fl->fl_type = F_RDLCK; 13562306a36Sopenharmony_ci else if (CEPH_LOCK_EXCL == req->r_reply_info.filelock_reply->type) 13662306a36Sopenharmony_ci fl->fl_type = F_WRLCK; 13762306a36Sopenharmony_ci else 13862306a36Sopenharmony_ci fl->fl_type = F_UNLCK; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci fl->fl_start = le64_to_cpu(req->r_reply_info.filelock_reply->start); 14162306a36Sopenharmony_ci length = le64_to_cpu(req->r_reply_info.filelock_reply->start) + 14262306a36Sopenharmony_ci le64_to_cpu(req->r_reply_info.filelock_reply->length); 14362306a36Sopenharmony_ci if (length >= 1) 14462306a36Sopenharmony_ci fl->fl_end = length -1; 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci fl->fl_end = 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci ceph_mdsc_put_request(req); 15062306a36Sopenharmony_ci dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, " 15162306a36Sopenharmony_ci "length: %llu, wait: %d, type: %d, err code %d\n", (int)lock_type, 15262306a36Sopenharmony_ci (int)operation, (u64)fl->fl_pid, fl->fl_start, 15362306a36Sopenharmony_ci length, wait, fl->fl_type, err); 15462306a36Sopenharmony_ci return err; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc, 15862306a36Sopenharmony_ci struct ceph_mds_request *req) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct ceph_mds_request *intr_req; 16162306a36Sopenharmony_ci struct inode *inode = req->r_inode; 16262306a36Sopenharmony_ci int err, lock_type; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci BUG_ON(req->r_op != CEPH_MDS_OP_SETFILELOCK); 16562306a36Sopenharmony_ci if (req->r_args.filelock_change.rule == CEPH_LOCK_FCNTL) 16662306a36Sopenharmony_ci lock_type = CEPH_LOCK_FCNTL_INTR; 16762306a36Sopenharmony_ci else if (req->r_args.filelock_change.rule == CEPH_LOCK_FLOCK) 16862306a36Sopenharmony_ci lock_type = CEPH_LOCK_FLOCK_INTR; 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci BUG_ON(1); 17162306a36Sopenharmony_ci BUG_ON(req->r_args.filelock_change.type == CEPH_LOCK_UNLOCK); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci err = wait_for_completion_interruptible(&req->r_completion); 17462306a36Sopenharmony_ci if (!err) 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci dout("ceph_lock_wait_for_completion: request %llu was interrupted\n", 17862306a36Sopenharmony_ci req->r_tid); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mutex_lock(&mdsc->mutex); 18162306a36Sopenharmony_ci if (test_bit(CEPH_MDS_R_GOT_RESULT, &req->r_req_flags)) { 18262306a36Sopenharmony_ci err = 0; 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * ensure we aren't running concurrently with 18662306a36Sopenharmony_ci * ceph_fill_trace or ceph_readdir_prepopulate, which 18762306a36Sopenharmony_ci * rely on locks (dir mutex) held by our caller. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci mutex_lock(&req->r_fill_mutex); 19062306a36Sopenharmony_ci req->r_err = err; 19162306a36Sopenharmony_ci set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags); 19262306a36Sopenharmony_ci mutex_unlock(&req->r_fill_mutex); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!req->r_session) { 19562306a36Sopenharmony_ci // haven't sent the request 19662306a36Sopenharmony_ci err = 0; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci mutex_unlock(&mdsc->mutex); 20062306a36Sopenharmony_ci if (!err) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci intr_req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETFILELOCK, 20462306a36Sopenharmony_ci USE_AUTH_MDS); 20562306a36Sopenharmony_ci if (IS_ERR(intr_req)) 20662306a36Sopenharmony_ci return PTR_ERR(intr_req); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci intr_req->r_inode = inode; 20962306a36Sopenharmony_ci ihold(inode); 21062306a36Sopenharmony_ci intr_req->r_num_caps = 1; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci intr_req->r_args.filelock_change = req->r_args.filelock_change; 21362306a36Sopenharmony_ci intr_req->r_args.filelock_change.rule = lock_type; 21462306a36Sopenharmony_ci intr_req->r_args.filelock_change.type = CEPH_LOCK_UNLOCK; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci err = ceph_mdsc_do_request(mdsc, inode, intr_req); 21762306a36Sopenharmony_ci ceph_mdsc_put_request(intr_req); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (err && err != -ERESTARTSYS) 22062306a36Sopenharmony_ci return err; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci wait_for_completion_killable(&req->r_safe_completion); 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int try_unlock_file(struct file *file, struct file_lock *fl) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci int err; 22962306a36Sopenharmony_ci unsigned int orig_flags = fl->fl_flags; 23062306a36Sopenharmony_ci fl->fl_flags |= FL_EXISTS; 23162306a36Sopenharmony_ci err = locks_lock_file_wait(file, fl); 23262306a36Sopenharmony_ci fl->fl_flags = orig_flags; 23362306a36Sopenharmony_ci if (err == -ENOENT) { 23462306a36Sopenharmony_ci if (!(orig_flags & FL_EXISTS)) 23562306a36Sopenharmony_ci err = 0; 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci return 1; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * Attempt to set an fcntl lock. 24362306a36Sopenharmony_ci * For now, this just goes away to the server. Later it may be more awesome. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ciint ceph_lock(struct file *file, int cmd, struct file_lock *fl) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct inode *inode = file_inode(file); 24862306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 24962306a36Sopenharmony_ci int err = 0; 25062306a36Sopenharmony_ci u16 op = CEPH_MDS_OP_SETFILELOCK; 25162306a36Sopenharmony_ci u8 wait = 0; 25262306a36Sopenharmony_ci u8 lock_cmd; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (!(fl->fl_flags & FL_POSIX)) 25562306a36Sopenharmony_ci return -ENOLCK; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (ceph_inode_is_shutdown(inode)) 25862306a36Sopenharmony_ci return -ESTALE; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci dout("ceph_lock, fl_owner: %p\n", fl->fl_owner); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* set wait bit as appropriate, then make command as Ceph expects it*/ 26362306a36Sopenharmony_ci if (IS_GETLK(cmd)) 26462306a36Sopenharmony_ci op = CEPH_MDS_OP_GETFILELOCK; 26562306a36Sopenharmony_ci else if (IS_SETLKW(cmd)) 26662306a36Sopenharmony_ci wait = 1; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 26962306a36Sopenharmony_ci if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { 27062306a36Sopenharmony_ci err = -EIO; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 27362306a36Sopenharmony_ci if (err < 0) { 27462306a36Sopenharmony_ci if (op == CEPH_MDS_OP_SETFILELOCK && F_UNLCK == fl->fl_type) 27562306a36Sopenharmony_ci posix_lock_file(file, fl, NULL); 27662306a36Sopenharmony_ci return err; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (F_RDLCK == fl->fl_type) 28062306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_SHARED; 28162306a36Sopenharmony_ci else if (F_WRLCK == fl->fl_type) 28262306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_EXCL; 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_UNLOCK; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (op == CEPH_MDS_OP_SETFILELOCK && F_UNLCK == fl->fl_type) { 28762306a36Sopenharmony_ci err = try_unlock_file(file, fl); 28862306a36Sopenharmony_ci if (err <= 0) 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci err = ceph_lock_message(CEPH_LOCK_FCNTL, op, inode, lock_cmd, wait, fl); 29362306a36Sopenharmony_ci if (!err) { 29462306a36Sopenharmony_ci if (op == CEPH_MDS_OP_SETFILELOCK && F_UNLCK != fl->fl_type) { 29562306a36Sopenharmony_ci dout("mds locked, locking locally\n"); 29662306a36Sopenharmony_ci err = posix_lock_file(file, fl, NULL); 29762306a36Sopenharmony_ci if (err) { 29862306a36Sopenharmony_ci /* undo! This should only happen if 29962306a36Sopenharmony_ci * the kernel detects local 30062306a36Sopenharmony_ci * deadlock. */ 30162306a36Sopenharmony_ci ceph_lock_message(CEPH_LOCK_FCNTL, op, inode, 30262306a36Sopenharmony_ci CEPH_LOCK_UNLOCK, 0, fl); 30362306a36Sopenharmony_ci dout("got %d on posix_lock_file, undid lock\n", 30462306a36Sopenharmony_ci err); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci return err; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint ceph_flock(struct file *file, int cmd, struct file_lock *fl) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct inode *inode = file_inode(file); 31462306a36Sopenharmony_ci struct ceph_inode_info *ci = ceph_inode(inode); 31562306a36Sopenharmony_ci int err = 0; 31662306a36Sopenharmony_ci u8 wait = 0; 31762306a36Sopenharmony_ci u8 lock_cmd; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!(fl->fl_flags & FL_FLOCK)) 32062306a36Sopenharmony_ci return -ENOLCK; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (ceph_inode_is_shutdown(inode)) 32362306a36Sopenharmony_ci return -ESTALE; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dout("ceph_flock, fl_file: %p\n", fl->fl_file); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock(&ci->i_ceph_lock); 32862306a36Sopenharmony_ci if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { 32962306a36Sopenharmony_ci err = -EIO; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci spin_unlock(&ci->i_ceph_lock); 33262306a36Sopenharmony_ci if (err < 0) { 33362306a36Sopenharmony_ci if (F_UNLCK == fl->fl_type) 33462306a36Sopenharmony_ci locks_lock_file_wait(file, fl); 33562306a36Sopenharmony_ci return err; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (IS_SETLKW(cmd)) 33962306a36Sopenharmony_ci wait = 1; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (F_RDLCK == fl->fl_type) 34262306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_SHARED; 34362306a36Sopenharmony_ci else if (F_WRLCK == fl->fl_type) 34462306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_EXCL; 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci lock_cmd = CEPH_LOCK_UNLOCK; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (F_UNLCK == fl->fl_type) { 34962306a36Sopenharmony_ci err = try_unlock_file(file, fl); 35062306a36Sopenharmony_ci if (err <= 0) 35162306a36Sopenharmony_ci return err; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci err = ceph_lock_message(CEPH_LOCK_FLOCK, CEPH_MDS_OP_SETFILELOCK, 35562306a36Sopenharmony_ci inode, lock_cmd, wait, fl); 35662306a36Sopenharmony_ci if (!err && F_UNLCK != fl->fl_type) { 35762306a36Sopenharmony_ci err = locks_lock_file_wait(file, fl); 35862306a36Sopenharmony_ci if (err) { 35962306a36Sopenharmony_ci ceph_lock_message(CEPH_LOCK_FLOCK, 36062306a36Sopenharmony_ci CEPH_MDS_OP_SETFILELOCK, 36162306a36Sopenharmony_ci inode, CEPH_LOCK_UNLOCK, 0, fl); 36262306a36Sopenharmony_ci dout("got %d on locks_lock_file_wait, undid lock\n", err); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci return err; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Fills in the passed counter variables, so you can prepare pagelist metadata 37062306a36Sopenharmony_ci * before calling ceph_encode_locks. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_civoid ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct file_lock *lock; 37562306a36Sopenharmony_ci struct file_lock_context *ctx; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci *fcntl_count = 0; 37862306a36Sopenharmony_ci *flock_count = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ctx = locks_inode_context(inode); 38162306a36Sopenharmony_ci if (ctx) { 38262306a36Sopenharmony_ci spin_lock(&ctx->flc_lock); 38362306a36Sopenharmony_ci list_for_each_entry(lock, &ctx->flc_posix, fl_list) 38462306a36Sopenharmony_ci ++(*fcntl_count); 38562306a36Sopenharmony_ci list_for_each_entry(lock, &ctx->flc_flock, fl_list) 38662306a36Sopenharmony_ci ++(*flock_count); 38762306a36Sopenharmony_ci spin_unlock(&ctx->flc_lock); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci dout("counted %d flock locks and %d fcntl locks\n", 39062306a36Sopenharmony_ci *flock_count, *fcntl_count); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* 39462306a36Sopenharmony_ci * Given a pointer to a lock, convert it to a ceph filelock 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_cistatic int lock_to_ceph_filelock(struct file_lock *lock, 39762306a36Sopenharmony_ci struct ceph_filelock *cephlock) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int err = 0; 40062306a36Sopenharmony_ci cephlock->start = cpu_to_le64(lock->fl_start); 40162306a36Sopenharmony_ci cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1); 40262306a36Sopenharmony_ci cephlock->client = cpu_to_le64(0); 40362306a36Sopenharmony_ci cephlock->pid = cpu_to_le64((u64)lock->fl_pid); 40462306a36Sopenharmony_ci cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner)); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (lock->fl_type) { 40762306a36Sopenharmony_ci case F_RDLCK: 40862306a36Sopenharmony_ci cephlock->type = CEPH_LOCK_SHARED; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case F_WRLCK: 41162306a36Sopenharmony_ci cephlock->type = CEPH_LOCK_EXCL; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case F_UNLCK: 41462306a36Sopenharmony_ci cephlock->type = CEPH_LOCK_UNLOCK; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci dout("Have unknown lock type %d\n", lock->fl_type); 41862306a36Sopenharmony_ci err = -EINVAL; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return err; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/* 42562306a36Sopenharmony_ci * Encode the flock and fcntl locks for the given inode into the ceph_filelock 42662306a36Sopenharmony_ci * array. Must be called with inode->i_lock already held. 42762306a36Sopenharmony_ci * If we encounter more of a specific lock type than expected, return -ENOSPC. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ciint ceph_encode_locks_to_buffer(struct inode *inode, 43062306a36Sopenharmony_ci struct ceph_filelock *flocks, 43162306a36Sopenharmony_ci int num_fcntl_locks, int num_flock_locks) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct file_lock *lock; 43462306a36Sopenharmony_ci struct file_lock_context *ctx = locks_inode_context(inode); 43562306a36Sopenharmony_ci int err = 0; 43662306a36Sopenharmony_ci int seen_fcntl = 0; 43762306a36Sopenharmony_ci int seen_flock = 0; 43862306a36Sopenharmony_ci int l = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci dout("encoding %d flock and %d fcntl locks\n", num_flock_locks, 44162306a36Sopenharmony_ci num_fcntl_locks); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!ctx) 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci spin_lock(&ctx->flc_lock); 44762306a36Sopenharmony_ci list_for_each_entry(lock, &ctx->flc_posix, fl_list) { 44862306a36Sopenharmony_ci ++seen_fcntl; 44962306a36Sopenharmony_ci if (seen_fcntl > num_fcntl_locks) { 45062306a36Sopenharmony_ci err = -ENOSPC; 45162306a36Sopenharmony_ci goto fail; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci err = lock_to_ceph_filelock(lock, &flocks[l]); 45462306a36Sopenharmony_ci if (err) 45562306a36Sopenharmony_ci goto fail; 45662306a36Sopenharmony_ci ++l; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci list_for_each_entry(lock, &ctx->flc_flock, fl_list) { 45962306a36Sopenharmony_ci ++seen_flock; 46062306a36Sopenharmony_ci if (seen_flock > num_flock_locks) { 46162306a36Sopenharmony_ci err = -ENOSPC; 46262306a36Sopenharmony_ci goto fail; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci err = lock_to_ceph_filelock(lock, &flocks[l]); 46562306a36Sopenharmony_ci if (err) 46662306a36Sopenharmony_ci goto fail; 46762306a36Sopenharmony_ci ++l; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_cifail: 47062306a36Sopenharmony_ci spin_unlock(&ctx->flc_lock); 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* 47562306a36Sopenharmony_ci * Copy the encoded flock and fcntl locks into the pagelist. 47662306a36Sopenharmony_ci * Format is: #fcntl locks, sequential fcntl locks, #flock locks, 47762306a36Sopenharmony_ci * sequential flock locks. 47862306a36Sopenharmony_ci * Returns zero on success. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint ceph_locks_to_pagelist(struct ceph_filelock *flocks, 48162306a36Sopenharmony_ci struct ceph_pagelist *pagelist, 48262306a36Sopenharmony_ci int num_fcntl_locks, int num_flock_locks) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci int err = 0; 48562306a36Sopenharmony_ci __le32 nlocks; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci nlocks = cpu_to_le32(num_fcntl_locks); 48862306a36Sopenharmony_ci err = ceph_pagelist_append(pagelist, &nlocks, sizeof(nlocks)); 48962306a36Sopenharmony_ci if (err) 49062306a36Sopenharmony_ci goto out_fail; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (num_fcntl_locks > 0) { 49362306a36Sopenharmony_ci err = ceph_pagelist_append(pagelist, flocks, 49462306a36Sopenharmony_ci num_fcntl_locks * sizeof(*flocks)); 49562306a36Sopenharmony_ci if (err) 49662306a36Sopenharmony_ci goto out_fail; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci nlocks = cpu_to_le32(num_flock_locks); 50062306a36Sopenharmony_ci err = ceph_pagelist_append(pagelist, &nlocks, sizeof(nlocks)); 50162306a36Sopenharmony_ci if (err) 50262306a36Sopenharmony_ci goto out_fail; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (num_flock_locks > 0) { 50562306a36Sopenharmony_ci err = ceph_pagelist_append(pagelist, &flocks[num_fcntl_locks], 50662306a36Sopenharmony_ci num_flock_locks * sizeof(*flocks)); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ciout_fail: 50962306a36Sopenharmony_ci return err; 51062306a36Sopenharmony_ci} 511