Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3 : : * Copyright (C) 2016 Intel Corporation.
4 : : * All rights reserved.
5 : : */
6 : :
7 : : #include "scsi_internal.h"
8 : :
9 : : static struct spdk_scsi_dev g_devs[SPDK_SCSI_MAX_DEVS];
10 : :
11 : : struct spdk_scsi_dev *
12 : 123 : scsi_dev_get_list(void)
13 : : {
14 : 123 : return g_devs;
15 : : }
16 : :
17 : : static struct spdk_scsi_dev *
18 : 250 : allocate_dev(void)
19 : : {
20 : : struct spdk_scsi_dev *dev;
21 : : int i;
22 : :
23 [ + - ]: 1591 : for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) {
24 : 1591 : dev = &g_devs[i];
25 [ + + ]: 1591 : if (!dev->is_allocated) {
26 [ - + ]: 250 : memset(dev, 0, sizeof(*dev));
27 : 250 : dev->id = i;
28 : 250 : dev->is_allocated = 1;
29 : 250 : TAILQ_INIT(&dev->luns);
30 : 250 : return dev;
31 : : }
32 : : }
33 : :
34 : 0 : return NULL;
35 : : }
36 : :
37 : : static void
38 : 235 : free_dev(struct spdk_scsi_dev *dev)
39 : : {
40 [ - + ]: 235 : assert(dev->is_allocated == 1);
41 [ - + - + ]: 235 : assert(dev->removed == true);
42 : :
43 : 235 : dev->is_allocated = 0;
44 : :
45 [ + + ]: 235 : if (dev->remove_cb) {
46 : 162 : dev->remove_cb(dev->remove_ctx, 0);
47 : 162 : dev->remove_cb = NULL;
48 : : }
49 : 235 : }
50 : :
51 : : void
52 : 280 : spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev,
53 : : spdk_scsi_dev_destruct_cb_t cb_fn, void *cb_arg)
54 : : {
55 : : struct spdk_scsi_lun *lun, *tmp_lun;
56 : :
57 [ + + ]: 280 : if (dev == NULL) {
58 [ - + ]: 5 : if (cb_fn) {
59 : 0 : cb_fn(cb_arg, -EINVAL);
60 : : }
61 : 5 : return;
62 : : }
63 : :
64 [ - + - + ]: 275 : if (dev->removed) {
65 [ # # ]: 0 : if (cb_fn) {
66 : 0 : cb_fn(cb_arg, -EINVAL);
67 : : }
68 : 0 : return;
69 : : }
70 : :
71 : 275 : dev->removed = true;
72 : 275 : dev->remove_cb = cb_fn;
73 : 275 : dev->remove_ctx = cb_arg;
74 : :
75 [ + + ]: 275 : if (TAILQ_EMPTY(&dev->luns)) {
76 : 88 : free_dev(dev);
77 : 88 : return;
78 : : }
79 : :
80 [ + + ]: 575 : TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
81 : : /*
82 : : * LUN will remove itself from this dev when all outstanding IO
83 : : * is done. When no more LUNs, dev will be deleted.
84 : : */
85 : 388 : scsi_lun_destruct(lun);
86 : : }
87 : : }
88 : :
89 : : /*
90 : : * Search the lowest free LUN ID if the LUN ID is default, or check if the LUN ID is free otherwise,
91 : : * and also return the LUN which comes just before where we want to insert an new LUN.
92 : : */
93 : : static int
94 : 593 : scsi_dev_find_free_lun(struct spdk_scsi_dev *dev, int lun_id,
95 : : struct spdk_scsi_lun **prev_lun)
96 : : {
97 : 593 : struct spdk_scsi_lun *lun, *_prev_lun = NULL;
98 : :
99 [ - + ]: 593 : if (prev_lun == NULL) {
100 : 0 : return -EINVAL;
101 : : }
102 : :
103 [ + + ]: 593 : if (lun_id == -1) {
104 : 45 : lun_id = 0;
105 : :
106 [ + + ]: 5325 : TAILQ_FOREACH(lun, &dev->luns, tailq) {
107 [ + + ]: 5305 : if (lun->id > lun_id) {
108 : 25 : break;
109 : : }
110 : 5280 : lun_id = lun->id + 1;
111 : 5280 : _prev_lun = lun;
112 : : }
113 : :
114 [ + + ]: 45 : if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
115 : 10 : return -ENOSPC;
116 : : }
117 : : } else {
118 [ + + ]: 5856 : TAILQ_FOREACH(lun, &dev->luns, tailq) {
119 [ + + ]: 5383 : if (lun->id == lun_id) {
120 : 30 : return -EEXIST;
121 [ + + ]: 5353 : } else if (lun->id > lun_id) {
122 : 45 : break;
123 : : }
124 : 5308 : _prev_lun = lun;
125 : : }
126 : : }
127 : :
128 : 553 : *prev_lun = _prev_lun;
129 : 553 : return 0;
130 : : }
131 : :
132 : : int
133 : 29 : spdk_scsi_dev_add_lun(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id,
134 : : void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
135 : : void *hotremove_ctx)
136 : : {
137 : 29 : return spdk_scsi_dev_add_lun_ext(dev, bdev_name, lun_id,
138 : : NULL, NULL,
139 : : hotremove_cb, hotremove_ctx);
140 : : }
141 : :
142 : : int
143 : 478 : spdk_scsi_dev_add_lun_ext(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id,
144 : : void (*resize_cb)(const struct spdk_scsi_lun *, void *),
145 : : void *resize_ctx,
146 : : void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
147 : : void *hotremove_ctx)
148 : : {
149 : 478 : struct spdk_scsi_lun *lun, *prev_lun = NULL;
150 : : int rc;
151 : :
152 [ - + ]: 478 : if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
153 : 0 : SPDK_ERRLOG("LUN ID %d is more than the maximum.\n", lun_id);
154 : 0 : return -1;
155 : : }
156 : :
157 : 478 : rc = scsi_dev_find_free_lun(dev, lun_id, &prev_lun);
158 [ + + ]: 478 : if (rc != 0) {
159 [ - + ]: 5 : SPDK_ERRLOG("%s\n", rc == -EEXIST ? "LUN ID is duplicated" : "Free LUN ID is not found");
160 : 5 : return rc;
161 : : }
162 : :
163 : 473 : lun = scsi_lun_construct(bdev_name, resize_cb, resize_ctx, hotremove_cb, hotremove_ctx);
164 [ + + ]: 473 : if (lun == NULL) {
165 : 6 : return -1;
166 : : }
167 : :
168 : 467 : lun->dev = dev;
169 : :
170 [ + + ]: 467 : if (lun_id != -1) {
171 : 462 : lun->id = lun_id;
172 [ + - ]: 5 : } else if (prev_lun == NULL) {
173 : 5 : lun->id = 0;
174 : : } else {
175 : 0 : lun->id = prev_lun->id + 1;
176 : : }
177 : :
178 [ + + ]: 467 : if (prev_lun == NULL) {
179 [ + + ]: 269 : TAILQ_INSERT_HEAD(&dev->luns, lun, tailq);
180 : : } else {
181 [ - + ]: 198 : TAILQ_INSERT_AFTER(&dev->luns, prev_lun, lun, tailq);
182 : : }
183 : 467 : return 0;
184 : : }
185 : :
186 : : void
187 : 412 : spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
188 : : struct spdk_scsi_lun *lun)
189 : : {
190 [ + + ]: 412 : TAILQ_REMOVE(&dev->luns, lun, tailq);
191 : :
192 [ - + + + : 412 : if (dev->removed && TAILQ_EMPTY(&dev->luns)) {
+ + ]
193 : 147 : free_dev(dev);
194 : : }
195 : 412 : }
196 : :
197 : 207 : struct spdk_scsi_dev *spdk_scsi_dev_construct(const char *name, const char *bdev_name_list[],
198 : : int *lun_id_list, int num_luns, uint8_t protocol_id,
199 : : void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
200 : : void *hotremove_ctx)
201 : : {
202 : 207 : return spdk_scsi_dev_construct_ext(name, bdev_name_list, lun_id_list,
203 : : num_luns, protocol_id,
204 : : NULL, NULL,
205 : : hotremove_cb, hotremove_ctx);
206 : : }
207 : :
208 : 270 : struct spdk_scsi_dev *spdk_scsi_dev_construct_ext(const char *name, const char *bdev_name_list[],
209 : : int *lun_id_list, int num_luns, uint8_t protocol_id,
210 : : void (*resize_cb)(const struct spdk_scsi_lun *, void *),
211 : : void *resize_ctx,
212 : : void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
213 : : void *hotremove_ctx)
214 : : {
215 : : struct spdk_scsi_dev *dev;
216 : : size_t name_len;
217 : : bool found_lun_0;
218 : : int i, rc;
219 : :
220 [ - + ]: 270 : name_len = strlen(name);
221 [ + + ]: 270 : if (name_len > sizeof(dev->name) - 1) {
222 : 5 : SPDK_ERRLOG("device %s: name longer than maximum allowed length %zu\n",
223 : : name, sizeof(dev->name) - 1);
224 : 5 : return NULL;
225 : : }
226 : :
227 [ + + ]: 265 : if (num_luns == 0) {
228 : 5 : SPDK_ERRLOG("device %s: no LUNs specified\n", name);
229 : 5 : return NULL;
230 : : }
231 : :
232 : 260 : found_lun_0 = false;
233 [ + + ]: 270 : for (i = 0; i < num_luns; i++) {
234 [ + + ]: 265 : if (lun_id_list[i] == 0) {
235 : 255 : found_lun_0 = true;
236 : 255 : break;
237 : : }
238 : : }
239 : :
240 [ + + ]: 260 : if (!found_lun_0) {
241 : 5 : SPDK_ERRLOG("device %s: no LUN 0 specified\n", name);
242 : 5 : return NULL;
243 : : }
244 : :
245 [ + + ]: 704 : for (i = 0; i < num_luns; i++) {
246 [ + + ]: 454 : if (bdev_name_list[i] == NULL) {
247 : 5 : SPDK_ERRLOG("NULL spdk_scsi_lun for LUN %d\n",
248 : : lun_id_list[i]);
249 : 5 : return NULL;
250 : : }
251 : : }
252 : :
253 : 250 : dev = allocate_dev();
254 [ - + ]: 250 : if (dev == NULL) {
255 : 0 : return NULL;
256 : : }
257 : :
258 [ - + - + ]: 250 : memcpy(dev->name, name, name_len + 1);
259 : :
260 : 250 : dev->num_ports = 0;
261 : 250 : dev->protocol_id = protocol_id;
262 : :
263 [ + + ]: 698 : for (i = 0; i < num_luns; i++) {
264 : 449 : rc = spdk_scsi_dev_add_lun_ext(dev, bdev_name_list[i], lun_id_list[i],
265 : : resize_cb, resize_ctx,
266 : : hotremove_cb, hotremove_ctx);
267 [ + + ]: 449 : if (rc < 0) {
268 : 1 : spdk_scsi_dev_destruct(dev, NULL, NULL);
269 : 1 : return NULL;
270 : : }
271 : : }
272 : :
273 : 249 : return dev;
274 : : }
275 : :
276 : : void
277 : 21 : spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev,
278 : : struct spdk_scsi_task *task)
279 : : {
280 [ - + ]: 21 : assert(task != NULL);
281 : :
282 : 21 : scsi_lun_execute_mgmt_task(task->lun, task);
283 : 21 : }
284 : :
285 : : void
286 : 28298871 : spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev,
287 : : struct spdk_scsi_task *task)
288 : : {
289 [ - + ]: 28298871 : assert(task != NULL);
290 : :
291 : 28298871 : scsi_lun_execute_task(task->lun, task);
292 : 28298871 : }
293 : :
294 : : static struct spdk_scsi_port *
295 : 256 : scsi_dev_find_free_port(struct spdk_scsi_dev *dev)
296 : : {
297 : : int i;
298 : :
299 [ + - ]: 263 : for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
300 [ + + ]: 263 : if (!dev->port[i].is_used) {
301 : 256 : return &dev->port[i];
302 : : }
303 : : }
304 : :
305 : 0 : return NULL;
306 : : }
307 : :
308 : : int
309 : 266 : spdk_scsi_dev_add_port(struct spdk_scsi_dev *dev, uint64_t id, const char *name)
310 : : {
311 : : struct spdk_scsi_port *port;
312 : : int rc;
313 : :
314 [ + + ]: 266 : if (dev->num_ports == SPDK_SCSI_DEV_MAX_PORTS) {
315 : 5 : SPDK_ERRLOG("device already has %d ports\n", SPDK_SCSI_DEV_MAX_PORTS);
316 : 5 : return -1;
317 : : }
318 : :
319 : 261 : port = spdk_scsi_dev_find_port_by_id(dev, id);
320 [ + + ]: 261 : if (port != NULL) {
321 : 5 : SPDK_ERRLOG("device already has port(%" PRIu64 ")\n", id);
322 : 5 : return -1;
323 : : }
324 : :
325 : 256 : port = scsi_dev_find_free_port(dev);
326 [ - + ]: 256 : if (port == NULL) {
327 : 0 : assert(false);
328 : : return -1;
329 : : }
330 : :
331 : 256 : rc = scsi_port_construct(port, id, dev->num_ports, name);
332 [ + + ]: 256 : if (rc != 0) {
333 : 5 : return rc;
334 : : }
335 : :
336 : 251 : dev->num_ports++;
337 : 251 : return 0;
338 : : }
339 : :
340 : : int
341 : 164 : spdk_scsi_dev_delete_port(struct spdk_scsi_dev *dev, uint64_t id)
342 : : {
343 : : struct spdk_scsi_port *port;
344 : :
345 : 164 : port = spdk_scsi_dev_find_port_by_id(dev, id);
346 [ - + ]: 164 : if (port == NULL) {
347 : 0 : SPDK_ERRLOG("device does not have specified port(%" PRIu64 ")\n", id);
348 : 0 : return -1;
349 : : }
350 : :
351 : 164 : scsi_port_destruct(port);
352 : :
353 : 164 : dev->num_ports--;
354 : :
355 : 164 : return 0;
356 : : }
357 : :
358 : : struct spdk_scsi_port *
359 : 5355016 : spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id)
360 : : {
361 : : int i;
362 : :
363 [ + + ]: 5356082 : for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
364 [ + + ]: 5355816 : if (!dev->port[i].is_used) {
365 : 1052 : continue;
366 : : }
367 [ + + ]: 5354764 : if (dev->port[i].id == id) {
368 : 5354750 : return &dev->port[i];
369 : : }
370 : : }
371 : :
372 : : /* No matching port found. */
373 : 266 : return NULL;
374 : : }
375 : :
376 : : void
377 : 63 : spdk_scsi_dev_free_io_channels(struct spdk_scsi_dev *dev)
378 : : {
379 : : struct spdk_scsi_lun *lun, *tmp_lun;
380 : :
381 [ + + ]: 126 : TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
382 : 63 : scsi_lun_free_io_channel(lun);
383 : : }
384 : 63 : }
385 : :
386 : : int
387 : 63 : spdk_scsi_dev_allocate_io_channels(struct spdk_scsi_dev *dev)
388 : : {
389 : : struct spdk_scsi_lun *lun, *tmp_lun;
390 : : int rc;
391 : :
392 [ + + ]: 126 : TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
393 : 63 : rc = scsi_lun_allocate_io_channel(lun);
394 [ - + ]: 63 : if (rc < 0) {
395 : 0 : spdk_scsi_dev_free_io_channels(dev);
396 : 0 : return -1;
397 : : }
398 : : }
399 : :
400 : 63 : return 0;
401 : : }
402 : :
403 : : const char *
404 : 764 : spdk_scsi_dev_get_name(const struct spdk_scsi_dev *dev)
405 : : {
406 : 764 : return dev->name;
407 : : }
408 : :
409 : : int
410 : 600 : spdk_scsi_dev_get_id(const struct spdk_scsi_dev *dev)
411 : : {
412 : 600 : return dev->id;
413 : : }
414 : :
415 : : struct spdk_scsi_lun *
416 : 64719607 : spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id)
417 : : {
418 : : struct spdk_scsi_lun *lun;
419 : :
420 [ + + ]: 87038586 : TAILQ_FOREACH(lun, &dev->luns, tailq) {
421 [ + + ]: 86912582 : if (lun->id == lun_id) {
422 [ + + ]: 64593603 : if (!spdk_scsi_lun_is_removing(lun)) {
423 : 64593561 : return lun;
424 : : } else {
425 : 42 : return NULL;
426 : : }
427 : : }
428 : : }
429 : :
430 : 126004 : return NULL;
431 : : }
432 : :
433 : : struct spdk_scsi_lun *
434 : 1358 : spdk_scsi_dev_get_first_lun(struct spdk_scsi_dev *dev)
435 : : {
436 : : struct spdk_scsi_lun *lun;
437 : :
438 [ + - ]: 1358 : TAILQ_FOREACH(lun, &dev->luns, tailq) {
439 [ + - ]: 1358 : if (!spdk_scsi_lun_is_removing(lun)) {
440 : 1358 : return lun;
441 : : }
442 : : }
443 : :
444 : 0 : return NULL;
445 : : }
446 : :
447 : : struct spdk_scsi_lun *
448 : 1804 : spdk_scsi_dev_get_next_lun(struct spdk_scsi_lun *prev_lun)
449 : : {
450 : : struct spdk_scsi_dev *dev;
451 : : struct spdk_scsi_lun *lun;
452 : :
453 [ - + ]: 1804 : if (prev_lun == NULL) {
454 : 0 : return NULL;
455 : : }
456 : :
457 : 1804 : dev = prev_lun->dev;
458 : :
459 : 1804 : lun = TAILQ_NEXT(prev_lun, tailq);
460 [ + + ]: 1804 : if (lun == NULL) {
461 : 1358 : return NULL;
462 : : }
463 : :
464 [ - + + - ]: 446 : TAILQ_FOREACH_FROM(lun, &dev->luns, tailq) {
465 [ + - ]: 446 : if (!spdk_scsi_lun_is_removing(lun)) {
466 : 446 : break;
467 : : }
468 : : }
469 : :
470 : 446 : return lun;
471 : : }
472 : :
473 : : bool
474 : 565 : spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev,
475 : : const struct spdk_scsi_port *initiator_port)
476 : : {
477 : : struct spdk_scsi_lun *lun;
478 : :
479 [ + + ]: 1276 : TAILQ_FOREACH(lun, &dev->luns, tailq) {
480 [ + + + + ]: 1457 : if (scsi_lun_has_pending_tasks(lun, initiator_port) ||
481 : 721 : scsi_lun_has_pending_mgmt_tasks(lun, initiator_port)) {
482 : 25 : return true;
483 : : }
484 : : }
485 : :
486 : 540 : return false;
487 : : }
|