xref: /spdk/lib/scsi/lun.c (revision 13a58c41ad5c01eefaf68f049b6c7da2b9d19426)
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 spdk_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 spdk_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 spdk_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 (spdk_scsi_lun_has_outstanding_tasks(lun)) {
78 		return 0;
79 	}
80 	spdk_poller_unregister(&lun->reset_poller);
81 
82 	spdk_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 (spdk_scsi_lun_has_outstanding_tasks(lun)) {
91 			lun->reset_poller =
92 				spdk_poller_register(spdk_scsi_lun_reset_check_outstanding_tasks,
93 						     task, 10);
94 			return;
95 		}
96 	}
97 
98 	spdk_scsi_lun_complete_mgmt_task(lun, task);
99 }
100 
101 static void
102 _spdk_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 	spdk_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 	_spdk_scsi_lun_execute_mgmt_task(lun, task);
161 }
162 
163 static void
164 _spdk_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 		_spdk_scsi_lun_execute_task(lun, task);
211 	}
212 }
213 
214 static void
215 spdk_scsi_lun_remove(struct spdk_scsi_lun *lun)
216 {
217 	spdk_bdev_close(lun->bdev_desc);
218 
219 	spdk_scsi_dev_delete_lun(lun->dev, lun);
220 	free(lun);
221 }
222 
223 static int
224 spdk_scsi_lun_check_io_channel(void *arg)
225 {
226 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
227 
228 	if (lun->io_channel) {
229 		return -1;
230 	}
231 	spdk_poller_unregister(&lun->hotremove_poller);
232 
233 	spdk_scsi_lun_remove(lun);
234 	return -1;
235 }
236 
237 static void
238 spdk_scsi_lun_notify_hot_remove(struct spdk_scsi_lun *lun)
239 {
240 	struct spdk_scsi_desc *desc, *tmp;
241 
242 	if (lun->hotremove_cb) {
243 		lun->hotremove_cb(lun, lun->hotremove_ctx);
244 	}
245 
246 	TAILQ_FOREACH_SAFE(desc, &lun->open_descs, link, tmp) {
247 		if (desc->hotremove_cb) {
248 			desc->hotremove_cb(lun, desc->hotremove_ctx);
249 		} else {
250 			spdk_scsi_lun_close(desc);
251 		}
252 	}
253 
254 	if (lun->io_channel) {
255 		lun->hotremove_poller = spdk_poller_register(spdk_scsi_lun_check_io_channel,
256 					lun, 10);
257 	} else {
258 		spdk_scsi_lun_remove(lun);
259 	}
260 }
261 
262 static int
263 spdk_scsi_lun_check_pending_tasks(void *arg)
264 {
265 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)arg;
266 
267 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
268 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
269 		return -1;
270 	}
271 	spdk_poller_unregister(&lun->hotremove_poller);
272 
273 	spdk_scsi_lun_notify_hot_remove(lun);
274 	return -1;
275 }
276 
277 static void
278 _spdk_scsi_lun_hot_remove(void *arg1)
279 {
280 	struct spdk_scsi_lun *lun = arg1;
281 
282 	if (spdk_scsi_lun_has_pending_tasks(lun) ||
283 	    spdk_scsi_lun_has_pending_mgmt_tasks(lun)) {
284 		lun->hotremove_poller = spdk_poller_register(spdk_scsi_lun_check_pending_tasks,
285 					lun, 10);
286 	} else {
287 		spdk_scsi_lun_notify_hot_remove(lun);
288 	}
289 }
290 
291 static void
292 spdk_scsi_lun_hot_remove(void *remove_ctx)
293 {
294 	struct spdk_scsi_lun *lun = (struct spdk_scsi_lun *)remove_ctx;
295 	struct spdk_thread *thread;
296 
297 	if (lun->removed) {
298 		return;
299 	}
300 
301 	lun->removed = true;
302 	if (lun->io_channel == NULL) {
303 		_spdk_scsi_lun_hot_remove(lun);
304 		return;
305 	}
306 
307 	thread = spdk_io_channel_get_thread(lun->io_channel);
308 	if (thread != spdk_get_thread()) {
309 		spdk_thread_send_msg(thread, _spdk_scsi_lun_hot_remove, lun);
310 	} else {
311 		_spdk_scsi_lun_hot_remove(lun);
312 	}
313 }
314 
315 /**
316  * \brief Constructs a new spdk_scsi_lun object based on the provided parameters.
317  *
318  * \param bdev  bdev associated with this LUN
319  *
320  * \return NULL if bdev == NULL
321  * \return pointer to the new spdk_scsi_lun object otherwise
322  */
323 _spdk_scsi_lun *
324 spdk_scsi_lun_construct(struct spdk_bdev *bdev,
325 			void (*hotremove_cb)(const struct spdk_scsi_lun *, void *),
326 			void *hotremove_ctx)
327 {
328 	struct spdk_scsi_lun *lun;
329 	int rc;
330 
331 	if (bdev == NULL) {
332 		SPDK_ERRLOG("bdev must be non-NULL\n");
333 		return NULL;
334 	}
335 
336 	lun = calloc(1, sizeof(*lun));
337 	if (lun == NULL) {
338 		SPDK_ERRLOG("could not allocate lun\n");
339 		return NULL;
340 	}
341 
342 	rc = spdk_bdev_open(bdev, true, spdk_scsi_lun_hot_remove, lun, &lun->bdev_desc);
343 
344 	if (rc != 0) {
345 		SPDK_ERRLOG("bdev %s cannot be opened, error=%d\n", spdk_bdev_get_name(bdev), rc);
346 		free(lun);
347 		return NULL;
348 	}
349 
350 	TAILQ_INIT(&lun->tasks);
351 	TAILQ_INIT(&lun->pending_tasks);
352 	TAILQ_INIT(&lun->mgmt_tasks);
353 	TAILQ_INIT(&lun->pending_mgmt_tasks);
354 
355 	lun->bdev = bdev;
356 	lun->io_channel = NULL;
357 	lun->hotremove_cb = hotremove_cb;
358 	lun->hotremove_ctx = hotremove_ctx;
359 	TAILQ_INIT(&lun->open_descs);
360 
361 	return lun;
362 }
363 
364 void
365 spdk_scsi_lun_destruct(struct spdk_scsi_lun *lun)
366 {
367 	spdk_scsi_lun_hot_remove(lun);
368 }
369 
370 int
371 spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_remove_cb_t hotremove_cb,
372 		   void *hotremove_ctx, struct spdk_scsi_desc **_desc)
373 {
374 	struct spdk_scsi_desc *desc;
375 
376 	desc = calloc(1, sizeof(*desc));
377 	if (desc == NULL) {
378 		SPDK_ERRLOG("calloc() failed for LUN descriptor.\n");
379 		return -ENOMEM;
380 	}
381 
382 	TAILQ_INSERT_TAIL(&lun->open_descs, desc, link);
383 
384 	desc->lun = lun;
385 	desc->hotremove_cb = hotremove_cb;
386 	desc->hotremove_ctx = hotremove_ctx;
387 	*_desc = desc;
388 
389 	return 0;
390 }
391 
392 void
393 spdk_scsi_lun_close(struct spdk_scsi_desc *desc)
394 {
395 	struct spdk_scsi_lun *lun = desc->lun;
396 
397 	TAILQ_REMOVE(&lun->open_descs, desc, link);
398 	free(desc);
399 
400 	assert(!TAILQ_EMPTY(&lun->open_descs) || lun->io_channel == NULL);
401 }
402 
403 int
404 _spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_lun *lun)
405 {
406 	if (lun->io_channel != NULL) {
407 		if (spdk_get_thread() == spdk_io_channel_get_thread(lun->io_channel)) {
408 			lun->ref++;
409 			return 0;
410 		}
411 		SPDK_ERRLOG("io_channel already allocated for lun %s\n",
412 			    spdk_bdev_get_name(lun->bdev));
413 		return -1;
414 	}
415 
416 	lun->io_channel = spdk_bdev_get_io_channel(lun->bdev_desc);
417 	if (lun->io_channel == NULL) {
418 		return -1;
419 	}
420 	lun->ref = 1;
421 	return 0;
422 }
423 
424 void
425 _spdk_scsi_lun_free_io_channel(struct spdk_scsi_lun *lun)
426 {
427 	if (lun->io_channel == NULL) {
428 		return;
429 	}
430 
431 	if (spdk_get_thread() != spdk_io_channel_get_thread(lun->io_channel)) {
432 		SPDK_ERRLOG("io_channel was freed by different thread\n");
433 		return;
434 	}
435 
436 	lun->ref--;
437 	if (lun->ref == 0) {
438 		spdk_put_io_channel(lun->io_channel);
439 		lun->io_channel = NULL;
440 	}
441 }
442 
443 int
444 spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_desc *desc)
445 {
446 	struct spdk_scsi_lun *lun = desc->lun;
447 
448 	return _spdk_scsi_lun_allocate_io_channel(lun);
449 }
450 
451 void
452 spdk_scsi_lun_free_io_channel(struct spdk_scsi_desc *desc)
453 {
454 	struct spdk_scsi_lun *lun = desc->lun;
455 
456 	_spdk_scsi_lun_free_io_channel(lun);
457 }
458 
459 int
460 spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun)
461 {
462 	return lun->id;
463 }
464 
465 const char *
466 spdk_scsi_lun_get_bdev_name(const struct spdk_scsi_lun *lun)
467 {
468 	return spdk_bdev_get_name(lun->bdev);
469 }
470 
471 const struct spdk_scsi_dev *
472 spdk_scsi_lun_get_dev(const struct spdk_scsi_lun *lun)
473 {
474 	return lun->dev;
475 }
476 
477 bool
478 spdk_scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun)
479 {
480 	return !TAILQ_EMPTY(&lun->pending_mgmt_tasks) ||
481 	       !TAILQ_EMPTY(&lun->mgmt_tasks);
482 }
483 
484 /* This check includes both pending and submitted (outstanding) tasks. */
485 bool
486 spdk_scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun)
487 {
488 	return !TAILQ_EMPTY(&lun->pending_tasks) ||
489 	       !TAILQ_EMPTY(&lun->tasks);
490 }
491 
492 bool
493 spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun)
494 {
495 	return lun->removed;
496 }
497