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(¶m->rkey);
521d0d19eb8SChangpeng Liu sa_rkey = from_be64(¶m->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(¶m->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(¶m->rkey, 0);
6102b09903cSChangpeng Liu } else {
6112b09903cSChangpeng Liu to_be64(¶m->rkey, lun->reservation.crkey);
6122b09903cSChangpeng Liu }
613fecac0d8SChangpeng Liu to_be32(¶m->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(¶m->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(¶m->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(¶m->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(¶m->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