Commit | Line | Data |
---|---|---|
77141dc6 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a07b4970 CH |
2 | /* |
3 | * Configfs interface for the NVMe target. | |
4 | * Copyright (c) 2015-2016 HGST, a Western Digital Company. | |
a07b4970 CH |
5 | */ |
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
7 | #include <linux/kernel.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/slab.h> | |
10 | #include <linux/stat.h> | |
11 | #include <linux/ctype.h> | |
c6925093 LG |
12 | #include <linux/pci.h> |
13 | #include <linux/pci-p2pdma.h> | |
a07b4970 CH |
14 | |
15 | #include "nvmet.h" | |
16 | ||
66603a31 BG |
17 | static const struct config_item_type nvmet_host_type; |
18 | static const struct config_item_type nvmet_subsys_type; | |
a07b4970 | 19 | |
b662a078 JS |
20 | static LIST_HEAD(nvmet_ports_list); |
21 | struct list_head *nvmet_ports = &nvmet_ports_list; | |
22 | ||
a5d18612 CH |
23 | static const struct nvmet_transport_name { |
24 | u8 type; | |
25 | const char *name; | |
26 | } nvmet_transport_names[] = { | |
27 | { NVMF_TRTYPE_RDMA, "rdma" }, | |
28 | { NVMF_TRTYPE_FC, "fc" }, | |
ad4f530e | 29 | { NVMF_TRTYPE_TCP, "tcp" }, |
a5d18612 CH |
30 | { NVMF_TRTYPE_LOOP, "loop" }, |
31 | }; | |
32 | ||
a07b4970 CH |
33 | /* |
34 | * nvmet_port Generic ConfigFS definitions. | |
35 | * Used in any place in the ConfigFS tree that refers to an address. | |
36 | */ | |
37 | static ssize_t nvmet_addr_adrfam_show(struct config_item *item, | |
38 | char *page) | |
39 | { | |
40 | switch (to_nvmet_port(item)->disc_addr.adrfam) { | |
41 | case NVMF_ADDR_FAMILY_IP4: | |
42 | return sprintf(page, "ipv4\n"); | |
43 | case NVMF_ADDR_FAMILY_IP6: | |
44 | return sprintf(page, "ipv6\n"); | |
45 | case NVMF_ADDR_FAMILY_IB: | |
46 | return sprintf(page, "ib\n"); | |
885aa401 JS |
47 | case NVMF_ADDR_FAMILY_FC: |
48 | return sprintf(page, "fc\n"); | |
a07b4970 CH |
49 | default: |
50 | return sprintf(page, "\n"); | |
51 | } | |
52 | } | |
53 | ||
54 | static ssize_t nvmet_addr_adrfam_store(struct config_item *item, | |
55 | const char *page, size_t count) | |
56 | { | |
57 | struct nvmet_port *port = to_nvmet_port(item); | |
58 | ||
59 | if (port->enabled) { | |
60 | pr_err("Cannot modify address while enabled\n"); | |
61 | pr_err("Disable the address before modifying\n"); | |
62 | return -EACCES; | |
63 | } | |
64 | ||
65 | if (sysfs_streq(page, "ipv4")) { | |
66 | port->disc_addr.adrfam = NVMF_ADDR_FAMILY_IP4; | |
67 | } else if (sysfs_streq(page, "ipv6")) { | |
68 | port->disc_addr.adrfam = NVMF_ADDR_FAMILY_IP6; | |
69 | } else if (sysfs_streq(page, "ib")) { | |
70 | port->disc_addr.adrfam = NVMF_ADDR_FAMILY_IB; | |
885aa401 JS |
71 | } else if (sysfs_streq(page, "fc")) { |
72 | port->disc_addr.adrfam = NVMF_ADDR_FAMILY_FC; | |
a07b4970 CH |
73 | } else { |
74 | pr_err("Invalid value '%s' for adrfam\n", page); | |
75 | return -EINVAL; | |
76 | } | |
77 | ||
78 | return count; | |
79 | } | |
80 | ||
81 | CONFIGFS_ATTR(nvmet_, addr_adrfam); | |
82 | ||
83 | static ssize_t nvmet_addr_portid_show(struct config_item *item, | |
84 | char *page) | |
85 | { | |
86 | struct nvmet_port *port = to_nvmet_port(item); | |
87 | ||
88 | return snprintf(page, PAGE_SIZE, "%d\n", | |
89 | le16_to_cpu(port->disc_addr.portid)); | |
90 | } | |
91 | ||
92 | static ssize_t nvmet_addr_portid_store(struct config_item *item, | |
93 | const char *page, size_t count) | |
94 | { | |
95 | struct nvmet_port *port = to_nvmet_port(item); | |
96 | u16 portid = 0; | |
97 | ||
98 | if (kstrtou16(page, 0, &portid)) { | |
99 | pr_err("Invalid value '%s' for portid\n", page); | |
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | if (port->enabled) { | |
104 | pr_err("Cannot modify address while enabled\n"); | |
105 | pr_err("Disable the address before modifying\n"); | |
106 | return -EACCES; | |
107 | } | |
108 | port->disc_addr.portid = cpu_to_le16(portid); | |
109 | return count; | |
110 | } | |
111 | ||
112 | CONFIGFS_ATTR(nvmet_, addr_portid); | |
113 | ||
114 | static ssize_t nvmet_addr_traddr_show(struct config_item *item, | |
115 | char *page) | |
116 | { | |
117 | struct nvmet_port *port = to_nvmet_port(item); | |
118 | ||
119 | return snprintf(page, PAGE_SIZE, "%s\n", | |
120 | port->disc_addr.traddr); | |
121 | } | |
122 | ||
123 | static ssize_t nvmet_addr_traddr_store(struct config_item *item, | |
124 | const char *page, size_t count) | |
125 | { | |
126 | struct nvmet_port *port = to_nvmet_port(item); | |
127 | ||
128 | if (count > NVMF_TRADDR_SIZE) { | |
129 | pr_err("Invalid value '%s' for traddr\n", page); | |
130 | return -EINVAL; | |
131 | } | |
132 | ||
133 | if (port->enabled) { | |
134 | pr_err("Cannot modify address while enabled\n"); | |
135 | pr_err("Disable the address before modifying\n"); | |
136 | return -EACCES; | |
137 | } | |
9ba2a5cb SG |
138 | |
139 | if (sscanf(page, "%s\n", port->disc_addr.traddr) != 1) | |
140 | return -EINVAL; | |
141 | return count; | |
a07b4970 CH |
142 | } |
143 | ||
144 | CONFIGFS_ATTR(nvmet_, addr_traddr); | |
145 | ||
146 | static ssize_t nvmet_addr_treq_show(struct config_item *item, | |
147 | char *page) | |
148 | { | |
0445e1b5 SG |
149 | switch (to_nvmet_port(item)->disc_addr.treq & |
150 | NVME_TREQ_SECURE_CHANNEL_MASK) { | |
a07b4970 CH |
151 | case NVMF_TREQ_NOT_SPECIFIED: |
152 | return sprintf(page, "not specified\n"); | |
153 | case NVMF_TREQ_REQUIRED: | |
154 | return sprintf(page, "required\n"); | |
155 | case NVMF_TREQ_NOT_REQUIRED: | |
156 | return sprintf(page, "not required\n"); | |
157 | default: | |
158 | return sprintf(page, "\n"); | |
159 | } | |
160 | } | |
161 | ||
162 | static ssize_t nvmet_addr_treq_store(struct config_item *item, | |
163 | const char *page, size_t count) | |
164 | { | |
165 | struct nvmet_port *port = to_nvmet_port(item); | |
0445e1b5 | 166 | u8 treq = port->disc_addr.treq & ~NVME_TREQ_SECURE_CHANNEL_MASK; |
a07b4970 CH |
167 | |
168 | if (port->enabled) { | |
169 | pr_err("Cannot modify address while enabled\n"); | |
170 | pr_err("Disable the address before modifying\n"); | |
171 | return -EACCES; | |
172 | } | |
173 | ||
174 | if (sysfs_streq(page, "not specified")) { | |
0445e1b5 | 175 | treq |= NVMF_TREQ_NOT_SPECIFIED; |
a07b4970 | 176 | } else if (sysfs_streq(page, "required")) { |
0445e1b5 | 177 | treq |= NVMF_TREQ_REQUIRED; |
a07b4970 | 178 | } else if (sysfs_streq(page, "not required")) { |
0445e1b5 | 179 | treq |= NVMF_TREQ_NOT_REQUIRED; |
a07b4970 CH |
180 | } else { |
181 | pr_err("Invalid value '%s' for treq\n", page); | |
182 | return -EINVAL; | |
183 | } | |
0445e1b5 | 184 | port->disc_addr.treq = treq; |
a07b4970 CH |
185 | |
186 | return count; | |
187 | } | |
188 | ||
189 | CONFIGFS_ATTR(nvmet_, addr_treq); | |
190 | ||
191 | static ssize_t nvmet_addr_trsvcid_show(struct config_item *item, | |
192 | char *page) | |
193 | { | |
194 | struct nvmet_port *port = to_nvmet_port(item); | |
195 | ||
196 | return snprintf(page, PAGE_SIZE, "%s\n", | |
197 | port->disc_addr.trsvcid); | |
198 | } | |
199 | ||
200 | static ssize_t nvmet_addr_trsvcid_store(struct config_item *item, | |
201 | const char *page, size_t count) | |
202 | { | |
203 | struct nvmet_port *port = to_nvmet_port(item); | |
204 | ||
205 | if (count > NVMF_TRSVCID_SIZE) { | |
206 | pr_err("Invalid value '%s' for trsvcid\n", page); | |
207 | return -EINVAL; | |
208 | } | |
209 | if (port->enabled) { | |
210 | pr_err("Cannot modify address while enabled\n"); | |
211 | pr_err("Disable the address before modifying\n"); | |
212 | return -EACCES; | |
213 | } | |
9ba2a5cb SG |
214 | |
215 | if (sscanf(page, "%s\n", port->disc_addr.trsvcid) != 1) | |
216 | return -EINVAL; | |
217 | return count; | |
a07b4970 CH |
218 | } |
219 | ||
220 | CONFIGFS_ATTR(nvmet_, addr_trsvcid); | |
221 | ||
0d5ee2b2 SW |
222 | static ssize_t nvmet_param_inline_data_size_show(struct config_item *item, |
223 | char *page) | |
224 | { | |
225 | struct nvmet_port *port = to_nvmet_port(item); | |
226 | ||
227 | return snprintf(page, PAGE_SIZE, "%d\n", port->inline_data_size); | |
228 | } | |
229 | ||
230 | static ssize_t nvmet_param_inline_data_size_store(struct config_item *item, | |
231 | const char *page, size_t count) | |
232 | { | |
233 | struct nvmet_port *port = to_nvmet_port(item); | |
234 | int ret; | |
235 | ||
236 | if (port->enabled) { | |
237 | pr_err("Cannot modify inline_data_size while port enabled\n"); | |
238 | pr_err("Disable the port before modifying\n"); | |
239 | return -EACCES; | |
240 | } | |
241 | ret = kstrtoint(page, 0, &port->inline_data_size); | |
242 | if (ret) { | |
243 | pr_err("Invalid value '%s' for inline_data_size\n", page); | |
244 | return -EINVAL; | |
245 | } | |
246 | return count; | |
247 | } | |
248 | ||
249 | CONFIGFS_ATTR(nvmet_, param_inline_data_size); | |
250 | ||
a07b4970 CH |
251 | static ssize_t nvmet_addr_trtype_show(struct config_item *item, |
252 | char *page) | |
253 | { | |
a5d18612 CH |
254 | struct nvmet_port *port = to_nvmet_port(item); |
255 | int i; | |
256 | ||
257 | for (i = 0; i < ARRAY_SIZE(nvmet_transport_names); i++) { | |
258 | if (port->disc_addr.trtype != nvmet_transport_names[i].type) | |
259 | continue; | |
260 | return sprintf(page, "%s\n", nvmet_transport_names[i].name); | |
a07b4970 | 261 | } |
a5d18612 CH |
262 | |
263 | return sprintf(page, "\n"); | |
a07b4970 CH |
264 | } |
265 | ||
266 | static void nvmet_port_init_tsas_rdma(struct nvmet_port *port) | |
267 | { | |
a07b4970 CH |
268 | port->disc_addr.tsas.rdma.qptype = NVMF_RDMA_QPTYPE_CONNECTED; |
269 | port->disc_addr.tsas.rdma.prtype = NVMF_RDMA_PRTYPE_NOT_SPECIFIED; | |
270 | port->disc_addr.tsas.rdma.cms = NVMF_RDMA_CMS_RDMA_CM; | |
271 | } | |
272 | ||
a07b4970 CH |
273 | static ssize_t nvmet_addr_trtype_store(struct config_item *item, |
274 | const char *page, size_t count) | |
275 | { | |
276 | struct nvmet_port *port = to_nvmet_port(item); | |
a5d18612 | 277 | int i; |
a07b4970 CH |
278 | |
279 | if (port->enabled) { | |
280 | pr_err("Cannot modify address while enabled\n"); | |
281 | pr_err("Disable the address before modifying\n"); | |
282 | return -EACCES; | |
283 | } | |
284 | ||
a5d18612 CH |
285 | for (i = 0; i < ARRAY_SIZE(nvmet_transport_names); i++) { |
286 | if (sysfs_streq(page, nvmet_transport_names[i].name)) | |
287 | goto found; | |
a07b4970 CH |
288 | } |
289 | ||
a5d18612 CH |
290 | pr_err("Invalid value '%s' for trtype\n", page); |
291 | return -EINVAL; | |
292 | found: | |
293 | memset(&port->disc_addr.tsas, 0, NVMF_TSAS_SIZE); | |
294 | port->disc_addr.trtype = nvmet_transport_names[i].type; | |
295 | if (port->disc_addr.trtype == NVMF_TRTYPE_RDMA) | |
296 | nvmet_port_init_tsas_rdma(port); | |
a07b4970 CH |
297 | return count; |
298 | } | |
299 | ||
300 | CONFIGFS_ATTR(nvmet_, addr_trtype); | |
301 | ||
302 | /* | |
303 | * Namespace structures & file operation functions below | |
304 | */ | |
305 | static ssize_t nvmet_ns_device_path_show(struct config_item *item, char *page) | |
306 | { | |
307 | return sprintf(page, "%s\n", to_nvmet_ns(item)->device_path); | |
308 | } | |
309 | ||
310 | static ssize_t nvmet_ns_device_path_store(struct config_item *item, | |
311 | const char *page, size_t count) | |
312 | { | |
313 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
314 | struct nvmet_subsys *subsys = ns->subsys; | |
5613d312 | 315 | size_t len; |
a07b4970 CH |
316 | int ret; |
317 | ||
318 | mutex_lock(&subsys->lock); | |
319 | ret = -EBUSY; | |
e4fcf07c | 320 | if (ns->enabled) |
a07b4970 CH |
321 | goto out_unlock; |
322 | ||
5613d312 HR |
323 | ret = -EINVAL; |
324 | len = strcspn(page, "\n"); | |
325 | if (!len) | |
326 | goto out_unlock; | |
a07b4970 | 327 | |
5613d312 | 328 | kfree(ns->device_path); |
a07b4970 | 329 | ret = -ENOMEM; |
5613d312 | 330 | ns->device_path = kstrndup(page, len, GFP_KERNEL); |
a07b4970 CH |
331 | if (!ns->device_path) |
332 | goto out_unlock; | |
333 | ||
334 | mutex_unlock(&subsys->lock); | |
335 | return count; | |
336 | ||
337 | out_unlock: | |
338 | mutex_unlock(&subsys->lock); | |
339 | return ret; | |
340 | } | |
341 | ||
342 | CONFIGFS_ATTR(nvmet_ns_, device_path); | |
343 | ||
c6925093 LG |
344 | #ifdef CONFIG_PCI_P2PDMA |
345 | static ssize_t nvmet_ns_p2pmem_show(struct config_item *item, char *page) | |
346 | { | |
347 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
348 | ||
349 | return pci_p2pdma_enable_show(page, ns->p2p_dev, ns->use_p2pmem); | |
350 | } | |
351 | ||
352 | static ssize_t nvmet_ns_p2pmem_store(struct config_item *item, | |
353 | const char *page, size_t count) | |
354 | { | |
355 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
356 | struct pci_dev *p2p_dev = NULL; | |
357 | bool use_p2pmem; | |
358 | int ret = count; | |
359 | int error; | |
360 | ||
361 | mutex_lock(&ns->subsys->lock); | |
362 | if (ns->enabled) { | |
363 | ret = -EBUSY; | |
364 | goto out_unlock; | |
365 | } | |
366 | ||
367 | error = pci_p2pdma_enable_store(page, &p2p_dev, &use_p2pmem); | |
368 | if (error) { | |
369 | ret = error; | |
370 | goto out_unlock; | |
371 | } | |
372 | ||
373 | ns->use_p2pmem = use_p2pmem; | |
374 | pci_dev_put(ns->p2p_dev); | |
375 | ns->p2p_dev = p2p_dev; | |
376 | ||
377 | out_unlock: | |
378 | mutex_unlock(&ns->subsys->lock); | |
379 | ||
380 | return ret; | |
381 | } | |
382 | ||
383 | CONFIGFS_ATTR(nvmet_ns_, p2pmem); | |
384 | #endif /* CONFIG_PCI_P2PDMA */ | |
385 | ||
430c7bef JT |
386 | static ssize_t nvmet_ns_device_uuid_show(struct config_item *item, char *page) |
387 | { | |
388 | return sprintf(page, "%pUb\n", &to_nvmet_ns(item)->uuid); | |
389 | } | |
390 | ||
391 | static ssize_t nvmet_ns_device_uuid_store(struct config_item *item, | |
392 | const char *page, size_t count) | |
393 | { | |
394 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
395 | struct nvmet_subsys *subsys = ns->subsys; | |
396 | int ret = 0; | |
397 | ||
430c7bef JT |
398 | mutex_lock(&subsys->lock); |
399 | if (ns->enabled) { | |
400 | ret = -EBUSY; | |
401 | goto out_unlock; | |
402 | } | |
403 | ||
430c7bef JT |
404 | if (uuid_parse(page, &ns->uuid)) |
405 | ret = -EINVAL; | |
406 | ||
407 | out_unlock: | |
408 | mutex_unlock(&subsys->lock); | |
409 | return ret ? ret : count; | |
410 | } | |
411 | ||
f871749a MG |
412 | CONFIGFS_ATTR(nvmet_ns_, device_uuid); |
413 | ||
a07b4970 CH |
414 | static ssize_t nvmet_ns_device_nguid_show(struct config_item *item, char *page) |
415 | { | |
416 | return sprintf(page, "%pUb\n", &to_nvmet_ns(item)->nguid); | |
417 | } | |
418 | ||
419 | static ssize_t nvmet_ns_device_nguid_store(struct config_item *item, | |
420 | const char *page, size_t count) | |
421 | { | |
422 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
423 | struct nvmet_subsys *subsys = ns->subsys; | |
424 | u8 nguid[16]; | |
425 | const char *p = page; | |
426 | int i; | |
427 | int ret = 0; | |
428 | ||
429 | mutex_lock(&subsys->lock); | |
e4fcf07c | 430 | if (ns->enabled) { |
a07b4970 CH |
431 | ret = -EBUSY; |
432 | goto out_unlock; | |
433 | } | |
434 | ||
435 | for (i = 0; i < 16; i++) { | |
436 | if (p + 2 > page + count) { | |
437 | ret = -EINVAL; | |
438 | goto out_unlock; | |
439 | } | |
440 | if (!isxdigit(p[0]) || !isxdigit(p[1])) { | |
441 | ret = -EINVAL; | |
442 | goto out_unlock; | |
443 | } | |
444 | ||
445 | nguid[i] = (hex_to_bin(p[0]) << 4) | hex_to_bin(p[1]); | |
446 | p += 2; | |
447 | ||
448 | if (*p == '-' || *p == ':') | |
449 | p++; | |
450 | } | |
451 | ||
452 | memcpy(&ns->nguid, nguid, sizeof(nguid)); | |
453 | out_unlock: | |
454 | mutex_unlock(&subsys->lock); | |
455 | return ret ? ret : count; | |
456 | } | |
457 | ||
458 | CONFIGFS_ATTR(nvmet_ns_, device_nguid); | |
459 | ||
62ac0d32 CH |
460 | static ssize_t nvmet_ns_ana_grpid_show(struct config_item *item, char *page) |
461 | { | |
462 | return sprintf(page, "%u\n", to_nvmet_ns(item)->anagrpid); | |
463 | } | |
464 | ||
465 | static ssize_t nvmet_ns_ana_grpid_store(struct config_item *item, | |
466 | const char *page, size_t count) | |
467 | { | |
468 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
469 | u32 oldgrpid, newgrpid; | |
470 | int ret; | |
471 | ||
472 | ret = kstrtou32(page, 0, &newgrpid); | |
473 | if (ret) | |
474 | return ret; | |
475 | ||
476 | if (newgrpid < 1 || newgrpid > NVMET_MAX_ANAGRPS) | |
477 | return -EINVAL; | |
478 | ||
479 | down_write(&nvmet_ana_sem); | |
480 | oldgrpid = ns->anagrpid; | |
481 | nvmet_ana_group_enabled[newgrpid]++; | |
482 | ns->anagrpid = newgrpid; | |
483 | nvmet_ana_group_enabled[oldgrpid]--; | |
484 | nvmet_ana_chgcnt++; | |
485 | up_write(&nvmet_ana_sem); | |
486 | ||
487 | nvmet_send_ana_event(ns->subsys, NULL); | |
488 | return count; | |
489 | } | |
490 | ||
491 | CONFIGFS_ATTR(nvmet_ns_, ana_grpid); | |
492 | ||
a07b4970 CH |
493 | static ssize_t nvmet_ns_enable_show(struct config_item *item, char *page) |
494 | { | |
e4fcf07c | 495 | return sprintf(page, "%d\n", to_nvmet_ns(item)->enabled); |
a07b4970 CH |
496 | } |
497 | ||
498 | static ssize_t nvmet_ns_enable_store(struct config_item *item, | |
499 | const char *page, size_t count) | |
500 | { | |
501 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
502 | bool enable; | |
503 | int ret = 0; | |
504 | ||
505 | if (strtobool(page, &enable)) | |
506 | return -EINVAL; | |
507 | ||
508 | if (enable) | |
509 | ret = nvmet_ns_enable(ns); | |
510 | else | |
511 | nvmet_ns_disable(ns); | |
512 | ||
513 | return ret ? ret : count; | |
514 | } | |
515 | ||
516 | CONFIGFS_ATTR(nvmet_ns_, enable); | |
517 | ||
55eb942e CK |
518 | static ssize_t nvmet_ns_buffered_io_show(struct config_item *item, char *page) |
519 | { | |
520 | return sprintf(page, "%d\n", to_nvmet_ns(item)->buffered_io); | |
521 | } | |
522 | ||
523 | static ssize_t nvmet_ns_buffered_io_store(struct config_item *item, | |
524 | const char *page, size_t count) | |
525 | { | |
526 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
527 | bool val; | |
528 | ||
529 | if (strtobool(page, &val)) | |
530 | return -EINVAL; | |
531 | ||
532 | mutex_lock(&ns->subsys->lock); | |
533 | if (ns->enabled) { | |
534 | pr_err("disable ns before setting buffered_io value.\n"); | |
535 | mutex_unlock(&ns->subsys->lock); | |
536 | return -EINVAL; | |
537 | } | |
538 | ||
539 | ns->buffered_io = val; | |
540 | mutex_unlock(&ns->subsys->lock); | |
541 | return count; | |
542 | } | |
543 | ||
544 | CONFIGFS_ATTR(nvmet_ns_, buffered_io); | |
545 | ||
a07b4970 CH |
546 | static struct configfs_attribute *nvmet_ns_attrs[] = { |
547 | &nvmet_ns_attr_device_path, | |
548 | &nvmet_ns_attr_device_nguid, | |
430c7bef | 549 | &nvmet_ns_attr_device_uuid, |
62ac0d32 | 550 | &nvmet_ns_attr_ana_grpid, |
a07b4970 | 551 | &nvmet_ns_attr_enable, |
55eb942e | 552 | &nvmet_ns_attr_buffered_io, |
c6925093 LG |
553 | #ifdef CONFIG_PCI_P2PDMA |
554 | &nvmet_ns_attr_p2pmem, | |
555 | #endif | |
a07b4970 CH |
556 | NULL, |
557 | }; | |
558 | ||
559 | static void nvmet_ns_release(struct config_item *item) | |
560 | { | |
561 | struct nvmet_ns *ns = to_nvmet_ns(item); | |
562 | ||
563 | nvmet_ns_free(ns); | |
564 | } | |
565 | ||
566 | static struct configfs_item_operations nvmet_ns_item_ops = { | |
567 | .release = nvmet_ns_release, | |
568 | }; | |
569 | ||
66603a31 | 570 | static const struct config_item_type nvmet_ns_type = { |
a07b4970 CH |
571 | .ct_item_ops = &nvmet_ns_item_ops, |
572 | .ct_attrs = nvmet_ns_attrs, | |
573 | .ct_owner = THIS_MODULE, | |
574 | }; | |
575 | ||
576 | static struct config_group *nvmet_ns_make(struct config_group *group, | |
577 | const char *name) | |
578 | { | |
579 | struct nvmet_subsys *subsys = namespaces_to_subsys(&group->cg_item); | |
580 | struct nvmet_ns *ns; | |
581 | int ret; | |
582 | u32 nsid; | |
583 | ||
584 | ret = kstrtou32(name, 0, &nsid); | |
585 | if (ret) | |
586 | goto out; | |
587 | ||
588 | ret = -EINVAL; | |
5ba89503 MS |
589 | if (nsid == 0 || nsid == NVME_NSID_ALL) { |
590 | pr_err("invalid nsid %#x", nsid); | |
a07b4970 | 591 | goto out; |
5ba89503 | 592 | } |
a07b4970 CH |
593 | |
594 | ret = -ENOMEM; | |
595 | ns = nvmet_ns_alloc(subsys, nsid); | |
596 | if (!ns) | |
597 | goto out; | |
598 | config_group_init_type_name(&ns->group, name, &nvmet_ns_type); | |
599 | ||
600 | pr_info("adding nsid %d to subsystem %s\n", nsid, subsys->subsysnqn); | |
601 | ||
602 | return &ns->group; | |
603 | out: | |
604 | return ERR_PTR(ret); | |
605 | } | |
606 | ||
607 | static struct configfs_group_operations nvmet_namespaces_group_ops = { | |
608 | .make_group = nvmet_ns_make, | |
609 | }; | |
610 | ||
66603a31 | 611 | static const struct config_item_type nvmet_namespaces_type = { |
a07b4970 CH |
612 | .ct_group_ops = &nvmet_namespaces_group_ops, |
613 | .ct_owner = THIS_MODULE, | |
614 | }; | |
615 | ||
616 | static int nvmet_port_subsys_allow_link(struct config_item *parent, | |
617 | struct config_item *target) | |
618 | { | |
619 | struct nvmet_port *port = to_nvmet_port(parent->ci_parent); | |
620 | struct nvmet_subsys *subsys; | |
621 | struct nvmet_subsys_link *link, *p; | |
622 | int ret; | |
623 | ||
624 | if (target->ci_type != &nvmet_subsys_type) { | |
625 | pr_err("can only link subsystems into the subsystems dir.!\n"); | |
626 | return -EINVAL; | |
627 | } | |
628 | subsys = to_subsys(target); | |
629 | link = kmalloc(sizeof(*link), GFP_KERNEL); | |
630 | if (!link) | |
631 | return -ENOMEM; | |
632 | link->subsys = subsys; | |
633 | ||
634 | down_write(&nvmet_config_sem); | |
635 | ret = -EEXIST; | |
636 | list_for_each_entry(p, &port->subsystems, entry) { | |
637 | if (p->subsys == subsys) | |
638 | goto out_free_link; | |
639 | } | |
640 | ||
641 | if (list_empty(&port->subsystems)) { | |
642 | ret = nvmet_enable_port(port); | |
643 | if (ret) | |
644 | goto out_free_link; | |
645 | } | |
646 | ||
647 | list_add_tail(&link->entry, &port->subsystems); | |
b662a078 JS |
648 | nvmet_port_disc_changed(port, subsys); |
649 | ||
a07b4970 CH |
650 | up_write(&nvmet_config_sem); |
651 | return 0; | |
652 | ||
653 | out_free_link: | |
654 | up_write(&nvmet_config_sem); | |
655 | kfree(link); | |
656 | return ret; | |
657 | } | |
658 | ||
e16769d4 | 659 | static void nvmet_port_subsys_drop_link(struct config_item *parent, |
a07b4970 CH |
660 | struct config_item *target) |
661 | { | |
662 | struct nvmet_port *port = to_nvmet_port(parent->ci_parent); | |
663 | struct nvmet_subsys *subsys = to_subsys(target); | |
664 | struct nvmet_subsys_link *p; | |
665 | ||
666 | down_write(&nvmet_config_sem); | |
667 | list_for_each_entry(p, &port->subsystems, entry) { | |
668 | if (p->subsys == subsys) | |
669 | goto found; | |
670 | } | |
671 | up_write(&nvmet_config_sem); | |
e16769d4 | 672 | return; |
a07b4970 CH |
673 | |
674 | found: | |
675 | list_del(&p->entry); | |
3aed8673 | 676 | nvmet_port_del_ctrls(port, subsys); |
b662a078 JS |
677 | nvmet_port_disc_changed(port, subsys); |
678 | ||
a07b4970 CH |
679 | if (list_empty(&port->subsystems)) |
680 | nvmet_disable_port(port); | |
681 | up_write(&nvmet_config_sem); | |
682 | kfree(p); | |
a07b4970 CH |
683 | } |
684 | ||
685 | static struct configfs_item_operations nvmet_port_subsys_item_ops = { | |
686 | .allow_link = nvmet_port_subsys_allow_link, | |
687 | .drop_link = nvmet_port_subsys_drop_link, | |
688 | }; | |
689 | ||
66603a31 | 690 | static const struct config_item_type nvmet_port_subsys_type = { |
a07b4970 CH |
691 | .ct_item_ops = &nvmet_port_subsys_item_ops, |
692 | .ct_owner = THIS_MODULE, | |
693 | }; | |
694 | ||
695 | static int nvmet_allowed_hosts_allow_link(struct config_item *parent, | |
696 | struct config_item *target) | |
697 | { | |
698 | struct nvmet_subsys *subsys = to_subsys(parent->ci_parent); | |
699 | struct nvmet_host *host; | |
700 | struct nvmet_host_link *link, *p; | |
701 | int ret; | |
702 | ||
703 | if (target->ci_type != &nvmet_host_type) { | |
704 | pr_err("can only link hosts into the allowed_hosts directory!\n"); | |
705 | return -EINVAL; | |
706 | } | |
707 | ||
708 | host = to_host(target); | |
709 | link = kmalloc(sizeof(*link), GFP_KERNEL); | |
710 | if (!link) | |
711 | return -ENOMEM; | |
712 | link->host = host; | |
713 | ||
714 | down_write(&nvmet_config_sem); | |
715 | ret = -EINVAL; | |
716 | if (subsys->allow_any_host) { | |
717 | pr_err("can't add hosts when allow_any_host is set!\n"); | |
718 | goto out_free_link; | |
719 | } | |
720 | ||
721 | ret = -EEXIST; | |
722 | list_for_each_entry(p, &subsys->hosts, entry) { | |
723 | if (!strcmp(nvmet_host_name(p->host), nvmet_host_name(host))) | |
724 | goto out_free_link; | |
725 | } | |
726 | list_add_tail(&link->entry, &subsys->hosts); | |
b662a078 JS |
727 | nvmet_subsys_disc_changed(subsys, host); |
728 | ||
a07b4970 CH |
729 | up_write(&nvmet_config_sem); |
730 | return 0; | |
731 | out_free_link: | |
732 | up_write(&nvmet_config_sem); | |
733 | kfree(link); | |
734 | return ret; | |
735 | } | |
736 | ||
e16769d4 | 737 | static void nvmet_allowed_hosts_drop_link(struct config_item *parent, |
a07b4970 CH |
738 | struct config_item *target) |
739 | { | |
740 | struct nvmet_subsys *subsys = to_subsys(parent->ci_parent); | |
741 | struct nvmet_host *host = to_host(target); | |
742 | struct nvmet_host_link *p; | |
743 | ||
744 | down_write(&nvmet_config_sem); | |
745 | list_for_each_entry(p, &subsys->hosts, entry) { | |
746 | if (!strcmp(nvmet_host_name(p->host), nvmet_host_name(host))) | |
747 | goto found; | |
748 | } | |
749 | up_write(&nvmet_config_sem); | |
e16769d4 | 750 | return; |
a07b4970 CH |
751 | |
752 | found: | |
753 | list_del(&p->entry); | |
b662a078 JS |
754 | nvmet_subsys_disc_changed(subsys, host); |
755 | ||
a07b4970 CH |
756 | up_write(&nvmet_config_sem); |
757 | kfree(p); | |
a07b4970 CH |
758 | } |
759 | ||
760 | static struct configfs_item_operations nvmet_allowed_hosts_item_ops = { | |
761 | .allow_link = nvmet_allowed_hosts_allow_link, | |
762 | .drop_link = nvmet_allowed_hosts_drop_link, | |
763 | }; | |
764 | ||
66603a31 | 765 | static const struct config_item_type nvmet_allowed_hosts_type = { |
a07b4970 CH |
766 | .ct_item_ops = &nvmet_allowed_hosts_item_ops, |
767 | .ct_owner = THIS_MODULE, | |
768 | }; | |
769 | ||
770 | static ssize_t nvmet_subsys_attr_allow_any_host_show(struct config_item *item, | |
771 | char *page) | |
772 | { | |
773 | return snprintf(page, PAGE_SIZE, "%d\n", | |
774 | to_subsys(item)->allow_any_host); | |
775 | } | |
776 | ||
777 | static ssize_t nvmet_subsys_attr_allow_any_host_store(struct config_item *item, | |
778 | const char *page, size_t count) | |
779 | { | |
780 | struct nvmet_subsys *subsys = to_subsys(item); | |
781 | bool allow_any_host; | |
782 | int ret = 0; | |
783 | ||
784 | if (strtobool(page, &allow_any_host)) | |
785 | return -EINVAL; | |
786 | ||
787 | down_write(&nvmet_config_sem); | |
788 | if (allow_any_host && !list_empty(&subsys->hosts)) { | |
789 | pr_err("Can't set allow_any_host when explicit hosts are set!\n"); | |
790 | ret = -EINVAL; | |
791 | goto out_unlock; | |
792 | } | |
793 | ||
b662a078 JS |
794 | if (subsys->allow_any_host != allow_any_host) { |
795 | subsys->allow_any_host = allow_any_host; | |
796 | nvmet_subsys_disc_changed(subsys, NULL); | |
797 | } | |
798 | ||
a07b4970 CH |
799 | out_unlock: |
800 | up_write(&nvmet_config_sem); | |
801 | return ret ? ret : count; | |
802 | } | |
803 | ||
804 | CONFIGFS_ATTR(nvmet_subsys_, attr_allow_any_host); | |
805 | ||
41528f80 | 806 | static ssize_t nvmet_subsys_attr_version_show(struct config_item *item, |
c61d788b JT |
807 | char *page) |
808 | { | |
809 | struct nvmet_subsys *subsys = to_subsys(item); | |
810 | ||
811 | if (NVME_TERTIARY(subsys->ver)) | |
812 | return snprintf(page, PAGE_SIZE, "%d.%d.%d\n", | |
813 | (int)NVME_MAJOR(subsys->ver), | |
814 | (int)NVME_MINOR(subsys->ver), | |
815 | (int)NVME_TERTIARY(subsys->ver)); | |
527123c7 CK |
816 | |
817 | return snprintf(page, PAGE_SIZE, "%d.%d\n", | |
818 | (int)NVME_MAJOR(subsys->ver), | |
819 | (int)NVME_MINOR(subsys->ver)); | |
c61d788b JT |
820 | } |
821 | ||
41528f80 | 822 | static ssize_t nvmet_subsys_attr_version_store(struct config_item *item, |
c61d788b JT |
823 | const char *page, size_t count) |
824 | { | |
825 | struct nvmet_subsys *subsys = to_subsys(item); | |
826 | int major, minor, tertiary = 0; | |
827 | int ret; | |
828 | ||
c61d788b JT |
829 | ret = sscanf(page, "%d.%d.%d\n", &major, &minor, &tertiary); |
830 | if (ret != 2 && ret != 3) | |
831 | return -EINVAL; | |
832 | ||
833 | down_write(&nvmet_config_sem); | |
834 | subsys->ver = NVME_VS(major, minor, tertiary); | |
835 | up_write(&nvmet_config_sem); | |
836 | ||
837 | return count; | |
838 | } | |
41528f80 | 839 | CONFIGFS_ATTR(nvmet_subsys_, attr_version); |
c61d788b | 840 | |
fcbc5459 JT |
841 | static ssize_t nvmet_subsys_attr_serial_show(struct config_item *item, |
842 | char *page) | |
843 | { | |
844 | struct nvmet_subsys *subsys = to_subsys(item); | |
845 | ||
846 | return snprintf(page, PAGE_SIZE, "%llx\n", subsys->serial); | |
847 | } | |
848 | ||
849 | static ssize_t nvmet_subsys_attr_serial_store(struct config_item *item, | |
850 | const char *page, size_t count) | |
851 | { | |
d3a9b0ca CK |
852 | u64 serial; |
853 | ||
854 | if (sscanf(page, "%llx\n", &serial) != 1) | |
855 | return -EINVAL; | |
fcbc5459 JT |
856 | |
857 | down_write(&nvmet_config_sem); | |
d3a9b0ca | 858 | to_subsys(item)->serial = serial; |
fcbc5459 JT |
859 | up_write(&nvmet_config_sem); |
860 | ||
861 | return count; | |
862 | } | |
863 | CONFIGFS_ATTR(nvmet_subsys_, attr_serial); | |
864 | ||
94a39d61 CK |
865 | static ssize_t nvmet_subsys_attr_cntlid_min_show(struct config_item *item, |
866 | char *page) | |
867 | { | |
868 | return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_min); | |
869 | } | |
870 | ||
871 | static ssize_t nvmet_subsys_attr_cntlid_min_store(struct config_item *item, | |
872 | const char *page, size_t cnt) | |
873 | { | |
874 | u16 cntlid_min; | |
875 | ||
876 | if (sscanf(page, "%hu\n", &cntlid_min) != 1) | |
877 | return -EINVAL; | |
878 | ||
879 | if (cntlid_min == 0) | |
880 | return -EINVAL; | |
881 | ||
882 | down_write(&nvmet_config_sem); | |
883 | if (cntlid_min >= to_subsys(item)->cntlid_max) | |
884 | goto out_unlock; | |
885 | to_subsys(item)->cntlid_min = cntlid_min; | |
886 | up_write(&nvmet_config_sem); | |
887 | return cnt; | |
888 | ||
889 | out_unlock: | |
890 | up_write(&nvmet_config_sem); | |
891 | return -EINVAL; | |
892 | } | |
893 | CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_min); | |
894 | ||
895 | static ssize_t nvmet_subsys_attr_cntlid_max_show(struct config_item *item, | |
896 | char *page) | |
897 | { | |
898 | return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_max); | |
899 | } | |
900 | ||
901 | static ssize_t nvmet_subsys_attr_cntlid_max_store(struct config_item *item, | |
902 | const char *page, size_t cnt) | |
903 | { | |
904 | u16 cntlid_max; | |
905 | ||
906 | if (sscanf(page, "%hu\n", &cntlid_max) != 1) | |
907 | return -EINVAL; | |
908 | ||
909 | if (cntlid_max == 0) | |
910 | return -EINVAL; | |
911 | ||
912 | down_write(&nvmet_config_sem); | |
913 | if (cntlid_max <= to_subsys(item)->cntlid_min) | |
914 | goto out_unlock; | |
915 | to_subsys(item)->cntlid_max = cntlid_max; | |
916 | up_write(&nvmet_config_sem); | |
917 | return cnt; | |
918 | ||
919 | out_unlock: | |
920 | up_write(&nvmet_config_sem); | |
921 | return -EINVAL; | |
922 | } | |
923 | CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_max); | |
924 | ||
013b7ebe MR |
925 | static ssize_t nvmet_subsys_attr_model_show(struct config_item *item, |
926 | char *page) | |
927 | { | |
928 | struct nvmet_subsys *subsys = to_subsys(item); | |
929 | struct nvmet_subsys_model *subsys_model; | |
930 | char *model = NVMET_DEFAULT_CTRL_MODEL; | |
931 | int ret; | |
932 | ||
933 | rcu_read_lock(); | |
934 | subsys_model = rcu_dereference(subsys->model); | |
935 | if (subsys_model) | |
936 | model = subsys_model->number; | |
937 | ret = snprintf(page, PAGE_SIZE, "%s\n", model); | |
938 | rcu_read_unlock(); | |
939 | ||
940 | return ret; | |
941 | } | |
942 | ||
943 | /* See Section 1.5 of NVMe 1.4 */ | |
944 | static bool nvmet_is_ascii(const char c) | |
945 | { | |
946 | return c >= 0x20 && c <= 0x7e; | |
947 | } | |
948 | ||
949 | static ssize_t nvmet_subsys_attr_model_store(struct config_item *item, | |
950 | const char *page, size_t count) | |
951 | { | |
952 | struct nvmet_subsys *subsys = to_subsys(item); | |
953 | struct nvmet_subsys_model *new_model; | |
954 | char *new_model_number; | |
955 | int pos = 0, len; | |
956 | ||
957 | len = strcspn(page, "\n"); | |
958 | if (!len) | |
959 | return -EINVAL; | |
960 | ||
961 | for (pos = 0; pos < len; pos++) { | |
962 | if (!nvmet_is_ascii(page[pos])) | |
963 | return -EINVAL; | |
964 | } | |
965 | ||
966 | new_model_number = kstrndup(page, len, GFP_KERNEL); | |
967 | if (!new_model_number) | |
968 | return -ENOMEM; | |
969 | ||
970 | new_model = kzalloc(sizeof(*new_model) + len + 1, GFP_KERNEL); | |
971 | if (!new_model) { | |
972 | kfree(new_model_number); | |
973 | return -ENOMEM; | |
974 | } | |
975 | memcpy(new_model->number, new_model_number, len); | |
976 | ||
977 | down_write(&nvmet_config_sem); | |
978 | mutex_lock(&subsys->lock); | |
979 | new_model = rcu_replace_pointer(subsys->model, new_model, | |
980 | mutex_is_locked(&subsys->lock)); | |
981 | mutex_unlock(&subsys->lock); | |
982 | up_write(&nvmet_config_sem); | |
983 | ||
984 | kfree_rcu(new_model, rcuhead); | |
985 | ||
986 | return count; | |
987 | } | |
988 | CONFIGFS_ATTR(nvmet_subsys_, attr_model); | |
989 | ||
a07b4970 CH |
990 | static struct configfs_attribute *nvmet_subsys_attrs[] = { |
991 | &nvmet_subsys_attr_attr_allow_any_host, | |
41528f80 | 992 | &nvmet_subsys_attr_attr_version, |
fcbc5459 | 993 | &nvmet_subsys_attr_attr_serial, |
94a39d61 CK |
994 | &nvmet_subsys_attr_attr_cntlid_min, |
995 | &nvmet_subsys_attr_attr_cntlid_max, | |
013b7ebe | 996 | &nvmet_subsys_attr_attr_model, |
a07b4970 CH |
997 | NULL, |
998 | }; | |
999 | ||
1000 | /* | |
1001 | * Subsystem structures & folder operation functions below | |
1002 | */ | |
1003 | static void nvmet_subsys_release(struct config_item *item) | |
1004 | { | |
1005 | struct nvmet_subsys *subsys = to_subsys(item); | |
1006 | ||
344770b0 | 1007 | nvmet_subsys_del_ctrls(subsys); |
a07b4970 CH |
1008 | nvmet_subsys_put(subsys); |
1009 | } | |
1010 | ||
1011 | static struct configfs_item_operations nvmet_subsys_item_ops = { | |
1012 | .release = nvmet_subsys_release, | |
1013 | }; | |
1014 | ||
66603a31 | 1015 | static const struct config_item_type nvmet_subsys_type = { |
a07b4970 CH |
1016 | .ct_item_ops = &nvmet_subsys_item_ops, |
1017 | .ct_attrs = nvmet_subsys_attrs, | |
1018 | .ct_owner = THIS_MODULE, | |
1019 | }; | |
1020 | ||
1021 | static struct config_group *nvmet_subsys_make(struct config_group *group, | |
1022 | const char *name) | |
1023 | { | |
1024 | struct nvmet_subsys *subsys; | |
1025 | ||
1026 | if (sysfs_streq(name, NVME_DISC_SUBSYS_NAME)) { | |
1027 | pr_err("can't create discovery subsystem through configfs\n"); | |
1028 | return ERR_PTR(-EINVAL); | |
1029 | } | |
1030 | ||
1031 | subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME); | |
6b7e631b MI |
1032 | if (IS_ERR(subsys)) |
1033 | return ERR_CAST(subsys); | |
a07b4970 CH |
1034 | |
1035 | config_group_init_type_name(&subsys->group, name, &nvmet_subsys_type); | |
1036 | ||
1037 | config_group_init_type_name(&subsys->namespaces_group, | |
1038 | "namespaces", &nvmet_namespaces_type); | |
1039 | configfs_add_default_group(&subsys->namespaces_group, &subsys->group); | |
1040 | ||
1041 | config_group_init_type_name(&subsys->allowed_hosts_group, | |
1042 | "allowed_hosts", &nvmet_allowed_hosts_type); | |
1043 | configfs_add_default_group(&subsys->allowed_hosts_group, | |
1044 | &subsys->group); | |
1045 | ||
1046 | return &subsys->group; | |
1047 | } | |
1048 | ||
1049 | static struct configfs_group_operations nvmet_subsystems_group_ops = { | |
1050 | .make_group = nvmet_subsys_make, | |
1051 | }; | |
1052 | ||
66603a31 | 1053 | static const struct config_item_type nvmet_subsystems_type = { |
a07b4970 CH |
1054 | .ct_group_ops = &nvmet_subsystems_group_ops, |
1055 | .ct_owner = THIS_MODULE, | |
1056 | }; | |
1057 | ||
1058 | static ssize_t nvmet_referral_enable_show(struct config_item *item, | |
1059 | char *page) | |
1060 | { | |
1061 | return snprintf(page, PAGE_SIZE, "%d\n", to_nvmet_port(item)->enabled); | |
1062 | } | |
1063 | ||
1064 | static ssize_t nvmet_referral_enable_store(struct config_item *item, | |
1065 | const char *page, size_t count) | |
1066 | { | |
1067 | struct nvmet_port *parent = to_nvmet_port(item->ci_parent->ci_parent); | |
1068 | struct nvmet_port *port = to_nvmet_port(item); | |
1069 | bool enable; | |
1070 | ||
1071 | if (strtobool(page, &enable)) | |
1072 | goto inval; | |
1073 | ||
1074 | if (enable) | |
1075 | nvmet_referral_enable(parent, port); | |
1076 | else | |
b662a078 | 1077 | nvmet_referral_disable(parent, port); |
a07b4970 CH |
1078 | |
1079 | return count; | |
1080 | inval: | |
1081 | pr_err("Invalid value '%s' for enable\n", page); | |
1082 | return -EINVAL; | |
1083 | } | |
1084 | ||
1085 | CONFIGFS_ATTR(nvmet_referral_, enable); | |
1086 | ||
1087 | /* | |
1088 | * Discovery Service subsystem definitions | |
1089 | */ | |
1090 | static struct configfs_attribute *nvmet_referral_attrs[] = { | |
1091 | &nvmet_attr_addr_adrfam, | |
1092 | &nvmet_attr_addr_portid, | |
1093 | &nvmet_attr_addr_treq, | |
1094 | &nvmet_attr_addr_traddr, | |
1095 | &nvmet_attr_addr_trsvcid, | |
1096 | &nvmet_attr_addr_trtype, | |
1097 | &nvmet_referral_attr_enable, | |
1098 | NULL, | |
1099 | }; | |
1100 | ||
1101 | static void nvmet_referral_release(struct config_item *item) | |
1102 | { | |
b662a078 | 1103 | struct nvmet_port *parent = to_nvmet_port(item->ci_parent->ci_parent); |
a07b4970 CH |
1104 | struct nvmet_port *port = to_nvmet_port(item); |
1105 | ||
b662a078 | 1106 | nvmet_referral_disable(parent, port); |
a07b4970 CH |
1107 | kfree(port); |
1108 | } | |
1109 | ||
1110 | static struct configfs_item_operations nvmet_referral_item_ops = { | |
1111 | .release = nvmet_referral_release, | |
1112 | }; | |
1113 | ||
66603a31 | 1114 | static const struct config_item_type nvmet_referral_type = { |
a07b4970 CH |
1115 | .ct_owner = THIS_MODULE, |
1116 | .ct_attrs = nvmet_referral_attrs, | |
1117 | .ct_item_ops = &nvmet_referral_item_ops, | |
1118 | }; | |
1119 | ||
1120 | static struct config_group *nvmet_referral_make( | |
1121 | struct config_group *group, const char *name) | |
1122 | { | |
1123 | struct nvmet_port *port; | |
1124 | ||
1125 | port = kzalloc(sizeof(*port), GFP_KERNEL); | |
1126 | if (!port) | |
f98d9ca1 | 1127 | return ERR_PTR(-ENOMEM); |
a07b4970 CH |
1128 | |
1129 | INIT_LIST_HEAD(&port->entry); | |
1130 | config_group_init_type_name(&port->group, name, &nvmet_referral_type); | |
1131 | ||
1132 | return &port->group; | |
1133 | } | |
1134 | ||
1135 | static struct configfs_group_operations nvmet_referral_group_ops = { | |
1136 | .make_group = nvmet_referral_make, | |
1137 | }; | |
1138 | ||
66603a31 | 1139 | static const struct config_item_type nvmet_referrals_type = { |
a07b4970 CH |
1140 | .ct_owner = THIS_MODULE, |
1141 | .ct_group_ops = &nvmet_referral_group_ops, | |
1142 | }; | |
1143 | ||
62ac0d32 CH |
1144 | static struct { |
1145 | enum nvme_ana_state state; | |
1146 | const char *name; | |
1147 | } nvmet_ana_state_names[] = { | |
1148 | { NVME_ANA_OPTIMIZED, "optimized" }, | |
1149 | { NVME_ANA_NONOPTIMIZED, "non-optimized" }, | |
1150 | { NVME_ANA_INACCESSIBLE, "inaccessible" }, | |
1151 | { NVME_ANA_PERSISTENT_LOSS, "persistent-loss" }, | |
1152 | { NVME_ANA_CHANGE, "change" }, | |
1153 | }; | |
1154 | ||
1155 | static ssize_t nvmet_ana_group_ana_state_show(struct config_item *item, | |
1156 | char *page) | |
1157 | { | |
1158 | struct nvmet_ana_group *grp = to_ana_group(item); | |
1159 | enum nvme_ana_state state = grp->port->ana_state[grp->grpid]; | |
1160 | int i; | |
1161 | ||
1162 | for (i = 0; i < ARRAY_SIZE(nvmet_ana_state_names); i++) { | |
1163 | if (state != nvmet_ana_state_names[i].state) | |
1164 | continue; | |
1165 | return sprintf(page, "%s\n", nvmet_ana_state_names[i].name); | |
1166 | } | |
1167 | ||
1168 | return sprintf(page, "\n"); | |
1169 | } | |
1170 | ||
1171 | static ssize_t nvmet_ana_group_ana_state_store(struct config_item *item, | |
1172 | const char *page, size_t count) | |
1173 | { | |
1174 | struct nvmet_ana_group *grp = to_ana_group(item); | |
1175 | int i; | |
1176 | ||
1177 | for (i = 0; i < ARRAY_SIZE(nvmet_ana_state_names); i++) { | |
1178 | if (sysfs_streq(page, nvmet_ana_state_names[i].name)) | |
1179 | goto found; | |
1180 | } | |
1181 | ||
1182 | pr_err("Invalid value '%s' for ana_state\n", page); | |
1183 | return -EINVAL; | |
1184 | ||
1185 | found: | |
1186 | down_write(&nvmet_ana_sem); | |
1187 | grp->port->ana_state[grp->grpid] = nvmet_ana_state_names[i].state; | |
1188 | nvmet_ana_chgcnt++; | |
1189 | up_write(&nvmet_ana_sem); | |
1190 | ||
1191 | nvmet_port_send_ana_event(grp->port); | |
1192 | return count; | |
1193 | } | |
1194 | ||
1195 | CONFIGFS_ATTR(nvmet_ana_group_, ana_state); | |
1196 | ||
1197 | static struct configfs_attribute *nvmet_ana_group_attrs[] = { | |
1198 | &nvmet_ana_group_attr_ana_state, | |
1199 | NULL, | |
1200 | }; | |
1201 | ||
1202 | static void nvmet_ana_group_release(struct config_item *item) | |
1203 | { | |
1204 | struct nvmet_ana_group *grp = to_ana_group(item); | |
1205 | ||
1206 | if (grp == &grp->port->ana_default_group) | |
1207 | return; | |
1208 | ||
1209 | down_write(&nvmet_ana_sem); | |
1210 | grp->port->ana_state[grp->grpid] = NVME_ANA_INACCESSIBLE; | |
1211 | nvmet_ana_group_enabled[grp->grpid]--; | |
1212 | up_write(&nvmet_ana_sem); | |
1213 | ||
1214 | nvmet_port_send_ana_event(grp->port); | |
1215 | kfree(grp); | |
1216 | } | |
1217 | ||
1218 | static struct configfs_item_operations nvmet_ana_group_item_ops = { | |
1219 | .release = nvmet_ana_group_release, | |
1220 | }; | |
1221 | ||
1222 | static const struct config_item_type nvmet_ana_group_type = { | |
1223 | .ct_item_ops = &nvmet_ana_group_item_ops, | |
1224 | .ct_attrs = nvmet_ana_group_attrs, | |
1225 | .ct_owner = THIS_MODULE, | |
1226 | }; | |
1227 | ||
1228 | static struct config_group *nvmet_ana_groups_make_group( | |
1229 | struct config_group *group, const char *name) | |
1230 | { | |
1231 | struct nvmet_port *port = ana_groups_to_port(&group->cg_item); | |
1232 | struct nvmet_ana_group *grp; | |
1233 | u32 grpid; | |
1234 | int ret; | |
1235 | ||
1236 | ret = kstrtou32(name, 0, &grpid); | |
1237 | if (ret) | |
1238 | goto out; | |
1239 | ||
1240 | ret = -EINVAL; | |
1241 | if (grpid <= 1 || grpid > NVMET_MAX_ANAGRPS) | |
1242 | goto out; | |
1243 | ||
1244 | ret = -ENOMEM; | |
1245 | grp = kzalloc(sizeof(*grp), GFP_KERNEL); | |
1246 | if (!grp) | |
1247 | goto out; | |
1248 | grp->port = port; | |
1249 | grp->grpid = grpid; | |
1250 | ||
1251 | down_write(&nvmet_ana_sem); | |
1252 | nvmet_ana_group_enabled[grpid]++; | |
1253 | up_write(&nvmet_ana_sem); | |
1254 | ||
1255 | nvmet_port_send_ana_event(grp->port); | |
1256 | ||
1257 | config_group_init_type_name(&grp->group, name, &nvmet_ana_group_type); | |
1258 | return &grp->group; | |
1259 | out: | |
1260 | return ERR_PTR(ret); | |
1261 | } | |
1262 | ||
1263 | static struct configfs_group_operations nvmet_ana_groups_group_ops = { | |
1264 | .make_group = nvmet_ana_groups_make_group, | |
1265 | }; | |
1266 | ||
1267 | static const struct config_item_type nvmet_ana_groups_type = { | |
1268 | .ct_group_ops = &nvmet_ana_groups_group_ops, | |
1269 | .ct_owner = THIS_MODULE, | |
1270 | }; | |
1271 | ||
a07b4970 CH |
1272 | /* |
1273 | * Ports definitions. | |
1274 | */ | |
1275 | static void nvmet_port_release(struct config_item *item) | |
1276 | { | |
1277 | struct nvmet_port *port = to_nvmet_port(item); | |
1278 | ||
b662a078 JS |
1279 | list_del(&port->global_entry); |
1280 | ||
72efd25d | 1281 | kfree(port->ana_state); |
a07b4970 CH |
1282 | kfree(port); |
1283 | } | |
1284 | ||
1285 | static struct configfs_attribute *nvmet_port_attrs[] = { | |
1286 | &nvmet_attr_addr_adrfam, | |
1287 | &nvmet_attr_addr_treq, | |
1288 | &nvmet_attr_addr_traddr, | |
1289 | &nvmet_attr_addr_trsvcid, | |
1290 | &nvmet_attr_addr_trtype, | |
0d5ee2b2 | 1291 | &nvmet_attr_param_inline_data_size, |
a07b4970 CH |
1292 | NULL, |
1293 | }; | |
1294 | ||
1295 | static struct configfs_item_operations nvmet_port_item_ops = { | |
1296 | .release = nvmet_port_release, | |
1297 | }; | |
1298 | ||
66603a31 | 1299 | static const struct config_item_type nvmet_port_type = { |
a07b4970 CH |
1300 | .ct_attrs = nvmet_port_attrs, |
1301 | .ct_item_ops = &nvmet_port_item_ops, | |
1302 | .ct_owner = THIS_MODULE, | |
1303 | }; | |
1304 | ||
1305 | static struct config_group *nvmet_ports_make(struct config_group *group, | |
1306 | const char *name) | |
1307 | { | |
1308 | struct nvmet_port *port; | |
1309 | u16 portid; | |
62ac0d32 | 1310 | u32 i; |
a07b4970 CH |
1311 | |
1312 | if (kstrtou16(name, 0, &portid)) | |
1313 | return ERR_PTR(-EINVAL); | |
1314 | ||
1315 | port = kzalloc(sizeof(*port), GFP_KERNEL); | |
1316 | if (!port) | |
f98d9ca1 | 1317 | return ERR_PTR(-ENOMEM); |
a07b4970 | 1318 | |
72efd25d CH |
1319 | port->ana_state = kcalloc(NVMET_MAX_ANAGRPS + 1, |
1320 | sizeof(*port->ana_state), GFP_KERNEL); | |
1321 | if (!port->ana_state) { | |
1322 | kfree(port); | |
1323 | return ERR_PTR(-ENOMEM); | |
1324 | } | |
1325 | ||
62ac0d32 CH |
1326 | for (i = 1; i <= NVMET_MAX_ANAGRPS; i++) { |
1327 | if (i == NVMET_DEFAULT_ANA_GRPID) | |
1328 | port->ana_state[1] = NVME_ANA_OPTIMIZED; | |
1329 | else | |
1330 | port->ana_state[i] = NVME_ANA_INACCESSIBLE; | |
1331 | } | |
72efd25d | 1332 | |
b662a078 JS |
1333 | list_add(&port->global_entry, &nvmet_ports_list); |
1334 | ||
a07b4970 CH |
1335 | INIT_LIST_HEAD(&port->entry); |
1336 | INIT_LIST_HEAD(&port->subsystems); | |
1337 | INIT_LIST_HEAD(&port->referrals); | |
0d5ee2b2 | 1338 | port->inline_data_size = -1; /* < 0 == let the transport choose */ |
a07b4970 CH |
1339 | |
1340 | port->disc_addr.portid = cpu_to_le16(portid); | |
9b95d2fb | 1341 | port->disc_addr.treq = NVMF_TREQ_DISABLE_SQFLOW; |
a07b4970 CH |
1342 | config_group_init_type_name(&port->group, name, &nvmet_port_type); |
1343 | ||
1344 | config_group_init_type_name(&port->subsys_group, | |
1345 | "subsystems", &nvmet_port_subsys_type); | |
1346 | configfs_add_default_group(&port->subsys_group, &port->group); | |
1347 | ||
1348 | config_group_init_type_name(&port->referrals_group, | |
1349 | "referrals", &nvmet_referrals_type); | |
1350 | configfs_add_default_group(&port->referrals_group, &port->group); | |
1351 | ||
62ac0d32 CH |
1352 | config_group_init_type_name(&port->ana_groups_group, |
1353 | "ana_groups", &nvmet_ana_groups_type); | |
1354 | configfs_add_default_group(&port->ana_groups_group, &port->group); | |
1355 | ||
1356 | port->ana_default_group.port = port; | |
1357 | port->ana_default_group.grpid = NVMET_DEFAULT_ANA_GRPID; | |
1358 | config_group_init_type_name(&port->ana_default_group.group, | |
1359 | __stringify(NVMET_DEFAULT_ANA_GRPID), | |
1360 | &nvmet_ana_group_type); | |
1361 | configfs_add_default_group(&port->ana_default_group.group, | |
1362 | &port->ana_groups_group); | |
1363 | ||
a07b4970 CH |
1364 | return &port->group; |
1365 | } | |
1366 | ||
1367 | static struct configfs_group_operations nvmet_ports_group_ops = { | |
1368 | .make_group = nvmet_ports_make, | |
1369 | }; | |
1370 | ||
66603a31 | 1371 | static const struct config_item_type nvmet_ports_type = { |
a07b4970 CH |
1372 | .ct_group_ops = &nvmet_ports_group_ops, |
1373 | .ct_owner = THIS_MODULE, | |
1374 | }; | |
1375 | ||
1376 | static struct config_group nvmet_subsystems_group; | |
1377 | static struct config_group nvmet_ports_group; | |
1378 | ||
1379 | static void nvmet_host_release(struct config_item *item) | |
1380 | { | |
1381 | struct nvmet_host *host = to_host(item); | |
1382 | ||
1383 | kfree(host); | |
1384 | } | |
1385 | ||
1386 | static struct configfs_item_operations nvmet_host_item_ops = { | |
1387 | .release = nvmet_host_release, | |
1388 | }; | |
1389 | ||
66603a31 | 1390 | static const struct config_item_type nvmet_host_type = { |
a07b4970 CH |
1391 | .ct_item_ops = &nvmet_host_item_ops, |
1392 | .ct_owner = THIS_MODULE, | |
1393 | }; | |
1394 | ||
1395 | static struct config_group *nvmet_hosts_make_group(struct config_group *group, | |
1396 | const char *name) | |
1397 | { | |
1398 | struct nvmet_host *host; | |
1399 | ||
1400 | host = kzalloc(sizeof(*host), GFP_KERNEL); | |
1401 | if (!host) | |
1402 | return ERR_PTR(-ENOMEM); | |
1403 | ||
1404 | config_group_init_type_name(&host->group, name, &nvmet_host_type); | |
1405 | ||
1406 | return &host->group; | |
1407 | } | |
1408 | ||
1409 | static struct configfs_group_operations nvmet_hosts_group_ops = { | |
1410 | .make_group = nvmet_hosts_make_group, | |
1411 | }; | |
1412 | ||
66603a31 | 1413 | static const struct config_item_type nvmet_hosts_type = { |
a07b4970 CH |
1414 | .ct_group_ops = &nvmet_hosts_group_ops, |
1415 | .ct_owner = THIS_MODULE, | |
1416 | }; | |
1417 | ||
1418 | static struct config_group nvmet_hosts_group; | |
1419 | ||
66603a31 | 1420 | static const struct config_item_type nvmet_root_type = { |
a07b4970 CH |
1421 | .ct_owner = THIS_MODULE, |
1422 | }; | |
1423 | ||
1424 | static struct configfs_subsystem nvmet_configfs_subsystem = { | |
1425 | .su_group = { | |
1426 | .cg_item = { | |
1427 | .ci_namebuf = "nvmet", | |
1428 | .ci_type = &nvmet_root_type, | |
1429 | }, | |
1430 | }, | |
1431 | }; | |
1432 | ||
1433 | int __init nvmet_init_configfs(void) | |
1434 | { | |
1435 | int ret; | |
1436 | ||
1437 | config_group_init(&nvmet_configfs_subsystem.su_group); | |
1438 | mutex_init(&nvmet_configfs_subsystem.su_mutex); | |
1439 | ||
1440 | config_group_init_type_name(&nvmet_subsystems_group, | |
1441 | "subsystems", &nvmet_subsystems_type); | |
1442 | configfs_add_default_group(&nvmet_subsystems_group, | |
1443 | &nvmet_configfs_subsystem.su_group); | |
1444 | ||
1445 | config_group_init_type_name(&nvmet_ports_group, | |
1446 | "ports", &nvmet_ports_type); | |
1447 | configfs_add_default_group(&nvmet_ports_group, | |
1448 | &nvmet_configfs_subsystem.su_group); | |
1449 | ||
1450 | config_group_init_type_name(&nvmet_hosts_group, | |
1451 | "hosts", &nvmet_hosts_type); | |
1452 | configfs_add_default_group(&nvmet_hosts_group, | |
1453 | &nvmet_configfs_subsystem.su_group); | |
1454 | ||
1455 | ret = configfs_register_subsystem(&nvmet_configfs_subsystem); | |
1456 | if (ret) { | |
1457 | pr_err("configfs_register_subsystem: %d\n", ret); | |
1458 | return ret; | |
1459 | } | |
1460 | ||
1461 | return 0; | |
1462 | } | |
1463 | ||
1464 | void __exit nvmet_exit_configfs(void) | |
1465 | { | |
1466 | configfs_unregister_subsystem(&nvmet_configfs_subsystem); | |
1467 | } |