1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/ceph/ceph_debug.h> |
4 | #include <linux/backing-dev.h> |
5 | #include <linux/ctype.h> |
6 | #include <linux/fs.h> |
7 | #include <linux/inet.h> |
8 | #include <linux/in6.h> |
9 | #include <linux/key.h> |
10 | #include <keys/ceph-type.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mount.h> |
13 | #include <linux/nsproxy.h> |
14 | #include <linux/fs_parser.h> |
15 | #include <linux/sched.h> |
16 | #include <linux/sched/mm.h> |
17 | #include <linux/seq_file.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/statfs.h> |
20 | #include <linux/string.h> |
21 | #include <linux/vmalloc.h> |
22 | |
23 | |
24 | #include <linux/ceph/ceph_features.h> |
25 | #include <linux/ceph/libceph.h> |
26 | #include <linux/ceph/debugfs.h> |
27 | #include <linux/ceph/decode.h> |
28 | #include <linux/ceph/mon_client.h> |
29 | #include <linux/ceph/auth.h> |
30 | #include "crypto.h" |
31 | |
32 | |
33 | /* |
34 | * Module compatibility interface. For now it doesn't do anything, |
35 | * but its existence signals a certain level of functionality. |
36 | * |
37 | * The data buffer is used to pass information both to and from |
38 | * libceph. The return value indicates whether libceph determines |
39 | * it is compatible with the caller (from another kernel module), |
40 | * given the provided data. |
41 | * |
42 | * The data pointer can be null. |
43 | */ |
44 | bool libceph_compatible(void *data) |
45 | { |
46 | return true; |
47 | } |
48 | EXPORT_SYMBOL(libceph_compatible); |
49 | |
50 | static int param_get_supported_features(char *buffer, |
51 | const struct kernel_param *kp) |
52 | { |
53 | return sprintf(buf: buffer, fmt: "0x%llx" , CEPH_FEATURES_SUPPORTED_DEFAULT); |
54 | } |
55 | static const struct kernel_param_ops param_ops_supported_features = { |
56 | .get = param_get_supported_features, |
57 | }; |
58 | module_param_cb(supported_features, ¶m_ops_supported_features, NULL, |
59 | 0444); |
60 | |
61 | const char *ceph_msg_type_name(int type) |
62 | { |
63 | switch (type) { |
64 | case CEPH_MSG_SHUTDOWN: return "shutdown" ; |
65 | case CEPH_MSG_PING: return "ping" ; |
66 | case CEPH_MSG_AUTH: return "auth" ; |
67 | case CEPH_MSG_AUTH_REPLY: return "auth_reply" ; |
68 | case CEPH_MSG_MON_MAP: return "mon_map" ; |
69 | case CEPH_MSG_MON_GET_MAP: return "mon_get_map" ; |
70 | case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe" ; |
71 | case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack" ; |
72 | case CEPH_MSG_STATFS: return "statfs" ; |
73 | case CEPH_MSG_STATFS_REPLY: return "statfs_reply" ; |
74 | case CEPH_MSG_MON_GET_VERSION: return "mon_get_version" ; |
75 | case CEPH_MSG_MON_GET_VERSION_REPLY: return "mon_get_version_reply" ; |
76 | case CEPH_MSG_MDS_MAP: return "mds_map" ; |
77 | case CEPH_MSG_FS_MAP_USER: return "fs_map_user" ; |
78 | case CEPH_MSG_CLIENT_SESSION: return "client_session" ; |
79 | case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect" ; |
80 | case CEPH_MSG_CLIENT_REQUEST: return "client_request" ; |
81 | case CEPH_MSG_CLIENT_REQUEST_FORWARD: return "client_request_forward" ; |
82 | case CEPH_MSG_CLIENT_REPLY: return "client_reply" ; |
83 | case CEPH_MSG_CLIENT_CAPS: return "client_caps" ; |
84 | case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release" ; |
85 | case CEPH_MSG_CLIENT_QUOTA: return "client_quota" ; |
86 | case CEPH_MSG_CLIENT_SNAP: return "client_snap" ; |
87 | case CEPH_MSG_CLIENT_LEASE: return "client_lease" ; |
88 | case CEPH_MSG_POOLOP_REPLY: return "poolop_reply" ; |
89 | case CEPH_MSG_POOLOP: return "poolop" ; |
90 | case CEPH_MSG_MON_COMMAND: return "mon_command" ; |
91 | case CEPH_MSG_MON_COMMAND_ACK: return "mon_command_ack" ; |
92 | case CEPH_MSG_OSD_MAP: return "osd_map" ; |
93 | case CEPH_MSG_OSD_OP: return "osd_op" ; |
94 | case CEPH_MSG_OSD_OPREPLY: return "osd_opreply" ; |
95 | case CEPH_MSG_WATCH_NOTIFY: return "watch_notify" ; |
96 | case CEPH_MSG_OSD_BACKOFF: return "osd_backoff" ; |
97 | default: return "unknown" ; |
98 | } |
99 | } |
100 | EXPORT_SYMBOL(ceph_msg_type_name); |
101 | |
102 | /* |
103 | * Initially learn our fsid, or verify an fsid matches. |
104 | */ |
105 | int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) |
106 | { |
107 | if (client->have_fsid) { |
108 | if (ceph_fsid_compare(a: &client->fsid, b: fsid)) { |
109 | pr_err("bad fsid, had %pU got %pU" , |
110 | &client->fsid, fsid); |
111 | return -1; |
112 | } |
113 | } else { |
114 | memcpy(&client->fsid, fsid, sizeof(*fsid)); |
115 | } |
116 | return 0; |
117 | } |
118 | EXPORT_SYMBOL(ceph_check_fsid); |
119 | |
120 | static int strcmp_null(const char *s1, const char *s2) |
121 | { |
122 | if (!s1 && !s2) |
123 | return 0; |
124 | if (s1 && !s2) |
125 | return -1; |
126 | if (!s1 && s2) |
127 | return 1; |
128 | return strcmp(s1, s2); |
129 | } |
130 | |
131 | int ceph_compare_options(struct ceph_options *new_opt, |
132 | struct ceph_client *client) |
133 | { |
134 | struct ceph_options *opt1 = new_opt; |
135 | struct ceph_options *opt2 = client->options; |
136 | int ofs = offsetof(struct ceph_options, mon_addr); |
137 | int i; |
138 | int ret; |
139 | |
140 | /* |
141 | * Don't bother comparing options if network namespaces don't |
142 | * match. |
143 | */ |
144 | if (!net_eq(current->nsproxy->net_ns, net2: read_pnet(pnet: &client->msgr.net))) |
145 | return -1; |
146 | |
147 | ret = memcmp(p: opt1, q: opt2, size: ofs); |
148 | if (ret) |
149 | return ret; |
150 | |
151 | ret = strcmp_null(s1: opt1->name, s2: opt2->name); |
152 | if (ret) |
153 | return ret; |
154 | |
155 | if (opt1->key && !opt2->key) |
156 | return -1; |
157 | if (!opt1->key && opt2->key) |
158 | return 1; |
159 | if (opt1->key && opt2->key) { |
160 | if (opt1->key->type != opt2->key->type) |
161 | return -1; |
162 | if (opt1->key->created.tv_sec != opt2->key->created.tv_sec) |
163 | return -1; |
164 | if (opt1->key->created.tv_nsec != opt2->key->created.tv_nsec) |
165 | return -1; |
166 | if (opt1->key->len != opt2->key->len) |
167 | return -1; |
168 | if (opt1->key->key && !opt2->key->key) |
169 | return -1; |
170 | if (!opt1->key->key && opt2->key->key) |
171 | return 1; |
172 | if (opt1->key->key && opt2->key->key) { |
173 | ret = memcmp(p: opt1->key->key, q: opt2->key->key, size: opt1->key->len); |
174 | if (ret) |
175 | return ret; |
176 | } |
177 | } |
178 | |
179 | ret = ceph_compare_crush_locs(locs1: &opt1->crush_locs, locs2: &opt2->crush_locs); |
180 | if (ret) |
181 | return ret; |
182 | |
183 | /* any matching mon ip implies a match */ |
184 | for (i = 0; i < opt1->num_mon; i++) { |
185 | if (ceph_monmap_contains(m: client->monc.monmap, |
186 | addr: &opt1->mon_addr[i])) |
187 | return 0; |
188 | } |
189 | return -1; |
190 | } |
191 | EXPORT_SYMBOL(ceph_compare_options); |
192 | |
193 | int ceph_parse_fsid(const char *str, struct ceph_fsid *fsid) |
194 | { |
195 | int i = 0; |
196 | char tmp[3]; |
197 | int err = -EINVAL; |
198 | int d; |
199 | |
200 | dout("%s '%s'\n" , __func__, str); |
201 | tmp[2] = 0; |
202 | while (*str && i < 16) { |
203 | if (ispunct(*str)) { |
204 | str++; |
205 | continue; |
206 | } |
207 | if (!isxdigit(str[0]) || !isxdigit(str[1])) |
208 | break; |
209 | tmp[0] = str[0]; |
210 | tmp[1] = str[1]; |
211 | if (sscanf(tmp, "%x" , &d) < 1) |
212 | break; |
213 | fsid->fsid[i] = d & 0xff; |
214 | i++; |
215 | str += 2; |
216 | } |
217 | |
218 | if (i == 16) |
219 | err = 0; |
220 | dout("%s ret %d got fsid %pU\n" , __func__, err, fsid); |
221 | return err; |
222 | } |
223 | EXPORT_SYMBOL(ceph_parse_fsid); |
224 | |
225 | /* |
226 | * ceph options |
227 | */ |
228 | enum { |
229 | Opt_osdkeepalivetimeout, |
230 | Opt_mount_timeout, |
231 | Opt_osd_idle_ttl, |
232 | Opt_osd_request_timeout, |
233 | /* int args above */ |
234 | Opt_fsid, |
235 | Opt_name, |
236 | Opt_secret, |
237 | Opt_key, |
238 | Opt_ip, |
239 | Opt_crush_location, |
240 | Opt_read_from_replica, |
241 | Opt_ms_mode, |
242 | /* string args above */ |
243 | Opt_share, |
244 | Opt_crc, |
245 | Opt_cephx_require_signatures, |
246 | Opt_cephx_sign_messages, |
247 | Opt_tcp_nodelay, |
248 | Opt_abort_on_full, |
249 | Opt_rxbounce, |
250 | }; |
251 | |
252 | enum { |
253 | Opt_read_from_replica_no, |
254 | Opt_read_from_replica_balance, |
255 | Opt_read_from_replica_localize, |
256 | }; |
257 | |
258 | static const struct constant_table ceph_param_read_from_replica[] = { |
259 | {"no" , Opt_read_from_replica_no}, |
260 | {"balance" , Opt_read_from_replica_balance}, |
261 | {"localize" , Opt_read_from_replica_localize}, |
262 | {} |
263 | }; |
264 | |
265 | enum ceph_ms_mode { |
266 | Opt_ms_mode_legacy, |
267 | Opt_ms_mode_crc, |
268 | Opt_ms_mode_secure, |
269 | Opt_ms_mode_prefer_crc, |
270 | Opt_ms_mode_prefer_secure |
271 | }; |
272 | |
273 | static const struct constant_table ceph_param_ms_mode[] = { |
274 | {"legacy" , Opt_ms_mode_legacy}, |
275 | {"crc" , Opt_ms_mode_crc}, |
276 | {"secure" , Opt_ms_mode_secure}, |
277 | {"prefer-crc" , Opt_ms_mode_prefer_crc}, |
278 | {"prefer-secure" , Opt_ms_mode_prefer_secure}, |
279 | {} |
280 | }; |
281 | |
282 | static const struct fs_parameter_spec ceph_parameters[] = { |
283 | fsparam_flag ("abort_on_full" , Opt_abort_on_full), |
284 | __fsparam (NULL, "cephx_require_signatures" , Opt_cephx_require_signatures, |
285 | fs_param_neg_with_no|fs_param_deprecated, NULL), |
286 | fsparam_flag_no ("cephx_sign_messages" , Opt_cephx_sign_messages), |
287 | fsparam_flag_no ("crc" , Opt_crc), |
288 | fsparam_string ("crush_location" , Opt_crush_location), |
289 | fsparam_string ("fsid" , Opt_fsid), |
290 | fsparam_string ("ip" , Opt_ip), |
291 | fsparam_string ("key" , Opt_key), |
292 | fsparam_u32 ("mount_timeout" , Opt_mount_timeout), |
293 | fsparam_string ("name" , Opt_name), |
294 | fsparam_u32 ("osd_idle_ttl" , Opt_osd_idle_ttl), |
295 | fsparam_u32 ("osd_request_timeout" , Opt_osd_request_timeout), |
296 | fsparam_u32 ("osdkeepalive" , Opt_osdkeepalivetimeout), |
297 | fsparam_enum ("read_from_replica" , Opt_read_from_replica, |
298 | ceph_param_read_from_replica), |
299 | fsparam_flag ("rxbounce" , Opt_rxbounce), |
300 | fsparam_enum ("ms_mode" , Opt_ms_mode, |
301 | ceph_param_ms_mode), |
302 | fsparam_string ("secret" , Opt_secret), |
303 | fsparam_flag_no ("share" , Opt_share), |
304 | fsparam_flag_no ("tcp_nodelay" , Opt_tcp_nodelay), |
305 | {} |
306 | }; |
307 | |
308 | struct ceph_options *ceph_alloc_options(void) |
309 | { |
310 | struct ceph_options *opt; |
311 | |
312 | opt = kzalloc(size: sizeof(*opt), GFP_KERNEL); |
313 | if (!opt) |
314 | return NULL; |
315 | |
316 | opt->crush_locs = RB_ROOT; |
317 | opt->mon_addr = kcalloc(CEPH_MAX_MON, size: sizeof(*opt->mon_addr), |
318 | GFP_KERNEL); |
319 | if (!opt->mon_addr) { |
320 | kfree(objp: opt); |
321 | return NULL; |
322 | } |
323 | |
324 | opt->flags = CEPH_OPT_DEFAULT; |
325 | opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT; |
326 | opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; |
327 | opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; |
328 | opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT; |
329 | opt->read_from_replica = CEPH_READ_FROM_REPLICA_DEFAULT; |
330 | opt->con_modes[0] = CEPH_CON_MODE_UNKNOWN; |
331 | opt->con_modes[1] = CEPH_CON_MODE_UNKNOWN; |
332 | return opt; |
333 | } |
334 | EXPORT_SYMBOL(ceph_alloc_options); |
335 | |
336 | void ceph_destroy_options(struct ceph_options *opt) |
337 | { |
338 | dout("destroy_options %p\n" , opt); |
339 | if (!opt) |
340 | return; |
341 | |
342 | ceph_clear_crush_locs(locs: &opt->crush_locs); |
343 | kfree(objp: opt->name); |
344 | if (opt->key) { |
345 | ceph_crypto_key_destroy(key: opt->key); |
346 | kfree(objp: opt->key); |
347 | } |
348 | kfree(objp: opt->mon_addr); |
349 | kfree(objp: opt); |
350 | } |
351 | EXPORT_SYMBOL(ceph_destroy_options); |
352 | |
353 | /* get secret from key store */ |
354 | static int get_secret(struct ceph_crypto_key *dst, const char *name, |
355 | struct p_log *log) |
356 | { |
357 | struct key *ukey; |
358 | int key_err; |
359 | int err = 0; |
360 | struct ceph_crypto_key *ckey; |
361 | |
362 | ukey = request_key(type: &key_type_ceph, description: name, NULL); |
363 | if (IS_ERR(ptr: ukey)) { |
364 | /* request_key errors don't map nicely to mount(2) |
365 | errors; don't even try, but still printk */ |
366 | key_err = PTR_ERR(ptr: ukey); |
367 | switch (key_err) { |
368 | case -ENOKEY: |
369 | error_plog(log, "Failed due to key not found: %s" , |
370 | name); |
371 | break; |
372 | case -EKEYEXPIRED: |
373 | error_plog(log, "Failed due to expired key: %s" , |
374 | name); |
375 | break; |
376 | case -EKEYREVOKED: |
377 | error_plog(log, "Failed due to revoked key: %s" , |
378 | name); |
379 | break; |
380 | default: |
381 | error_plog(log, "Failed due to key error %d: %s" , |
382 | key_err, name); |
383 | } |
384 | err = -EPERM; |
385 | goto out; |
386 | } |
387 | |
388 | ckey = ukey->payload.data[0]; |
389 | err = ceph_crypto_key_clone(dst, src: ckey); |
390 | if (err) |
391 | goto out_key; |
392 | /* pass through, err is 0 */ |
393 | |
394 | out_key: |
395 | key_put(key: ukey); |
396 | out: |
397 | return err; |
398 | } |
399 | |
400 | int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt, |
401 | struct fc_log *l, char delim) |
402 | { |
403 | struct p_log log = {.prefix = "libceph" , .log = l}; |
404 | int ret; |
405 | |
406 | /* ip1[:port1][<delim>ip2[:port2]...] */ |
407 | ret = ceph_parse_ips(c: buf, end: buf + len, addr: opt->mon_addr, CEPH_MAX_MON, |
408 | count: &opt->num_mon, delim); |
409 | if (ret) { |
410 | error_plog(&log, "Failed to parse monitor IPs: %d" , ret); |
411 | return ret; |
412 | } |
413 | |
414 | return 0; |
415 | } |
416 | EXPORT_SYMBOL(ceph_parse_mon_ips); |
417 | |
418 | int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt, |
419 | struct fc_log *l) |
420 | { |
421 | struct fs_parse_result result; |
422 | int token, err; |
423 | struct p_log log = {.prefix = "libceph" , .log = l}; |
424 | |
425 | token = __fs_parse(log: &log, desc: ceph_parameters, value: param, result: &result); |
426 | dout("%s fs_parse '%s' token %d\n" , __func__, param->key, token); |
427 | if (token < 0) |
428 | return token; |
429 | |
430 | switch (token) { |
431 | case Opt_ip: |
432 | err = ceph_parse_ips(c: param->string, |
433 | end: param->string + param->size, |
434 | addr: &opt->my_addr, max_count: 1, NULL, delim: ','); |
435 | if (err) { |
436 | error_plog(&log, "Failed to parse ip: %d" , err); |
437 | return err; |
438 | } |
439 | opt->flags |= CEPH_OPT_MYIP; |
440 | break; |
441 | |
442 | case Opt_fsid: |
443 | err = ceph_parse_fsid(param->string, &opt->fsid); |
444 | if (err) { |
445 | error_plog(&log, "Failed to parse fsid: %d" , err); |
446 | return err; |
447 | } |
448 | opt->flags |= CEPH_OPT_FSID; |
449 | break; |
450 | case Opt_name: |
451 | kfree(objp: opt->name); |
452 | opt->name = param->string; |
453 | param->string = NULL; |
454 | break; |
455 | case Opt_secret: |
456 | ceph_crypto_key_destroy(key: opt->key); |
457 | kfree(objp: opt->key); |
458 | |
459 | opt->key = kzalloc(size: sizeof(*opt->key), GFP_KERNEL); |
460 | if (!opt->key) |
461 | return -ENOMEM; |
462 | err = ceph_crypto_key_unarmor(key: opt->key, in: param->string); |
463 | if (err) { |
464 | error_plog(&log, "Failed to parse secret: %d" , err); |
465 | return err; |
466 | } |
467 | break; |
468 | case Opt_key: |
469 | ceph_crypto_key_destroy(key: opt->key); |
470 | kfree(objp: opt->key); |
471 | |
472 | opt->key = kzalloc(size: sizeof(*opt->key), GFP_KERNEL); |
473 | if (!opt->key) |
474 | return -ENOMEM; |
475 | return get_secret(dst: opt->key, name: param->string, log: &log); |
476 | case Opt_crush_location: |
477 | ceph_clear_crush_locs(locs: &opt->crush_locs); |
478 | err = ceph_parse_crush_location(crush_location: param->string, |
479 | locs: &opt->crush_locs); |
480 | if (err) { |
481 | error_plog(&log, "Failed to parse CRUSH location: %d" , |
482 | err); |
483 | return err; |
484 | } |
485 | break; |
486 | case Opt_read_from_replica: |
487 | switch (result.uint_32) { |
488 | case Opt_read_from_replica_no: |
489 | opt->read_from_replica = 0; |
490 | break; |
491 | case Opt_read_from_replica_balance: |
492 | opt->read_from_replica = CEPH_OSD_FLAG_BALANCE_READS; |
493 | break; |
494 | case Opt_read_from_replica_localize: |
495 | opt->read_from_replica = CEPH_OSD_FLAG_LOCALIZE_READS; |
496 | break; |
497 | default: |
498 | BUG(); |
499 | } |
500 | break; |
501 | case Opt_ms_mode: |
502 | switch (result.uint_32) { |
503 | case Opt_ms_mode_legacy: |
504 | opt->con_modes[0] = CEPH_CON_MODE_UNKNOWN; |
505 | opt->con_modes[1] = CEPH_CON_MODE_UNKNOWN; |
506 | break; |
507 | case Opt_ms_mode_crc: |
508 | opt->con_modes[0] = CEPH_CON_MODE_CRC; |
509 | opt->con_modes[1] = CEPH_CON_MODE_UNKNOWN; |
510 | break; |
511 | case Opt_ms_mode_secure: |
512 | opt->con_modes[0] = CEPH_CON_MODE_SECURE; |
513 | opt->con_modes[1] = CEPH_CON_MODE_UNKNOWN; |
514 | break; |
515 | case Opt_ms_mode_prefer_crc: |
516 | opt->con_modes[0] = CEPH_CON_MODE_CRC; |
517 | opt->con_modes[1] = CEPH_CON_MODE_SECURE; |
518 | break; |
519 | case Opt_ms_mode_prefer_secure: |
520 | opt->con_modes[0] = CEPH_CON_MODE_SECURE; |
521 | opt->con_modes[1] = CEPH_CON_MODE_CRC; |
522 | break; |
523 | default: |
524 | BUG(); |
525 | } |
526 | break; |
527 | |
528 | case Opt_osdkeepalivetimeout: |
529 | /* 0 isn't well defined right now, reject it */ |
530 | if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000) |
531 | goto out_of_range; |
532 | opt->osd_keepalive_timeout = |
533 | msecs_to_jiffies(m: result.uint_32 * 1000); |
534 | break; |
535 | case Opt_osd_idle_ttl: |
536 | /* 0 isn't well defined right now, reject it */ |
537 | if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000) |
538 | goto out_of_range; |
539 | opt->osd_idle_ttl = msecs_to_jiffies(m: result.uint_32 * 1000); |
540 | break; |
541 | case Opt_mount_timeout: |
542 | /* 0 is "wait forever" (i.e. infinite timeout) */ |
543 | if (result.uint_32 > INT_MAX / 1000) |
544 | goto out_of_range; |
545 | opt->mount_timeout = msecs_to_jiffies(m: result.uint_32 * 1000); |
546 | break; |
547 | case Opt_osd_request_timeout: |
548 | /* 0 is "wait forever" (i.e. infinite timeout) */ |
549 | if (result.uint_32 > INT_MAX / 1000) |
550 | goto out_of_range; |
551 | opt->osd_request_timeout = |
552 | msecs_to_jiffies(m: result.uint_32 * 1000); |
553 | break; |
554 | |
555 | case Opt_share: |
556 | if (!result.negated) |
557 | opt->flags &= ~CEPH_OPT_NOSHARE; |
558 | else |
559 | opt->flags |= CEPH_OPT_NOSHARE; |
560 | break; |
561 | case Opt_crc: |
562 | if (!result.negated) |
563 | opt->flags &= ~CEPH_OPT_NOCRC; |
564 | else |
565 | opt->flags |= CEPH_OPT_NOCRC; |
566 | break; |
567 | case Opt_cephx_require_signatures: |
568 | if (!result.negated) |
569 | warn_plog(&log, "Ignoring cephx_require_signatures" ); |
570 | else |
571 | warn_plog(&log, "Ignoring nocephx_require_signatures, use nocephx_sign_messages" ); |
572 | break; |
573 | case Opt_cephx_sign_messages: |
574 | if (!result.negated) |
575 | opt->flags &= ~CEPH_OPT_NOMSGSIGN; |
576 | else |
577 | opt->flags |= CEPH_OPT_NOMSGSIGN; |
578 | break; |
579 | case Opt_tcp_nodelay: |
580 | if (!result.negated) |
581 | opt->flags |= CEPH_OPT_TCP_NODELAY; |
582 | else |
583 | opt->flags &= ~CEPH_OPT_TCP_NODELAY; |
584 | break; |
585 | |
586 | case Opt_abort_on_full: |
587 | opt->flags |= CEPH_OPT_ABORT_ON_FULL; |
588 | break; |
589 | case Opt_rxbounce: |
590 | opt->flags |= CEPH_OPT_RXBOUNCE; |
591 | break; |
592 | |
593 | default: |
594 | BUG(); |
595 | } |
596 | |
597 | return 0; |
598 | |
599 | out_of_range: |
600 | return inval_plog(&log, "%s out of range" , param->key); |
601 | } |
602 | EXPORT_SYMBOL(ceph_parse_param); |
603 | |
604 | int ceph_print_client_options(struct seq_file *m, struct ceph_client *client, |
605 | bool show_all) |
606 | { |
607 | struct ceph_options *opt = client->options; |
608 | size_t pos = m->count; |
609 | struct rb_node *n; |
610 | |
611 | if (opt->name) { |
612 | seq_puts(m, s: "name=" ); |
613 | seq_escape(m, s: opt->name, esc: ", \t\n\\" ); |
614 | seq_putc(m, c: ','); |
615 | } |
616 | if (opt->key) |
617 | seq_puts(m, s: "secret=<hidden>," ); |
618 | |
619 | if (!RB_EMPTY_ROOT(&opt->crush_locs)) { |
620 | seq_puts(m, s: "crush_location=" ); |
621 | for (n = rb_first(&opt->crush_locs); ; ) { |
622 | struct crush_loc_node *loc = |
623 | rb_entry(n, struct crush_loc_node, cl_node); |
624 | |
625 | seq_printf(m, fmt: "%s:%s" , loc->cl_loc.cl_type_name, |
626 | loc->cl_loc.cl_name); |
627 | n = rb_next(n); |
628 | if (!n) |
629 | break; |
630 | |
631 | seq_putc(m, c: '|'); |
632 | } |
633 | seq_putc(m, c: ','); |
634 | } |
635 | if (opt->read_from_replica == CEPH_OSD_FLAG_BALANCE_READS) { |
636 | seq_puts(m, s: "read_from_replica=balance," ); |
637 | } else if (opt->read_from_replica == CEPH_OSD_FLAG_LOCALIZE_READS) { |
638 | seq_puts(m, s: "read_from_replica=localize," ); |
639 | } |
640 | if (opt->con_modes[0] != CEPH_CON_MODE_UNKNOWN) { |
641 | if (opt->con_modes[0] == CEPH_CON_MODE_CRC && |
642 | opt->con_modes[1] == CEPH_CON_MODE_UNKNOWN) { |
643 | seq_puts(m, s: "ms_mode=crc," ); |
644 | } else if (opt->con_modes[0] == CEPH_CON_MODE_SECURE && |
645 | opt->con_modes[1] == CEPH_CON_MODE_UNKNOWN) { |
646 | seq_puts(m, s: "ms_mode=secure," ); |
647 | } else if (opt->con_modes[0] == CEPH_CON_MODE_CRC && |
648 | opt->con_modes[1] == CEPH_CON_MODE_SECURE) { |
649 | seq_puts(m, s: "ms_mode=prefer-crc," ); |
650 | } else if (opt->con_modes[0] == CEPH_CON_MODE_SECURE && |
651 | opt->con_modes[1] == CEPH_CON_MODE_CRC) { |
652 | seq_puts(m, s: "ms_mode=prefer-secure," ); |
653 | } |
654 | } |
655 | |
656 | if (opt->flags & CEPH_OPT_FSID) |
657 | seq_printf(m, fmt: "fsid=%pU," , &opt->fsid); |
658 | if (opt->flags & CEPH_OPT_NOSHARE) |
659 | seq_puts(m, s: "noshare," ); |
660 | if (opt->flags & CEPH_OPT_NOCRC) |
661 | seq_puts(m, s: "nocrc," ); |
662 | if (opt->flags & CEPH_OPT_NOMSGSIGN) |
663 | seq_puts(m, s: "nocephx_sign_messages," ); |
664 | if ((opt->flags & CEPH_OPT_TCP_NODELAY) == 0) |
665 | seq_puts(m, s: "notcp_nodelay," ); |
666 | if (show_all && (opt->flags & CEPH_OPT_ABORT_ON_FULL)) |
667 | seq_puts(m, s: "abort_on_full," ); |
668 | if (opt->flags & CEPH_OPT_RXBOUNCE) |
669 | seq_puts(m, s: "rxbounce," ); |
670 | |
671 | if (opt->mount_timeout != CEPH_MOUNT_TIMEOUT_DEFAULT) |
672 | seq_printf(m, fmt: "mount_timeout=%d," , |
673 | jiffies_to_msecs(j: opt->mount_timeout) / 1000); |
674 | if (opt->osd_idle_ttl != CEPH_OSD_IDLE_TTL_DEFAULT) |
675 | seq_printf(m, fmt: "osd_idle_ttl=%d," , |
676 | jiffies_to_msecs(j: opt->osd_idle_ttl) / 1000); |
677 | if (opt->osd_keepalive_timeout != CEPH_OSD_KEEPALIVE_DEFAULT) |
678 | seq_printf(m, fmt: "osdkeepalivetimeout=%d," , |
679 | jiffies_to_msecs(j: opt->osd_keepalive_timeout) / 1000); |
680 | if (opt->osd_request_timeout != CEPH_OSD_REQUEST_TIMEOUT_DEFAULT) |
681 | seq_printf(m, fmt: "osd_request_timeout=%d," , |
682 | jiffies_to_msecs(j: opt->osd_request_timeout) / 1000); |
683 | |
684 | /* drop redundant comma */ |
685 | if (m->count != pos) |
686 | m->count--; |
687 | |
688 | return 0; |
689 | } |
690 | EXPORT_SYMBOL(ceph_print_client_options); |
691 | |
692 | struct ceph_entity_addr *ceph_client_addr(struct ceph_client *client) |
693 | { |
694 | return &client->msgr.inst.addr; |
695 | } |
696 | EXPORT_SYMBOL(ceph_client_addr); |
697 | |
698 | u64 ceph_client_gid(struct ceph_client *client) |
699 | { |
700 | return client->monc.auth->global_id; |
701 | } |
702 | EXPORT_SYMBOL(ceph_client_gid); |
703 | |
704 | /* |
705 | * create a fresh client instance |
706 | */ |
707 | struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) |
708 | { |
709 | struct ceph_client *client; |
710 | struct ceph_entity_addr *myaddr = NULL; |
711 | int err; |
712 | |
713 | err = wait_for_random_bytes(); |
714 | if (err < 0) |
715 | return ERR_PTR(error: err); |
716 | |
717 | client = kzalloc(size: sizeof(*client), GFP_KERNEL); |
718 | if (client == NULL) |
719 | return ERR_PTR(error: -ENOMEM); |
720 | |
721 | client->private = private; |
722 | client->options = opt; |
723 | |
724 | mutex_init(&client->mount_mutex); |
725 | init_waitqueue_head(&client->auth_wq); |
726 | client->auth_err = 0; |
727 | |
728 | client->extra_mon_dispatch = NULL; |
729 | client->supported_features = CEPH_FEATURES_SUPPORTED_DEFAULT; |
730 | client->required_features = CEPH_FEATURES_REQUIRED_DEFAULT; |
731 | |
732 | if (!ceph_test_opt(client, NOMSGSIGN)) |
733 | client->required_features |= CEPH_FEATURE_MSG_AUTH; |
734 | |
735 | /* msgr */ |
736 | if (ceph_test_opt(client, MYIP)) |
737 | myaddr = &client->options->my_addr; |
738 | |
739 | ceph_messenger_init(msgr: &client->msgr, myaddr); |
740 | |
741 | /* subsystems */ |
742 | err = ceph_monc_init(monc: &client->monc, cl: client); |
743 | if (err < 0) |
744 | goto fail; |
745 | err = ceph_osdc_init(osdc: &client->osdc, client); |
746 | if (err < 0) |
747 | goto fail_monc; |
748 | |
749 | return client; |
750 | |
751 | fail_monc: |
752 | ceph_monc_stop(monc: &client->monc); |
753 | fail: |
754 | ceph_messenger_fini(msgr: &client->msgr); |
755 | kfree(objp: client); |
756 | return ERR_PTR(error: err); |
757 | } |
758 | EXPORT_SYMBOL(ceph_create_client); |
759 | |
760 | void ceph_destroy_client(struct ceph_client *client) |
761 | { |
762 | dout("destroy_client %p\n" , client); |
763 | |
764 | atomic_set(v: &client->msgr.stopping, i: 1); |
765 | |
766 | /* unmount */ |
767 | ceph_osdc_stop(osdc: &client->osdc); |
768 | ceph_monc_stop(monc: &client->monc); |
769 | ceph_messenger_fini(msgr: &client->msgr); |
770 | |
771 | ceph_debugfs_client_cleanup(client); |
772 | |
773 | ceph_destroy_options(client->options); |
774 | |
775 | kfree(objp: client); |
776 | dout("destroy_client %p done\n" , client); |
777 | } |
778 | EXPORT_SYMBOL(ceph_destroy_client); |
779 | |
780 | void ceph_reset_client_addr(struct ceph_client *client) |
781 | { |
782 | ceph_messenger_reset_nonce(msgr: &client->msgr); |
783 | ceph_monc_reopen_session(monc: &client->monc); |
784 | ceph_osdc_reopen_osds(osdc: &client->osdc); |
785 | } |
786 | EXPORT_SYMBOL(ceph_reset_client_addr); |
787 | |
788 | /* |
789 | * true if we have the mon map (and have thus joined the cluster) |
790 | */ |
791 | static bool have_mon_and_osd_map(struct ceph_client *client) |
792 | { |
793 | return client->monc.monmap && client->monc.monmap->epoch && |
794 | client->osdc.osdmap && client->osdc.osdmap->epoch; |
795 | } |
796 | |
797 | /* |
798 | * mount: join the ceph cluster, and open root directory. |
799 | */ |
800 | int __ceph_open_session(struct ceph_client *client, unsigned long started) |
801 | { |
802 | unsigned long timeout = client->options->mount_timeout; |
803 | long err; |
804 | |
805 | /* open session, and wait for mon and osd maps */ |
806 | err = ceph_monc_open_session(monc: &client->monc); |
807 | if (err < 0) |
808 | return err; |
809 | |
810 | while (!have_mon_and_osd_map(client)) { |
811 | if (timeout && time_after_eq(jiffies, started + timeout)) |
812 | return -ETIMEDOUT; |
813 | |
814 | /* wait */ |
815 | dout("mount waiting for mon_map\n" ); |
816 | err = wait_event_interruptible_timeout(client->auth_wq, |
817 | have_mon_and_osd_map(client) || (client->auth_err < 0), |
818 | ceph_timeout_jiffies(timeout)); |
819 | if (err < 0) |
820 | return err; |
821 | if (client->auth_err < 0) |
822 | return client->auth_err; |
823 | } |
824 | |
825 | pr_info("client%llu fsid %pU\n" , ceph_client_gid(client), |
826 | &client->fsid); |
827 | ceph_debugfs_client_init(client); |
828 | |
829 | return 0; |
830 | } |
831 | EXPORT_SYMBOL(__ceph_open_session); |
832 | |
833 | int ceph_open_session(struct ceph_client *client) |
834 | { |
835 | int ret; |
836 | unsigned long started = jiffies; /* note the start time */ |
837 | |
838 | dout("open_session start\n" ); |
839 | mutex_lock(&client->mount_mutex); |
840 | |
841 | ret = __ceph_open_session(client, started); |
842 | |
843 | mutex_unlock(lock: &client->mount_mutex); |
844 | return ret; |
845 | } |
846 | EXPORT_SYMBOL(ceph_open_session); |
847 | |
848 | int ceph_wait_for_latest_osdmap(struct ceph_client *client, |
849 | unsigned long timeout) |
850 | { |
851 | u64 newest_epoch; |
852 | int ret; |
853 | |
854 | ret = ceph_monc_get_version(monc: &client->monc, what: "osdmap" , newest: &newest_epoch); |
855 | if (ret) |
856 | return ret; |
857 | |
858 | if (client->osdc.osdmap->epoch >= newest_epoch) |
859 | return 0; |
860 | |
861 | ceph_osdc_maybe_request_map(osdc: &client->osdc); |
862 | return ceph_monc_wait_osdmap(monc: &client->monc, epoch: newest_epoch, timeout); |
863 | } |
864 | EXPORT_SYMBOL(ceph_wait_for_latest_osdmap); |
865 | |
866 | static int __init init_ceph_lib(void) |
867 | { |
868 | int ret = 0; |
869 | |
870 | ceph_debugfs_init(); |
871 | |
872 | ret = ceph_crypto_init(); |
873 | if (ret < 0) |
874 | goto out_debugfs; |
875 | |
876 | ret = ceph_msgr_init(); |
877 | if (ret < 0) |
878 | goto out_crypto; |
879 | |
880 | ret = ceph_osdc_setup(); |
881 | if (ret < 0) |
882 | goto out_msgr; |
883 | |
884 | pr_info("loaded (mon/osd proto %d/%d)\n" , |
885 | CEPH_MONC_PROTOCOL, CEPH_OSDC_PROTOCOL); |
886 | |
887 | return 0; |
888 | |
889 | out_msgr: |
890 | ceph_msgr_exit(); |
891 | out_crypto: |
892 | ceph_crypto_shutdown(); |
893 | out_debugfs: |
894 | ceph_debugfs_cleanup(); |
895 | return ret; |
896 | } |
897 | |
898 | static void __exit exit_ceph_lib(void) |
899 | { |
900 | dout("exit_ceph_lib\n" ); |
901 | WARN_ON(!ceph_strings_empty()); |
902 | |
903 | ceph_osdc_cleanup(); |
904 | ceph_msgr_exit(); |
905 | ceph_crypto_shutdown(); |
906 | ceph_debugfs_cleanup(); |
907 | } |
908 | |
909 | module_init(init_ceph_lib); |
910 | module_exit(exit_ceph_lib); |
911 | |
912 | MODULE_AUTHOR("Sage Weil <sage@newdream.net>" ); |
913 | MODULE_AUTHOR("Yehuda Sadeh <yehuda@hq.newdream.net>" ); |
914 | MODULE_AUTHOR("Patience Warnick <patience@newdream.net>" ); |
915 | MODULE_DESCRIPTION("Ceph core library" ); |
916 | MODULE_LICENSE("GPL" ); |
917 | |