1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/ceph/ceph_debug.h>
4 #include <linux/file.h>
5 #include <linux/namei.h>
6 #include <linux/random.h>
9 #include "mds_client.h"
10 #include <linux/ceph/pagelist.h>
12 static u64 lock_secret
;
13 static int ceph_lock_wait_for_completion(struct ceph_mds_client
*mdsc
,
14 struct ceph_mds_request
*req
);
16 static inline u64
secure_addr(void *addr
)
18 u64 v
= lock_secret
^ (u64
)(unsigned long)addr
;
20 * Set the most significant bit, so that MDS knows the 'owner'
21 * is sufficient to identify the owner of lock. (old code uses
22 * both 'owner' and 'pid')
28 void __init
ceph_flock_init(void)
30 get_random_bytes(&lock_secret
, sizeof(lock_secret
));
33 static void ceph_fl_copy_lock(struct file_lock
*dst
, struct file_lock
*src
)
35 struct ceph_file_info
*fi
= dst
->fl_file
->private_data
;
36 struct inode
*inode
= file_inode(dst
->fl_file
);
37 atomic_inc(&ceph_inode(inode
)->i_filelock_ref
);
38 atomic_inc(&fi
->num_locks
);
41 static void ceph_fl_release_lock(struct file_lock
*fl
)
43 struct ceph_file_info
*fi
= fl
->fl_file
->private_data
;
44 struct inode
*inode
= file_inode(fl
->fl_file
);
45 struct ceph_inode_info
*ci
= ceph_inode(inode
);
46 atomic_dec(&fi
->num_locks
);
47 if (atomic_dec_and_test(&ci
->i_filelock_ref
)) {
48 /* clear error when all locks are released */
49 spin_lock(&ci
->i_ceph_lock
);
50 ci
->i_ceph_flags
&= ~CEPH_I_ERROR_FILELOCK
;
51 spin_unlock(&ci
->i_ceph_lock
);
55 static const struct file_lock_operations ceph_fl_lock_ops
= {
56 .fl_copy_lock
= ceph_fl_copy_lock
,
57 .fl_release_private
= ceph_fl_release_lock
,
61 * Implement fcntl and flock locking functions.
63 static int ceph_lock_message(u8 lock_type
, u16 operation
, struct inode
*inode
,
64 int cmd
, u8 wait
, struct file_lock
*fl
)
66 struct ceph_mds_client
*mdsc
= ceph_sb_to_mdsc(inode
->i_sb
);
67 struct ceph_mds_request
*req
;
72 if (operation
== CEPH_MDS_OP_SETFILELOCK
) {
74 * increasing i_filelock_ref closes race window between
75 * handling request reply and adding file_lock struct to
76 * inode. Otherwise, auth caps may get trimmed in the
77 * window. Caller function will decrease the counter.
79 fl
->fl_ops
= &ceph_fl_lock_ops
;
80 fl
->fl_ops
->fl_copy_lock(fl
, NULL
);
83 if (operation
!= CEPH_MDS_OP_SETFILELOCK
|| cmd
== CEPH_LOCK_UNLOCK
)
86 req
= ceph_mdsc_create_request(mdsc
, operation
, USE_AUTH_MDS
);
93 /* mds requires start and length rather than start and end */
94 if (LLONG_MAX
== fl
->fl_end
)
97 length
= fl
->fl_end
- fl
->fl_start
+ 1;
99 owner
= secure_addr(fl
->fl_owner
);
101 dout("ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, "
102 "start: %llu, length: %llu, wait: %d, type: %d\n", (int)lock_type
,
103 (int)operation
, owner
, (u64
)fl
->fl_pid
, fl
->fl_start
, length
,
106 req
->r_args
.filelock_change
.rule
= lock_type
;
107 req
->r_args
.filelock_change
.type
= cmd
;
108 req
->r_args
.filelock_change
.owner
= cpu_to_le64(owner
);
109 req
->r_args
.filelock_change
.pid
= cpu_to_le64((u64
)fl
->fl_pid
);
110 req
->r_args
.filelock_change
.start
= cpu_to_le64(fl
->fl_start
);
111 req
->r_args
.filelock_change
.length
= cpu_to_le64(length
);
112 req
->r_args
.filelock_change
.wait
= wait
;
115 req
->r_wait_for_completion
= ceph_lock_wait_for_completion
;
117 err
= ceph_mdsc_do_request(mdsc
, inode
, req
);
118 if (!err
&& operation
== CEPH_MDS_OP_GETFILELOCK
) {
119 fl
->fl_pid
= -le64_to_cpu(req
->r_reply_info
.filelock_reply
->pid
);
120 if (CEPH_LOCK_SHARED
== req
->r_reply_info
.filelock_reply
->type
)
121 fl
->fl_type
= F_RDLCK
;
122 else if (CEPH_LOCK_EXCL
== req
->r_reply_info
.filelock_reply
->type
)
123 fl
->fl_type
= F_WRLCK
;
125 fl
->fl_type
= F_UNLCK
;
127 fl
->fl_start
= le64_to_cpu(req
->r_reply_info
.filelock_reply
->start
);
128 length
= le64_to_cpu(req
->r_reply_info
.filelock_reply
->start
) +
129 le64_to_cpu(req
->r_reply_info
.filelock_reply
->length
);
131 fl
->fl_end
= length
-1;
136 ceph_mdsc_put_request(req
);
137 dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
138 "length: %llu, wait: %d, type: %d, err code %d\n", (int)lock_type
,
139 (int)operation
, (u64
)fl
->fl_pid
, fl
->fl_start
,
140 length
, wait
, fl
->fl_type
, err
);
144 static int ceph_lock_wait_for_completion(struct ceph_mds_client
*mdsc
,
145 struct ceph_mds_request
*req
)
147 struct ceph_mds_request
*intr_req
;
148 struct inode
*inode
= req
->r_inode
;
151 BUG_ON(req
->r_op
!= CEPH_MDS_OP_SETFILELOCK
);
152 if (req
->r_args
.filelock_change
.rule
== CEPH_LOCK_FCNTL
)
153 lock_type
= CEPH_LOCK_FCNTL_INTR
;
154 else if (req
->r_args
.filelock_change
.rule
== CEPH_LOCK_FLOCK
)
155 lock_type
= CEPH_LOCK_FLOCK_INTR
;
158 BUG_ON(req
->r_args
.filelock_change
.type
== CEPH_LOCK_UNLOCK
);
160 err
= wait_for_completion_interruptible(&req
->r_completion
);
164 dout("ceph_lock_wait_for_completion: request %llu was interrupted\n",
167 mutex_lock(&mdsc
->mutex
);
168 if (test_bit(CEPH_MDS_R_GOT_RESULT
, &req
->r_req_flags
)) {
172 * ensure we aren't running concurrently with
173 * ceph_fill_trace or ceph_readdir_prepopulate, which
174 * rely on locks (dir mutex) held by our caller.
176 mutex_lock(&req
->r_fill_mutex
);
178 set_bit(CEPH_MDS_R_ABORTED
, &req
->r_req_flags
);
179 mutex_unlock(&req
->r_fill_mutex
);
181 if (!req
->r_session
) {
182 // haven't sent the request
186 mutex_unlock(&mdsc
->mutex
);
190 intr_req
= ceph_mdsc_create_request(mdsc
, CEPH_MDS_OP_SETFILELOCK
,
192 if (IS_ERR(intr_req
))
193 return PTR_ERR(intr_req
);
195 intr_req
->r_inode
= inode
;
197 intr_req
->r_num_caps
= 1;
199 intr_req
->r_args
.filelock_change
= req
->r_args
.filelock_change
;
200 intr_req
->r_args
.filelock_change
.rule
= lock_type
;
201 intr_req
->r_args
.filelock_change
.type
= CEPH_LOCK_UNLOCK
;
203 err
= ceph_mdsc_do_request(mdsc
, inode
, intr_req
);
204 ceph_mdsc_put_request(intr_req
);
206 if (err
&& err
!= -ERESTARTSYS
)
209 wait_for_completion_killable(&req
->r_safe_completion
);
213 static int try_unlock_file(struct file
*file
, struct file_lock
*fl
)
216 unsigned int orig_flags
= fl
->fl_flags
;
217 fl
->fl_flags
|= FL_EXISTS
;
218 err
= locks_lock_file_wait(file
, fl
);
219 fl
->fl_flags
= orig_flags
;
220 if (err
== -ENOENT
) {
221 if (!(orig_flags
& FL_EXISTS
))
229 * Attempt to set an fcntl lock.
230 * For now, this just goes away to the server. Later it may be more awesome.
232 int ceph_lock(struct file
*file
, int cmd
, struct file_lock
*fl
)
234 struct inode
*inode
= file_inode(file
);
235 struct ceph_inode_info
*ci
= ceph_inode(inode
);
237 u16 op
= CEPH_MDS_OP_SETFILELOCK
;
241 if (!(fl
->fl_flags
& FL_POSIX
))
243 /* No mandatory locks */
244 if (__mandatory_lock(file
->f_mapping
->host
) && fl
->fl_type
!= F_UNLCK
)
247 dout("ceph_lock, fl_owner: %p\n", fl
->fl_owner
);
249 /* set wait bit as appropriate, then make command as Ceph expects it*/
251 op
= CEPH_MDS_OP_GETFILELOCK
;
252 else if (IS_SETLKW(cmd
))
255 spin_lock(&ci
->i_ceph_lock
);
256 if (ci
->i_ceph_flags
& CEPH_I_ERROR_FILELOCK
) {
259 spin_unlock(&ci
->i_ceph_lock
);
261 if (op
== CEPH_MDS_OP_SETFILELOCK
&& F_UNLCK
== fl
->fl_type
)
262 posix_lock_file(file
, fl
, NULL
);
266 if (F_RDLCK
== fl
->fl_type
)
267 lock_cmd
= CEPH_LOCK_SHARED
;
268 else if (F_WRLCK
== fl
->fl_type
)
269 lock_cmd
= CEPH_LOCK_EXCL
;
271 lock_cmd
= CEPH_LOCK_UNLOCK
;
273 if (op
== CEPH_MDS_OP_SETFILELOCK
&& F_UNLCK
== fl
->fl_type
) {
274 err
= try_unlock_file(file
, fl
);
279 err
= ceph_lock_message(CEPH_LOCK_FCNTL
, op
, inode
, lock_cmd
, wait
, fl
);
281 if (op
== CEPH_MDS_OP_SETFILELOCK
&& F_UNLCK
!= fl
->fl_type
) {
282 dout("mds locked, locking locally\n");
283 err
= posix_lock_file(file
, fl
, NULL
);
285 /* undo! This should only happen if
286 * the kernel detects local
288 ceph_lock_message(CEPH_LOCK_FCNTL
, op
, inode
,
289 CEPH_LOCK_UNLOCK
, 0, fl
);
290 dout("got %d on posix_lock_file, undid lock\n",
298 int ceph_flock(struct file
*file
, int cmd
, struct file_lock
*fl
)
300 struct inode
*inode
= file_inode(file
);
301 struct ceph_inode_info
*ci
= ceph_inode(inode
);
306 if (!(fl
->fl_flags
& FL_FLOCK
))
308 /* No mandatory locks */
309 if (fl
->fl_type
& LOCK_MAND
)
312 dout("ceph_flock, fl_file: %p\n", fl
->fl_file
);
314 spin_lock(&ci
->i_ceph_lock
);
315 if (ci
->i_ceph_flags
& CEPH_I_ERROR_FILELOCK
) {
318 spin_unlock(&ci
->i_ceph_lock
);
320 if (F_UNLCK
== fl
->fl_type
)
321 locks_lock_file_wait(file
, fl
);
328 if (F_RDLCK
== fl
->fl_type
)
329 lock_cmd
= CEPH_LOCK_SHARED
;
330 else if (F_WRLCK
== fl
->fl_type
)
331 lock_cmd
= CEPH_LOCK_EXCL
;
333 lock_cmd
= CEPH_LOCK_UNLOCK
;
335 if (F_UNLCK
== fl
->fl_type
) {
336 err
= try_unlock_file(file
, fl
);
341 err
= ceph_lock_message(CEPH_LOCK_FLOCK
, CEPH_MDS_OP_SETFILELOCK
,
342 inode
, lock_cmd
, wait
, fl
);
343 if (!err
&& F_UNLCK
!= fl
->fl_type
) {
344 err
= locks_lock_file_wait(file
, fl
);
346 ceph_lock_message(CEPH_LOCK_FLOCK
,
347 CEPH_MDS_OP_SETFILELOCK
,
348 inode
, CEPH_LOCK_UNLOCK
, 0, fl
);
349 dout("got %d on locks_lock_file_wait, undid lock\n", err
);
356 * Fills in the passed counter variables, so you can prepare pagelist metadata
357 * before calling ceph_encode_locks.
359 void ceph_count_locks(struct inode
*inode
, int *fcntl_count
, int *flock_count
)
361 struct file_lock
*lock
;
362 struct file_lock_context
*ctx
;
367 ctx
= inode
->i_flctx
;
369 spin_lock(&ctx
->flc_lock
);
370 list_for_each_entry(lock
, &ctx
->flc_posix
, fl_list
)
372 list_for_each_entry(lock
, &ctx
->flc_flock
, fl_list
)
374 spin_unlock(&ctx
->flc_lock
);
376 dout("counted %d flock locks and %d fcntl locks\n",
377 *flock_count
, *fcntl_count
);
381 * Given a pointer to a lock, convert it to a ceph filelock
383 static int lock_to_ceph_filelock(struct file_lock
*lock
,
384 struct ceph_filelock
*cephlock
)
387 cephlock
->start
= cpu_to_le64(lock
->fl_start
);
388 cephlock
->length
= cpu_to_le64(lock
->fl_end
- lock
->fl_start
+ 1);
389 cephlock
->client
= cpu_to_le64(0);
390 cephlock
->pid
= cpu_to_le64((u64
)lock
->fl_pid
);
391 cephlock
->owner
= cpu_to_le64(secure_addr(lock
->fl_owner
));
393 switch (lock
->fl_type
) {
395 cephlock
->type
= CEPH_LOCK_SHARED
;
398 cephlock
->type
= CEPH_LOCK_EXCL
;
401 cephlock
->type
= CEPH_LOCK_UNLOCK
;
404 dout("Have unknown lock type %d\n", lock
->fl_type
);
412 * Encode the flock and fcntl locks for the given inode into the ceph_filelock
413 * array. Must be called with inode->i_lock already held.
414 * If we encounter more of a specific lock type than expected, return -ENOSPC.
416 int ceph_encode_locks_to_buffer(struct inode
*inode
,
417 struct ceph_filelock
*flocks
,
418 int num_fcntl_locks
, int num_flock_locks
)
420 struct file_lock
*lock
;
421 struct file_lock_context
*ctx
= inode
->i_flctx
;
427 dout("encoding %d flock and %d fcntl locks\n", num_flock_locks
,
433 spin_lock(&ctx
->flc_lock
);
434 list_for_each_entry(lock
, &ctx
->flc_posix
, fl_list
) {
436 if (seen_fcntl
> num_fcntl_locks
) {
440 err
= lock_to_ceph_filelock(lock
, &flocks
[l
]);
445 list_for_each_entry(lock
, &ctx
->flc_flock
, fl_list
) {
447 if (seen_flock
> num_flock_locks
) {
451 err
= lock_to_ceph_filelock(lock
, &flocks
[l
]);
457 spin_unlock(&ctx
->flc_lock
);
462 * Copy the encoded flock and fcntl locks into the pagelist.
463 * Format is: #fcntl locks, sequential fcntl locks, #flock locks,
464 * sequential flock locks.
465 * Returns zero on success.
467 int ceph_locks_to_pagelist(struct ceph_filelock
*flocks
,
468 struct ceph_pagelist
*pagelist
,
469 int num_fcntl_locks
, int num_flock_locks
)
474 nlocks
= cpu_to_le32(num_fcntl_locks
);
475 err
= ceph_pagelist_append(pagelist
, &nlocks
, sizeof(nlocks
));
479 if (num_fcntl_locks
> 0) {
480 err
= ceph_pagelist_append(pagelist
, flocks
,
481 num_fcntl_locks
* sizeof(*flocks
));
486 nlocks
= cpu_to_le32(num_flock_locks
);
487 err
= ceph_pagelist_append(pagelist
, &nlocks
, sizeof(nlocks
));
491 if (num_flock_locks
> 0) {
492 err
= ceph_pagelist_append(pagelist
, &flocks
[num_fcntl_locks
],
493 num_flock_locks
* sizeof(*flocks
));