xref: /spdk/lib/scsi/lun.c (revision 407e88fd2ab020d753e33014cf759353a9901b51)
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 		/* Check the command is allowed or not when reservation is exist */
173 		rc = spdk_scsi_pr_check(task);
174 		if (spdk_unlikely(rc < 0)) {
175 			/* Reservation Conflict */
176 			rc = SPDK_SCSI_TASK_COMPLETE;
177 		} else {
178 			rc = spdk_bdev_scsi_execute(task);
179 		}
180 	} else {
181 		spdk_scsi_task_process_abort(task);
182 		rc = SPDK_SCSI_TASK_COMPLETE;
183 	}
184 
185 	switch (rc) {
186 	case SPDK_SCSI_TASK_PENDING:
187 		break;
188 
189 	case SPDK_SCSI_TASK_COMPLETE:
190 		spdk_scsi_lun_complete_task(lun, task);
191 		break;
192 
193 	default:
194 		abort();
195 	}
196 }
197 
198 void
199 spdk_scsi_lun_append_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
200 {
201 	TAILQ_INSERT_TAIL(&lun->pending_tasks, task, scsi_link);
202 }
203 
204 void
205 spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun)
206 {
207 	struct spdk_scsi_task *task, *task_tmp;
208 
209 	if (spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
210 		/* Pending IO tasks will wait for completion of existing mgmt tasks.
211 		 */
212 		return;
213 	}
214 
215 	TAILQ_FOREACH_SAFE(task, &lun->pending_tasks, scsi_link, task_tmp) {
216 		TAILQ_REMOVE(&lun->pending_tasks, task, scsi_link);
217 		_scsi_lun_execute_task(lun, task);
218 	}
219 }
220 
221 static void
222 scsi_lun_remove(struct spdk_scsi_lun *lun)
223 {
224 	struct spdk_scsi_pr_registrant *reg, *tmp;
225 
226 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
227 		TAILQ_REMOVE(&lun->reg_head, reg, link);
228 		free(reg);
229 	}
230 	spdk_bdev_close(lun->bdev_desc);
231 
232 	spdk_scsi_dev_delete_lun(lun->dev, lun);
233 	free(lun);
234 }
235 
236 static int
237 scsi_lun_check_io_channel(void *arg)
238 {
239 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
240 
241 	if (lun->io_channel) {
242 		return -1;
243 	}
244 	spdk_poller_unregister(&lun->hotremove_poller);
245 
246 	scsi_lun_remove(lun);
247 	return -1;
248 }
249 
250 static void
251 scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun)
252 {
253 	struct spdk_scsi_lun_desc *desc, *tmp;
254 
255 	if (lun->hotremove_cb) {
256 		lun->hotremove_cb(lun, lun->hotremove_ctx);
257 	}
258 
259 	TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) {
260 		if (desc->hotremove_cb) {
261 			desc->hotremove_cb(lun, desc->hotremove_ctx);
262 		} else {
263 			spdk_scsi_lun_close(desc);
264 		}
265 	}
266 
267 	if (lun->io_channel) {
268 		lun->hotremove_poller = spdk_poller_register(scsi_lun_check_io_channel,
269 					lun, 10);
270 	} else {
271 		scsi_lun_remove(lun);
272 	}
273 }
274 
275 static int
276 scsi_lun_check_pending_tasks(void *arg)
277 {
278 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
279 
280 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
281 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
282 		return -1;
283 	}
284 	spdk_poller_unregister(&lun->hotremove_poller);
285 
286 	scsi_lun_notify_hot_remove(lun);
287 	return -1;
288 }
289 
290 static void
291 _scsi_lun_hot_remove(void *arg1)
292 {
293 	struct spdk_scsi_lun *lun = arg1;
294 
295 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
296 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
297 		lun->hotremove_poller = spdk_poller_register(scsi_lun_check_pending_tasks,
298 					lun, 10);
299 	} else {
300 		scsi_lun_notify_hot_remove(lun);
301 	}
302 }
303 
304 static void
305 scsi_lun_hot_remove(void *remove_ctx)
306 {
307 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx;
308 	struct spdk_thread *thread;
309 
310 	if (lun->removed) {
311 		return;
312 	}
313 
314 	lun->removed = true;
315 	if (lun->io_channel == NULL) {
316 		_scsi_lun_hot_remove(lun);
317 		return;
318 	}
319 
320 	thread = spdk_io_channel_get_thread(lun->io_channel);
321 	if (thread != spdk_get_thread()) {
322 		spdk_thread_send_msg(thread, _scsi_lun_hot_remove, lun);
323 	} else {
324 		_scsi_lun_hot_remove(lun);
325 	}
326 }
327 
328 /**
329  * \brief Constructs a new spdk_scsi_lun object based on the provided parameters.
330  *
331  * \param bdev  bdev associated with this LUN
332  *
333  * \return NULL if bdev == NULL
334  * \return pointer to the new spdk_scsi_lun object otherwise
335  */
336 _spdk_scsi_lun *
337 spdk_scsi_lun_construct(struct spdk_bdev *bdev,
338 			void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
339 			void *hotremove_ctx)
340 {
341 	struct spdk_scsi_lun *lun;
342 	int rc;
343 
344 	if (bdev == NULL) {
345 		SPDK_ERRLOG("bdev must be non-NULL\n");
346 		return NULL;
347 	}
348 
349 	lun = calloc(1, sizeof(*lun));
350 	if (lun == NULL) {
351 		SPDK_ERRLOG("could not allocate lun\n");
352 		return NULL;
353 	}
354 
355 	rc = spdk_bdev_open(bdev, true, scsi_lun_hot_remove, lun, &lun->bdev_desc);
356 
357 	if (rc != 0) {
358 		SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc);
359 		free(lun);
360 		return NULL;
361 	}
362 
363 	TAILQ_INIT(&lun->tasks);
364 	TAILQ_INIT(&lun->pending_tasks);
365 	TAILQ_INIT(&lun->mgmt_tasks);
366 	TAILQ_INIT(&lun->pending_mgmt_tasks);
367 
368 	lun->bdev = bdev;
369 	lun->io_channel = NULL;
370 	lun->hotremove_cb = hotremove_cb;
371 	lun->hotremove_ctx = hotremove_ctx;
372 	TAILQ_INIT(&lun->open_descs);
373 	TAILQ_INIT(&lun->reg_head);
374 
375 	return lun;
376 }
377 
378 void
379 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun)
380 {
381 	scsi_lun_hot_remove(lun);
382 }
383 
384 int
385 spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_lun_remove_cb_t hotremove_cb,
386 		   void *hotremove_ctx, struct spdk_scsi_lun_desc **_desc)
387 {
388 	struct spdk_scsi_lun_desc *desc;
389 
390 	desc = calloc(1, sizeof(*desc));
391 	if (desc == NULL) {
392 		SPDK_ERRLOG("calloc() failed for LUN descriptor.\n");
393 		return -ENOMEM;
394 	}
395 
396 	TAILQ_INSERT_TAIL(&lun->open_descs, desc, link);
397 
398 	desc->lun = lun;
399 	desc->hotremove_cb = hotremove_cb;
400 	desc->hotremove_ctx = hotremove_ctx;
401 	*_desc = desc;
402 
403 	return 0;
404 }
405 
406 void
407 spdk_scsi_lun_close(struct spdk_scsi_lun_desc *desc)
408 {
409 	struct spdk_scsi_lun *lun = desc->lun;
410 
411 	TAILQ_REMOVE(&lun->open_descs, desc, link);
412 	free(desc);
413 
414 	assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL);
415 }
416 
417 int
418 _spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun)
419 {
420 	if (lun->io_channel != NULL) {
421 		if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) {
422 			lun->ref++;
423 			return 0;
424 		}
425 		SPDK_ERRLOG("io_channel already allocated for lun %s\n",
426 			    spdk_bdev_get_name(lun->bdev));
427 		return -1;
428 	}
429 
430 	lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc);
431 	if (lun->io_channel == NULL) {
432 		return -1;
433 	}
434 	lun->ref = 1;
435 	return 0;
436 }
437 
438 void
439 _spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun)
440 {
441 	if (lun->io_channel == NULL) {
442 		return;
443 	}
444 
445 	if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) {
446 		SPDK_ERRLOG("io_channel was freed by different thread\n");
447 		return;
448 	}
449 
450 	lun->ref--;
451 	if (lun->ref == 0) {
452 		spdk_put_io_channel(lun->io_channel);
453 		lun->io_channel = NULL;
454 	}
455 }
456 
457 int
458 spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun_desc *desc)
459 {
460 	struct spdk_scsi_lun *lun = desc->lun;
461 
462 	return _spdk_scsi_lun_allocate_io_channel(lun);
463 }
464 
465 void
466 spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun_desc *desc)
467 {
468 	struct spdk_scsi_lun *lun = desc->lun;
469 
470 	_spdk_scsi_lun_free_io_channel(lun);
471 }
472 
473 int
474 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun)
475 {
476 	return lun->id;
477 }
478 
479 const char *
480 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun)
481 {
482 	return spdk_bdev_get_name(lun->bdev);
483 }
484 
485 const struct spdk_scsi_dev *
486 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun)
487 {
488 	return lun->dev;
489 }
490 
491 bool
492 spdk_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun)
493 {
494 	return !TAILQ_EMPTY(&lun->pending_mgmt_tasks) ||
495 	       !TAILQ_EMPTY(&lun->mgmt_tasks);
496 }
497 
498 /* This check includes both pending and submitted (outstanding) tasks. */
499 bool
500 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun)
501 {
502 	return !TAILQ_EMPTY(&lun->pending_tasks) ||
503 	       !TAILQ_EMPTY(&lun->tasks);
504 }
505 
506 bool
507 spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun)
508 {
509 	return lun->removed;
510 }
511 
512 bool
513 spdk_scsi_lun_get_dif_ctx(struct spdk_scsi_lun *lun, uint8_t *cdb,
514 			  uint32_t data_offset, struct spdk_dif_ctx *dif_ctx)
515 {
516 	return spdk_scsi_bdev_get_dif_ctx(lun->bdev, cdb, data_offset, dif_ctx);
517 }
518