License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[sfrench/cifs-2.6.git] / fs / ceph / locks.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/ceph/ceph_debug.h>
3
4 #include <linux/file.h>
5 #include <linux/namei.h>
6 #include <linux/random.h>
7
8 #include "super.h"
9 #include "mds_client.h"
10 #include <linux/ceph/pagelist.h>
11
12 static u64 lock_secret;
13 static int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc,
14                                          struct ceph_mds_request *req);
15
16 static inline u64 secure_addr(void *addr)
17 {
18         u64 v = lock_secret ^ (u64)(unsigned long)addr;
19         /*
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')
23          */
24         v |= (1ULL << 63);
25         return v;
26 }
27
28 void __init ceph_flock_init(void)
29 {
30         get_random_bytes(&lock_secret, sizeof(lock_secret));
31 }
32
33 /**
34  * Implement fcntl and flock locking functions.
35  */
36 static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
37                              int cmd, u8 wait, struct file_lock *fl)
38 {
39         struct inode *inode = file_inode(file);
40         struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
41         struct ceph_mds_request *req;
42         int err;
43         u64 length = 0;
44         u64 owner;
45
46         if (operation != CEPH_MDS_OP_SETFILELOCK || cmd == CEPH_LOCK_UNLOCK)
47                 wait = 0;
48
49         req = ceph_mdsc_create_request(mdsc, operation, USE_AUTH_MDS);
50         if (IS_ERR(req))
51                 return PTR_ERR(req);
52         req->r_inode = inode;
53         ihold(inode);
54         req->r_num_caps = 1;
55
56         /* mds requires start and length rather than start and end */
57         if (LLONG_MAX == fl->fl_end)
58                 length = 0;
59         else
60                 length = fl->fl_end - fl->fl_start + 1;
61
62         owner = secure_addr(fl->fl_owner);
63
64         dout("ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, "
65              "start: %llu, length: %llu, wait: %d, type: %d", (int)lock_type,
66              (int)operation, owner, (u64)fl->fl_pid, fl->fl_start, length,
67              wait, fl->fl_type);
68
69         req->r_args.filelock_change.rule = lock_type;
70         req->r_args.filelock_change.type = cmd;
71         req->r_args.filelock_change.owner = cpu_to_le64(owner);
72         req->r_args.filelock_change.pid = cpu_to_le64((u64)fl->fl_pid);
73         req->r_args.filelock_change.start = cpu_to_le64(fl->fl_start);
74         req->r_args.filelock_change.length = cpu_to_le64(length);
75         req->r_args.filelock_change.wait = wait;
76
77         if (wait)
78                 req->r_wait_for_completion = ceph_lock_wait_for_completion;
79
80         err = ceph_mdsc_do_request(mdsc, inode, req);
81
82         if (operation == CEPH_MDS_OP_GETFILELOCK) {
83                 fl->fl_pid = -le64_to_cpu(req->r_reply_info.filelock_reply->pid);
84                 if (CEPH_LOCK_SHARED == req->r_reply_info.filelock_reply->type)
85                         fl->fl_type = F_RDLCK;
86                 else if (CEPH_LOCK_EXCL == req->r_reply_info.filelock_reply->type)
87                         fl->fl_type = F_WRLCK;
88                 else
89                         fl->fl_type = F_UNLCK;
90
91                 fl->fl_start = le64_to_cpu(req->r_reply_info.filelock_reply->start);
92                 length = le64_to_cpu(req->r_reply_info.filelock_reply->start) +
93                                                  le64_to_cpu(req->r_reply_info.filelock_reply->length);
94                 if (length >= 1)
95                         fl->fl_end = length -1;
96                 else
97                         fl->fl_end = 0;
98
99         }
100         ceph_mdsc_put_request(req);
101         dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
102              "length: %llu, wait: %d, type: %d, err code %d", (int)lock_type,
103              (int)operation, (u64)fl->fl_pid, fl->fl_start,
104              length, wait, fl->fl_type, err);
105         return err;
106 }
107
108 static int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc,
109                                          struct ceph_mds_request *req)
110 {
111         struct ceph_mds_request *intr_req;
112         struct inode *inode = req->r_inode;
113         int err, lock_type;
114
115         BUG_ON(req->r_op != CEPH_MDS_OP_SETFILELOCK);
116         if (req->r_args.filelock_change.rule == CEPH_LOCK_FCNTL)
117                 lock_type = CEPH_LOCK_FCNTL_INTR;
118         else if (req->r_args.filelock_change.rule == CEPH_LOCK_FLOCK)
119                 lock_type = CEPH_LOCK_FLOCK_INTR;
120         else
121                 BUG_ON(1);
122         BUG_ON(req->r_args.filelock_change.type == CEPH_LOCK_UNLOCK);
123
124         err = wait_for_completion_interruptible(&req->r_completion);
125         if (!err)
126                 return 0;
127
128         dout("ceph_lock_wait_for_completion: request %llu was interrupted\n",
129              req->r_tid);
130
131         mutex_lock(&mdsc->mutex);
132         if (test_bit(CEPH_MDS_R_GOT_RESULT, &req->r_req_flags)) {
133                 err = 0;
134         } else {
135                 /*
136                  * ensure we aren't running concurrently with
137                  * ceph_fill_trace or ceph_readdir_prepopulate, which
138                  * rely on locks (dir mutex) held by our caller.
139                  */
140                 mutex_lock(&req->r_fill_mutex);
141                 req->r_err = err;
142                 set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags);
143                 mutex_unlock(&req->r_fill_mutex);
144
145                 if (!req->r_session) {
146                         // haven't sent the request
147                         err = 0;
148                 }
149         }
150         mutex_unlock(&mdsc->mutex);
151         if (!err)
152                 return 0;
153
154         intr_req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETFILELOCK,
155                                             USE_AUTH_MDS);
156         if (IS_ERR(intr_req))
157                 return PTR_ERR(intr_req);
158
159         intr_req->r_inode = inode;
160         ihold(inode);
161         intr_req->r_num_caps = 1;
162
163         intr_req->r_args.filelock_change = req->r_args.filelock_change;
164         intr_req->r_args.filelock_change.rule = lock_type;
165         intr_req->r_args.filelock_change.type = CEPH_LOCK_UNLOCK;
166
167         err = ceph_mdsc_do_request(mdsc, inode, intr_req);
168         ceph_mdsc_put_request(intr_req);
169
170         if (err && err != -ERESTARTSYS)
171                 return err;
172
173         wait_for_completion_killable(&req->r_safe_completion);
174         return 0;
175 }
176
177 /**
178  * Attempt to set an fcntl lock.
179  * For now, this just goes away to the server. Later it may be more awesome.
180  */
181 int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
182 {
183         u8 lock_cmd;
184         int err;
185         u8 wait = 0;
186         u16 op = CEPH_MDS_OP_SETFILELOCK;
187
188         if (!(fl->fl_flags & FL_POSIX))
189                 return -ENOLCK;
190         /* No mandatory locks */
191         if (__mandatory_lock(file->f_mapping->host) && fl->fl_type != F_UNLCK)
192                 return -ENOLCK;
193
194         dout("ceph_lock, fl_owner: %p", fl->fl_owner);
195
196         /* set wait bit as appropriate, then make command as Ceph expects it*/
197         if (IS_GETLK(cmd))
198                 op = CEPH_MDS_OP_GETFILELOCK;
199         else if (IS_SETLKW(cmd))
200                 wait = 1;
201
202         if (F_RDLCK == fl->fl_type)
203                 lock_cmd = CEPH_LOCK_SHARED;
204         else if (F_WRLCK == fl->fl_type)
205                 lock_cmd = CEPH_LOCK_EXCL;
206         else
207                 lock_cmd = CEPH_LOCK_UNLOCK;
208
209         err = ceph_lock_message(CEPH_LOCK_FCNTL, op, file, lock_cmd, wait, fl);
210         if (!err) {
211                 if (op != CEPH_MDS_OP_GETFILELOCK) {
212                         dout("mds locked, locking locally");
213                         err = posix_lock_file(file, fl, NULL);
214                         if (err && (CEPH_MDS_OP_SETFILELOCK == op)) {
215                                 /* undo! This should only happen if
216                                  * the kernel detects local
217                                  * deadlock. */
218                                 ceph_lock_message(CEPH_LOCK_FCNTL, op, file,
219                                                   CEPH_LOCK_UNLOCK, 0, fl);
220                                 dout("got %d on posix_lock_file, undid lock",
221                                      err);
222                         }
223                 }
224         }
225         return err;
226 }
227
228 int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
229 {
230         u8 lock_cmd;
231         int err;
232         u8 wait = 0;
233
234         if (!(fl->fl_flags & FL_FLOCK))
235                 return -ENOLCK;
236         /* No mandatory locks */
237         if (fl->fl_type & LOCK_MAND)
238                 return -EOPNOTSUPP;
239
240         dout("ceph_flock, fl_file: %p", fl->fl_file);
241
242         if (IS_SETLKW(cmd))
243                 wait = 1;
244
245         if (F_RDLCK == fl->fl_type)
246                 lock_cmd = CEPH_LOCK_SHARED;
247         else if (F_WRLCK == fl->fl_type)
248                 lock_cmd = CEPH_LOCK_EXCL;
249         else
250                 lock_cmd = CEPH_LOCK_UNLOCK;
251
252         err = ceph_lock_message(CEPH_LOCK_FLOCK, CEPH_MDS_OP_SETFILELOCK,
253                                 file, lock_cmd, wait, fl);
254         if (!err) {
255                 err = locks_lock_file_wait(file, fl);
256                 if (err) {
257                         ceph_lock_message(CEPH_LOCK_FLOCK,
258                                           CEPH_MDS_OP_SETFILELOCK,
259                                           file, CEPH_LOCK_UNLOCK, 0, fl);
260                         dout("got %d on locks_lock_file_wait, undid lock", err);
261                 }
262         }
263         return err;
264 }
265
266 /*
267  * Fills in the passed counter variables, so you can prepare pagelist metadata
268  * before calling ceph_encode_locks.
269  */
270 void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count)
271 {
272         struct file_lock *lock;
273         struct file_lock_context *ctx;
274
275         *fcntl_count = 0;
276         *flock_count = 0;
277
278         ctx = inode->i_flctx;
279         if (ctx) {
280                 spin_lock(&ctx->flc_lock);
281                 list_for_each_entry(lock, &ctx->flc_posix, fl_list)
282                         ++(*fcntl_count);
283                 list_for_each_entry(lock, &ctx->flc_flock, fl_list)
284                         ++(*flock_count);
285                 spin_unlock(&ctx->flc_lock);
286         }
287         dout("counted %d flock locks and %d fcntl locks",
288              *flock_count, *fcntl_count);
289 }
290
291 /**
292  * Encode the flock and fcntl locks for the given inode into the ceph_filelock
293  * array. Must be called with inode->i_lock already held.
294  * If we encounter more of a specific lock type than expected, return -ENOSPC.
295  */
296 int ceph_encode_locks_to_buffer(struct inode *inode,
297                                 struct ceph_filelock *flocks,
298                                 int num_fcntl_locks, int num_flock_locks)
299 {
300         struct file_lock *lock;
301         struct file_lock_context *ctx = inode->i_flctx;
302         int err = 0;
303         int seen_fcntl = 0;
304         int seen_flock = 0;
305         int l = 0;
306
307         dout("encoding %d flock and %d fcntl locks", num_flock_locks,
308              num_fcntl_locks);
309
310         if (!ctx)
311                 return 0;
312
313         spin_lock(&ctx->flc_lock);
314         list_for_each_entry(lock, &ctx->flc_posix, fl_list) {
315                 ++seen_fcntl;
316                 if (seen_fcntl > num_fcntl_locks) {
317                         err = -ENOSPC;
318                         goto fail;
319                 }
320                 err = lock_to_ceph_filelock(lock, &flocks[l]);
321                 if (err)
322                         goto fail;
323                 ++l;
324         }
325         list_for_each_entry(lock, &ctx->flc_flock, fl_list) {
326                 ++seen_flock;
327                 if (seen_flock > num_flock_locks) {
328                         err = -ENOSPC;
329                         goto fail;
330                 }
331                 err = lock_to_ceph_filelock(lock, &flocks[l]);
332                 if (err)
333                         goto fail;
334                 ++l;
335         }
336 fail:
337         spin_unlock(&ctx->flc_lock);
338         return err;
339 }
340
341 /**
342  * Copy the encoded flock and fcntl locks into the pagelist.
343  * Format is: #fcntl locks, sequential fcntl locks, #flock locks,
344  * sequential flock locks.
345  * Returns zero on success.
346  */
347 int ceph_locks_to_pagelist(struct ceph_filelock *flocks,
348                            struct ceph_pagelist *pagelist,
349                            int num_fcntl_locks, int num_flock_locks)
350 {
351         int err = 0;
352         __le32 nlocks;
353
354         nlocks = cpu_to_le32(num_fcntl_locks);
355         err = ceph_pagelist_append(pagelist, &nlocks, sizeof(nlocks));
356         if (err)
357                 goto out_fail;
358
359         err = ceph_pagelist_append(pagelist, flocks,
360                                    num_fcntl_locks * sizeof(*flocks));
361         if (err)
362                 goto out_fail;
363
364         nlocks = cpu_to_le32(num_flock_locks);
365         err = ceph_pagelist_append(pagelist, &nlocks, sizeof(nlocks));
366         if (err)
367                 goto out_fail;
368
369         err = ceph_pagelist_append(pagelist,
370                                    &flocks[num_fcntl_locks],
371                                    num_flock_locks * sizeof(*flocks));
372 out_fail:
373         return err;
374 }
375
376 /*
377  * Given a pointer to a lock, convert it to a ceph filelock
378  */
379 int lock_to_ceph_filelock(struct file_lock *lock,
380                           struct ceph_filelock *cephlock)
381 {
382         int err = 0;
383         cephlock->start = cpu_to_le64(lock->fl_start);
384         cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
385         cephlock->client = cpu_to_le64(0);
386         cephlock->pid = cpu_to_le64((u64)lock->fl_pid);
387         cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
388
389         switch (lock->fl_type) {
390         case F_RDLCK:
391                 cephlock->type = CEPH_LOCK_SHARED;
392                 break;
393         case F_WRLCK:
394                 cephlock->type = CEPH_LOCK_EXCL;
395                 break;
396         case F_UNLCK:
397                 cephlock->type = CEPH_LOCK_UNLOCK;
398                 break;
399         default:
400                 dout("Have unknown lock type %d", lock->fl_type);
401                 err = -EINVAL;
402         }
403
404         return err;
405 }