xref: /spdk/lib/scsi/scsi_pr.c (revision 1fa071d332db21bf893d581a8e93b425ba788a24)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "scsi_internal.h"
35 
36 #include "spdk/endian.h"
37 
38 /* Get registrant by I_T nexus */
39 static struct spdk_scsi_pr_registrant *
40 scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
41 		       struct spdk_scsi_port *initiator_port,
42 		       struct spdk_scsi_port *target_port)
43 {
44 	struct spdk_scsi_pr_registrant *reg, *tmp;
45 
46 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
47 		if (initiator_port == reg->initiator_port &&
48 		    target_port == reg->target_port) {
49 			return reg;
50 		}
51 	}
52 
53 	return NULL;
54 }
55 
56 /* Reservation type is all registrants or not */
57 static inline bool
58 scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
59 {
60 	return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
61 		lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
62 }
63 
64 /* Registrant is reservation holder or not */
65 static inline bool
66 scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
67 			     struct spdk_scsi_pr_registrant *reg)
68 {
69 	if (scsi_pr_is_all_registrants_type(lun)) {
70 		return true;
71 	}
72 
73 	return (lun->reservation.holder == reg);
74 }
75 
76 /* LUN holds a reservation or not */
77 static inline bool
78 scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
79 {
80 	return !(lun->reservation.holder == NULL);
81 }
82 
83 static int
84 scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
85 			    struct spdk_scsi_port *initiator_port,
86 			    struct spdk_scsi_port *target_port,
87 			    uint64_t sa_rkey)
88 {
89 	struct spdk_scsi_pr_registrant *reg;
90 
91 	/* Register sa_rkey with the I_T nexus */
92 	reg = calloc(1, sizeof(*reg));
93 	if (!reg) {
94 		return -ENOMEM;
95 	}
96 
97 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: new registrant registered "
98 		      "with key 0x%"PRIx64"\n", sa_rkey);
99 
100 	/* New I_T nexus */
101 	reg->initiator_port = initiator_port;
102 	if (initiator_port) {
103 		snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
104 			 initiator_port->name);
105 		reg->transport_id_len = initiator_port->transport_id_len;
106 		memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
107 	}
108 	reg->target_port = target_port;
109 	if (target_port) {
110 		snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
111 			 target_port->name);
112 		reg->relative_target_port_id = target_port->index;
113 	}
114 	reg->rkey = sa_rkey;
115 	TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
116 	lun->pr_generation++;
117 
118 	return 0;
119 }
120 
121 static void
122 scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
123 {
124 	bool all_regs = false;
125 
126 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: release reservation "
127 		      "with type %u\n", lun->reservation.rtype);
128 
129 	/* TODO: Unit Attention */
130 	all_regs = scsi_pr_is_all_registrants_type(lun);
131 	if (all_regs && !TAILQ_EMPTY(&lun->reg_head)) {
132 		lun->reservation.holder = TAILQ_FIRST(&lun->reg_head);
133 		return;
134 	}
135 
136 	memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
137 }
138 
139 static void
140 scsi_pr_reserve_reservation(struct spdk_scsi_lun *lun,
141 			    enum spdk_scsi_pr_type_code type,
142 			    uint64_t rkey,
143 			    struct spdk_scsi_pr_registrant *holder)
144 {
145 	lun->reservation.rtype = type;
146 	lun->reservation.crkey = rkey;
147 	lun->reservation.holder = holder;
148 }
149 
150 static void
151 scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
152 			      struct spdk_scsi_pr_registrant *reg)
153 {
154 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: unregister registrant\n");
155 
156 	TAILQ_REMOVE(&lun->reg_head, reg, link);
157 	if (scsi_pr_registrant_is_holder(lun, reg)) {
158 		scsi_pr_release_reservation(lun, reg);
159 	}
160 
161 	free(reg);
162 	lun->pr_generation++;
163 }
164 
165 static void
166 scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
167 			       struct spdk_scsi_pr_registrant *reg,
168 			       uint64_t sa_rkey)
169 {
170 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: replace with new "
171 		      "reservation key 0x%"PRIx64"\n", sa_rkey);
172 	reg->rkey = sa_rkey;
173 	lun->pr_generation++;
174 }
175 
176 static int
177 scsi_pr_out_reserve(struct spdk_scsi_task *task,
178 		    enum spdk_scsi_pr_type_code rtype, uint64_t rkey,
179 		    uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
180 {
181 	struct spdk_scsi_lun *lun = task->lun;
182 	struct spdk_scsi_pr_registrant *reg;
183 
184 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT RESERVE: rkey 0x%"PRIx64", requested "
185 		      "reservation type %u, type %u\n", rkey, rtype, lun->reservation.rtype);
186 
187 	/* TODO: don't support now */
188 	if (spec_i_pt || all_tg_pt || aptpl) {
189 		SPDK_ERRLOG("Unspported spec_i_pt/all_tg_pt fields "
190 			    "or invalid aptpl field\n");
191 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
192 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
193 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
194 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
195 		return -EINVAL;
196 	}
197 
198 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
199 	/* No registration for the I_T nexus */
200 	if (!reg) {
201 		SPDK_ERRLOG("No registration\n");
202 		goto conflict;
203 	}
204 
205 	/* invalid reservation key */
206 	if (reg->rkey != rkey) {
207 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match 0x%"PRIx64"\n",
208 			    rkey, reg->rkey);
209 		goto conflict;
210 	}
211 
212 	/* reservation holder already exists */
213 	if (scsi_pr_has_reservation(lun)) {
214 		if (rtype != lun->reservation.rtype) {
215 			SPDK_ERRLOG("Reservation type doesn't match\n");
216 			goto conflict;
217 		}
218 
219 		if (!scsi_pr_registrant_is_holder(lun, reg)) {
220 			SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
221 			goto conflict;
222 		}
223 	} else {
224 		/* current I_T nexus is the first reservation holder */
225 		scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
226 	}
227 
228 	return 0;
229 
230 conflict:
231 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
232 				  SPDK_SCSI_SENSE_NO_SENSE,
233 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
234 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
235 	return -EINVAL;
236 }
237 
238 static int
239 scsi_pr_out_register(struct spdk_scsi_task *task,
240 		     enum spdk_scsi_pr_out_service_action_code action,
241 		     uint64_t rkey, uint64_t sa_rkey,
242 		     uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
243 {
244 	struct spdk_scsi_lun *lun = task->lun;
245 	struct spdk_scsi_pr_registrant *reg;
246 	int sc, sk, asc;
247 
248 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT REGISTER: rkey 0x%"PRIx64", "
249 		      "sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
250 
251 	/* TODO: don't support now */
252 	if (spec_i_pt || all_tg_pt || aptpl) {
253 		SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
254 		sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
255 		sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
256 		asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
257 		goto error_exit;
258 	}
259 
260 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
261 	/* an unregistered I_T nexus session */
262 	if (!reg) {
263 		if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
264 			SPDK_ERRLOG("Reservation key field is not empty\n");
265 			sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
266 			sk = SPDK_SCSI_SENSE_NO_SENSE;
267 			asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
268 			goto error_exit;
269 		}
270 
271 		if (!sa_rkey) {
272 			/* Do nothing except return GOOD status */
273 			SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: service action "
274 				      "reservation key is zero, do noting\n");
275 			return 0;
276 		}
277 		/* Add a new registrant for the I_T nexus */
278 		return scsi_pr_register_registrant(lun, task->initiator_port,
279 						   task->target_port, sa_rkey);
280 	} else {
281 		/* a registered I_T nexus */
282 		if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
283 			SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
284 				    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
285 			sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
286 			sk = SPDK_SCSI_SENSE_NO_SENSE;
287 			asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
288 			goto error_exit;
289 		}
290 
291 		if (!sa_rkey) {
292 			/* unregister */
293 			scsi_pr_unregister_registrant(lun, reg);
294 		} else {
295 			/* replace */
296 			scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
297 		}
298 	}
299 
300 	return 0;
301 
302 error_exit:
303 	spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
304 	return -EINVAL;
305 }
306 
307 static int
308 scsi_pr_out_release(struct spdk_scsi_task *task,
309 		    enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
310 {
311 	struct spdk_scsi_lun *lun = task->lun;
312 	struct spdk_scsi_pr_registrant *reg;
313 	int sk, asc;
314 
315 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT RELEASE: rkey 0x%"PRIx64", "
316 		      "reservation type %u\n", rkey, rtype);
317 
318 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
319 	if (!reg) {
320 		SPDK_ERRLOG("No registration\n");
321 		sk = SPDK_SCSI_SENSE_NOT_READY;
322 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
323 		goto check_condition;
324 	}
325 
326 	/* no reservation holder */
327 	if (!scsi_pr_has_reservation(lun)) {
328 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "RELEASE: no reservation holder\n");
329 		return 0;
330 	}
331 
332 	if (lun->reservation.rtype != rtype || rkey != lun->reservation.crkey) {
333 		sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
334 		asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
335 		goto check_condition;
336 	}
337 
338 	/* I_T nexus is not a persistent reservation holder */
339 	if (!scsi_pr_registrant_is_holder(lun, reg)) {
340 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "RELEASE: current I_T nexus is not holder\n");
341 		return 0;
342 	}
343 
344 	scsi_pr_release_reservation(lun, reg);
345 
346 	return 0;
347 
348 check_condition:
349 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, sk, asc,
350 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
351 	return -EINVAL;
352 }
353 
354 static int
355 scsi_pr_out_clear(struct spdk_scsi_task *task, uint64_t rkey)
356 {
357 	struct spdk_scsi_lun *lun = task->lun;
358 	struct spdk_scsi_pr_registrant *reg, *tmp;
359 	int sc, sk, asc;
360 
361 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT CLEAR: rkey 0x%"PRIx64"\n", rkey);
362 
363 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
364 	if (!reg) {
365 		SPDK_ERRLOG("No registration\n");
366 		sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
367 		sk = SPDK_SCSI_SENSE_NOT_READY;
368 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
369 		goto error_exit;
370 	}
371 
372 	if (rkey != reg->rkey) {
373 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
374 			    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
375 		sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
376 		sk = SPDK_SCSI_SENSE_NO_SENSE;
377 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
378 		goto error_exit;
379 	}
380 
381 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
382 		scsi_pr_unregister_registrant(lun, reg);
383 	}
384 
385 	return 0;
386 
387 error_exit:
388 	spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
389 	return -EINVAL;
390 }
391 
392 static void
393 scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun *lun, uint64_t sa_rkey)
394 {
395 	struct spdk_scsi_pr_registrant *reg, *tmp;
396 
397 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
398 		if (reg->rkey == sa_rkey) {
399 			scsi_pr_unregister_registrant(lun, reg);
400 		}
401 	}
402 }
403 
404 static void
405 scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
406 {
407 	struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
408 
409 	TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
410 		if (reg_tmp != reg) {
411 			scsi_pr_unregister_registrant(lun, reg_tmp);
412 		}
413 	}
414 }
415 
416 static int
417 scsi_pr_out_preempt(struct spdk_scsi_task *task,
418 		    enum spdk_scsi_pr_out_service_action_code action,
419 		    enum spdk_scsi_pr_type_code rtype,
420 		    uint64_t rkey, uint64_t sa_rkey)
421 {
422 	struct spdk_scsi_lun *lun = task->lun;
423 	struct spdk_scsi_pr_registrant *reg;
424 	bool all_regs = false;
425 
426 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT PREEMPT: rkey 0x%"PRIx64", sa_rkey 0x%"PRIx64" "
427 		      "action %u, type %u, reservation type %u\n",
428 		      rkey, sa_rkey, action, rtype, lun->reservation.rtype);
429 
430 	/* I_T nexus is not registered */
431 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
432 	if (!reg) {
433 		SPDK_ERRLOG("No registration\n");
434 		goto conflict;
435 	}
436 	if (rkey != reg->rkey) {
437 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
438 			    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
439 		goto conflict;
440 	}
441 
442 	/* no persistent reservation */
443 	if (!scsi_pr_has_reservation(lun)) {
444 		scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
445 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: no persistent reservation\n");
446 		goto exit;
447 	}
448 
449 	all_regs = scsi_pr_is_all_registrants_type(lun);
450 
451 	if (all_regs) {
452 		if (sa_rkey != 0) {
453 			scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
454 			SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: All registrants type with sa_rkey\n");
455 		} else {
456 			/* remove all other registrants and release persistent reservation if any */
457 			scsi_pr_remove_all_other_regs(lun, reg);
458 			/* create persistent reservation using new type and scope */
459 			scsi_pr_reserve_reservation(lun, rtype, 0, reg);
460 			SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: All registrants type with sa_rkey zeroed\n");
461 		}
462 		goto exit;
463 	}
464 
465 	assert(lun->reservation.crkey != 0);
466 
467 	if (sa_rkey != lun->reservation.crkey) {
468 		if (!sa_rkey) {
469 			SPDK_ERRLOG("Zeroed sa_rkey\n");
470 			spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
471 						  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
472 						  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
473 						  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
474 			return -EINVAL;
475 		}
476 		scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
477 		goto exit;
478 	}
479 
480 	if (scsi_pr_registrant_is_holder(lun, reg)) {
481 		scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
482 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PREEMPT: preempt itself with type %u\n", rtype);
483 		goto exit;
484 	}
485 
486 	/* unregister registrants if any */
487 	scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
488 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
489 	if (!reg) {
490 		SPDK_ERRLOG("Current I_T nexus registrant was removed\n");
491 		goto conflict;
492 	}
493 
494 	/* preempt the holder */
495 	scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
496 
497 exit:
498 	lun->pr_generation++;
499 	return 0;
500 
501 conflict:
502 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
503 				  SPDK_SCSI_SENSE_NO_SENSE,
504 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
505 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
506 	return -EINVAL;
507 }
508 
509 int
510 scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb,
511 	    uint8_t *data, uint16_t data_len)
512 {
513 	int rc = -1;
514 	uint64_t rkey, sa_rkey;
515 	uint8_t spec_i_pt, all_tg_pt, aptpl;
516 	enum spdk_scsi_pr_out_service_action_code action;
517 	enum spdk_scsi_pr_scope_code scope;
518 	enum spdk_scsi_pr_type_code rtype;
519 	struct spdk_scsi_pr_out_param_list *param = (struct spdk_scsi_pr_out_param_list *)data;
520 
521 	action = cdb[1] & 0x0f;
522 	scope = (cdb[2] >> 4) & 0x0f;
523 	rtype = cdb[2] & 0x0f;
524 
525 	rkey = from_be64(&param->rkey);
526 	sa_rkey = from_be64(&param->sa_rkey);
527 	aptpl = param->aptpl;
528 	spec_i_pt = param->spec_i_pt;
529 	all_tg_pt = param->all_tg_pt;
530 
531 	switch (action) {
532 	case SPDK_SCSI_PR_OUT_REGISTER:
533 	case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
534 		rc = scsi_pr_out_register(task, action, rkey, sa_rkey,
535 					  spec_i_pt, all_tg_pt, aptpl);
536 		break;
537 	case SPDK_SCSI_PR_OUT_RESERVE:
538 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
539 			goto invalid;
540 		}
541 		rc = scsi_pr_out_reserve(task, rtype, rkey,
542 					 spec_i_pt, all_tg_pt, aptpl);
543 		break;
544 	case SPDK_SCSI_PR_OUT_RELEASE:
545 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
546 			goto invalid;
547 		}
548 		rc = scsi_pr_out_release(task, rtype, rkey);
549 		break;
550 	case SPDK_SCSI_PR_OUT_CLEAR:
551 		rc = scsi_pr_out_clear(task, rkey);
552 		break;
553 	case SPDK_SCSI_PR_OUT_PREEMPT:
554 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
555 			goto invalid;
556 		}
557 		rc = scsi_pr_out_preempt(task, action, rtype, rkey, sa_rkey);
558 		break;
559 	default:
560 		SPDK_ERRLOG("Invalid service action code %u\n", action);
561 		goto invalid;
562 	}
563 
564 	return rc;
565 
566 invalid:
567 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
568 				  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
569 				  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
570 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
571 	return -EINVAL;
572 }
573 
574 static int
575 scsi_pr_in_read_keys(struct spdk_scsi_task *task, uint8_t *data,
576 		     uint16_t data_len)
577 {
578 	struct spdk_scsi_lun *lun = task->lun;
579 	struct spdk_scsi_pr_in_read_keys_data *keys;
580 	struct spdk_scsi_pr_registrant *reg, *tmp;
581 	uint16_t count = 0;
582 
583 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ KEYS\n");
584 	keys = (struct spdk_scsi_pr_in_read_keys_data *)data;
585 
586 	to_be32(&keys->header.pr_generation, lun->pr_generation);
587 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
588 		if (((count + 1) * 8 + sizeof(keys->header)) > data_len) {
589 			break;
590 		}
591 		to_be64(&keys->rkeys[count], reg->rkey);
592 		count++;
593 	}
594 	to_be32(&keys->header.additional_len, count * 8);
595 
596 	return (sizeof(keys->header) + count * 8);
597 }
598 
599 static int
600 scsi_pr_in_read_reservations(struct spdk_scsi_task *task,
601 			     uint8_t *data, uint16_t data_len)
602 {
603 	struct spdk_scsi_lun *lun = task->lun;
604 	struct spdk_scsi_pr_in_read_reservations_data *param;
605 	bool all_regs = false;
606 
607 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ RESERVATIONS\n");
608 	param = (struct spdk_scsi_pr_in_read_reservations_data *)(data);
609 
610 	to_be32(&param->header.pr_generation, lun->pr_generation);
611 	if (scsi_pr_has_reservation(lun)) {
612 		all_regs = scsi_pr_is_all_registrants_type(lun);
613 		if (all_regs) {
614 			to_be64(&param->rkey, 0);
615 		} else {
616 			to_be64(&param->rkey, lun->reservation.crkey);
617 		}
618 		to_be32(&param->header.additional_len, 16);
619 		param->scope = SPDK_SCSI_PR_LU_SCOPE;
620 		param->type = lun->reservation.rtype;
621 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "READ RESERVATIONS with valid reservation\n");
622 		return sizeof(*param);
623 	}
624 
625 	/* no reservation */
626 	to_be32(&param->header.additional_len, 0);
627 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "READ RESERVATIONS no reservation\n");
628 	return sizeof(param->header);
629 }
630 
631 static int
632 scsi_pr_in_report_capabilities(struct spdk_scsi_task *task,
633 			       uint8_t *data, uint16_t data_len)
634 {
635 	struct spdk_scsi_pr_in_report_capabilities_data *param;
636 
637 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN REPORT CAPABILITIES\n");
638 	param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
639 
640 	memset(param, 0, sizeof(*param));
641 	/* TODO: can support more capabilities bits */
642 	to_be16(&param->length, sizeof(*param));
643 	param->tmv = 1;
644 	param->wr_ex = 1;
645 	param->ex_ac = 1;
646 	param->wr_ex_ro = 1;
647 	param->ex_ac_ro = 1;
648 	param->wr_ex_ar = 1;
649 	param->ex_ac_ar = 1;
650 
651 	return sizeof(*param);
652 }
653 
654 static int
655 scsi_pr_in_read_full_status(struct spdk_scsi_task *task,
656 			    uint8_t *data, uint16_t data_len)
657 {
658 	struct spdk_scsi_lun *lun = task->lun;
659 	struct spdk_scsi_pr_in_full_status_data *param;
660 	struct spdk_scsi_pr_in_full_status_desc *desc;
661 	struct spdk_scsi_pr_registrant *reg, *tmp;
662 	bool all_regs = false;
663 	uint32_t add_len = 0;
664 
665 	SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR IN READ FULL STATUS\n");
666 
667 	all_regs = scsi_pr_is_all_registrants_type(lun);
668 	param = (struct spdk_scsi_pr_in_full_status_data *)data;
669 	to_be32(&param->header.pr_generation, lun->pr_generation);
670 
671 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
672 		desc = (struct spdk_scsi_pr_in_full_status_desc *)
673 		       ((uint8_t *)param->desc_list + add_len);
674 		if (add_len + sizeof(*desc) + sizeof(param->header) > data_len) {
675 			break;
676 		}
677 		add_len += sizeof(*desc);
678 		desc->rkey = reg->rkey;
679 		if (all_regs || lun->reservation.holder == reg) {
680 			desc->r_holder = true;
681 			desc->type = lun->reservation.rtype;
682 		} else {
683 			desc->r_holder = false;
684 			desc->type = 0;
685 		}
686 		desc->all_tg_pt = 0;
687 		desc->scope = SPDK_SCSI_PR_LU_SCOPE;
688 		desc->relative_target_port_id = reg->relative_target_port_id;
689 		if (add_len + reg->transport_id_len + sizeof(param->header) > data_len) {
690 			break;
691 		}
692 		add_len += reg->transport_id_len;
693 		memcpy(&desc->transport_id, reg->transport_id, reg->transport_id_len);
694 		to_be32(&desc->desc_len, reg->transport_id_len);
695 	}
696 	to_be32(&param->header.additional_len, add_len);
697 
698 	return (sizeof(param->header) + add_len);
699 }
700 
701 int
702 scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb,
703 	   uint8_t *data, uint16_t data_len)
704 {
705 	enum spdk_scsi_pr_in_action_code action;
706 	int rc = 0;
707 
708 	action = cdb[1] & 0x1f;
709 	if (data_len < sizeof(struct spdk_scsi_pr_in_read_header)) {
710 		goto invalid;
711 	}
712 
713 	switch (action) {
714 	case SPDK_SCSI_PR_IN_READ_KEYS:
715 		rc = scsi_pr_in_read_keys(task, data, data_len);
716 		break;
717 	case SPDK_SCSI_PR_IN_READ_RESERVATION:
718 		if (data_len < sizeof(struct spdk_scsi_pr_in_read_reservations_data)) {
719 			goto invalid;
720 		}
721 		rc = scsi_pr_in_read_reservations(task, data, data_len);
722 		break;
723 	case SPDK_SCSI_PR_IN_REPORT_CAPABILITIES:
724 		rc = scsi_pr_in_report_capabilities(task, data, data_len);
725 		break;
726 	case SPDK_SCSI_PR_IN_READ_FULL_STATUS:
727 		rc = scsi_pr_in_read_full_status(task, data, data_len);
728 		break;
729 	default:
730 		goto invalid;
731 	}
732 
733 	return rc;
734 
735 invalid:
736 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
737 				  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
738 				  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
739 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
740 	return -EINVAL;
741 }
742 
743 int
744 scsi_pr_check(struct spdk_scsi_task *task)
745 {
746 	struct spdk_scsi_lun *lun = task->lun;
747 	uint8_t *cdb = task->cdb;
748 	enum spdk_scsi_pr_type_code rtype;
749 	enum spdk_scsi_pr_out_service_action_code action;
750 	struct spdk_scsi_pr_registrant *reg;
751 	bool dma_to_device = false;
752 
753 	/* no reservation holders */
754 	if (!scsi_pr_has_reservation(lun)) {
755 		return 0;
756 	}
757 
758 	rtype = lun->reservation.rtype;
759 	assert(rtype != 0);
760 
761 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
762 	/* current I_T nexus hold the reservation */
763 	if (scsi_pr_registrant_is_holder(lun, reg)) {
764 		return 0;
765 	}
766 
767 	/* reservation is held by other I_T nexus */
768 	switch (cdb[0]) {
769 	case SPDK_SPC_INQUIRY:
770 	case SPDK_SPC_REPORT_LUNS:
771 	case SPDK_SPC_REQUEST_SENSE:
772 	case SPDK_SPC_LOG_SENSE:
773 	case SPDK_SPC_TEST_UNIT_READY:
774 	case SPDK_SBC_START_STOP_UNIT:
775 	case SPDK_SBC_READ_CAPACITY_10:
776 	case SPDK_SPC_PERSISTENT_RESERVE_IN:
777 	case SPDK_SPC_SERVICE_ACTION_IN_16:
778 		return 0;
779 	case SPDK_SPC_MODE_SELECT_6:
780 	case SPDK_SPC_MODE_SELECT_10:
781 	case SPDK_SPC_MODE_SENSE_6:
782 	case SPDK_SPC_MODE_SENSE_10:
783 	case SPDK_SPC_LOG_SELECT:
784 		/* I_T nexus is registrant but not holder */
785 		if (!reg) {
786 			SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: current I_T nexus "
787 				      "is not registered, cdb 0x%x\n", cdb[0]);
788 			goto conflict;
789 		}
790 		return 0;
791 	case SPDK_SPC_PERSISTENT_RESERVE_OUT:
792 		action = cdb[1] & 0x1f;
793 		SPDK_DEBUGLOG(SPDK_LOG_SCSI, "CHECK: PR OUT action %u\n", action);
794 		switch (action) {
795 		case SPDK_SCSI_PR_OUT_RELEASE:
796 		case SPDK_SCSI_PR_OUT_CLEAR:
797 		case SPDK_SCSI_PR_OUT_PREEMPT:
798 		case SPDK_SCSI_PR_OUT_PREEMPT_AND_ABORT:
799 			if (!reg) {
800 				SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
801 				goto conflict;
802 			}
803 			return 0;
804 		case SPDK_SCSI_PR_OUT_REGISTER:
805 		case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
806 			return 0;
807 		case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
808 			SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
809 			goto conflict;
810 		default:
811 			SPDK_ERRLOG("CHECK: PR OUT invalid action %u\n", action);
812 			goto conflict;
813 		}
814 
815 	/* For most SBC R/W commands */
816 	default:
817 		break;
818 	}
819 
820 	switch (cdb[0]) {
821 	case SPDK_SBC_READ_6:
822 	case SPDK_SBC_READ_10:
823 	case SPDK_SBC_READ_12:
824 	case SPDK_SBC_READ_16:
825 		break;
826 	case SPDK_SBC_WRITE_6:
827 	case SPDK_SBC_WRITE_10:
828 	case SPDK_SBC_WRITE_12:
829 	case SPDK_SBC_WRITE_16:
830 	case SPDK_SBC_UNMAP:
831 	case SPDK_SBC_SYNCHRONIZE_CACHE_10:
832 	case SPDK_SBC_SYNCHRONIZE_CACHE_16:
833 		dma_to_device = true;
834 		break;
835 	default:
836 		SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
837 		goto conflict;
838 	}
839 
840 	switch (rtype) {
841 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE:
842 		if (dma_to_device) {
843 			SPDK_ERRLOG("CHECK: Write Exclusive reservation type "
844 				    "rejects command 0x%x\n", cdb[0]);
845 			goto conflict;
846 		}
847 		break;
848 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
849 		SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
850 			    "rejects command 0x%x\n", cdb[0]);
851 		goto conflict;
852 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
853 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
854 		if (!reg && dma_to_device) {
855 			SPDK_ERRLOG("CHECK: Registrants only reservation "
856 				    "type  reject command 0x%x\n", cdb[0]);
857 			goto conflict;
858 		}
859 		break;
860 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
861 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
862 		if (!reg) {
863 			SPDK_ERRLOG("CHECK: All Registrants reservation "
864 				    "type  reject command 0x%x\n", cdb[0]);
865 			goto conflict;
866 		}
867 		break;
868 	default:
869 		break;
870 	}
871 
872 	return 0;
873 
874 conflict:
875 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
876 				  SPDK_SCSI_SENSE_NO_SENSE,
877 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
878 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
879 	return -1;
880 }
881