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 *
scsi_dev_get_list(void)12 scsi_dev_get_list(void)
13 {
14 return g_devs;
15 }
16
17 static struct spdk_scsi_dev *
allocate_dev(void)18 allocate_dev(void)
19 {
20 struct spdk_scsi_dev *dev;
21 int i;
22
23 for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) {
24 dev = &g_devs[i];
25 if (!dev->is_allocated) {
26 memset(dev, 0, sizeof(*dev));
27 dev->id = i;
28 dev->is_allocated = 1;
29 TAILQ_INIT(&dev->luns);
30 return dev;
31 }
32 }
33
34 return NULL;
35 }
36
37 static void
free_dev(struct spdk_scsi_dev * dev)38 free_dev(struct spdk_scsi_dev *dev)
39 {
40 assert(dev->is_allocated == 1);
41 assert(dev->removed == true);
42
43 dev->is_allocated = 0;
44
45 if (dev->remove_cb) {
46 dev->remove_cb(dev->remove_ctx, 0);
47 dev->remove_cb = NULL;
48 }
49 }
50
51 void
spdk_scsi_dev_destruct(struct spdk_scsi_dev * dev,spdk_scsi_dev_destruct_cb_t cb_fn,void * cb_arg)52 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 if (dev == NULL) {
58 if (cb_fn) {
59 cb_fn(cb_arg, -EINVAL);
60 }
61 return;
62 }
63
64 if (dev->removed) {
65 if (cb_fn) {
66 cb_fn(cb_arg, -EINVAL);
67 }
68 return;
69 }
70
71 dev->removed = true;
72 dev->remove_cb = cb_fn;
73 dev->remove_ctx = cb_arg;
74
75 if (TAILQ_EMPTY(&dev->luns)) {
76 free_dev(dev);
77 return;
78 }
79
80 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 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
scsi_dev_find_free_lun(struct spdk_scsi_dev * dev,int lun_id,struct spdk_scsi_lun ** prev_lun)94 scsi_dev_find_free_lun(struct spdk_scsi_dev *dev, int lun_id,
95 struct spdk_scsi_lun **prev_lun)
96 {
97 struct spdk_scsi_lun *lun, *_prev_lun = NULL;
98
99 if (prev_lun == NULL) {
100 return -EINVAL;
101 }
102
103 if (lun_id == -1) {
104 lun_id = 0;
105
106 TAILQ_FOREACH(lun, &dev->luns, tailq) {
107 if (lun->id > lun_id) {
108 break;
109 }
110 lun_id = lun->id + 1;
111 _prev_lun = lun;
112 }
113
114 if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
115 return -ENOSPC;
116 }
117 } else {
118 TAILQ_FOREACH(lun, &dev->luns, tailq) {
119 if (lun->id == lun_id) {
120 return -EEXIST;
121 } else if (lun->id > lun_id) {
122 break;
123 }
124 _prev_lun = lun;
125 }
126 }
127
128 *prev_lun = _prev_lun;
129 return 0;
130 }
131
132 int
spdk_scsi_dev_add_lun(struct spdk_scsi_dev * dev,const char * bdev_name,int lun_id,void (* hotremove_cb)(const struct spdk_scsi_lun *,void *),void * hotremove_ctx)133 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 return spdk_scsi_dev_add_lun_ext(dev, bdev_name, lun_id,
138 NULL, NULL,
139 hotremove_cb, hotremove_ctx);
140 }
141
142 int
spdk_scsi_dev_add_lun_ext(struct spdk_scsi_dev * dev,const char * bdev_name,int lun_id,void (* resize_cb)(const struct spdk_scsi_lun *,void *),void * resize_ctx,void (* hotremove_cb)(const struct spdk_scsi_lun *,void *),void * hotremove_ctx)143 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 struct spdk_scsi_lun *lun, *prev_lun = NULL;
150 int rc;
151
152 if (lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
153 SPDK_ERRLOG("LUN ID %d is more than the maximum.\n", lun_id);
154 return -1;
155 }
156
157 rc = scsi_dev_find_free_lun(dev, lun_id, &prev_lun);
158 if (rc != 0) {
159 SPDK_ERRLOG("%s\n", rc == -EEXIST ? "LUN ID is duplicated" : "Free LUN ID is not found");
160 return rc;
161 }
162
163 lun = scsi_lun_construct(bdev_name, resize_cb, resize_ctx, hotremove_cb, hotremove_ctx);
164 if (lun == NULL) {
165 return -1;
166 }
167
168 lun->dev = dev;
169
170 if (lun_id != -1) {
171 lun->id = lun_id;
172 } else if (prev_lun == NULL) {
173 lun->id = 0;
174 } else {
175 lun->id = prev_lun->id + 1;
176 }
177
178 if (prev_lun == NULL) {
179 TAILQ_INSERT_HEAD(&dev->luns, lun, tailq);
180 } else {
181 TAILQ_INSERT_AFTER(&dev->luns, prev_lun, lun, tailq);
182 }
183 return 0;
184 }
185
186 void
spdk_scsi_dev_delete_lun(struct spdk_scsi_dev * dev,struct spdk_scsi_lun * lun)187 spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev,
188 struct spdk_scsi_lun *lun)
189 {
190 TAILQ_REMOVE(&dev->luns, lun, tailq);
191
192 if (dev->removed && TAILQ_EMPTY(&dev->luns)) {
193 free_dev(dev);
194 }
195 }
196
spdk_scsi_dev_construct(const char * name,const char * bdev_name_list[],int * lun_id_list,int num_luns,uint8_t protocol_id,void (* hotremove_cb)(const struct spdk_scsi_lun *,void *),void * hotremove_ctx)197 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 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
spdk_scsi_dev_construct_ext(const char * name,const char * bdev_name_list[],int * lun_id_list,int num_luns,uint8_t protocol_id,void (* resize_cb)(const struct spdk_scsi_lun *,void *),void * resize_ctx,void (* hotremove_cb)(const struct spdk_scsi_lun *,void *),void * hotremove_ctx)208 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 name_len = strlen(name);
221 if (name_len > sizeof(dev->name) - 1) {
222 SPDK_ERRLOG("device %s: name longer than maximum allowed length %zu\n",
223 name, sizeof(dev->name) - 1);
224 return NULL;
225 }
226
227 if (num_luns == 0) {
228 SPDK_ERRLOG("device %s: no LUNs specified\n", name);
229 return NULL;
230 }
231
232 found_lun_0 = false;
233 for (i = 0; i < num_luns; i++) {
234 if (lun_id_list[i] == 0) {
235 found_lun_0 = true;
236 break;
237 }
238 }
239
240 if (!found_lun_0) {
241 SPDK_ERRLOG("device %s: no LUN 0 specified\n", name);
242 return NULL;
243 }
244
245 for (i = 0; i < num_luns; i++) {
246 if (bdev_name_list[i] == NULL) {
247 SPDK_ERRLOG("NULL spdk_scsi_lun for LUN %d\n",
248 lun_id_list[i]);
249 return NULL;
250 }
251 }
252
253 dev = allocate_dev();
254 if (dev == NULL) {
255 return NULL;
256 }
257
258 memcpy(dev->name, name, name_len + 1);
259
260 dev->num_ports = 0;
261 dev->protocol_id = protocol_id;
262
263 for (i = 0; i < num_luns; i++) {
264 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 if (rc < 0) {
268 spdk_scsi_dev_destruct(dev, NULL, NULL);
269 return NULL;
270 }
271 }
272
273 return dev;
274 }
275
276 void
spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev * dev,struct spdk_scsi_task * task)277 spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev,
278 struct spdk_scsi_task *task)
279 {
280 assert(task != NULL);
281
282 scsi_lun_execute_mgmt_task(task->lun, task);
283 }
284
285 void
spdk_scsi_dev_queue_task(struct spdk_scsi_dev * dev,struct spdk_scsi_task * task)286 spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev,
287 struct spdk_scsi_task *task)
288 {
289 assert(task != NULL);
290
291 scsi_lun_execute_task(task->lun, task);
292 }
293
294 static struct spdk_scsi_port *
scsi_dev_find_free_port(struct spdk_scsi_dev * dev)295 scsi_dev_find_free_port(struct spdk_scsi_dev *dev)
296 {
297 int i;
298
299 for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
300 if (!dev->port[i].is_used) {
301 return &dev->port[i];
302 }
303 }
304
305 return NULL;
306 }
307
308 int
spdk_scsi_dev_add_port(struct spdk_scsi_dev * dev,uint64_t id,const char * name)309 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 if (dev->num_ports == SPDK_SCSI_DEV_MAX_PORTS) {
315 SPDK_ERRLOG("device already has %d ports\n", SPDK_SCSI_DEV_MAX_PORTS);
316 return -1;
317 }
318
319 port = spdk_scsi_dev_find_port_by_id(dev, id);
320 if (port != NULL) {
321 SPDK_ERRLOG("device already has port(%" PRIu64 ")\n", id);
322 return -1;
323 }
324
325 port = scsi_dev_find_free_port(dev);
326 if (port == NULL) {
327 assert(false);
328 return -1;
329 }
330
331 rc = scsi_port_construct(port, id, dev->num_ports, name);
332 if (rc != 0) {
333 return rc;
334 }
335
336 dev->num_ports++;
337 return 0;
338 }
339
340 int
spdk_scsi_dev_delete_port(struct spdk_scsi_dev * dev,uint64_t id)341 spdk_scsi_dev_delete_port(struct spdk_scsi_dev *dev, uint64_t id)
342 {
343 struct spdk_scsi_port *port;
344
345 port = spdk_scsi_dev_find_port_by_id(dev, id);
346 if (port == NULL) {
347 SPDK_ERRLOG("device does not have specified port(%" PRIu64 ")\n", id);
348 return -1;
349 }
350
351 scsi_port_destruct(port);
352
353 dev->num_ports--;
354
355 return 0;
356 }
357
358 struct spdk_scsi_port *
spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev * dev,uint64_t id)359 spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id)
360 {
361 int i;
362
363 for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) {
364 if (!dev->port[i].is_used) {
365 continue;
366 }
367 if (dev->port[i].id == id) {
368 return &dev->port[i];
369 }
370 }
371
372 /* No matching port found. */
373 return NULL;
374 }
375
376 void
spdk_scsi_dev_free_io_channels(struct spdk_scsi_dev * dev)377 spdk_scsi_dev_free_io_channels(struct spdk_scsi_dev *dev)
378 {
379 struct spdk_scsi_lun *lun, *tmp_lun;
380
381 TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
382 scsi_lun_free_io_channel(lun);
383 }
384 }
385
386 int
spdk_scsi_dev_allocate_io_channels(struct spdk_scsi_dev * dev)387 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 TAILQ_FOREACH_SAFE(lun, &dev->luns, tailq, tmp_lun) {
393 rc = scsi_lun_allocate_io_channel(lun);
394 if (rc < 0) {
395 spdk_scsi_dev_free_io_channels(dev);
396 return -1;
397 }
398 }
399
400 return 0;
401 }
402
403 const char *
spdk_scsi_dev_get_name(const struct spdk_scsi_dev * dev)404 spdk_scsi_dev_get_name(const struct spdk_scsi_dev *dev)
405 {
406 return dev->name;
407 }
408
409 int
spdk_scsi_dev_get_id(const struct spdk_scsi_dev * dev)410 spdk_scsi_dev_get_id(const struct spdk_scsi_dev *dev)
411 {
412 return dev->id;
413 }
414
415 struct spdk_scsi_lun *
spdk_scsi_dev_get_lun(struct spdk_scsi_dev * dev,int lun_id)416 spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id)
417 {
418 struct spdk_scsi_lun *lun;
419
420 TAILQ_FOREACH(lun, &dev->luns, tailq) {
421 if (lun->id == lun_id) {
422 if (!spdk_scsi_lun_is_removing(lun)) {
423 return lun;
424 } else {
425 return NULL;
426 }
427 }
428 }
429
430 return NULL;
431 }
432
433 struct spdk_scsi_lun *
spdk_scsi_dev_get_first_lun(struct spdk_scsi_dev * dev)434 spdk_scsi_dev_get_first_lun(struct spdk_scsi_dev *dev)
435 {
436 struct spdk_scsi_lun *lun;
437
438 TAILQ_FOREACH(lun, &dev->luns, tailq) {
439 if (!spdk_scsi_lun_is_removing(lun)) {
440 return lun;
441 }
442 }
443
444 return NULL;
445 }
446
447 struct spdk_scsi_lun *
spdk_scsi_dev_get_next_lun(struct spdk_scsi_lun * prev_lun)448 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 if (prev_lun == NULL) {
454 return NULL;
455 }
456
457 dev = prev_lun->dev;
458
459 lun = TAILQ_NEXT(prev_lun, tailq);
460 if (lun == NULL) {
461 return NULL;
462 }
463
464 TAILQ_FOREACH_FROM(lun, &dev->luns, tailq) {
465 if (!spdk_scsi_lun_is_removing(lun)) {
466 break;
467 }
468 }
469
470 return lun;
471 }
472
473 bool
spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev * dev,const struct spdk_scsi_port * initiator_port)474 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 TAILQ_FOREACH(lun, &dev->luns, tailq) {
480 if (scsi_lun_has_pending_tasks(lun, initiator_port) ||
481 scsi_lun_has_pending_mgmt_tasks(lun, initiator_port)) {
482 return true;
483 }
484 }
485
486 return false;
487 }
488