xref: /spdk/lib/scsi/lun.c (revision c4d9daeb7bf491bc0eb6e8d417b75d44773cb009)
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 #include "spdk/endian.h"
37 #include "spdk/env.h"
38 #include "spdk/thread.h"
39 #include "spdk/event.h"
40 #include "spdk/util.h"
41 #include "spdk/likely.h"
42 
43 void
44 spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
45 {
46 	if (lun) {
47 		TAILQ_REMOVE(&lun->tasks, task, scsi_link);
48 		spdk_trace_record(TRACE_SCSI_TASK_DONE, lun->dev->id, 0, (uintptr_t)task, 0);
49 	}
50 	task->cpl_fn(task);
51 }
52 
53 static void
54 scsi_lun_complete_mgmt_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
55 {
56 	TAILQ_REMOVE(&lun->mgmt_tasks, task, scsi_link);
57 
58 	task->cpl_fn(task);
59 
60 	/* Try to execute the first pending mgmt task if it exists. */
61 	spdk_scsi_lun_execute_mgmt_task(lun);
62 }
63 
64 static bool
65 scsi_lun_has_outstanding_tasks(struct spdk_scsi_lun *lun)
66 {
67 	return !TAILQ_EMPTY(&lun->tasks);
68 }
69 
70 /* Reset task have to wait until all prior outstanding tasks complete. */
71 static int
72 scsi_lun_reset_check_outstanding_tasks(void *arg)
73 {
74 	struct spdk_scsi_task *task = (struct spdk_scsi_task *)arg;
75 	struct spdk_scsi_lun *lun = task->lun;
76 
77 	if (scsi_lun_has_outstanding_tasks(lun)) {
78 		return 0;
79 	}
80 	spdk_poller_unregister(&lun->reset_poller);
81 
82 	scsi_lun_complete_mgmt_task(lun, task);
83 	return 1;
84 }
85 
86 void
87 spdk_scsi_lun_complete_reset_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
88 {
89 	if (task->status == SPDK_SCSI_STATUS_GOOD) {
90 		if (scsi_lun_has_outstanding_tasks(lun)) {
91 			lun->reset_poller =
92 				spdk_poller_register(scsi_lun_reset_check_outstanding_tasks,
93 						     task, 10);
94 			return;
95 		}
96 	}
97 
98 	scsi_lun_complete_mgmt_task(lun, task);
99 }
100 
101 static void
102 _scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun,
103 			    struct spdk_scsi_task *task)
104 {
105 	TAILQ_INSERT_TAIL(&lun->mgmt_tasks, task, scsi_link);
106 
107 	switch (task->function) {
108 	case SPDK_SCSI_TASK_FUNC_ABORT_TASK:
109 		task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
110 		SPDK_ERRLOG("ABORT_TASK failed\n");
111 		break;
112 
113 	case SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET:
114 		task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
115 		SPDK_ERRLOG("ABORT_TASK_SET failed\n");
116 		break;
117 
118 	case SPDK_SCSI_TASK_FUNC_LUN_RESET:
119 		spdk_bdev_scsi_reset(task);
120 		return;
121 
122 	default:
123 		SPDK_ERRLOG("Unknown Task Management Function!\n");
124 		/*
125 		 * Task management functions other than those above should never
126 		 * reach this point having been filtered by the frontend. Reject
127 		 * the task as being unsupported.
128 		 */
129 		task->response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
130 		break;
131 	}
132 
133 	scsi_lun_complete_mgmt_task(lun, task);
134 }
135 
136 void
137 spdk_scsi_lun_append_mgmt_task(struct spdk_scsi_lun *lun,
138 			       struct spdk_scsi_task *task)
139 {
140 	TAILQ_INSERT_TAIL(&lun->pending_mgmt_tasks, task, scsi_link);
141 }
142 
143 void
144 spdk_scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun)
145 {
146 	struct spdk_scsi_task *task;
147 
148 	if (!TAILQ_EMPTY(&lun->mgmt_tasks)) {
149 		return;
150 	}
151 
152 	task = TAILQ_FIRST(&lun->pending_mgmt_tasks);
153 	if (spdk_likely(task == NULL)) {
154 		/* Try to execute all pending tasks */
155 		spdk_scsi_lun_execute_tasks(lun);
156 		return;
157 	}
158 	TAILQ_REMOVE(&lun->pending_mgmt_tasks, task, scsi_link);
159 
160 	_scsi_lun_execute_mgmt_task(lun, task);
161 }
162 
163 static void
164 _scsi_lun_execute_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
165 {
166 	int rc;
167 
168 	task->status = SPDK_SCSI_STATUS_GOOD;
169 	spdk_trace_record(TRACE_SCSI_TASK_START, lun->dev->id, task->length, (uintptr_t)task, 0);
170 	TAILQ_INSERT_TAIL(&lun->tasks, task, scsi_link);
171 	if (!lun->removed) {
172 		rc = spdk_bdev_scsi_execute(task);
173 	} else {
174 		spdk_scsi_task_process_abort(task);
175 		rc = SPDK_SCSI_TASK_COMPLETE;
176 	}
177 
178 	switch (rc) {
179 	case SPDK_SCSI_TASK_PENDING:
180 		break;
181 
182 	case SPDK_SCSI_TASK_COMPLETE:
183 		spdk_scsi_lun_complete_task(lun, task);
184 		break;
185 
186 	default:
187 		abort();
188 	}
189 }
190 
191 void
192 spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
193 {
194 	TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link);
195 }
196 
197 void
198 spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun)
199 {
200 	struct spdk_scsi_task *task, *task_tmp;
201 
202 	if (spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
203 		/* Pending IO tasks will wait for completion of existing mgmt tasks.
204 		 */
205 		return;
206 	}
207 
208 	TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) {
209 		TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link);
210 		_scsi_lun_execute_task(lun, task);
211 	}
212 }
213 
214 static void
215 scsi_lun_remove(struct spdk_scsi_lun *lun)
216 {
217 	spdk_bdev_close(lun->bdev_desc);
218 
219 	free(lun);
220 }
221 
222 static int
223 scsi_lun_check_io_channel(void *arg)
224 {
225 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
226 
227 	if (lun->io_channel) {
228 		return -1;
229 	}
230 	spdk_poller_unregister(&lun->hotremove_poller);
231 
232 	scsi_lun_remove(lun);
233 	return -1;
234 }
235 
236 static void
237 scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun)
238 {
239 	struct spdk_scsi_lun_desc *desc, *tmp;
240 
241 	if (lun->hotremove_cb) {
242 		lun->hotremove_cb(lun, lun->hotremove_ctx);
243 	}
244 
245 	TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) {
246 		if (desc->hotremove_cb) {
247 			desc->hotremove_cb(lun, desc->hotremove_ctx);
248 		} else {
249 			spdk_scsi_lun_close(desc);
250 		}
251 	}
252 
253 	if (lun->io_channel) {
254 		lun->hotremove_poller = spdk_poller_register(scsi_lun_check_io_channel,
255 					lun, 10);
256 	} else {
257 		scsi_lun_remove(lun);
258 	}
259 }
260 
261 static int
262 scsi_lun_check_pending_tasks(void *arg)
263 {
264 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
265 
266 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
267 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
268 		return -1;
269 	}
270 	spdk_poller_unregister(&lun->hotremove_poller);
271 
272 	scsi_lun_notify_hot_remove(lun);
273 	return -1;
274 }
275 
276 static void
277 _scsi_lun_hot_remove(void *arg1)
278 {
279 	struct spdk_scsi_lun *lun = arg1;
280 
281 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
282 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
283 		lun->hotremove_poller = spdk_poller_register(scsi_lun_check_pending_tasks,
284 					lun, 10);
285 	} else {
286 		scsi_lun_notify_hot_remove(lun);
287 	}
288 }
289 
290 static void
291 scsi_lun_hot_remove(void *remove_ctx)
292 {
293 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx;
294 	struct spdk_thread *thread;
295 
296 	if (lun->removed) {
297 		return;
298 	}
299 
300 	lun->removed = true;
301 	spdk_scsi_dev_delete_lun(lun->dev, lun);
302 
303 	if (lun->io_channel == NULL) {
304 		scsi_lun_remove(lun);
305 		return;
306 	}
307 
308 	thread = spdk_io_channel_get_thread(lun->io_channel);
309 	if (thread != spdk_get_thread()) {
310 		spdk_thread_send_msg(thread, _scsi_lun_hot_remove, lun);
311 	} else {
312 		_scsi_lun_hot_remove(lun);
313 	}
314 }
315 
316 /**
317  * \brief Constructs a new spdk_scsi_lun object based on the provided parameters.
318  *
319  * \param bdev  bdev associated with this LUN
320  *
321  * \return NULL if bdev == NULL
322  * \return pointer to the new spdk_scsi_lun object otherwise
323  */
324 _spdk_scsi_lun *
325 spdk_scsi_lun_construct(struct spdk_bdev *bdev,
326 			void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
327 			void *hotremove_ctx)
328 {
329 	struct spdk_scsi_lun *lun;
330 	int rc;
331 
332 	if (bdev == NULL) {
333 		SPDK_ERRLOG("bdev must be non-NULL\n");
334 		return NULL;
335 	}
336 
337 	lun = calloc(1, sizeof(*lun));
338 	if (lun == NULL) {
339 		SPDK_ERRLOG("could not allocate lun\n");
340 		return NULL;
341 	}
342 
343 	rc = spdk_bdev_open(bdev, true, scsi_lun_hot_remove, lun, &lun->bdev_desc);
344 
345 	if (rc != 0) {
346 		SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc);
347 		free(lun);
348 		return NULL;
349 	}
350 
351 	TAILQ_INIT(&lun->tasks);
352 	TAILQ_INIT(&lun->pending_tasks);
353 	TAILQ_INIT(&lun->mgmt_tasks);
354 	TAILQ_INIT(&lun->pending_mgmt_tasks);
355 
356 	lun->bdev = bdev;
357 	lun->io_channel = NULL;
358 	lun->hotremove_cb = hotremove_cb;
359 	lun->hotremove_ctx = hotremove_ctx;
360 	TAILQ_INIT(&lun->open_descs);
361 
362 	return lun;
363 }
364 
365 void
366 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun)
367 {
368 	scsi_lun_hot_remove(lun);
369 }
370 
371 int
372 spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb,
373 		   void *hotremove_ctx, struct spdk_scsi_lun_desc **_desc)
374 {
375 	struct spdk_scsi_lun_desc *desc;
376 
377 	desc = calloc(1, sizeof(*desc));
378 	if (desc == NULL) {
379 		SPDK_ERRLOG("calloc() failed for LUN descriptor.\n");
380 		return -ENOMEM;
381 	}
382 
383 	TAILQ_INSERT_TAIL(&lun->open_descs, desc, link);
384 
385 	desc->lun = lun;
386 	desc->hotremove_cb = hotremove_cb;
387 	desc->hotremove_ctx = hotremove_ctx;
388 	*_desc = desc;
389 
390 	return 0;
391 }
392 
393 void
394 spdk_scsi_lun_close(struct spdk_scsi_lun_desc *desc)
395 {
396 	struct spdk_scsi_lun *lun = desc->lun;
397 
398 	TAILQ_REMOVE(&lun->open_descs, desc, link);
399 	free(desc);
400 
401 	assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL);
402 }
403 
404 int
405 _spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun)
406 {
407 	if (lun->io_channel != NULL) {
408 		if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) {
409 			lun->ref++;
410 			return 0;
411 		}
412 		SPDK_ERRLOG("io_channel already allocated for lun %s\n",
413 			    spdk_bdev_get_name(lun->bdev));
414 		return -1;
415 	}
416 
417 	lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc);
418 	if (lun->io_channel == NULL) {
419 		return -1;
420 	}
421 	lun->ref = 1;
422 	return 0;
423 }
424 
425 void
426 _spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun)
427 {
428 	if (lun->io_channel == NULL) {
429 		return;
430 	}
431 
432 	if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) {
433 		SPDK_ERRLOG("io_channel was freed by different thread\n");
434 		return;
435 	}
436 
437 	lun->ref--;
438 	if (lun->ref == 0) {
439 		spdk_put_io_channel(lun->io_channel);
440 		lun->io_channel = NULL;
441 	}
442 }
443 
444 int
445 spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun_desc *desc)
446 {
447 	struct spdk_scsi_lun *lun = desc->lun;
448 
449 	return _spdk_scsi_lun_allocate_io_channel(lun);
450 }
451 
452 void
453 spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun_desc *desc)
454 {
455 	struct spdk_scsi_lun *lun = desc->lun;
456 
457 	_spdk_scsi_lun_free_io_channel(lun);
458 }
459 
460 int
461 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun)
462 {
463 	return lun->id;
464 }
465 
466 const char *
467 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun)
468 {
469 	return spdk_bdev_get_name(lun->bdev);
470 }
471 
472 const struct spdk_scsi_dev *
473 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun)
474 {
475 	return lun->dev;
476 }
477 
478 bool
479 spdk_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun)
480 {
481 	return !TAILQ_EMPTY(&lun->pending_mgmt_tasks) ||
482 	       !TAILQ_EMPTY(&lun->mgmt_tasks);
483 }
484 
485 /* This check includes both pending and submitted (outstanding) tasks. */
486 bool
487 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun)
488 {
489 	return !TAILQ_EMPTY(&lun->pending_tasks) ||
490 	       !TAILQ_EMPTY(&lun->tasks);
491 }
492 
493 bool
494 spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun)
495 {
496 	return lun->removed;
497 }
498 
499 bool
500 spdk_scsi_lun_get_dif_ctx(struct spdk_scsi_lun *lun, uint8_t *cdb,
501 			  uint32_t offset, struct spdk_dif_ctx *dif_ctx)
502 {
503 	return spdk_scsi_bdev_get_dif_ctx(lun->bdev, cdb, offset, dif_ctx);
504 }
505