xref: /spdk/lib/scsi/scsi_pr.c (revision cde2142cd008f2fa6b0edb2a9194c36c7df6da52)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2019 Intel Corporation.
3d0d19eb8SChangpeng Liu  *   All rights reserved.
4d0d19eb8SChangpeng Liu  */
5d0d19eb8SChangpeng Liu 
6d0d19eb8SChangpeng Liu #include "scsi_internal.h"
7d0d19eb8SChangpeng Liu 
8d0d19eb8SChangpeng Liu #include "spdk/endian.h"
9d0d19eb8SChangpeng Liu 
10d0d19eb8SChangpeng Liu /* Get registrant by I_T nexus */
11d0d19eb8SChangpeng Liu static struct spdk_scsi_pr_registrant *
scsi_pr_get_registrant(struct spdk_scsi_lun * lun,struct spdk_scsi_port * initiator_port,struct spdk_scsi_port * target_port)1233a29747SShuhei Matsumoto scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
13d0d19eb8SChangpeng Liu 		       struct spdk_scsi_port *initiator_port,
14d0d19eb8SChangpeng Liu 		       struct spdk_scsi_port *target_port)
15d0d19eb8SChangpeng Liu {
16d0d19eb8SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg, *tmp;
17d0d19eb8SChangpeng Liu 
18d0d19eb8SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
19d0d19eb8SChangpeng Liu 		if (initiator_port == reg->initiator_port &&
20d0d19eb8SChangpeng Liu 		    target_port == reg->target_port) {
21d0d19eb8SChangpeng Liu 			return reg;
22d0d19eb8SChangpeng Liu 		}
23d0d19eb8SChangpeng Liu 	}
24d0d19eb8SChangpeng Liu 
25d0d19eb8SChangpeng Liu 	return NULL;
26d0d19eb8SChangpeng Liu }
27d0d19eb8SChangpeng Liu 
286b6a3ff9SChangpeng Liu static bool
scsi2_it_nexus_is_holder(struct spdk_scsi_lun * lun,struct spdk_scsi_port * initiator_port,struct spdk_scsi_port * target_port)296b6a3ff9SChangpeng Liu scsi2_it_nexus_is_holder(struct spdk_scsi_lun *lun,
306b6a3ff9SChangpeng Liu 			 struct spdk_scsi_port *initiator_port,
316b6a3ff9SChangpeng Liu 			 struct spdk_scsi_port *target_port)
326b6a3ff9SChangpeng Liu {
336b6a3ff9SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
346b6a3ff9SChangpeng Liu 
356b6a3ff9SChangpeng Liu 	assert(reg != NULL);
366b6a3ff9SChangpeng Liu 
376b6a3ff9SChangpeng Liu 	if ((reg->initiator_port == initiator_port) &&
386b6a3ff9SChangpeng Liu 	    (reg->target_port == target_port)) {
396b6a3ff9SChangpeng Liu 		return true;
406b6a3ff9SChangpeng Liu 	}
416b6a3ff9SChangpeng Liu 
426b6a3ff9SChangpeng Liu 	return false;
436b6a3ff9SChangpeng Liu }
446b6a3ff9SChangpeng Liu 
45d0d19eb8SChangpeng Liu /* Reservation type is all registrants or not */
46d0d19eb8SChangpeng Liu static inline bool
scsi_pr_is_all_registrants_type(struct spdk_scsi_lun * lun)4733a29747SShuhei Matsumoto scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
48d0d19eb8SChangpeng Liu {
49d0d19eb8SChangpeng Liu 	return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
50d0d19eb8SChangpeng Liu 		lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
51d0d19eb8SChangpeng Liu }
52d0d19eb8SChangpeng Liu 
53d0d19eb8SChangpeng Liu /* Registrant is reservation holder or not */
54d0d19eb8SChangpeng Liu static inline bool
scsi_pr_registrant_is_holder(struct spdk_scsi_lun * lun,struct spdk_scsi_pr_registrant * reg)5533a29747SShuhei Matsumoto scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
56d0d19eb8SChangpeng Liu 			     struct spdk_scsi_pr_registrant *reg)
57d0d19eb8SChangpeng Liu {
58*cde2142cSOded Mashbach 	if (reg != NULL && scsi_pr_is_all_registrants_type(lun)) {
59d0d19eb8SChangpeng Liu 		return true;
60d0d19eb8SChangpeng Liu 	}
61d0d19eb8SChangpeng Liu 
62d0d19eb8SChangpeng Liu 	return (lun->reservation.holder == reg);
63d0d19eb8SChangpeng Liu }
64d0d19eb8SChangpeng Liu 
654f95ec42SChangpeng Liu /* LUN holds a reservation or not */
664f95ec42SChangpeng Liu static inline bool
scsi_pr_has_reservation(struct spdk_scsi_lun * lun)6733a29747SShuhei Matsumoto scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
684f95ec42SChangpeng Liu {
694f95ec42SChangpeng Liu 	return !(lun->reservation.holder == NULL);
704f95ec42SChangpeng Liu }
714f95ec42SChangpeng Liu 
72d0d19eb8SChangpeng Liu static int
scsi_pr_register_registrant(struct spdk_scsi_lun * lun,struct spdk_scsi_port * initiator_port,struct spdk_scsi_port * target_port,uint64_t sa_rkey)7333a29747SShuhei Matsumoto scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
74d0d19eb8SChangpeng Liu 			    struct spdk_scsi_port *initiator_port,
75d0d19eb8SChangpeng Liu 			    struct spdk_scsi_port *target_port,
76d0d19eb8SChangpeng Liu 			    uint64_t sa_rkey)
77d0d19eb8SChangpeng Liu {
78d0d19eb8SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
79d0d19eb8SChangpeng Liu 
80d0d19eb8SChangpeng Liu 	/* Register sa_rkey with the I_T nexus */
81d0d19eb8SChangpeng Liu 	reg = calloc(1, sizeof(*reg));
82d0d19eb8SChangpeng Liu 	if (!reg) {
83d0d19eb8SChangpeng Liu 		return -ENOMEM;
84d0d19eb8SChangpeng Liu 	}
85d0d19eb8SChangpeng Liu 
862172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "REGISTER: new registrant registered "
87d0d19eb8SChangpeng Liu 		      "with key 0x%"PRIx64"\n", sa_rkey);
88d0d19eb8SChangpeng Liu 
89d0d19eb8SChangpeng Liu 	/* New I_T nexus */
90d0d19eb8SChangpeng Liu 	reg->initiator_port = initiator_port;
9114f6d724SChangpeng Liu 	if (initiator_port) {
92d0d19eb8SChangpeng Liu 		snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
93d0d19eb8SChangpeng Liu 			 initiator_port->name);
94d0d19eb8SChangpeng Liu 		reg->transport_id_len = initiator_port->transport_id_len;
95d0d19eb8SChangpeng Liu 		memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
9614f6d724SChangpeng Liu 	}
97d0d19eb8SChangpeng Liu 	reg->target_port = target_port;
9814f6d724SChangpeng Liu 	if (target_port) {
99d0d19eb8SChangpeng Liu 		snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
100d0d19eb8SChangpeng Liu 			 target_port->name);
101d0d19eb8SChangpeng Liu 		reg->relative_target_port_id = target_port->index;
10214f6d724SChangpeng Liu 	}
103d0d19eb8SChangpeng Liu 	reg->rkey = sa_rkey;
104d0d19eb8SChangpeng Liu 	TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
105d0d19eb8SChangpeng Liu 	lun->pr_generation++;
106d0d19eb8SChangpeng Liu 
107d0d19eb8SChangpeng Liu 	return 0;
108d0d19eb8SChangpeng Liu }
109d0d19eb8SChangpeng Liu 
110d0d19eb8SChangpeng Liu static void
scsi_pr_release_reservation(struct spdk_scsi_lun * lun,struct spdk_scsi_pr_registrant * reg)11133a29747SShuhei Matsumoto scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
112d0d19eb8SChangpeng Liu {
113d0d19eb8SChangpeng Liu 	bool all_regs = false;
114be633ff1SOded Mashbach 	struct spdk_scsi_pr_registrant *curr_reg, *tmp;
115d0d19eb8SChangpeng Liu 
1162172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "REGISTER: release reservation "
117d0d19eb8SChangpeng Liu 		      "with type %u\n", lun->reservation.rtype);
118d0d19eb8SChangpeng Liu 
119d0d19eb8SChangpeng Liu 	/* TODO: Unit Attention */
12033a29747SShuhei Matsumoto 	all_regs = scsi_pr_is_all_registrants_type(lun);
121be633ff1SOded Mashbach 	if (all_regs) {
122be633ff1SOded Mashbach 		TAILQ_FOREACH_SAFE(curr_reg, &lun->reg_head, link, tmp) {
123be633ff1SOded Mashbach 			if (curr_reg != reg) {
124be633ff1SOded Mashbach 				lun->reservation.holder = curr_reg;
125be633ff1SOded Mashbach 				lun->reservation.crkey = curr_reg->rkey;
126d0d19eb8SChangpeng Liu 				return;
127d0d19eb8SChangpeng Liu 			}
128be633ff1SOded Mashbach 		}
129be633ff1SOded Mashbach 	}
130d0d19eb8SChangpeng Liu 
131d0d19eb8SChangpeng Liu 	memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
132d0d19eb8SChangpeng Liu }
133d0d19eb8SChangpeng Liu 
134d0d19eb8SChangpeng Liu static void
scsi_pr_reserve_reservation(struct spdk_scsi_lun * lun,enum spdk_scsi_pr_type_code type,uint64_t rkey,struct spdk_scsi_pr_registrant * holder)13533a29747SShuhei Matsumoto scsi_pr_reserve_reservation(struct spdk_scsi_lun *lun,
136601fbbf9SChangpeng Liu 			    enum spdk_scsi_pr_type_code type,
137601fbbf9SChangpeng Liu 			    uint64_t rkey,
138601fbbf9SChangpeng Liu 			    struct spdk_scsi_pr_registrant *holder)
139601fbbf9SChangpeng Liu {
140601fbbf9SChangpeng Liu 	lun->reservation.rtype = type;
141601fbbf9SChangpeng Liu 	lun->reservation.crkey = rkey;
142601fbbf9SChangpeng Liu 	lun->reservation.holder = holder;
143601fbbf9SChangpeng Liu }
144601fbbf9SChangpeng Liu 
145601fbbf9SChangpeng Liu static void
scsi_pr_unregister_registrant(struct spdk_scsi_lun * lun,struct spdk_scsi_pr_registrant * reg)14633a29747SShuhei Matsumoto scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
147d0d19eb8SChangpeng Liu 			      struct spdk_scsi_pr_registrant *reg)
148d0d19eb8SChangpeng Liu {
1492172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "REGISTER: unregister registrant\n");
150d0d19eb8SChangpeng Liu 
151d0d19eb8SChangpeng Liu 	TAILQ_REMOVE(&lun->reg_head, reg, link);
15233a29747SShuhei Matsumoto 	if (scsi_pr_registrant_is_holder(lun, reg)) {
15333a29747SShuhei Matsumoto 		scsi_pr_release_reservation(lun, reg);
154d0d19eb8SChangpeng Liu 	}
155d0d19eb8SChangpeng Liu 
156d0d19eb8SChangpeng Liu 	free(reg);
157d0d19eb8SChangpeng Liu 	lun->pr_generation++;
158d0d19eb8SChangpeng Liu }
159d0d19eb8SChangpeng Liu 
160d0d19eb8SChangpeng Liu static void
scsi_pr_replace_registrant_key(struct spdk_scsi_lun * lun,struct spdk_scsi_pr_registrant * reg,uint64_t sa_rkey)16133a29747SShuhei Matsumoto scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
162d0d19eb8SChangpeng Liu 			       struct spdk_scsi_pr_registrant *reg,
163d0d19eb8SChangpeng Liu 			       uint64_t sa_rkey)
164d0d19eb8SChangpeng Liu {
1652172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "REGISTER: replace with new "
166d0d19eb8SChangpeng Liu 		      "reservation key 0x%"PRIx64"\n", sa_rkey);
167d0d19eb8SChangpeng Liu 	reg->rkey = sa_rkey;
168d0d19eb8SChangpeng Liu 	lun->pr_generation++;
169d0d19eb8SChangpeng Liu }
170d0d19eb8SChangpeng Liu 
171d0d19eb8SChangpeng Liu static int
scsi_pr_out_reserve(struct spdk_scsi_task * task,enum spdk_scsi_pr_type_code rtype,uint64_t rkey,uint8_t spec_i_pt,uint8_t all_tg_pt,uint8_t aptpl)17233a29747SShuhei Matsumoto scsi_pr_out_reserve(struct spdk_scsi_task *task,
173601fbbf9SChangpeng Liu 		    enum spdk_scsi_pr_type_code rtype, uint64_t rkey,
174601fbbf9SChangpeng Liu 		    uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
175601fbbf9SChangpeng Liu {
176601fbbf9SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
177601fbbf9SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
178601fbbf9SChangpeng Liu 
1792172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR OUT RESERVE: rkey 0x%"PRIx64", requested "
180601fbbf9SChangpeng Liu 		      "reservation type %u, type %u\n", rkey, rtype, lun->reservation.rtype);
181601fbbf9SChangpeng Liu 
182601fbbf9SChangpeng Liu 	/* TODO: don't support now */
183601fbbf9SChangpeng Liu 	if (spec_i_pt || all_tg_pt || aptpl) {
184cc6920a4SJosh Soref 		SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt fields "
185601fbbf9SChangpeng Liu 			    "or invalid aptpl field\n");
186601fbbf9SChangpeng Liu 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
187601fbbf9SChangpeng Liu 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
188601fbbf9SChangpeng Liu 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
189601fbbf9SChangpeng Liu 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
190601fbbf9SChangpeng Liu 		return -EINVAL;
191601fbbf9SChangpeng Liu 	}
192601fbbf9SChangpeng Liu 
19333a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
194601fbbf9SChangpeng Liu 	/* No registration for the I_T nexus */
195601fbbf9SChangpeng Liu 	if (!reg) {
196601fbbf9SChangpeng Liu 		SPDK_ERRLOG("No registration\n");
197601fbbf9SChangpeng Liu 		goto conflict;
198601fbbf9SChangpeng Liu 	}
199601fbbf9SChangpeng Liu 
200601fbbf9SChangpeng Liu 	/* invalid reservation key */
201601fbbf9SChangpeng Liu 	if (reg->rkey != rkey) {
202601fbbf9SChangpeng Liu 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match 0x%"PRIx64"\n",
203601fbbf9SChangpeng Liu 			    rkey, reg->rkey);
204601fbbf9SChangpeng Liu 		goto conflict;
205601fbbf9SChangpeng Liu 	}
206601fbbf9SChangpeng Liu 
207601fbbf9SChangpeng Liu 	/* reservation holder already exists */
20833a29747SShuhei Matsumoto 	if (scsi_pr_has_reservation(lun)) {
209601fbbf9SChangpeng Liu 		if (rtype != lun->reservation.rtype) {
210601fbbf9SChangpeng Liu 			SPDK_ERRLOG("Reservation type doesn't match\n");
211601fbbf9SChangpeng Liu 			goto conflict;
212601fbbf9SChangpeng Liu 		}
213601fbbf9SChangpeng Liu 
21433a29747SShuhei Matsumoto 		if (!scsi_pr_registrant_is_holder(lun, reg)) {
215601fbbf9SChangpeng Liu 			SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
216601fbbf9SChangpeng Liu 			goto conflict;
217601fbbf9SChangpeng Liu 		}
218601fbbf9SChangpeng Liu 	} else {
219601fbbf9SChangpeng Liu 		/* current I_T nexus is the first reservation holder */
22033a29747SShuhei Matsumoto 		scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
221601fbbf9SChangpeng Liu 	}
222601fbbf9SChangpeng Liu 
223601fbbf9SChangpeng Liu 	return 0;
224601fbbf9SChangpeng Liu 
225601fbbf9SChangpeng Liu conflict:
226601fbbf9SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
227601fbbf9SChangpeng Liu 				  SPDK_SCSI_SENSE_NO_SENSE,
228601fbbf9SChangpeng Liu 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
229601fbbf9SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
230601fbbf9SChangpeng Liu 	return -EINVAL;
231601fbbf9SChangpeng Liu }
232601fbbf9SChangpeng Liu 
233601fbbf9SChangpeng Liu static int
scsi_pr_out_register(struct spdk_scsi_task * task,enum spdk_scsi_pr_out_service_action_code action,uint64_t rkey,uint64_t sa_rkey,uint8_t spec_i_pt,uint8_t all_tg_pt,uint8_t aptpl)23433a29747SShuhei Matsumoto scsi_pr_out_register(struct spdk_scsi_task *task,
235d0d19eb8SChangpeng Liu 		     enum spdk_scsi_pr_out_service_action_code action,
236d0d19eb8SChangpeng Liu 		     uint64_t rkey, uint64_t sa_rkey,
237d0d19eb8SChangpeng Liu 		     uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
238d0d19eb8SChangpeng Liu {
239d0d19eb8SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
240d0d19eb8SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
241d0d19eb8SChangpeng Liu 	int sc, sk, asc;
242d0d19eb8SChangpeng Liu 
2432172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR OUT REGISTER: rkey 0x%"PRIx64", "
244d0d19eb8SChangpeng Liu 		      "sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
245d0d19eb8SChangpeng Liu 
246d0d19eb8SChangpeng Liu 	/* TODO: don't support now */
247d0d19eb8SChangpeng Liu 	if (spec_i_pt || all_tg_pt || aptpl) {
248d0d19eb8SChangpeng Liu 		SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
249d0d19eb8SChangpeng Liu 		sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
250d0d19eb8SChangpeng Liu 		sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
251d0d19eb8SChangpeng Liu 		asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
252d0d19eb8SChangpeng Liu 		goto error_exit;
253d0d19eb8SChangpeng Liu 	}
254d0d19eb8SChangpeng Liu 
25533a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
256d0d19eb8SChangpeng Liu 	/* an unregistered I_T nexus session */
257d0d19eb8SChangpeng Liu 	if (!reg) {
258d0d19eb8SChangpeng Liu 		if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
259d0d19eb8SChangpeng Liu 			SPDK_ERRLOG("Reservation key field is not empty\n");
260d0d19eb8SChangpeng Liu 			sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
261d0d19eb8SChangpeng Liu 			sk = SPDK_SCSI_SENSE_NO_SENSE;
262d0d19eb8SChangpeng Liu 			asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
263d0d19eb8SChangpeng Liu 			goto error_exit;
264d0d19eb8SChangpeng Liu 		}
265d0d19eb8SChangpeng Liu 
266d0d19eb8SChangpeng Liu 		if (!sa_rkey) {
267d0d19eb8SChangpeng Liu 			/* Do nothing except return GOOD status */
2682172c432STomasz Zawadzki 			SPDK_DEBUGLOG(scsi, "REGISTER: service action "
269d0d19eb8SChangpeng Liu 				      "reservation key is zero, do noting\n");
270d0d19eb8SChangpeng Liu 			return 0;
271d0d19eb8SChangpeng Liu 		}
272d0d19eb8SChangpeng Liu 		/* Add a new registrant for the I_T nexus */
27333a29747SShuhei Matsumoto 		return scsi_pr_register_registrant(lun, task->initiator_port,
274d0d19eb8SChangpeng Liu 						   task->target_port, sa_rkey);
275d0d19eb8SChangpeng Liu 	} else {
276d0d19eb8SChangpeng Liu 		/* a registered I_T nexus */
277d0d19eb8SChangpeng Liu 		if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
278d0d19eb8SChangpeng Liu 			SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
279d0d19eb8SChangpeng Liu 				    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
280d0d19eb8SChangpeng Liu 			sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
281d0d19eb8SChangpeng Liu 			sk = SPDK_SCSI_SENSE_NO_SENSE;
282d0d19eb8SChangpeng Liu 			asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
283d0d19eb8SChangpeng Liu 			goto error_exit;
284d0d19eb8SChangpeng Liu 		}
285d0d19eb8SChangpeng Liu 
286d0d19eb8SChangpeng Liu 		if (!sa_rkey) {
287d0d19eb8SChangpeng Liu 			/* unregister */
28833a29747SShuhei Matsumoto 			scsi_pr_unregister_registrant(lun, reg);
289d0d19eb8SChangpeng Liu 		} else {
290d0d19eb8SChangpeng Liu 			/* replace */
29133a29747SShuhei Matsumoto 			scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
292d0d19eb8SChangpeng Liu 		}
293d0d19eb8SChangpeng Liu 	}
294d0d19eb8SChangpeng Liu 
295d0d19eb8SChangpeng Liu 	return 0;
296d0d19eb8SChangpeng Liu 
297d0d19eb8SChangpeng Liu error_exit:
298d0d19eb8SChangpeng Liu 	spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
299d0d19eb8SChangpeng Liu 	return -EINVAL;
300d0d19eb8SChangpeng Liu }
301d0d19eb8SChangpeng Liu 
302e8e791f7SChangpeng Liu static int
scsi_pr_out_release(struct spdk_scsi_task * task,enum spdk_scsi_pr_type_code rtype,uint64_t rkey)30333a29747SShuhei Matsumoto scsi_pr_out_release(struct spdk_scsi_task *task,
304e8e791f7SChangpeng Liu 		    enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
305e8e791f7SChangpeng Liu {
306e8e791f7SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
307e8e791f7SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
308e8e791f7SChangpeng Liu 	int sk, asc;
309e8e791f7SChangpeng Liu 
3102172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR OUT RELEASE: rkey 0x%"PRIx64", "
311e8e791f7SChangpeng Liu 		      "reservation type %u\n", rkey, rtype);
312e8e791f7SChangpeng Liu 
31333a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
314e8e791f7SChangpeng Liu 	if (!reg) {
315e8e791f7SChangpeng Liu 		SPDK_ERRLOG("No registration\n");
316e8e791f7SChangpeng Liu 		sk = SPDK_SCSI_SENSE_NOT_READY;
317e8e791f7SChangpeng Liu 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
318e8e791f7SChangpeng Liu 		goto check_condition;
319e8e791f7SChangpeng Liu 	}
320e8e791f7SChangpeng Liu 
321e8e791f7SChangpeng Liu 	/* no reservation holder */
32233a29747SShuhei Matsumoto 	if (!scsi_pr_has_reservation(lun)) {
3232172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "RELEASE: no reservation holder\n");
324e8e791f7SChangpeng Liu 		return 0;
325e8e791f7SChangpeng Liu 	}
326e8e791f7SChangpeng Liu 
327e8e791f7SChangpeng Liu 	if (lun->reservation.rtype != rtype || rkey != lun->reservation.crkey) {
328e8e791f7SChangpeng Liu 		sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
329e8e791f7SChangpeng Liu 		asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
330e8e791f7SChangpeng Liu 		goto check_condition;
331e8e791f7SChangpeng Liu 	}
332e8e791f7SChangpeng Liu 
333e8e791f7SChangpeng Liu 	/* I_T nexus is not a persistent reservation holder */
33433a29747SShuhei Matsumoto 	if (!scsi_pr_registrant_is_holder(lun, reg)) {
3352172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "RELEASE: current I_T nexus is not holder\n");
336e8e791f7SChangpeng Liu 		return 0;
337e8e791f7SChangpeng Liu 	}
338e8e791f7SChangpeng Liu 
33933a29747SShuhei Matsumoto 	scsi_pr_release_reservation(lun, reg);
340e8e791f7SChangpeng Liu 
341e8e791f7SChangpeng Liu 	return 0;
342e8e791f7SChangpeng Liu 
343e8e791f7SChangpeng Liu check_condition:
344e8e791f7SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, sk, asc,
345e8e791f7SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
346e8e791f7SChangpeng Liu 	return -EINVAL;
347e8e791f7SChangpeng Liu }
348e8e791f7SChangpeng Liu 
3498d79b410SChangpeng Liu static int
scsi_pr_out_clear(struct spdk_scsi_task * task,uint64_t rkey)35033a29747SShuhei Matsumoto scsi_pr_out_clear(struct spdk_scsi_task *task, uint64_t rkey)
3518d79b410SChangpeng Liu {
3528d79b410SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
3538d79b410SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg, *tmp;
3548d79b410SChangpeng Liu 	int sc, sk, asc;
3558d79b410SChangpeng Liu 
3562172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR OUT CLEAR: rkey 0x%"PRIx64"\n", rkey);
3578d79b410SChangpeng Liu 
35833a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
3598d79b410SChangpeng Liu 	if (!reg) {
3608d79b410SChangpeng Liu 		SPDK_ERRLOG("No registration\n");
3618d79b410SChangpeng Liu 		sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
3628d79b410SChangpeng Liu 		sk = SPDK_SCSI_SENSE_NOT_READY;
3638d79b410SChangpeng Liu 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
3648d79b410SChangpeng Liu 		goto error_exit;
3658d79b410SChangpeng Liu 	}
3668d79b410SChangpeng Liu 
3678d79b410SChangpeng Liu 	if (rkey != reg->rkey) {
3688d79b410SChangpeng Liu 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
3698d79b410SChangpeng Liu 			    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
3708d79b410SChangpeng Liu 		sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
3718d79b410SChangpeng Liu 		sk = SPDK_SCSI_SENSE_NO_SENSE;
3728d79b410SChangpeng Liu 		asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
3738d79b410SChangpeng Liu 		goto error_exit;
3748d79b410SChangpeng Liu 	}
3758d79b410SChangpeng Liu 
3768d79b410SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
37733a29747SShuhei Matsumoto 		scsi_pr_unregister_registrant(lun, reg);
3788d79b410SChangpeng Liu 	}
3798d79b410SChangpeng Liu 
3808d79b410SChangpeng Liu 	return 0;
3818d79b410SChangpeng Liu 
3828d79b410SChangpeng Liu error_exit:
3838d79b410SChangpeng Liu 	spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
3848d79b410SChangpeng Liu 	return -EINVAL;
3858d79b410SChangpeng Liu }
386e8e791f7SChangpeng Liu 
3878f2a8536SChangpeng Liu static void
scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun * lun,uint64_t sa_rkey)38833a29747SShuhei Matsumoto scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun *lun, uint64_t sa_rkey)
3898f2a8536SChangpeng Liu {
3908f2a8536SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg, *tmp;
3918f2a8536SChangpeng Liu 
3928f2a8536SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
3938f2a8536SChangpeng Liu 		if (reg->rkey == sa_rkey) {
39433a29747SShuhei Matsumoto 			scsi_pr_unregister_registrant(lun, reg);
3958f2a8536SChangpeng Liu 		}
3968f2a8536SChangpeng Liu 	}
3978f2a8536SChangpeng Liu }
3988f2a8536SChangpeng Liu 
3998f2a8536SChangpeng Liu static void
scsi_pr_remove_all_other_regs(struct spdk_scsi_lun * lun,struct spdk_scsi_pr_registrant * reg)40033a29747SShuhei Matsumoto scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
4018f2a8536SChangpeng Liu {
4028f2a8536SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
4038f2a8536SChangpeng Liu 
4048f2a8536SChangpeng Liu 	TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
4058f2a8536SChangpeng Liu 		if (reg_tmp != reg) {
40633a29747SShuhei Matsumoto 			scsi_pr_unregister_registrant(lun, reg_tmp);
4078f2a8536SChangpeng Liu 		}
4088f2a8536SChangpeng Liu 	}
4098f2a8536SChangpeng Liu }
4108f2a8536SChangpeng Liu 
4118f2a8536SChangpeng Liu static int
scsi_pr_out_preempt(struct spdk_scsi_task * task,enum spdk_scsi_pr_out_service_action_code action,enum spdk_scsi_pr_type_code rtype,uint64_t rkey,uint64_t sa_rkey)41233a29747SShuhei Matsumoto scsi_pr_out_preempt(struct spdk_scsi_task *task,
4138f2a8536SChangpeng Liu 		    enum spdk_scsi_pr_out_service_action_code action,
4148f2a8536SChangpeng Liu 		    enum spdk_scsi_pr_type_code rtype,
4158f2a8536SChangpeng Liu 		    uint64_t rkey, uint64_t sa_rkey)
4168f2a8536SChangpeng Liu {
4178f2a8536SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
4188f2a8536SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
4198f2a8536SChangpeng Liu 	bool all_regs = false;
4208f2a8536SChangpeng Liu 
4212172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR OUT PREEMPT: rkey 0x%"PRIx64", sa_rkey 0x%"PRIx64" "
4228f2a8536SChangpeng Liu 		      "action %u, type %u, reservation type %u\n",
4238f2a8536SChangpeng Liu 		      rkey, sa_rkey, action, rtype, lun->reservation.rtype);
4248f2a8536SChangpeng Liu 
4258f2a8536SChangpeng Liu 	/* I_T nexus is not registered */
42633a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
4278f2a8536SChangpeng Liu 	if (!reg) {
4288f2a8536SChangpeng Liu 		SPDK_ERRLOG("No registration\n");
4298f2a8536SChangpeng Liu 		goto conflict;
4308f2a8536SChangpeng Liu 	}
4318f2a8536SChangpeng Liu 	if (rkey != reg->rkey) {
4328f2a8536SChangpeng Liu 		SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
4338f2a8536SChangpeng Liu 			    "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
4348f2a8536SChangpeng Liu 		goto conflict;
4358f2a8536SChangpeng Liu 	}
4368f2a8536SChangpeng Liu 
4378f2a8536SChangpeng Liu 	/* no persistent reservation */
43833a29747SShuhei Matsumoto 	if (!scsi_pr_has_reservation(lun)) {
43933a29747SShuhei Matsumoto 		scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
4402172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "PREEMPT: no persistent reservation\n");
4418f2a8536SChangpeng Liu 		goto exit;
4428f2a8536SChangpeng Liu 	}
4438f2a8536SChangpeng Liu 
44433a29747SShuhei Matsumoto 	all_regs = scsi_pr_is_all_registrants_type(lun);
4458f2a8536SChangpeng Liu 
4468f2a8536SChangpeng Liu 	if (all_regs) {
4478f2a8536SChangpeng Liu 		if (sa_rkey != 0) {
44833a29747SShuhei Matsumoto 			scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
4492172c432STomasz Zawadzki 			SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey\n");
4508f2a8536SChangpeng Liu 		} else {
4518f2a8536SChangpeng Liu 			/* remove all other registrants and release persistent reservation if any */
45233a29747SShuhei Matsumoto 			scsi_pr_remove_all_other_regs(lun, reg);
4538f2a8536SChangpeng Liu 			/* create persistent reservation using new type and scope */
45433a29747SShuhei Matsumoto 			scsi_pr_reserve_reservation(lun, rtype, 0, reg);
4552172c432STomasz Zawadzki 			SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey zeroed\n");
4568f2a8536SChangpeng Liu 		}
4578f2a8536SChangpeng Liu 		goto exit;
4588f2a8536SChangpeng Liu 	}
4598f2a8536SChangpeng Liu 
4608f2a8536SChangpeng Liu 	assert(lun->reservation.crkey != 0);
4618f2a8536SChangpeng Liu 
4628f2a8536SChangpeng Liu 	if (sa_rkey != lun->reservation.crkey) {
4638f2a8536SChangpeng Liu 		if (!sa_rkey) {
4648f2a8536SChangpeng Liu 			SPDK_ERRLOG("Zeroed sa_rkey\n");
4658f2a8536SChangpeng Liu 			spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
4668f2a8536SChangpeng Liu 						  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
4678f2a8536SChangpeng Liu 						  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
4688f2a8536SChangpeng Liu 						  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
4698f2a8536SChangpeng Liu 			return -EINVAL;
4708f2a8536SChangpeng Liu 		}
47133a29747SShuhei Matsumoto 		scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
4728f2a8536SChangpeng Liu 		goto exit;
4738f2a8536SChangpeng Liu 	}
4748f2a8536SChangpeng Liu 
47533a29747SShuhei Matsumoto 	if (scsi_pr_registrant_is_holder(lun, reg)) {
47633a29747SShuhei Matsumoto 		scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
4772172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "PREEMPT: preempt itself with type %u\n", rtype);
4788f2a8536SChangpeng Liu 		goto exit;
4798f2a8536SChangpeng Liu 	}
4808f2a8536SChangpeng Liu 
4818f2a8536SChangpeng Liu 	/* unregister registrants if any */
48233a29747SShuhei Matsumoto 	scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
48333a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
4848f2a8536SChangpeng Liu 	if (!reg) {
4858f2a8536SChangpeng Liu 		SPDK_ERRLOG("Current I_T nexus registrant was removed\n");
4868f2a8536SChangpeng Liu 		goto conflict;
4878f2a8536SChangpeng Liu 	}
4888f2a8536SChangpeng Liu 
4898f2a8536SChangpeng Liu 	/* preempt the holder */
49033a29747SShuhei Matsumoto 	scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
4918f2a8536SChangpeng Liu 
4928f2a8536SChangpeng Liu exit:
4938f2a8536SChangpeng Liu 	lun->pr_generation++;
4948f2a8536SChangpeng Liu 	return 0;
4958f2a8536SChangpeng Liu 
4968f2a8536SChangpeng Liu conflict:
4978f2a8536SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
4988f2a8536SChangpeng Liu 				  SPDK_SCSI_SENSE_NO_SENSE,
4998f2a8536SChangpeng Liu 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
5008f2a8536SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
5018f2a8536SChangpeng Liu 	return -EINVAL;
5028f2a8536SChangpeng Liu }
5038f2a8536SChangpeng Liu 
504d0d19eb8SChangpeng Liu int
scsi_pr_out(struct spdk_scsi_task * task,uint8_t * cdb,uint8_t * data,uint16_t data_len)50518dec401SShuhei Matsumoto scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb,
50618dec401SShuhei Matsumoto 	    uint8_t *data, uint16_t data_len)
507d0d19eb8SChangpeng Liu {
508d0d19eb8SChangpeng Liu 	int rc = -1;
509d0d19eb8SChangpeng Liu 	uint64_t rkey, sa_rkey;
510d0d19eb8SChangpeng Liu 	uint8_t spec_i_pt, all_tg_pt, aptpl;
511d0d19eb8SChangpeng Liu 	enum spdk_scsi_pr_out_service_action_code action;
512601fbbf9SChangpeng Liu 	enum spdk_scsi_pr_scope_code scope;
513601fbbf9SChangpeng Liu 	enum spdk_scsi_pr_type_code rtype;
514d0d19eb8SChangpeng Liu 	struct spdk_scsi_pr_out_param_list *param = (struct spdk_scsi_pr_out_param_list *)data;
515d0d19eb8SChangpeng Liu 
516d0d19eb8SChangpeng Liu 	action = cdb[1] & 0x0f;
517601fbbf9SChangpeng Liu 	scope = (cdb[2] >> 4) & 0x0f;
518601fbbf9SChangpeng Liu 	rtype = cdb[2] & 0x0f;
519d0d19eb8SChangpeng Liu 
520d0d19eb8SChangpeng Liu 	rkey = from_be64(&param->rkey);
521d0d19eb8SChangpeng Liu 	sa_rkey = from_be64(&param->sa_rkey);
522d0d19eb8SChangpeng Liu 	aptpl = param->aptpl;
523d0d19eb8SChangpeng Liu 	spec_i_pt = param->spec_i_pt;
524d0d19eb8SChangpeng Liu 	all_tg_pt = param->all_tg_pt;
525d0d19eb8SChangpeng Liu 
526d0d19eb8SChangpeng Liu 	switch (action) {
527d0d19eb8SChangpeng Liu 	case SPDK_SCSI_PR_OUT_REGISTER:
528d0d19eb8SChangpeng Liu 	case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
52933a29747SShuhei Matsumoto 		rc = scsi_pr_out_register(task, action, rkey, sa_rkey,
530d0d19eb8SChangpeng Liu 					  spec_i_pt, all_tg_pt, aptpl);
531d0d19eb8SChangpeng Liu 		break;
532601fbbf9SChangpeng Liu 	case SPDK_SCSI_PR_OUT_RESERVE:
533601fbbf9SChangpeng Liu 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
534601fbbf9SChangpeng Liu 			goto invalid;
535601fbbf9SChangpeng Liu 		}
53633a29747SShuhei Matsumoto 		rc = scsi_pr_out_reserve(task, rtype, rkey,
537601fbbf9SChangpeng Liu 					 spec_i_pt, all_tg_pt, aptpl);
538601fbbf9SChangpeng Liu 		break;
539e8e791f7SChangpeng Liu 	case SPDK_SCSI_PR_OUT_RELEASE:
540e8e791f7SChangpeng Liu 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
541e8e791f7SChangpeng Liu 			goto invalid;
542e8e791f7SChangpeng Liu 		}
54333a29747SShuhei Matsumoto 		rc = scsi_pr_out_release(task, rtype, rkey);
544e8e791f7SChangpeng Liu 		break;
5458d79b410SChangpeng Liu 	case SPDK_SCSI_PR_OUT_CLEAR:
54633a29747SShuhei Matsumoto 		rc = scsi_pr_out_clear(task, rkey);
5478d79b410SChangpeng Liu 		break;
5488f2a8536SChangpeng Liu 	case SPDK_SCSI_PR_OUT_PREEMPT:
5498f2a8536SChangpeng Liu 		if (scope != SPDK_SCSI_PR_LU_SCOPE) {
5508f2a8536SChangpeng Liu 			goto invalid;
5518f2a8536SChangpeng Liu 		}
55233a29747SShuhei Matsumoto 		rc = scsi_pr_out_preempt(task, action, rtype, rkey, sa_rkey);
5538f2a8536SChangpeng Liu 		break;
554d0d19eb8SChangpeng Liu 	default:
555d0d19eb8SChangpeng Liu 		SPDK_ERRLOG("Invalid service action code %u\n", action);
556d0d19eb8SChangpeng Liu 		goto invalid;
557d0d19eb8SChangpeng Liu 	}
558d0d19eb8SChangpeng Liu 
559d0d19eb8SChangpeng Liu 	return rc;
560d0d19eb8SChangpeng Liu 
561d0d19eb8SChangpeng Liu invalid:
562d0d19eb8SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
563d0d19eb8SChangpeng Liu 				  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
564d0d19eb8SChangpeng Liu 				  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
565d0d19eb8SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
566d0d19eb8SChangpeng Liu 	return -EINVAL;
567d0d19eb8SChangpeng Liu }
5684e9e220eSChangpeng Liu 
5694e9e220eSChangpeng Liu static int
scsi_pr_in_read_keys(struct spdk_scsi_task * task,uint8_t * data,uint16_t data_len)57033a29747SShuhei Matsumoto scsi_pr_in_read_keys(struct spdk_scsi_task *task, uint8_t *data,
5714e9e220eSChangpeng Liu 		     uint16_t data_len)
5724e9e220eSChangpeng Liu {
5734e9e220eSChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
5744e9e220eSChangpeng Liu 	struct spdk_scsi_pr_in_read_keys_data *keys;
5754e9e220eSChangpeng Liu 	struct spdk_scsi_pr_registrant *reg, *tmp;
5764e9e220eSChangpeng Liu 	uint16_t count = 0;
5774e9e220eSChangpeng Liu 
5782172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR IN READ KEYS\n");
5794e9e220eSChangpeng Liu 	keys = (struct spdk_scsi_pr_in_read_keys_data *)data;
5804e9e220eSChangpeng Liu 
5814e9e220eSChangpeng Liu 	to_be32(&keys->header.pr_generation, lun->pr_generation);
5824e9e220eSChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
5834e9e220eSChangpeng Liu 		if (((count + 1) * 8 + sizeof(keys->header)) > data_len) {
5844e9e220eSChangpeng Liu 			break;
5854e9e220eSChangpeng Liu 		}
5864e9e220eSChangpeng Liu 		to_be64(&keys->rkeys[count], reg->rkey);
5874e9e220eSChangpeng Liu 		count++;
5884e9e220eSChangpeng Liu 	}
589fecac0d8SChangpeng Liu 	to_be32(&keys->header.additional_len, count * 8);
5904e9e220eSChangpeng Liu 
5914e9e220eSChangpeng Liu 	return (sizeof(keys->header) + count * 8);
5924e9e220eSChangpeng Liu }
5934e9e220eSChangpeng Liu 
5942b09903cSChangpeng Liu static int
scsi_pr_in_read_reservations(struct spdk_scsi_task * task,uint8_t * data,uint16_t data_len)59533a29747SShuhei Matsumoto scsi_pr_in_read_reservations(struct spdk_scsi_task *task,
5962b09903cSChangpeng Liu 			     uint8_t *data, uint16_t data_len)
5972b09903cSChangpeng Liu {
5982b09903cSChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
5992b09903cSChangpeng Liu 	struct spdk_scsi_pr_in_read_reservations_data *param;
6002b09903cSChangpeng Liu 	bool all_regs = false;
6012b09903cSChangpeng Liu 
6022172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR IN READ RESERVATIONS\n");
6032b09903cSChangpeng Liu 	param = (struct spdk_scsi_pr_in_read_reservations_data *)(data);
6042b09903cSChangpeng Liu 
6052b09903cSChangpeng Liu 	to_be32(&param->header.pr_generation, lun->pr_generation);
60633a29747SShuhei Matsumoto 	if (scsi_pr_has_reservation(lun)) {
60733a29747SShuhei Matsumoto 		all_regs = scsi_pr_is_all_registrants_type(lun);
6082b09903cSChangpeng Liu 		if (all_regs) {
6092b09903cSChangpeng Liu 			to_be64(&param->rkey, 0);
6102b09903cSChangpeng Liu 		} else {
6112b09903cSChangpeng Liu 			to_be64(&param->rkey, lun->reservation.crkey);
6122b09903cSChangpeng Liu 		}
613fecac0d8SChangpeng Liu 		to_be32(&param->header.additional_len, 16);
6142b09903cSChangpeng Liu 		param->scope = SPDK_SCSI_PR_LU_SCOPE;
6152b09903cSChangpeng Liu 		param->type = lun->reservation.rtype;
6162172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "READ RESERVATIONS with valid reservation\n");
6172b09903cSChangpeng Liu 		return sizeof(*param);
6182b09903cSChangpeng Liu 	}
6192b09903cSChangpeng Liu 
6202b09903cSChangpeng Liu 	/* no reservation */
621fecac0d8SChangpeng Liu 	to_be32(&param->header.additional_len, 0);
6222172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "READ RESERVATIONS no reservation\n");
6232b09903cSChangpeng Liu 	return sizeof(param->header);
6242b09903cSChangpeng Liu }
6252b09903cSChangpeng Liu 
626c25e9b24SChangpeng Liu static int
scsi_pr_in_report_capabilities(struct spdk_scsi_task * task,uint8_t * data,uint16_t data_len)62733a29747SShuhei Matsumoto scsi_pr_in_report_capabilities(struct spdk_scsi_task *task,
628c25e9b24SChangpeng Liu 			       uint8_t *data, uint16_t data_len)
629c25e9b24SChangpeng Liu {
630c25e9b24SChangpeng Liu 	struct spdk_scsi_pr_in_report_capabilities_data *param;
631c25e9b24SChangpeng Liu 
6322172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR IN REPORT CAPABILITIES\n");
633c25e9b24SChangpeng Liu 	param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
634c25e9b24SChangpeng Liu 
635c25e9b24SChangpeng Liu 	memset(param, 0, sizeof(*param));
636c25e9b24SChangpeng Liu 	to_be16(&param->length, sizeof(*param));
6376b6a3ff9SChangpeng Liu 	/* Compatible reservation handling to support RESERVE/RELEASE defined in SPC-2 */
6386b6a3ff9SChangpeng Liu 	param->crh = 1;
639c25e9b24SChangpeng Liu 	param->tmv = 1;
640c25e9b24SChangpeng Liu 	param->wr_ex = 1;
641c25e9b24SChangpeng Liu 	param->ex_ac = 1;
642c25e9b24SChangpeng Liu 	param->wr_ex_ro = 1;
643c25e9b24SChangpeng Liu 	param->ex_ac_ro = 1;
644c25e9b24SChangpeng Liu 	param->wr_ex_ar = 1;
645c25e9b24SChangpeng Liu 	param->ex_ac_ar = 1;
646c25e9b24SChangpeng Liu 
647c25e9b24SChangpeng Liu 	return sizeof(*param);
648c25e9b24SChangpeng Liu }
649c25e9b24SChangpeng Liu 
65094e9f0abSChangpeng Liu static int
scsi_pr_in_read_full_status(struct spdk_scsi_task * task,uint8_t * data,uint16_t data_len)65133a29747SShuhei Matsumoto scsi_pr_in_read_full_status(struct spdk_scsi_task *task,
65294e9f0abSChangpeng Liu 			    uint8_t *data, uint16_t data_len)
65394e9f0abSChangpeng Liu {
65494e9f0abSChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
65594e9f0abSChangpeng Liu 	struct spdk_scsi_pr_in_full_status_data *param;
65694e9f0abSChangpeng Liu 	struct spdk_scsi_pr_in_full_status_desc *desc;
65794e9f0abSChangpeng Liu 	struct spdk_scsi_pr_registrant *reg, *tmp;
65894e9f0abSChangpeng Liu 	bool all_regs = false;
65994e9f0abSChangpeng Liu 	uint32_t add_len = 0;
66094e9f0abSChangpeng Liu 
6612172c432STomasz Zawadzki 	SPDK_DEBUGLOG(scsi, "PR IN READ FULL STATUS\n");
66294e9f0abSChangpeng Liu 
66333a29747SShuhei Matsumoto 	all_regs = scsi_pr_is_all_registrants_type(lun);
66494e9f0abSChangpeng Liu 	param = (struct spdk_scsi_pr_in_full_status_data *)data;
66594e9f0abSChangpeng Liu 	to_be32(&param->header.pr_generation, lun->pr_generation);
66694e9f0abSChangpeng Liu 
66794e9f0abSChangpeng Liu 	TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
66894e9f0abSChangpeng Liu 		desc = (struct spdk_scsi_pr_in_full_status_desc *)
66994e9f0abSChangpeng Liu 		       ((uint8_t *)param->desc_list + add_len);
67094e9f0abSChangpeng Liu 		if (add_len + sizeof(*desc) + sizeof(param->header) > data_len) {
67194e9f0abSChangpeng Liu 			break;
67294e9f0abSChangpeng Liu 		}
67394e9f0abSChangpeng Liu 		add_len += sizeof(*desc);
67494e9f0abSChangpeng Liu 		desc->rkey = reg->rkey;
67594e9f0abSChangpeng Liu 		if (all_regs || lun->reservation.holder == reg) {
67694e9f0abSChangpeng Liu 			desc->r_holder = true;
67794e9f0abSChangpeng Liu 			desc->type = lun->reservation.rtype;
67894e9f0abSChangpeng Liu 		} else {
67994e9f0abSChangpeng Liu 			desc->r_holder = false;
68094e9f0abSChangpeng Liu 			desc->type = 0;
68194e9f0abSChangpeng Liu 		}
68294e9f0abSChangpeng Liu 		desc->all_tg_pt = 0;
68394e9f0abSChangpeng Liu 		desc->scope = SPDK_SCSI_PR_LU_SCOPE;
68494e9f0abSChangpeng Liu 		desc->relative_target_port_id = reg->relative_target_port_id;
68594e9f0abSChangpeng Liu 		if (add_len + reg->transport_id_len + sizeof(param->header) > data_len) {
68694e9f0abSChangpeng Liu 			break;
68794e9f0abSChangpeng Liu 		}
68894e9f0abSChangpeng Liu 		add_len += reg->transport_id_len;
68994e9f0abSChangpeng Liu 		memcpy(&desc->transport_id, reg->transport_id, reg->transport_id_len);
69094e9f0abSChangpeng Liu 		to_be32(&desc->desc_len, reg->transport_id_len);
69194e9f0abSChangpeng Liu 	}
69294e9f0abSChangpeng Liu 	to_be32(&param->header.additional_len, add_len);
69394e9f0abSChangpeng Liu 
69494e9f0abSChangpeng Liu 	return (sizeof(param->header) + add_len);
69594e9f0abSChangpeng Liu }
69694e9f0abSChangpeng Liu 
6974e9e220eSChangpeng Liu int
scsi_pr_in(struct spdk_scsi_task * task,uint8_t * cdb,uint8_t * data,uint16_t data_len)69818dec401SShuhei Matsumoto scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb,
69918dec401SShuhei Matsumoto 	   uint8_t *data, uint16_t data_len)
7004e9e220eSChangpeng Liu {
7014e9e220eSChangpeng Liu 	enum spdk_scsi_pr_in_action_code action;
7024e9e220eSChangpeng Liu 	int rc = 0;
7034e9e220eSChangpeng Liu 
7044e9e220eSChangpeng Liu 	action = cdb[1] & 0x1f;
7054e9e220eSChangpeng Liu 	if (data_len < sizeof(struct spdk_scsi_pr_in_read_header)) {
7064e9e220eSChangpeng Liu 		goto invalid;
7074e9e220eSChangpeng Liu 	}
7084e9e220eSChangpeng Liu 
7094e9e220eSChangpeng Liu 	switch (action) {
7104e9e220eSChangpeng Liu 	case SPDK_SCSI_PR_IN_READ_KEYS:
71133a29747SShuhei Matsumoto 		rc = scsi_pr_in_read_keys(task, data, data_len);
7124e9e220eSChangpeng Liu 		break;
7132b09903cSChangpeng Liu 	case SPDK_SCSI_PR_IN_READ_RESERVATION:
7142b09903cSChangpeng Liu 		if (data_len < sizeof(struct spdk_scsi_pr_in_read_reservations_data)) {
7152b09903cSChangpeng Liu 			goto invalid;
7162b09903cSChangpeng Liu 		}
71733a29747SShuhei Matsumoto 		rc = scsi_pr_in_read_reservations(task, data, data_len);
7182b09903cSChangpeng Liu 		break;
719c25e9b24SChangpeng Liu 	case SPDK_SCSI_PR_IN_REPORT_CAPABILITIES:
72033a29747SShuhei Matsumoto 		rc = scsi_pr_in_report_capabilities(task, data, data_len);
721c25e9b24SChangpeng Liu 		break;
72294e9f0abSChangpeng Liu 	case SPDK_SCSI_PR_IN_READ_FULL_STATUS:
72333a29747SShuhei Matsumoto 		rc = scsi_pr_in_read_full_status(task, data, data_len);
72494e9f0abSChangpeng Liu 		break;
7254e9e220eSChangpeng Liu 	default:
7264e9e220eSChangpeng Liu 		goto invalid;
7274e9e220eSChangpeng Liu 	}
7284e9e220eSChangpeng Liu 
7294e9e220eSChangpeng Liu 	return rc;
7304e9e220eSChangpeng Liu 
7314e9e220eSChangpeng Liu invalid:
7324e9e220eSChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
7334e9e220eSChangpeng Liu 				  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
7344e9e220eSChangpeng Liu 				  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
7354e9e220eSChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
7364e9e220eSChangpeng Liu 	return -EINVAL;
7374e9e220eSChangpeng Liu }
7388c0c8e53SChangpeng Liu 
7398c0c8e53SChangpeng Liu int
scsi_pr_check(struct spdk_scsi_task * task)74018dec401SShuhei Matsumoto scsi_pr_check(struct spdk_scsi_task *task)
7418c0c8e53SChangpeng Liu {
7428c0c8e53SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
7438c0c8e53SChangpeng Liu 	uint8_t *cdb = task->cdb;
7448c0c8e53SChangpeng Liu 	enum spdk_scsi_pr_type_code rtype;
7458c0c8e53SChangpeng Liu 	enum spdk_scsi_pr_out_service_action_code action;
7468c0c8e53SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
7478c0c8e53SChangpeng Liu 	bool dma_to_device = false;
7488c0c8e53SChangpeng Liu 
7498c0c8e53SChangpeng Liu 	/* no reservation holders */
75033a29747SShuhei Matsumoto 	if (!scsi_pr_has_reservation(lun)) {
7518c0c8e53SChangpeng Liu 		return 0;
7528c0c8e53SChangpeng Liu 	}
7538c0c8e53SChangpeng Liu 
7548c0c8e53SChangpeng Liu 	rtype = lun->reservation.rtype;
7558c0c8e53SChangpeng Liu 	assert(rtype != 0);
7568c0c8e53SChangpeng Liu 
75733a29747SShuhei Matsumoto 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
7588c0c8e53SChangpeng Liu 	/* current I_T nexus hold the reservation */
75933a29747SShuhei Matsumoto 	if (scsi_pr_registrant_is_holder(lun, reg)) {
7608c0c8e53SChangpeng Liu 		return 0;
7618c0c8e53SChangpeng Liu 	}
7628c0c8e53SChangpeng Liu 
7638c0c8e53SChangpeng Liu 	/* reservation is held by other I_T nexus */
7648c0c8e53SChangpeng Liu 	switch (cdb[0]) {
7658c0c8e53SChangpeng Liu 	case SPDK_SPC_INQUIRY:
7668c0c8e53SChangpeng Liu 	case SPDK_SPC_REPORT_LUNS:
7678c0c8e53SChangpeng Liu 	case SPDK_SPC_REQUEST_SENSE:
7688c0c8e53SChangpeng Liu 	case SPDK_SPC_LOG_SENSE:
7698c0c8e53SChangpeng Liu 	case SPDK_SPC_TEST_UNIT_READY:
7708c0c8e53SChangpeng Liu 	case SPDK_SBC_START_STOP_UNIT:
7718c0c8e53SChangpeng Liu 	case SPDK_SBC_READ_CAPACITY_10:
7728c0c8e53SChangpeng Liu 	case SPDK_SPC_PERSISTENT_RESERVE_IN:
7738c0c8e53SChangpeng Liu 	case SPDK_SPC_SERVICE_ACTION_IN_16:
7746b6a3ff9SChangpeng Liu 	/* CRH enabled, processed by scsi2_reserve() */
7756b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RESERVE_6:
7766b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RESERVE_10:
7776b6a3ff9SChangpeng Liu 	/* CRH enabled, processed by scsi2_release() */
7786b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RELEASE_6:
7796b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RELEASE_10:
7808c0c8e53SChangpeng Liu 		return 0;
7818c0c8e53SChangpeng Liu 	case SPDK_SPC_MODE_SELECT_6:
7828c0c8e53SChangpeng Liu 	case SPDK_SPC_MODE_SELECT_10:
7838c0c8e53SChangpeng Liu 	case SPDK_SPC_MODE_SENSE_6:
7848c0c8e53SChangpeng Liu 	case SPDK_SPC_MODE_SENSE_10:
7858c0c8e53SChangpeng Liu 	case SPDK_SPC_LOG_SELECT:
7868c0c8e53SChangpeng Liu 		/* I_T nexus is registrant but not holder */
7878c0c8e53SChangpeng Liu 		if (!reg) {
7882172c432STomasz Zawadzki 			SPDK_DEBUGLOG(scsi, "CHECK: current I_T nexus "
7898c0c8e53SChangpeng Liu 				      "is not registered, cdb 0x%x\n", cdb[0]);
7908c0c8e53SChangpeng Liu 			goto conflict;
7918c0c8e53SChangpeng Liu 		}
792bdb90726SChangpeng Liu 		return 0;
7938c0c8e53SChangpeng Liu 	case SPDK_SPC_PERSISTENT_RESERVE_OUT:
7948c0c8e53SChangpeng Liu 		action = cdb[1] & 0x1f;
7952172c432STomasz Zawadzki 		SPDK_DEBUGLOG(scsi, "CHECK: PR OUT action %u\n", action);
7968c0c8e53SChangpeng Liu 		switch (action) {
7978c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_RELEASE:
7988c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_CLEAR:
7998c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_PREEMPT:
8008c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_PREEMPT_AND_ABORT:
8018c0c8e53SChangpeng Liu 			if (!reg) {
8028c0c8e53SChangpeng Liu 				SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
8038c0c8e53SChangpeng Liu 				goto conflict;
8048c0c8e53SChangpeng Liu 			}
805bdb90726SChangpeng Liu 			return 0;
8068c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_REGISTER:
8078c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
8088c0c8e53SChangpeng Liu 			return 0;
8098c0c8e53SChangpeng Liu 		case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
8108c0c8e53SChangpeng Liu 			SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
8118c0c8e53SChangpeng Liu 			goto conflict;
8128c0c8e53SChangpeng Liu 		default:
8138c0c8e53SChangpeng Liu 			SPDK_ERRLOG("CHECK: PR OUT invalid action %u\n", action);
8148c0c8e53SChangpeng Liu 			goto conflict;
8158c0c8e53SChangpeng Liu 		}
8168c0c8e53SChangpeng Liu 
8178c0c8e53SChangpeng Liu 	/* For most SBC R/W commands */
8188c0c8e53SChangpeng Liu 	default:
8198c0c8e53SChangpeng Liu 		break;
8208c0c8e53SChangpeng Liu 	}
8218c0c8e53SChangpeng Liu 
8228c0c8e53SChangpeng Liu 	switch (cdb[0]) {
8238c0c8e53SChangpeng Liu 	case SPDK_SBC_READ_6:
8248c0c8e53SChangpeng Liu 	case SPDK_SBC_READ_10:
8258c0c8e53SChangpeng Liu 	case SPDK_SBC_READ_12:
8268c0c8e53SChangpeng Liu 	case SPDK_SBC_READ_16:
8278c0c8e53SChangpeng Liu 		break;
8288c0c8e53SChangpeng Liu 	case SPDK_SBC_WRITE_6:
8298c0c8e53SChangpeng Liu 	case SPDK_SBC_WRITE_10:
8308c0c8e53SChangpeng Liu 	case SPDK_SBC_WRITE_12:
8318c0c8e53SChangpeng Liu 	case SPDK_SBC_WRITE_16:
8328c0c8e53SChangpeng Liu 	case SPDK_SBC_UNMAP:
8338c0c8e53SChangpeng Liu 	case SPDK_SBC_SYNCHRONIZE_CACHE_10:
8348c0c8e53SChangpeng Liu 	case SPDK_SBC_SYNCHRONIZE_CACHE_16:
8358c0c8e53SChangpeng Liu 		dma_to_device = true;
8368c0c8e53SChangpeng Liu 		break;
8378c0c8e53SChangpeng Liu 	default:
8388c0c8e53SChangpeng Liu 		SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
8398c0c8e53SChangpeng Liu 		goto conflict;
8408c0c8e53SChangpeng Liu 	}
8418c0c8e53SChangpeng Liu 
8428c0c8e53SChangpeng Liu 	switch (rtype) {
8438c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE:
8448c0c8e53SChangpeng Liu 		if (dma_to_device) {
8458c0c8e53SChangpeng Liu 			SPDK_ERRLOG("CHECK: Write Exclusive reservation type "
8468c0c8e53SChangpeng Liu 				    "rejects command 0x%x\n", cdb[0]);
8478c0c8e53SChangpeng Liu 			goto conflict;
8488c0c8e53SChangpeng Liu 		}
8498c0c8e53SChangpeng Liu 		break;
8508c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
8518c0c8e53SChangpeng Liu 		SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
8528c0c8e53SChangpeng Liu 			    "rejects command 0x%x\n", cdb[0]);
8538c0c8e53SChangpeng Liu 		goto conflict;
8548c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
8558c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
8568c0c8e53SChangpeng Liu 		if (!reg && dma_to_device) {
8578c0c8e53SChangpeng Liu 			SPDK_ERRLOG("CHECK: Registrants only reservation "
8588c0c8e53SChangpeng Liu 				    "type  reject command 0x%x\n", cdb[0]);
8598c0c8e53SChangpeng Liu 			goto conflict;
8608c0c8e53SChangpeng Liu 		}
8618c0c8e53SChangpeng Liu 		break;
8628c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
8638c0c8e53SChangpeng Liu 	case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
8648c0c8e53SChangpeng Liu 		if (!reg) {
8658c0c8e53SChangpeng Liu 			SPDK_ERRLOG("CHECK: All Registrants reservation "
8668c0c8e53SChangpeng Liu 				    "type  reject command 0x%x\n", cdb[0]);
8678c0c8e53SChangpeng Liu 			goto conflict;
8688c0c8e53SChangpeng Liu 		}
8698c0c8e53SChangpeng Liu 		break;
8708c0c8e53SChangpeng Liu 	default:
8718c0c8e53SChangpeng Liu 		break;
8728c0c8e53SChangpeng Liu 	}
8738c0c8e53SChangpeng Liu 
8748c0c8e53SChangpeng Liu 	return 0;
8758c0c8e53SChangpeng Liu 
8768c0c8e53SChangpeng Liu conflict:
8778c0c8e53SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
8788c0c8e53SChangpeng Liu 				  SPDK_SCSI_SENSE_NO_SENSE,
8798c0c8e53SChangpeng Liu 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
8808c0c8e53SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
8818c0c8e53SChangpeng Liu 	return -1;
8828c0c8e53SChangpeng Liu }
8836b6a3ff9SChangpeng Liu 
8846b6a3ff9SChangpeng Liu static int
scsi2_check_reservation_conflict(struct spdk_scsi_task * task)8856b6a3ff9SChangpeng Liu scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
8866b6a3ff9SChangpeng Liu {
8876b6a3ff9SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
8886b6a3ff9SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg;
8896b6a3ff9SChangpeng Liu 	bool conflict = false;
8906b6a3ff9SChangpeng Liu 
8916b6a3ff9SChangpeng Liu 	reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
8926b6a3ff9SChangpeng Liu 	if (reg) {
8936b6a3ff9SChangpeng Liu 		/*
8946b6a3ff9SChangpeng Liu 		 * From spc4r31 5.9.3 Exceptions to SPC-2 RESERVE and RELEASE
8956b6a3ff9SChangpeng Liu 		 * behavior
8966b6a3ff9SChangpeng Liu 		 *
8976b6a3ff9SChangpeng Liu 		 * A RESERVE(6) or RESERVE(10) command shall complete with GOOD
8986b6a3ff9SChangpeng Liu 		 * status, but no reservation shall be established and the
8996b6a3ff9SChangpeng Liu 		 * persistent reservation shall not be changed, if the command
9006b6a3ff9SChangpeng Liu 		 * is received from a) and b) below.
9016b6a3ff9SChangpeng Liu 		 *
9026b6a3ff9SChangpeng Liu 		 * A RELEASE(6) or RELEASE(10) command shall complete with GOOD
9036b6a3ff9SChangpeng Liu 		 * status, but the persistent reservation shall not be released,
9046b6a3ff9SChangpeng Liu 		 * if the command is received from a) and b)
9056b6a3ff9SChangpeng Liu 		 *
9066b6a3ff9SChangpeng Liu 		 * a) An I_T nexus that is a persistent reservation holder; or
9076b6a3ff9SChangpeng Liu 		 * b) An I_T nexus that is registered if a registrants only or
9086b6a3ff9SChangpeng Liu 		 *    all registrants type persistent reservation is present.
9096b6a3ff9SChangpeng Liu 		 *
9106b6a3ff9SChangpeng Liu 		 * In all other cases, a RESERVE(6) command, RESERVE(10) command,
9116b6a3ff9SChangpeng Liu 		 * RELEASE(6) command, or RELEASE(10) command shall be processed
9126b6a3ff9SChangpeng Liu 		 * as defined in SPC-2.
9136b6a3ff9SChangpeng Liu 		 */
9146b6a3ff9SChangpeng Liu 		if (scsi_pr_registrant_is_holder(lun, reg)) {
9156b6a3ff9SChangpeng Liu 			return 1;
9166b6a3ff9SChangpeng Liu 		}
9176b6a3ff9SChangpeng Liu 
9186b6a3ff9SChangpeng Liu 		if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
9196b6a3ff9SChangpeng Liu 		    lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
9206b6a3ff9SChangpeng Liu 			return 1;
9216b6a3ff9SChangpeng Liu 		}
9226b6a3ff9SChangpeng Liu 
9236b6a3ff9SChangpeng Liu 		conflict = true;
9246b6a3ff9SChangpeng Liu 	} else {
9256b6a3ff9SChangpeng Liu 		/*
9266b6a3ff9SChangpeng Liu 		 * From spc2r20 5.5.1 Reservations overview:
9276b6a3ff9SChangpeng Liu 		 *
9286b6a3ff9SChangpeng Liu 		 * If a logical unit has executed a PERSISTENT RESERVE OUT
9296b6a3ff9SChangpeng Liu 		 * command with the REGISTER or the REGISTER AND IGNORE
9306b6a3ff9SChangpeng Liu 		 * EXISTING KEY service action and is still registered by any
9316b6a3ff9SChangpeng Liu 		 * initiator, all RESERVE commands and all RELEASE commands
9326b6a3ff9SChangpeng Liu 		 * regardless of initiator shall conflict and shall terminate
9336b6a3ff9SChangpeng Liu 		 * with a RESERVATION CONFLICT status.
9346b6a3ff9SChangpeng Liu 		 */
9356b6a3ff9SChangpeng Liu 		conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
9366b6a3ff9SChangpeng Liu 	}
9376b6a3ff9SChangpeng Liu 
9386b6a3ff9SChangpeng Liu 	if (conflict) {
9396b6a3ff9SChangpeng Liu 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
9406b6a3ff9SChangpeng Liu 					  SPDK_SCSI_SENSE_NO_SENSE,
9416b6a3ff9SChangpeng Liu 					  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
9426b6a3ff9SChangpeng Liu 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
9436b6a3ff9SChangpeng Liu 		return -1;
9446b6a3ff9SChangpeng Liu 	}
9456b6a3ff9SChangpeng Liu 
9466b6a3ff9SChangpeng Liu 	return 0;
9476b6a3ff9SChangpeng Liu }
9486b6a3ff9SChangpeng Liu 
9496b6a3ff9SChangpeng Liu int
scsi2_reserve(struct spdk_scsi_task * task,uint8_t * cdb)9506b6a3ff9SChangpeng Liu scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
9516b6a3ff9SChangpeng Liu {
9526b6a3ff9SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
9536b6a3ff9SChangpeng Liu 	struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
9546b6a3ff9SChangpeng Liu 	int ret;
9556b6a3ff9SChangpeng Liu 
9566b6a3ff9SChangpeng Liu 	/* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
9576b6a3ff9SChangpeng Liu 	if (cdb[1] & 0x3) {
9586b6a3ff9SChangpeng Liu 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
9596b6a3ff9SChangpeng Liu 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
9606b6a3ff9SChangpeng Liu 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
9616b6a3ff9SChangpeng Liu 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
9626b6a3ff9SChangpeng Liu 		return -1;
9636b6a3ff9SChangpeng Liu 	}
9646b6a3ff9SChangpeng Liu 
9656b6a3ff9SChangpeng Liu 	ret = scsi2_check_reservation_conflict(task);
9666b6a3ff9SChangpeng Liu 	/* PERSISTENT RESERVE is enabled */
9676b6a3ff9SChangpeng Liu 	if (ret == 1) {
9686b6a3ff9SChangpeng Liu 		return 0;
9696b6a3ff9SChangpeng Liu 	} else if (ret < 0) {
9706b6a3ff9SChangpeng Liu 		return ret;
9716b6a3ff9SChangpeng Liu 	}
9726b6a3ff9SChangpeng Liu 
9736b6a3ff9SChangpeng Liu 	/* SPC2 RESERVE */
9746b6a3ff9SChangpeng Liu 	reg->initiator_port = task->initiator_port;
9756b6a3ff9SChangpeng Liu 	if (task->initiator_port) {
9766b6a3ff9SChangpeng Liu 		snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
9776b6a3ff9SChangpeng Liu 			 task->initiator_port->name);
9786b6a3ff9SChangpeng Liu 		reg->transport_id_len = task->initiator_port->transport_id_len;
9796b6a3ff9SChangpeng Liu 		memcpy(reg->transport_id, task->initiator_port->transport_id,
9806b6a3ff9SChangpeng Liu 		       reg->transport_id_len);
9816b6a3ff9SChangpeng Liu 	}
9826b6a3ff9SChangpeng Liu 	reg->target_port = task->target_port;
9836b6a3ff9SChangpeng Liu 	if (task->target_port) {
9846b6a3ff9SChangpeng Liu 		snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
9856b6a3ff9SChangpeng Liu 			 task->target_port->name);
9866b6a3ff9SChangpeng Liu 	}
9876b6a3ff9SChangpeng Liu 
9886b6a3ff9SChangpeng Liu 	lun->reservation.flags = SCSI_SPC2_RESERVE;
9896b6a3ff9SChangpeng Liu 	lun->reservation.holder = &lun->scsi2_holder;
9906b6a3ff9SChangpeng Liu 
9916b6a3ff9SChangpeng Liu 	return 0;
9926b6a3ff9SChangpeng Liu }
9936b6a3ff9SChangpeng Liu 
9946b6a3ff9SChangpeng Liu int
scsi2_release(struct spdk_scsi_task * task)9956b6a3ff9SChangpeng Liu scsi2_release(struct spdk_scsi_task *task)
9966b6a3ff9SChangpeng Liu {
9976b6a3ff9SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
9986b6a3ff9SChangpeng Liu 	int ret;
9996b6a3ff9SChangpeng Liu 
10006b6a3ff9SChangpeng Liu 	ret = scsi2_check_reservation_conflict(task);
10016b6a3ff9SChangpeng Liu 	/* PERSISTENT RESERVE is enabled */
10026b6a3ff9SChangpeng Liu 	if (ret == 1) {
10036b6a3ff9SChangpeng Liu 		return 0;
10046b6a3ff9SChangpeng Liu 	} else if (ret < 0) {
10056b6a3ff9SChangpeng Liu 		return ret;
10066b6a3ff9SChangpeng Liu 	}
10076b6a3ff9SChangpeng Liu 
100844c70f82SChangpeng Liu 	if (!(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
100944c70f82SChangpeng Liu 		spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
101044c70f82SChangpeng Liu 					  SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
101144c70f82SChangpeng Liu 					  SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
101244c70f82SChangpeng Liu 					  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
101344c70f82SChangpeng Liu 		return -EINVAL;
101444c70f82SChangpeng Liu 	}
10156b6a3ff9SChangpeng Liu 
10166b6a3ff9SChangpeng Liu 	memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
10176b6a3ff9SChangpeng Liu 	memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
10186b6a3ff9SChangpeng Liu 
10196b6a3ff9SChangpeng Liu 	return 0;
10206b6a3ff9SChangpeng Liu }
10216b6a3ff9SChangpeng Liu 
10228dd1cd21SBen Walker int
scsi2_reserve_check(struct spdk_scsi_task * task)10238dd1cd21SBen Walker scsi2_reserve_check(struct spdk_scsi_task *task)
10246b6a3ff9SChangpeng Liu {
10256b6a3ff9SChangpeng Liu 	struct spdk_scsi_lun *lun = task->lun;
10266b6a3ff9SChangpeng Liu 	uint8_t *cdb = task->cdb;
10276b6a3ff9SChangpeng Liu 
10286b6a3ff9SChangpeng Liu 	switch (cdb[0]) {
10296b6a3ff9SChangpeng Liu 	case SPDK_SPC_INQUIRY:
10306b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RELEASE_6:
10316b6a3ff9SChangpeng Liu 	case SPDK_SPC2_RELEASE_10:
10326b6a3ff9SChangpeng Liu 		return 0;
10336b6a3ff9SChangpeng Liu 
10346b6a3ff9SChangpeng Liu 	default:
10356b6a3ff9SChangpeng Liu 		break;
10366b6a3ff9SChangpeng Liu 	}
10376b6a3ff9SChangpeng Liu 
10386b6a3ff9SChangpeng Liu 	/* no reservation holders */
10396b6a3ff9SChangpeng Liu 	if (!scsi_pr_has_reservation(lun)) {
10406b6a3ff9SChangpeng Liu 		return 0;
10416b6a3ff9SChangpeng Liu 	}
10426b6a3ff9SChangpeng Liu 
10436b6a3ff9SChangpeng Liu 	if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
10446b6a3ff9SChangpeng Liu 		return 0;
10456b6a3ff9SChangpeng Liu 	}
10466b6a3ff9SChangpeng Liu 
10476b6a3ff9SChangpeng Liu 	spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
10486b6a3ff9SChangpeng Liu 				  SPDK_SCSI_SENSE_NO_SENSE,
10496b6a3ff9SChangpeng Liu 				  SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
10506b6a3ff9SChangpeng Liu 				  SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
10516b6a3ff9SChangpeng Liu 	return -1;
10526b6a3ff9SChangpeng Liu }
1053