1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * RDMA Network Block Driver |
4 | * |
5 | * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved. |
6 | * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved. |
7 | * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved. |
8 | */ |
9 | #undef pr_fmt |
10 | #define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/blkdev.h> |
14 | |
15 | #include "rnbd-srv.h" |
16 | #include "rnbd-srv-trace.h" |
17 | |
18 | MODULE_DESCRIPTION("RDMA Network Block Device Server" ); |
19 | MODULE_LICENSE("GPL" ); |
20 | |
21 | static u16 port_nr = RTRS_PORT; |
22 | |
23 | module_param_named(port_nr, port_nr, ushort, 0444); |
24 | MODULE_PARM_DESC(port_nr, |
25 | "The port number the server is listening on (default: " |
26 | __stringify(RTRS_PORT)")" ); |
27 | |
28 | #define DEFAULT_DEV_SEARCH_PATH "/" |
29 | |
30 | static char dev_search_path[PATH_MAX] = DEFAULT_DEV_SEARCH_PATH; |
31 | |
32 | static int dev_search_path_set(const char *val, const struct kernel_param *kp) |
33 | { |
34 | const char *p = strrchr(val, '\n') ? : val + strlen(val); |
35 | |
36 | if (strlen(val) >= sizeof(dev_search_path)) |
37 | return -EINVAL; |
38 | |
39 | snprintf(buf: dev_search_path, size: sizeof(dev_search_path), fmt: "%.*s" , |
40 | (int)(p - val), val); |
41 | |
42 | pr_info("dev_search_path changed to '%s'\n" , dev_search_path); |
43 | |
44 | return 0; |
45 | } |
46 | |
47 | static struct kparam_string dev_search_path_kparam_str = { |
48 | .maxlen = sizeof(dev_search_path), |
49 | .string = dev_search_path |
50 | }; |
51 | |
52 | static const struct kernel_param_ops dev_search_path_ops = { |
53 | .set = dev_search_path_set, |
54 | .get = param_get_string, |
55 | }; |
56 | |
57 | module_param_cb(dev_search_path, &dev_search_path_ops, |
58 | &dev_search_path_kparam_str, 0444); |
59 | MODULE_PARM_DESC(dev_search_path, |
60 | "Sets the dev_search_path. When a device is mapped this path is prepended to the device path from the map device operation. If %SESSNAME% is specified in a path, then device will be searched in a session namespace. (default: " |
61 | DEFAULT_DEV_SEARCH_PATH ")" ); |
62 | |
63 | static DEFINE_MUTEX(sess_lock); |
64 | static DEFINE_SPINLOCK(dev_lock); |
65 | |
66 | static LIST_HEAD(sess_list); |
67 | static LIST_HEAD(dev_list); |
68 | |
69 | struct rnbd_io_private { |
70 | struct rtrs_srv_op *id; |
71 | struct rnbd_srv_sess_dev *sess_dev; |
72 | }; |
73 | |
74 | static void rnbd_sess_dev_release(struct kref *kref) |
75 | { |
76 | struct rnbd_srv_sess_dev *sess_dev; |
77 | |
78 | sess_dev = container_of(kref, struct rnbd_srv_sess_dev, kref); |
79 | complete(sess_dev->destroy_comp); |
80 | } |
81 | |
82 | static inline void rnbd_put_sess_dev(struct rnbd_srv_sess_dev *sess_dev) |
83 | { |
84 | kref_put(kref: &sess_dev->kref, release: rnbd_sess_dev_release); |
85 | } |
86 | |
87 | static struct rnbd_srv_sess_dev * |
88 | rnbd_get_sess_dev(int dev_id, struct rnbd_srv_session *srv_sess) |
89 | { |
90 | struct rnbd_srv_sess_dev *sess_dev; |
91 | int ret = 0; |
92 | |
93 | rcu_read_lock(); |
94 | sess_dev = xa_load(&srv_sess->index_idr, index: dev_id); |
95 | if (sess_dev) |
96 | ret = kref_get_unless_zero(kref: &sess_dev->kref); |
97 | rcu_read_unlock(); |
98 | |
99 | if (!ret) |
100 | return ERR_PTR(error: -ENXIO); |
101 | |
102 | return sess_dev; |
103 | } |
104 | |
105 | static void rnbd_dev_bi_end_io(struct bio *bio) |
106 | { |
107 | struct rnbd_io_private *rnbd_priv = bio->bi_private; |
108 | struct rnbd_srv_sess_dev *sess_dev = rnbd_priv->sess_dev; |
109 | |
110 | rnbd_put_sess_dev(sess_dev); |
111 | rtrs_srv_resp_rdma(id: rnbd_priv->id, errno: blk_status_to_errno(status: bio->bi_status)); |
112 | |
113 | kfree(objp: rnbd_priv); |
114 | bio_put(bio); |
115 | } |
116 | |
117 | static int process_rdma(struct rnbd_srv_session *srv_sess, |
118 | struct rtrs_srv_op *id, void *data, u32 datalen, |
119 | const void *usr, size_t usrlen) |
120 | { |
121 | const struct rnbd_msg_io *msg = usr; |
122 | struct rnbd_io_private *priv; |
123 | struct rnbd_srv_sess_dev *sess_dev; |
124 | u32 dev_id; |
125 | int err; |
126 | struct bio *bio; |
127 | short prio; |
128 | |
129 | trace_process_rdma(srv: srv_sess, msg, id, datalen, usrlen); |
130 | |
131 | priv = kmalloc(size: sizeof(*priv), GFP_KERNEL); |
132 | if (!priv) |
133 | return -ENOMEM; |
134 | |
135 | dev_id = le32_to_cpu(msg->device_id); |
136 | |
137 | sess_dev = rnbd_get_sess_dev(dev_id, srv_sess); |
138 | if (IS_ERR(ptr: sess_dev)) { |
139 | pr_err_ratelimited("Got I/O request on session %s for unknown device id %d: %pe\n" , |
140 | srv_sess->sessname, dev_id, sess_dev); |
141 | err = -ENOTCONN; |
142 | goto err; |
143 | } |
144 | |
145 | priv->sess_dev = sess_dev; |
146 | priv->id = id; |
147 | |
148 | bio = bio_alloc(bdev: file_bdev(bdev_file: sess_dev->bdev_file), nr_vecs: 1, |
149 | opf: rnbd_to_bio_flags(le32_to_cpu(msg->rw)), GFP_KERNEL); |
150 | if (bio_add_page(bio, virt_to_page(data), len: datalen, |
151 | offset_in_page(data)) != datalen) { |
152 | rnbd_srv_err(sess_dev, "Failed to map data to bio\n" ); |
153 | err = -EINVAL; |
154 | goto bio_put; |
155 | } |
156 | |
157 | bio->bi_end_io = rnbd_dev_bi_end_io; |
158 | bio->bi_private = priv; |
159 | bio->bi_iter.bi_sector = le64_to_cpu(msg->sector); |
160 | bio->bi_iter.bi_size = le32_to_cpu(msg->bi_size); |
161 | prio = srv_sess->ver < RNBD_PROTO_VER_MAJOR || |
162 | usrlen < sizeof(*msg) ? 0 : le16_to_cpu(msg->prio); |
163 | bio_set_prio(bio, prio); |
164 | |
165 | submit_bio(bio); |
166 | |
167 | return 0; |
168 | |
169 | bio_put: |
170 | bio_put(bio); |
171 | rnbd_put_sess_dev(sess_dev); |
172 | err: |
173 | kfree(objp: priv); |
174 | return err; |
175 | } |
176 | |
177 | static void destroy_device(struct kref *kref) |
178 | { |
179 | struct rnbd_srv_dev *dev = container_of(kref, struct rnbd_srv_dev, kref); |
180 | |
181 | WARN_ONCE(!list_empty(&dev->sess_dev_list), |
182 | "Device %s is being destroyed but still in use!\n" , |
183 | dev->name); |
184 | |
185 | spin_lock(lock: &dev_lock); |
186 | list_del(entry: &dev->list); |
187 | spin_unlock(lock: &dev_lock); |
188 | |
189 | mutex_destroy(lock: &dev->lock); |
190 | if (dev->dev_kobj.state_in_sysfs) |
191 | /* |
192 | * Destroy kobj only if it was really created. |
193 | */ |
194 | rnbd_srv_destroy_dev_sysfs(dev); |
195 | else |
196 | kfree(objp: dev); |
197 | } |
198 | |
199 | static void rnbd_put_srv_dev(struct rnbd_srv_dev *dev) |
200 | { |
201 | kref_put(kref: &dev->kref, release: destroy_device); |
202 | } |
203 | |
204 | void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id) |
205 | { |
206 | DECLARE_COMPLETION_ONSTACK(dc); |
207 | |
208 | if (keep_id) |
209 | /* free the resources for the id but don't */ |
210 | /* allow to re-use the id itself because it */ |
211 | /* is still used by the client */ |
212 | xa_cmpxchg(xa: &sess_dev->sess->index_idr, index: sess_dev->device_id, |
213 | old: sess_dev, NULL, gfp: 0); |
214 | else |
215 | xa_erase(&sess_dev->sess->index_idr, index: sess_dev->device_id); |
216 | synchronize_rcu(); |
217 | |
218 | sess_dev->destroy_comp = &dc; |
219 | rnbd_put_sess_dev(sess_dev); |
220 | wait_for_completion(&dc); /* wait for inflights to drop to zero */ |
221 | |
222 | fput(sess_dev->bdev_file); |
223 | mutex_lock(&sess_dev->dev->lock); |
224 | list_del(entry: &sess_dev->dev_list); |
225 | if (!sess_dev->readonly) |
226 | sess_dev->dev->open_write_cnt--; |
227 | mutex_unlock(lock: &sess_dev->dev->lock); |
228 | |
229 | rnbd_put_srv_dev(dev: sess_dev->dev); |
230 | |
231 | rnbd_srv_info(sess_dev, "Device closed\n" ); |
232 | kfree(objp: sess_dev); |
233 | } |
234 | |
235 | static void destroy_sess(struct rnbd_srv_session *srv_sess) |
236 | { |
237 | struct rnbd_srv_sess_dev *sess_dev; |
238 | unsigned long index; |
239 | |
240 | if (xa_empty(xa: &srv_sess->index_idr)) |
241 | goto out; |
242 | |
243 | trace_destroy_sess(srv: srv_sess); |
244 | |
245 | mutex_lock(&srv_sess->lock); |
246 | xa_for_each(&srv_sess->index_idr, index, sess_dev) |
247 | rnbd_srv_destroy_dev_session_sysfs(sess_dev); |
248 | mutex_unlock(lock: &srv_sess->lock); |
249 | |
250 | out: |
251 | xa_destroy(&srv_sess->index_idr); |
252 | |
253 | pr_info("RTRS Session %s disconnected\n" , srv_sess->sessname); |
254 | |
255 | mutex_lock(&sess_lock); |
256 | list_del(entry: &srv_sess->list); |
257 | mutex_unlock(lock: &sess_lock); |
258 | |
259 | mutex_destroy(lock: &srv_sess->lock); |
260 | kfree(objp: srv_sess); |
261 | } |
262 | |
263 | static int create_sess(struct rtrs_srv_sess *rtrs) |
264 | { |
265 | struct rnbd_srv_session *srv_sess; |
266 | char pathname[NAME_MAX]; |
267 | int err; |
268 | |
269 | err = rtrs_srv_get_path_name(sess: rtrs, pathname, len: sizeof(pathname)); |
270 | if (err) { |
271 | pr_err("rtrs_srv_get_path_name(%s): %d\n" , pathname, err); |
272 | |
273 | return err; |
274 | } |
275 | srv_sess = kzalloc(size: sizeof(*srv_sess), GFP_KERNEL); |
276 | if (!srv_sess) |
277 | return -ENOMEM; |
278 | |
279 | srv_sess->queue_depth = rtrs_srv_get_queue_depth(sess: rtrs); |
280 | xa_init_flags(xa: &srv_sess->index_idr, XA_FLAGS_ALLOC); |
281 | mutex_init(&srv_sess->lock); |
282 | mutex_lock(&sess_lock); |
283 | list_add(new: &srv_sess->list, head: &sess_list); |
284 | mutex_unlock(lock: &sess_lock); |
285 | |
286 | srv_sess->rtrs = rtrs; |
287 | strscpy(srv_sess->sessname, pathname, sizeof(srv_sess->sessname)); |
288 | |
289 | rtrs_srv_set_sess_priv(sess: rtrs, priv: srv_sess); |
290 | |
291 | trace_create_sess(srv: srv_sess); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static int rnbd_srv_link_ev(struct rtrs_srv_sess *rtrs, |
297 | enum rtrs_srv_link_ev ev, void *priv) |
298 | { |
299 | struct rnbd_srv_session *srv_sess = priv; |
300 | |
301 | switch (ev) { |
302 | case RTRS_SRV_LINK_EV_CONNECTED: |
303 | return create_sess(rtrs); |
304 | |
305 | case RTRS_SRV_LINK_EV_DISCONNECTED: |
306 | if (WARN_ON_ONCE(!srv_sess)) |
307 | return -EINVAL; |
308 | |
309 | destroy_sess(srv_sess); |
310 | return 0; |
311 | |
312 | default: |
313 | pr_warn("Received unknown RTRS session event %d from session %s\n" , |
314 | ev, srv_sess->sessname); |
315 | return -EINVAL; |
316 | } |
317 | } |
318 | |
319 | void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev, |
320 | struct kobj_attribute *attr) |
321 | { |
322 | struct rnbd_srv_session *sess = sess_dev->sess; |
323 | |
324 | /* It is already started to close by client's close message. */ |
325 | if (!mutex_trylock(lock: &sess->lock)) |
326 | return; |
327 | |
328 | sess_dev->keep_id = true; |
329 | /* first remove sysfs itself to avoid deadlock */ |
330 | sysfs_remove_file_self(kobj: &sess_dev->kobj, attr: &attr->attr); |
331 | rnbd_srv_destroy_dev_session_sysfs(sess_dev); |
332 | mutex_unlock(lock: &sess->lock); |
333 | } |
334 | |
335 | static void process_msg_close(struct rnbd_srv_session *srv_sess, |
336 | void *data, size_t datalen, const void *usr, |
337 | size_t usrlen) |
338 | { |
339 | const struct rnbd_msg_close *close_msg = usr; |
340 | struct rnbd_srv_sess_dev *sess_dev; |
341 | |
342 | trace_process_msg_close(srv: srv_sess, msg: close_msg); |
343 | |
344 | sess_dev = rnbd_get_sess_dev(le32_to_cpu(close_msg->device_id), |
345 | srv_sess); |
346 | if (IS_ERR(ptr: sess_dev)) |
347 | return; |
348 | |
349 | rnbd_put_sess_dev(sess_dev); |
350 | mutex_lock(&srv_sess->lock); |
351 | rnbd_srv_destroy_dev_session_sysfs(sess_dev); |
352 | mutex_unlock(lock: &srv_sess->lock); |
353 | } |
354 | |
355 | static int process_msg_open(struct rnbd_srv_session *srv_sess, |
356 | const void *msg, size_t len, |
357 | void *data, size_t datalen); |
358 | |
359 | static void process_msg_sess_info(struct rnbd_srv_session *srv_sess, |
360 | const void *msg, size_t len, |
361 | void *data, size_t datalen); |
362 | |
363 | static int rnbd_srv_rdma_ev(void *priv, struct rtrs_srv_op *id, |
364 | void *data, size_t datalen, |
365 | const void *usr, size_t usrlen) |
366 | { |
367 | struct rnbd_srv_session *srv_sess = priv; |
368 | const struct rnbd_msg_hdr *hdr = usr; |
369 | int ret = 0; |
370 | u16 type; |
371 | |
372 | if (WARN_ON_ONCE(!srv_sess)) |
373 | return -ENODEV; |
374 | |
375 | type = le16_to_cpu(hdr->type); |
376 | |
377 | switch (type) { |
378 | case RNBD_MSG_IO: |
379 | return process_rdma(srv_sess, id, data, datalen, usr, usrlen); |
380 | case RNBD_MSG_CLOSE: |
381 | process_msg_close(srv_sess, data, datalen, usr, usrlen); |
382 | break; |
383 | case RNBD_MSG_OPEN: |
384 | ret = process_msg_open(srv_sess, msg: usr, len: usrlen, data, datalen); |
385 | break; |
386 | case RNBD_MSG_SESS_INFO: |
387 | process_msg_sess_info(srv_sess, msg: usr, len: usrlen, data, datalen); |
388 | break; |
389 | default: |
390 | pr_warn("Received unexpected message type %d from session %s\n" , |
391 | type, srv_sess->sessname); |
392 | return -EINVAL; |
393 | } |
394 | |
395 | /* |
396 | * Since ret is passed to rtrs to handle the failure case, we |
397 | * just return 0 at the end otherwise callers in rtrs would call |
398 | * send_io_resp_imm again to print redundant err message. |
399 | */ |
400 | rtrs_srv_resp_rdma(id, errno: ret); |
401 | return 0; |
402 | } |
403 | |
404 | static struct rnbd_srv_sess_dev |
405 | *rnbd_sess_dev_alloc(struct rnbd_srv_session *srv_sess) |
406 | { |
407 | struct rnbd_srv_sess_dev *sess_dev; |
408 | int error; |
409 | |
410 | sess_dev = kzalloc(size: sizeof(*sess_dev), GFP_KERNEL); |
411 | if (!sess_dev) |
412 | return ERR_PTR(error: -ENOMEM); |
413 | |
414 | error = xa_alloc(xa: &srv_sess->index_idr, id: &sess_dev->device_id, entry: sess_dev, |
415 | xa_limit_32b, GFP_NOWAIT); |
416 | if (error < 0) { |
417 | pr_warn("Allocating idr failed, err: %d\n" , error); |
418 | kfree(objp: sess_dev); |
419 | return ERR_PTR(error); |
420 | } |
421 | |
422 | return sess_dev; |
423 | } |
424 | |
425 | static struct rnbd_srv_dev *rnbd_srv_init_srv_dev(struct block_device *bdev) |
426 | { |
427 | struct rnbd_srv_dev *dev; |
428 | |
429 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
430 | if (!dev) |
431 | return ERR_PTR(error: -ENOMEM); |
432 | |
433 | snprintf(buf: dev->name, size: sizeof(dev->name), fmt: "%pg" , bdev); |
434 | kref_init(kref: &dev->kref); |
435 | INIT_LIST_HEAD(list: &dev->sess_dev_list); |
436 | mutex_init(&dev->lock); |
437 | |
438 | return dev; |
439 | } |
440 | |
441 | static struct rnbd_srv_dev * |
442 | rnbd_srv_find_or_add_srv_dev(struct rnbd_srv_dev *new_dev) |
443 | { |
444 | struct rnbd_srv_dev *dev; |
445 | |
446 | spin_lock(lock: &dev_lock); |
447 | list_for_each_entry(dev, &dev_list, list) { |
448 | if (!strncmp(dev->name, new_dev->name, sizeof(dev->name))) { |
449 | if (!kref_get_unless_zero(kref: &dev->kref)) |
450 | /* |
451 | * We lost the race, device is almost dead. |
452 | * Continue traversing to find a valid one. |
453 | */ |
454 | continue; |
455 | spin_unlock(lock: &dev_lock); |
456 | return dev; |
457 | } |
458 | } |
459 | list_add(new: &new_dev->list, head: &dev_list); |
460 | spin_unlock(lock: &dev_lock); |
461 | |
462 | return new_dev; |
463 | } |
464 | |
465 | static int rnbd_srv_check_update_open_perm(struct rnbd_srv_dev *srv_dev, |
466 | struct rnbd_srv_session *srv_sess, |
467 | enum rnbd_access_mode access_mode) |
468 | { |
469 | int ret = 0; |
470 | |
471 | mutex_lock(&srv_dev->lock); |
472 | |
473 | switch (access_mode) { |
474 | case RNBD_ACCESS_RO: |
475 | break; |
476 | case RNBD_ACCESS_RW: |
477 | if (srv_dev->open_write_cnt == 0) { |
478 | srv_dev->open_write_cnt++; |
479 | } else { |
480 | pr_err("Mapping device '%s' for session %s with RW permissions failed. Device already opened as 'RW' by %d client(s), access mode %s.\n" , |
481 | srv_dev->name, srv_sess->sessname, |
482 | srv_dev->open_write_cnt, |
483 | rnbd_access_modes[access_mode].str); |
484 | ret = -EPERM; |
485 | } |
486 | break; |
487 | case RNBD_ACCESS_MIGRATION: |
488 | if (srv_dev->open_write_cnt < 2) { |
489 | srv_dev->open_write_cnt++; |
490 | } else { |
491 | pr_err("Mapping device '%s' for session %s with migration permissions failed. Device already opened as 'RW' by %d client(s), access mode %s.\n" , |
492 | srv_dev->name, srv_sess->sessname, |
493 | srv_dev->open_write_cnt, |
494 | rnbd_access_modes[access_mode].str); |
495 | ret = -EPERM; |
496 | } |
497 | break; |
498 | default: |
499 | pr_err("Received mapping request for device '%s' on session %s with invalid access mode: %d\n" , |
500 | srv_dev->name, srv_sess->sessname, access_mode); |
501 | ret = -EINVAL; |
502 | } |
503 | |
504 | mutex_unlock(lock: &srv_dev->lock); |
505 | |
506 | return ret; |
507 | } |
508 | |
509 | static struct rnbd_srv_dev * |
510 | rnbd_srv_get_or_create_srv_dev(struct block_device *bdev, |
511 | struct rnbd_srv_session *srv_sess, |
512 | enum rnbd_access_mode access_mode) |
513 | { |
514 | int ret; |
515 | struct rnbd_srv_dev *new_dev, *dev; |
516 | |
517 | new_dev = rnbd_srv_init_srv_dev(bdev); |
518 | if (IS_ERR(ptr: new_dev)) |
519 | return new_dev; |
520 | |
521 | dev = rnbd_srv_find_or_add_srv_dev(new_dev); |
522 | if (dev != new_dev) |
523 | kfree(objp: new_dev); |
524 | |
525 | ret = rnbd_srv_check_update_open_perm(srv_dev: dev, srv_sess, access_mode); |
526 | if (ret) { |
527 | rnbd_put_srv_dev(dev); |
528 | return ERR_PTR(error: ret); |
529 | } |
530 | |
531 | return dev; |
532 | } |
533 | |
534 | static void rnbd_srv_fill_msg_open_rsp(struct rnbd_msg_open_rsp *rsp, |
535 | struct rnbd_srv_sess_dev *sess_dev) |
536 | { |
537 | struct block_device *bdev = file_bdev(bdev_file: sess_dev->bdev_file); |
538 | |
539 | rsp->hdr.type = cpu_to_le16(RNBD_MSG_OPEN_RSP); |
540 | rsp->device_id = cpu_to_le32(sess_dev->device_id); |
541 | rsp->nsectors = cpu_to_le64(bdev_nr_sectors(bdev)); |
542 | rsp->logical_block_size = cpu_to_le16(bdev_logical_block_size(bdev)); |
543 | rsp->physical_block_size = cpu_to_le16(bdev_physical_block_size(bdev)); |
544 | rsp->max_segments = cpu_to_le16(bdev_max_segments(bdev)); |
545 | rsp->max_hw_sectors = |
546 | cpu_to_le32(queue_max_hw_sectors(bdev_get_queue(bdev))); |
547 | rsp->max_write_zeroes_sectors = |
548 | cpu_to_le32(bdev_write_zeroes_sectors(bdev)); |
549 | rsp->max_discard_sectors = cpu_to_le32(bdev_max_discard_sectors(bdev)); |
550 | rsp->discard_granularity = cpu_to_le32(bdev_discard_granularity(bdev)); |
551 | rsp->discard_alignment = cpu_to_le32(bdev_discard_alignment(bdev)); |
552 | rsp->secure_discard = cpu_to_le16(bdev_max_secure_erase_sectors(bdev)); |
553 | rsp->cache_policy = 0; |
554 | if (bdev_write_cache(bdev)) |
555 | rsp->cache_policy |= RNBD_WRITEBACK; |
556 | if (bdev_fua(bdev)) |
557 | rsp->cache_policy |= RNBD_FUA; |
558 | } |
559 | |
560 | static struct rnbd_srv_sess_dev * |
561 | rnbd_srv_create_set_sess_dev(struct rnbd_srv_session *srv_sess, |
562 | const struct rnbd_msg_open *open_msg, |
563 | struct file *bdev_file, bool readonly, |
564 | struct rnbd_srv_dev *srv_dev) |
565 | { |
566 | struct rnbd_srv_sess_dev *sdev = rnbd_sess_dev_alloc(srv_sess); |
567 | |
568 | if (IS_ERR(ptr: sdev)) |
569 | return sdev; |
570 | |
571 | kref_init(kref: &sdev->kref); |
572 | |
573 | strscpy(sdev->pathname, open_msg->dev_name, sizeof(sdev->pathname)); |
574 | |
575 | sdev->bdev_file = bdev_file; |
576 | sdev->sess = srv_sess; |
577 | sdev->dev = srv_dev; |
578 | sdev->readonly = readonly; |
579 | sdev->access_mode = open_msg->access_mode; |
580 | |
581 | return sdev; |
582 | } |
583 | |
584 | static char *rnbd_srv_get_full_path(struct rnbd_srv_session *srv_sess, |
585 | const char *dev_name) |
586 | { |
587 | char *full_path; |
588 | char *a, *b; |
589 | int len; |
590 | |
591 | full_path = kmalloc(PATH_MAX, GFP_KERNEL); |
592 | if (!full_path) |
593 | return ERR_PTR(error: -ENOMEM); |
594 | |
595 | /* |
596 | * Replace %SESSNAME% with a real session name in order to |
597 | * create device namespace. |
598 | */ |
599 | a = strnstr(dev_search_path, "%SESSNAME%" , sizeof(dev_search_path)); |
600 | if (a) { |
601 | len = a - dev_search_path; |
602 | |
603 | len = snprintf(buf: full_path, PATH_MAX, fmt: "%.*s/%s/%s" , len, |
604 | dev_search_path, srv_sess->sessname, dev_name); |
605 | } else { |
606 | len = snprintf(buf: full_path, PATH_MAX, fmt: "%s/%s" , |
607 | dev_search_path, dev_name); |
608 | } |
609 | if (len >= PATH_MAX) { |
610 | pr_err("Too long path: %s, %s, %s\n" , |
611 | dev_search_path, srv_sess->sessname, dev_name); |
612 | kfree(objp: full_path); |
613 | return ERR_PTR(error: -EINVAL); |
614 | } |
615 | |
616 | /* eliminitate duplicated slashes */ |
617 | a = strchr(full_path, '/'); |
618 | b = a; |
619 | while (*b != '\0') { |
620 | if (*b == '/' && *a == '/') { |
621 | b++; |
622 | } else { |
623 | a++; |
624 | *a = *b; |
625 | b++; |
626 | } |
627 | } |
628 | a++; |
629 | *a = '\0'; |
630 | |
631 | return full_path; |
632 | } |
633 | |
634 | static void process_msg_sess_info(struct rnbd_srv_session *srv_sess, |
635 | const void *msg, size_t len, |
636 | void *data, size_t datalen) |
637 | { |
638 | const struct rnbd_msg_sess_info *sess_info_msg = msg; |
639 | struct rnbd_msg_sess_info_rsp *rsp = data; |
640 | |
641 | srv_sess->ver = min_t(u8, sess_info_msg->ver, RNBD_PROTO_VER_MAJOR); |
642 | |
643 | trace_process_msg_sess_info(srv: srv_sess, msg: sess_info_msg); |
644 | |
645 | rsp->hdr.type = cpu_to_le16(RNBD_MSG_SESS_INFO_RSP); |
646 | rsp->ver = srv_sess->ver; |
647 | } |
648 | |
649 | /** |
650 | * find_srv_sess_dev() - a dev is already opened by this name |
651 | * @srv_sess: the session to search. |
652 | * @dev_name: string containing the name of the device. |
653 | * |
654 | * Return struct rnbd_srv_sess_dev if srv_sess already opened the dev_name |
655 | * NULL if the session didn't open the device yet. |
656 | */ |
657 | static struct rnbd_srv_sess_dev * |
658 | find_srv_sess_dev(struct rnbd_srv_session *srv_sess, const char *dev_name) |
659 | { |
660 | struct rnbd_srv_sess_dev *sess_dev; |
661 | unsigned long index; |
662 | |
663 | if (xa_empty(xa: &srv_sess->index_idr)) |
664 | return NULL; |
665 | |
666 | xa_for_each(&srv_sess->index_idr, index, sess_dev) |
667 | if (!strcmp(sess_dev->pathname, dev_name)) |
668 | return sess_dev; |
669 | |
670 | return NULL; |
671 | } |
672 | |
673 | static int process_msg_open(struct rnbd_srv_session *srv_sess, |
674 | const void *msg, size_t len, |
675 | void *data, size_t datalen) |
676 | { |
677 | int ret; |
678 | struct rnbd_srv_dev *srv_dev; |
679 | struct rnbd_srv_sess_dev *srv_sess_dev; |
680 | const struct rnbd_msg_open *open_msg = msg; |
681 | struct file *bdev_file; |
682 | blk_mode_t open_flags = BLK_OPEN_READ; |
683 | char *full_path; |
684 | struct rnbd_msg_open_rsp *rsp = data; |
685 | |
686 | trace_process_msg_open(srv: srv_sess, msg: open_msg); |
687 | |
688 | if (open_msg->access_mode != RNBD_ACCESS_RO) |
689 | open_flags |= BLK_OPEN_WRITE; |
690 | |
691 | mutex_lock(&srv_sess->lock); |
692 | |
693 | srv_sess_dev = find_srv_sess_dev(srv_sess, dev_name: open_msg->dev_name); |
694 | if (srv_sess_dev) |
695 | goto fill_response; |
696 | |
697 | if ((strlen(dev_search_path) + strlen(open_msg->dev_name)) |
698 | >= PATH_MAX) { |
699 | pr_err("Opening device for session %s failed, device path too long. '%s/%s' is longer than PATH_MAX (%d)\n" , |
700 | srv_sess->sessname, dev_search_path, open_msg->dev_name, |
701 | PATH_MAX); |
702 | ret = -EINVAL; |
703 | goto reject; |
704 | } |
705 | if (strstr(open_msg->dev_name, ".." )) { |
706 | pr_err("Opening device for session %s failed, device path %s contains relative path ..\n" , |
707 | srv_sess->sessname, open_msg->dev_name); |
708 | ret = -EINVAL; |
709 | goto reject; |
710 | } |
711 | full_path = rnbd_srv_get_full_path(srv_sess, dev_name: open_msg->dev_name); |
712 | if (IS_ERR(ptr: full_path)) { |
713 | ret = PTR_ERR(ptr: full_path); |
714 | pr_err("Opening device '%s' for client %s failed, failed to get device full path, err: %pe\n" , |
715 | open_msg->dev_name, srv_sess->sessname, full_path); |
716 | goto reject; |
717 | } |
718 | |
719 | bdev_file = bdev_file_open_by_path(path: full_path, mode: open_flags, NULL, NULL); |
720 | if (IS_ERR(ptr: bdev_file)) { |
721 | ret = PTR_ERR(ptr: bdev_file); |
722 | pr_err("Opening device '%s' on session %s failed, failed to open the block device, err: %pe\n" , |
723 | full_path, srv_sess->sessname, bdev_file); |
724 | goto free_path; |
725 | } |
726 | |
727 | srv_dev = rnbd_srv_get_or_create_srv_dev(bdev: file_bdev(bdev_file), srv_sess, |
728 | access_mode: open_msg->access_mode); |
729 | if (IS_ERR(ptr: srv_dev)) { |
730 | pr_err("Opening device '%s' on session %s failed, creating srv_dev failed, err: %pe\n" , |
731 | full_path, srv_sess->sessname, srv_dev); |
732 | ret = PTR_ERR(ptr: srv_dev); |
733 | goto blkdev_put; |
734 | } |
735 | |
736 | srv_sess_dev = rnbd_srv_create_set_sess_dev(srv_sess, open_msg, |
737 | bdev_file, |
738 | readonly: open_msg->access_mode == RNBD_ACCESS_RO, |
739 | srv_dev); |
740 | if (IS_ERR(ptr: srv_sess_dev)) { |
741 | pr_err("Opening device '%s' on session %s failed, creating sess_dev failed, err: %pe\n" , |
742 | full_path, srv_sess->sessname, srv_sess_dev); |
743 | ret = PTR_ERR(ptr: srv_sess_dev); |
744 | goto srv_dev_put; |
745 | } |
746 | |
747 | /* Create the srv_dev sysfs files if they haven't been created yet. The |
748 | * reason to delay the creation is not to create the sysfs files before |
749 | * we are sure the device can be opened. |
750 | */ |
751 | mutex_lock(&srv_dev->lock); |
752 | if (!srv_dev->dev_kobj.state_in_sysfs) { |
753 | ret = rnbd_srv_create_dev_sysfs(dev: srv_dev, bdev: file_bdev(bdev_file)); |
754 | if (ret) { |
755 | mutex_unlock(lock: &srv_dev->lock); |
756 | rnbd_srv_err(srv_sess_dev, |
757 | "Opening device failed, failed to create device sysfs files, err: %d\n" , |
758 | ret); |
759 | goto free_srv_sess_dev; |
760 | } |
761 | } |
762 | |
763 | ret = rnbd_srv_create_dev_session_sysfs(sess_dev: srv_sess_dev); |
764 | if (ret) { |
765 | mutex_unlock(lock: &srv_dev->lock); |
766 | rnbd_srv_err(srv_sess_dev, |
767 | "Opening device failed, failed to create dev client sysfs files, err: %d\n" , |
768 | ret); |
769 | goto free_srv_sess_dev; |
770 | } |
771 | |
772 | list_add(new: &srv_sess_dev->dev_list, head: &srv_dev->sess_dev_list); |
773 | mutex_unlock(lock: &srv_dev->lock); |
774 | |
775 | rnbd_srv_info(srv_sess_dev, "Opened device '%s'\n" , srv_dev->name); |
776 | |
777 | kfree(objp: full_path); |
778 | |
779 | fill_response: |
780 | rnbd_srv_fill_msg_open_rsp(rsp, sess_dev: srv_sess_dev); |
781 | mutex_unlock(lock: &srv_sess->lock); |
782 | return 0; |
783 | |
784 | free_srv_sess_dev: |
785 | xa_erase(&srv_sess->index_idr, index: srv_sess_dev->device_id); |
786 | synchronize_rcu(); |
787 | kfree(objp: srv_sess_dev); |
788 | srv_dev_put: |
789 | if (open_msg->access_mode != RNBD_ACCESS_RO) { |
790 | mutex_lock(&srv_dev->lock); |
791 | srv_dev->open_write_cnt--; |
792 | mutex_unlock(lock: &srv_dev->lock); |
793 | } |
794 | rnbd_put_srv_dev(dev: srv_dev); |
795 | blkdev_put: |
796 | fput(bdev_file); |
797 | free_path: |
798 | kfree(objp: full_path); |
799 | reject: |
800 | mutex_unlock(lock: &srv_sess->lock); |
801 | return ret; |
802 | } |
803 | |
804 | static struct rtrs_srv_ctx *rtrs_ctx; |
805 | |
806 | static struct rtrs_srv_ops rtrs_ops; |
807 | static int __init rnbd_srv_init_module(void) |
808 | { |
809 | int err = 0; |
810 | |
811 | BUILD_BUG_ON(sizeof(struct rnbd_msg_hdr) != 4); |
812 | BUILD_BUG_ON(sizeof(struct rnbd_msg_sess_info) != 36); |
813 | BUILD_BUG_ON(sizeof(struct rnbd_msg_sess_info_rsp) != 36); |
814 | BUILD_BUG_ON(sizeof(struct rnbd_msg_open) != 264); |
815 | BUILD_BUG_ON(sizeof(struct rnbd_msg_close) != 8); |
816 | BUILD_BUG_ON(sizeof(struct rnbd_msg_open_rsp) != 56); |
817 | rtrs_ops = (struct rtrs_srv_ops) { |
818 | .rdma_ev = rnbd_srv_rdma_ev, |
819 | .link_ev = rnbd_srv_link_ev, |
820 | }; |
821 | rtrs_ctx = rtrs_srv_open(ops: &rtrs_ops, port: port_nr); |
822 | if (IS_ERR(ptr: rtrs_ctx)) { |
823 | pr_err("rtrs_srv_open(), err: %pe\n" , rtrs_ctx); |
824 | return PTR_ERR(ptr: rtrs_ctx); |
825 | } |
826 | |
827 | err = rnbd_srv_create_sysfs_files(); |
828 | if (err) { |
829 | pr_err("rnbd_srv_create_sysfs_files(), err: %d\n" , err); |
830 | rtrs_srv_close(ctx: rtrs_ctx); |
831 | } |
832 | |
833 | return err; |
834 | } |
835 | |
836 | static void __exit rnbd_srv_cleanup_module(void) |
837 | { |
838 | rtrs_srv_close(ctx: rtrs_ctx); |
839 | WARN_ON(!list_empty(&sess_list)); |
840 | rnbd_srv_destroy_sysfs_files(); |
841 | } |
842 | |
843 | module_init(rnbd_srv_init_module); |
844 | module_exit(rnbd_srv_cleanup_module); |
845 | |