]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>. | |
5 | * Copyright (c) Intel Corporation. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * * Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * * Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in | |
16 | * the documentation and/or other materials provided with the | |
17 | * distribution. | |
18 | * * Neither the name of Intel Corporation nor the names of its | |
19 | * contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include "scsi_internal.h" | |
36 | ||
37 | static struct spdk_scsi_dev g_devs[SPDK_SCSI_MAX_DEVS]; | |
38 | ||
39 | struct spdk_scsi_dev * | |
40 | spdk_scsi_dev_get_list(void) | |
41 | { | |
42 | return g_devs; | |
43 | } | |
44 | ||
45 | static struct spdk_scsi_dev * | |
46 | allocate_dev(void) | |
47 | { | |
48 | struct spdk_scsi_dev *dev; | |
49 | int i; | |
50 | ||
51 | for (i = 0; i < SPDK_SCSI_MAX_DEVS; i++) { | |
52 | dev = &g_devs[i]; | |
53 | if (!dev->is_allocated) { | |
54 | memset(dev, 0, sizeof(*dev)); | |
55 | dev->id = i; | |
56 | dev->is_allocated = 1; | |
57 | return dev; | |
58 | } | |
59 | } | |
60 | ||
61 | return NULL; | |
62 | } | |
63 | ||
64 | static void | |
65 | free_dev(struct spdk_scsi_dev *dev) | |
66 | { | |
11fdf7f2 TL |
67 | assert(dev->is_allocated == 1); |
68 | assert(dev->removed == true); | |
69 | ||
7c673cae FG |
70 | dev->is_allocated = 0; |
71 | } | |
72 | ||
73 | void | |
74 | spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev) | |
75 | { | |
11fdf7f2 | 76 | int lun_cnt; |
7c673cae FG |
77 | int i; |
78 | ||
11fdf7f2 | 79 | if (dev == NULL || dev->removed) { |
7c673cae FG |
80 | return; |
81 | } | |
82 | ||
11fdf7f2 TL |
83 | dev->removed = true; |
84 | lun_cnt = 0; | |
85 | ||
86 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { | |
7c673cae FG |
87 | if (dev->lun[i] == NULL) { |
88 | continue; | |
89 | } | |
90 | ||
11fdf7f2 TL |
91 | /* |
92 | * LUN will remove itself from this dev when all outstanding IO | |
93 | * is done. When no more LUNs, dev will be deleted. | |
94 | */ | |
7c673cae | 95 | spdk_scsi_lun_destruct(dev->lun[i]); |
11fdf7f2 | 96 | lun_cnt++; |
7c673cae FG |
97 | } |
98 | ||
11fdf7f2 TL |
99 | if (lun_cnt == 0) { |
100 | free_dev(dev); | |
101 | return; | |
102 | } | |
7c673cae FG |
103 | } |
104 | ||
105 | static int | |
11fdf7f2 | 106 | spdk_scsi_dev_find_lowest_free_lun_id(struct spdk_scsi_dev *dev) |
7c673cae | 107 | { |
11fdf7f2 | 108 | int i; |
7c673cae | 109 | |
11fdf7f2 TL |
110 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { |
111 | if (dev->lun[i] == NULL) { | |
112 | return i; | |
113 | } | |
7c673cae FG |
114 | } |
115 | ||
11fdf7f2 TL |
116 | return -1; |
117 | } | |
118 | ||
119 | int | |
120 | spdk_scsi_dev_add_lun(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id, | |
121 | void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), | |
122 | void *hotremove_ctx) | |
123 | { | |
124 | struct spdk_bdev *bdev; | |
125 | struct spdk_scsi_lun *lun; | |
126 | ||
127 | bdev = spdk_bdev_get_by_name(bdev_name); | |
128 | if (bdev == NULL) { | |
129 | SPDK_ERRLOG("device %s: cannot find bdev '%s' (target %d)\n", | |
130 | dev->name, bdev_name, lun_id); | |
131 | return -1; | |
132 | } | |
133 | ||
134 | /* Search the lowest free LUN ID if LUN ID is default */ | |
135 | if (lun_id == -1) { | |
136 | lun_id = spdk_scsi_dev_find_lowest_free_lun_id(dev); | |
137 | if (lun_id == -1) { | |
138 | SPDK_ERRLOG("Free LUN ID is not found\n"); | |
139 | return -1; | |
140 | } | |
141 | } | |
142 | ||
143 | lun = spdk_scsi_lun_construct(bdev, hotremove_cb, hotremove_ctx); | |
144 | if (lun == NULL) { | |
145 | return -1; | |
7c673cae FG |
146 | } |
147 | ||
11fdf7f2 TL |
148 | lun->id = lun_id; |
149 | lun->dev = dev; | |
150 | dev->lun[lun_id] = lun; | |
7c673cae FG |
151 | return 0; |
152 | } | |
153 | ||
154 | void | |
155 | spdk_scsi_dev_delete_lun(struct spdk_scsi_dev *dev, | |
156 | struct spdk_scsi_lun *lun) | |
157 | { | |
11fdf7f2 | 158 | int lun_cnt = 0; |
7c673cae | 159 | int i; |
7c673cae | 160 | |
11fdf7f2 TL |
161 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { |
162 | if (dev->lun[i] == lun) { | |
7c673cae | 163 | dev->lun[i] = NULL; |
11fdf7f2 | 164 | } |
7c673cae | 165 | |
7c673cae | 166 | if (dev->lun[i]) { |
11fdf7f2 | 167 | lun_cnt++; |
7c673cae FG |
168 | } |
169 | } | |
11fdf7f2 TL |
170 | |
171 | if (dev->removed == true && lun_cnt == 0) { | |
172 | free_dev(dev); | |
173 | } | |
7c673cae FG |
174 | } |
175 | ||
176 | /* This typedef exists to work around an astyle 2.05 bug. | |
177 | * Remove it when astyle is fixed. | |
11fdf7f2 | 178 | */ |
7c673cae FG |
179 | typedef struct spdk_scsi_dev _spdk_scsi_dev; |
180 | ||
181 | _spdk_scsi_dev * | |
11fdf7f2 TL |
182 | spdk_scsi_dev_construct(const char *name, const char *bdev_name_list[], |
183 | int *lun_id_list, int num_luns, uint8_t protocol_id, | |
184 | void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), | |
185 | void *hotremove_ctx) | |
7c673cae FG |
186 | { |
187 | struct spdk_scsi_dev *dev; | |
11fdf7f2 TL |
188 | size_t name_len; |
189 | bool found_lun_0; | |
7c673cae FG |
190 | int i, rc; |
191 | ||
11fdf7f2 TL |
192 | name_len = strlen(name); |
193 | if (name_len > sizeof(dev->name) - 1) { | |
194 | SPDK_ERRLOG("device %s: name longer than maximum allowed length %zu\n", | |
195 | name, sizeof(dev->name) - 1); | |
196 | return NULL; | |
197 | } | |
198 | ||
7c673cae FG |
199 | if (num_luns == 0) { |
200 | SPDK_ERRLOG("device %s: no LUNs specified\n", name); | |
201 | return NULL; | |
202 | } | |
203 | ||
11fdf7f2 TL |
204 | found_lun_0 = false; |
205 | for (i = 0; i < num_luns; i++) { | |
206 | if (lun_id_list[i] == 0) { | |
207 | found_lun_0 = true; | |
208 | break; | |
209 | } | |
210 | } | |
211 | ||
212 | if (!found_lun_0) { | |
7c673cae FG |
213 | SPDK_ERRLOG("device %s: no LUN 0 specified\n", name); |
214 | return NULL; | |
215 | } | |
216 | ||
217 | for (i = 0; i < num_luns; i++) { | |
11fdf7f2 | 218 | if (bdev_name_list[i] == NULL) { |
7c673cae FG |
219 | SPDK_ERRLOG("NULL spdk_scsi_lun for LUN %d\n", |
220 | lun_id_list[i]); | |
221 | return NULL; | |
222 | } | |
223 | } | |
224 | ||
225 | dev = allocate_dev(); | |
226 | if (dev == NULL) { | |
227 | return NULL; | |
228 | } | |
229 | ||
11fdf7f2 | 230 | memcpy(dev->name, name, name_len + 1); |
7c673cae FG |
231 | |
232 | dev->num_ports = 0; | |
11fdf7f2 | 233 | dev->protocol_id = protocol_id; |
7c673cae FG |
234 | |
235 | for (i = 0; i < num_luns; i++) { | |
11fdf7f2 TL |
236 | rc = spdk_scsi_dev_add_lun(dev, bdev_name_list[i], lun_id_list[i], |
237 | hotremove_cb, hotremove_ctx); | |
7c673cae | 238 | if (rc < 0) { |
11fdf7f2 TL |
239 | spdk_scsi_dev_destruct(dev); |
240 | return NULL; | |
7c673cae FG |
241 | } |
242 | } | |
243 | ||
244 | return dev; | |
7c673cae FG |
245 | } |
246 | ||
247 | void | |
248 | spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, | |
11fdf7f2 TL |
249 | struct spdk_scsi_task *task, |
250 | enum spdk_scsi_task_func func) | |
7c673cae FG |
251 | { |
252 | assert(task != NULL); | |
253 | ||
11fdf7f2 TL |
254 | task->function = func; |
255 | spdk_scsi_lun_task_mgmt_execute(task, func); | |
7c673cae FG |
256 | } |
257 | ||
258 | void | |
259 | spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, | |
260 | struct spdk_scsi_task *task) | |
261 | { | |
262 | assert(task != NULL); | |
263 | ||
11fdf7f2 TL |
264 | spdk_scsi_lun_execute_task(task->lun, task); |
265 | } | |
266 | ||
267 | static struct spdk_scsi_port * | |
268 | spdk_scsi_dev_find_free_port(struct spdk_scsi_dev *dev) | |
269 | { | |
270 | int i; | |
271 | ||
272 | for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) { | |
273 | if (!dev->port[i].is_used) { | |
274 | return &dev->port[i]; | |
275 | } | |
7c673cae | 276 | } |
11fdf7f2 TL |
277 | |
278 | return NULL; | |
7c673cae FG |
279 | } |
280 | ||
281 | int | |
282 | spdk_scsi_dev_add_port(struct spdk_scsi_dev *dev, uint64_t id, const char *name) | |
283 | { | |
284 | struct spdk_scsi_port *port; | |
285 | int rc; | |
286 | ||
287 | if (dev->num_ports == SPDK_SCSI_DEV_MAX_PORTS) { | |
288 | SPDK_ERRLOG("device already has %d ports\n", SPDK_SCSI_DEV_MAX_PORTS); | |
289 | return -1; | |
290 | } | |
291 | ||
11fdf7f2 TL |
292 | port = spdk_scsi_dev_find_port_by_id(dev, id); |
293 | if (port != NULL) { | |
294 | SPDK_ERRLOG("device already has port(%" PRIu64 ")\n", id); | |
295 | return -1; | |
296 | } | |
297 | ||
298 | port = spdk_scsi_dev_find_free_port(dev); | |
299 | if (port == NULL) { | |
300 | assert(false); | |
301 | return -1; | |
302 | } | |
7c673cae FG |
303 | |
304 | rc = spdk_scsi_port_construct(port, id, dev->num_ports, name); | |
305 | if (rc != 0) { | |
306 | return rc; | |
307 | } | |
308 | ||
309 | dev->num_ports++; | |
310 | return 0; | |
311 | } | |
312 | ||
11fdf7f2 TL |
313 | int |
314 | spdk_scsi_dev_delete_port(struct spdk_scsi_dev *dev, uint64_t id) | |
315 | { | |
316 | struct spdk_scsi_port *port; | |
317 | ||
318 | port = spdk_scsi_dev_find_port_by_id(dev, id); | |
319 | if (port == NULL) { | |
320 | SPDK_ERRLOG("device does not have specified port(%" PRIu64 ")\n", id); | |
321 | return -1; | |
322 | } | |
323 | ||
324 | spdk_scsi_port_destruct(port); | |
325 | ||
326 | dev->num_ports--; | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
7c673cae FG |
331 | struct spdk_scsi_port * |
332 | spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id) | |
333 | { | |
334 | int i; | |
335 | ||
11fdf7f2 TL |
336 | for (i = 0; i < SPDK_SCSI_DEV_MAX_PORTS; i++) { |
337 | if (!dev->port[i].is_used) { | |
338 | continue; | |
339 | } | |
7c673cae FG |
340 | if (dev->port[i].id == id) { |
341 | return &dev->port[i]; | |
342 | } | |
343 | } | |
344 | ||
345 | /* No matching port found. */ | |
346 | return NULL; | |
347 | } | |
348 | ||
7c673cae FG |
349 | void |
350 | spdk_scsi_dev_free_io_channels(struct spdk_scsi_dev *dev) | |
351 | { | |
352 | int i; | |
353 | ||
354 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { | |
355 | if (dev->lun[i] == NULL) { | |
356 | continue; | |
357 | } | |
11fdf7f2 | 358 | _spdk_scsi_lun_free_io_channel(dev->lun[i]); |
7c673cae FG |
359 | } |
360 | } | |
361 | ||
362 | int | |
363 | spdk_scsi_dev_allocate_io_channels(struct spdk_scsi_dev *dev) | |
364 | { | |
365 | int i, rc; | |
366 | ||
367 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { | |
368 | if (dev->lun[i] == NULL) { | |
369 | continue; | |
370 | } | |
11fdf7f2 | 371 | rc = _spdk_scsi_lun_allocate_io_channel(dev->lun[i]); |
7c673cae FG |
372 | if (rc < 0) { |
373 | spdk_scsi_dev_free_io_channels(dev); | |
374 | return -1; | |
375 | } | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
11fdf7f2 TL |
380 | |
381 | const char * | |
382 | spdk_scsi_dev_get_name(const struct spdk_scsi_dev *dev) | |
383 | { | |
384 | return dev->name; | |
385 | } | |
386 | ||
387 | int | |
388 | spdk_scsi_dev_get_id(const struct spdk_scsi_dev *dev) | |
389 | { | |
390 | return dev->id; | |
391 | } | |
392 | ||
393 | struct spdk_scsi_lun * | |
394 | spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id) | |
395 | { | |
396 | if (lun_id < 0 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) { | |
397 | return NULL; | |
398 | } | |
399 | ||
400 | return dev->lun[lun_id]; | |
401 | } | |
402 | ||
403 | bool | |
404 | spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev) | |
405 | { | |
406 | int i; | |
407 | ||
408 | for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; ++i) { | |
409 | if (dev->lun[i] && spdk_scsi_lun_has_pending_tasks(dev->lun[i])) { | |
410 | return true; | |
411 | } | |
412 | } | |
413 | ||
414 | return false; | |
415 | } |