Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2019 Intel Corporation.
3 : * All rights reserved.
4 : */
5 :
6 : #include "scsi_internal.h"
7 :
8 : #include "spdk/endian.h"
9 :
10 : /* Get registrant by I_T nexus */
11 : static struct spdk_scsi_pr_registrant *
12 135 : scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
13 : struct spdk_scsi_port *initiator_port,
14 : struct spdk_scsi_port *target_port)
15 : {
16 135 : struct spdk_scsi_pr_registrant *reg, *tmp;
17 :
18 229 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
19 276 : if (initiator_port == reg->initiator_port &&
20 91 : target_port == reg->target_port) {
21 91 : return reg;
22 : }
23 94 : }
24 :
25 44 : return NULL;
26 135 : }
27 :
28 : static bool
29 2 : scsi2_it_nexus_is_holder(struct spdk_scsi_lun *lun,
30 : struct spdk_scsi_port *initiator_port,
31 : struct spdk_scsi_port *target_port)
32 : {
33 2 : struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
34 :
35 2 : assert(reg != NULL);
36 :
37 3 : if ((reg->initiator_port == initiator_port) &&
38 1 : (reg->target_port == target_port)) {
39 1 : return true;
40 : }
41 :
42 1 : return false;
43 2 : }
44 :
45 : /* Reservation type is all registrants or not */
46 : static inline bool
47 39 : scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
48 : {
49 66 : return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
50 27 : lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
51 : }
52 :
53 : /* Registrant is reservation holder or not */
54 : static inline bool
55 31 : scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
56 : struct spdk_scsi_pr_registrant *reg)
57 : {
58 31 : if (reg != NULL && scsi_pr_is_all_registrants_type(lun)) {
59 9 : return true;
60 : }
61 :
62 22 : return (lun->reservation.holder == reg);
63 31 : }
64 :
65 : /* LUN holds a reservation or not */
66 : static inline bool
67 35 : scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
68 : {
69 35 : return !(lun->reservation.holder == NULL);
70 : }
71 :
72 : static int
73 24 : scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
74 : struct spdk_scsi_port *initiator_port,
75 : struct spdk_scsi_port *target_port,
76 : uint64_t sa_rkey)
77 : {
78 24 : struct spdk_scsi_pr_registrant *reg;
79 :
80 : /* Register sa_rkey with the I_T nexus */
81 24 : reg = calloc(1, sizeof(*reg));
82 24 : if (!reg) {
83 0 : return -ENOMEM;
84 : }
85 :
86 24 : SPDK_DEBUGLOG(scsi, "REGISTER: new registrant registered "
87 : "with key 0x%"PRIx64"\n", sa_rkey);
88 :
89 : /* New I_T nexus */
90 24 : reg->initiator_port = initiator_port;
91 24 : if (initiator_port) {
92 24 : snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
93 24 : initiator_port->name);
94 24 : reg->transport_id_len = initiator_port->transport_id_len;
95 24 : memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
96 24 : }
97 24 : reg->target_port = target_port;
98 24 : if (target_port) {
99 24 : snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
100 24 : target_port->name);
101 24 : reg->relative_target_port_id = target_port->index;
102 24 : }
103 24 : reg->rkey = sa_rkey;
104 24 : TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
105 24 : lun->pr_generation++;
106 :
107 24 : return 0;
108 24 : }
109 :
110 : static void
111 8 : scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
112 : {
113 8 : bool all_regs = false;
114 8 : struct spdk_scsi_pr_registrant *curr_reg, *tmp;
115 :
116 8 : SPDK_DEBUGLOG(scsi, "REGISTER: release reservation "
117 : "with type %u\n", lun->reservation.rtype);
118 :
119 : /* TODO: Unit Attention */
120 8 : all_regs = scsi_pr_is_all_registrants_type(lun);
121 8 : if (all_regs) {
122 8 : TAILQ_FOREACH_SAFE(curr_reg, &lun->reg_head, link, tmp) {
123 7 : if (curr_reg != reg) {
124 5 : lun->reservation.holder = curr_reg;
125 5 : lun->reservation.crkey = curr_reg->rkey;
126 5 : return;
127 : }
128 2 : }
129 1 : }
130 :
131 3 : memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
132 8 : }
133 :
134 : static void
135 12 : scsi_pr_reserve_reservation(struct spdk_scsi_lun *lun,
136 : enum spdk_scsi_pr_type_code type,
137 : uint64_t rkey,
138 : struct spdk_scsi_pr_registrant *holder)
139 : {
140 12 : lun->reservation.rtype = type;
141 12 : lun->reservation.crkey = rkey;
142 12 : lun->reservation.holder = holder;
143 12 : }
144 :
145 : static void
146 9 : scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
147 : struct spdk_scsi_pr_registrant *reg)
148 : {
149 9 : SPDK_DEBUGLOG(scsi, "REGISTER: unregister registrant\n");
150 :
151 9 : TAILQ_REMOVE(&lun->reg_head, reg, link);
152 9 : if (scsi_pr_registrant_is_holder(lun, reg)) {
153 6 : scsi_pr_release_reservation(lun, reg);
154 6 : }
155 :
156 9 : free(reg);
157 9 : lun->pr_generation++;
158 9 : }
159 :
160 : static void
161 8 : scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
162 : struct spdk_scsi_pr_registrant *reg,
163 : uint64_t sa_rkey)
164 : {
165 8 : SPDK_DEBUGLOG(scsi, "REGISTER: replace with new "
166 : "reservation key 0x%"PRIx64"\n", sa_rkey);
167 8 : reg->rkey = sa_rkey;
168 8 : lun->pr_generation++;
169 8 : }
170 :
171 : static int
172 11 : scsi_pr_out_reserve(struct spdk_scsi_task *task,
173 : enum spdk_scsi_pr_type_code rtype, uint64_t rkey,
174 : uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
175 : {
176 11 : struct spdk_scsi_lun *lun = task->lun;
177 11 : struct spdk_scsi_pr_registrant *reg;
178 :
179 11 : SPDK_DEBUGLOG(scsi, "PR OUT RESERVE: rkey 0x%"PRIx64", requested "
180 : "reservation type %u, type %u\n", rkey, rtype, lun->reservation.rtype);
181 :
182 : /* TODO: don't support now */
183 11 : if (spec_i_pt || all_tg_pt || aptpl) {
184 0 : SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt fields "
185 : "or invalid aptpl field\n");
186 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
187 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
188 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
189 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
190 0 : return -EINVAL;
191 : }
192 :
193 11 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
194 : /* No registration for the I_T nexus */
195 11 : if (!reg) {
196 0 : SPDK_ERRLOG("No registration\n");
197 0 : goto conflict;
198 : }
199 :
200 : /* invalid reservation key */
201 11 : if (reg->rkey != rkey) {
202 0 : SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match 0x%"PRIx64"\n",
203 : rkey, reg->rkey);
204 0 : goto conflict;
205 : }
206 :
207 : /* reservation holder already exists */
208 11 : if (scsi_pr_has_reservation(lun)) {
209 3 : if (rtype != lun->reservation.rtype) {
210 1 : SPDK_ERRLOG("Reservation type doesn't match\n");
211 1 : goto conflict;
212 : }
213 :
214 2 : if (!scsi_pr_registrant_is_holder(lun, reg)) {
215 1 : SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
216 1 : goto conflict;
217 : }
218 1 : } else {
219 : /* current I_T nexus is the first reservation holder */
220 8 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
221 : }
222 :
223 9 : return 0;
224 :
225 : conflict:
226 2 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
227 : SPDK_SCSI_SENSE_NO_SENSE,
228 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
229 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
230 2 : return -EINVAL;
231 11 : }
232 :
233 : static int
234 45 : scsi_pr_out_register(struct spdk_scsi_task *task,
235 : enum spdk_scsi_pr_out_service_action_code action,
236 : uint64_t rkey, uint64_t sa_rkey,
237 : uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
238 : {
239 45 : struct spdk_scsi_lun *lun = task->lun;
240 45 : struct spdk_scsi_pr_registrant *reg;
241 45 : int sc, sk, asc;
242 :
243 45 : SPDK_DEBUGLOG(scsi, "PR OUT REGISTER: rkey 0x%"PRIx64", "
244 : "sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
245 :
246 : /* TODO: don't support now */
247 45 : if (spec_i_pt || all_tg_pt || aptpl) {
248 0 : SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
249 0 : sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
250 0 : sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
251 0 : asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
252 0 : goto error_exit;
253 : }
254 :
255 45 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
256 : /* an unregistered I_T nexus session */
257 45 : if (!reg) {
258 24 : if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
259 0 : SPDK_ERRLOG("Reservation key field is not empty\n");
260 0 : sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
261 0 : sk = SPDK_SCSI_SENSE_NO_SENSE;
262 0 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
263 0 : goto error_exit;
264 : }
265 :
266 24 : if (!sa_rkey) {
267 : /* Do nothing except return GOOD status */
268 0 : SPDK_DEBUGLOG(scsi, "REGISTER: service action "
269 : "reservation key is zero, do noting\n");
270 0 : return 0;
271 : }
272 : /* Add a new registrant for the I_T nexus */
273 48 : return scsi_pr_register_registrant(lun, task->initiator_port,
274 24 : task->target_port, sa_rkey);
275 : } else {
276 : /* a registered I_T nexus */
277 21 : if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
278 8 : SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
279 : "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
280 8 : sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
281 8 : sk = SPDK_SCSI_SENSE_NO_SENSE;
282 8 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
283 8 : goto error_exit;
284 : }
285 :
286 13 : if (!sa_rkey) {
287 : /* unregister */
288 5 : scsi_pr_unregister_registrant(lun, reg);
289 5 : } else {
290 : /* replace */
291 8 : scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
292 : }
293 : }
294 :
295 13 : return 0;
296 :
297 : error_exit:
298 8 : spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
299 8 : return -EINVAL;
300 45 : }
301 :
302 : static int
303 2 : scsi_pr_out_release(struct spdk_scsi_task *task,
304 : enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
305 : {
306 2 : struct spdk_scsi_lun *lun = task->lun;
307 2 : struct spdk_scsi_pr_registrant *reg;
308 2 : int sk, asc;
309 :
310 2 : SPDK_DEBUGLOG(scsi, "PR OUT RELEASE: rkey 0x%"PRIx64", "
311 : "reservation type %u\n", rkey, rtype);
312 :
313 2 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
314 2 : if (!reg) {
315 0 : SPDK_ERRLOG("No registration\n");
316 0 : sk = SPDK_SCSI_SENSE_NOT_READY;
317 0 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
318 0 : goto check_condition;
319 : }
320 :
321 : /* no reservation holder */
322 2 : if (!scsi_pr_has_reservation(lun)) {
323 0 : SPDK_DEBUGLOG(scsi, "RELEASE: no reservation holder\n");
324 0 : return 0;
325 : }
326 :
327 2 : if (lun->reservation.rtype != rtype || rkey != lun->reservation.crkey) {
328 0 : sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
329 0 : asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
330 0 : goto check_condition;
331 : }
332 :
333 : /* I_T nexus is not a persistent reservation holder */
334 2 : if (!scsi_pr_registrant_is_holder(lun, reg)) {
335 0 : SPDK_DEBUGLOG(scsi, "RELEASE: current I_T nexus is not holder\n");
336 0 : return 0;
337 : }
338 :
339 2 : scsi_pr_release_reservation(lun, reg);
340 :
341 2 : return 0;
342 :
343 : check_condition:
344 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION, sk, asc,
345 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
346 0 : return -EINVAL;
347 2 : }
348 :
349 : static int
350 0 : scsi_pr_out_clear(struct spdk_scsi_task *task, uint64_t rkey)
351 : {
352 0 : struct spdk_scsi_lun *lun = task->lun;
353 0 : struct spdk_scsi_pr_registrant *reg, *tmp;
354 0 : int sc, sk, asc;
355 :
356 0 : SPDK_DEBUGLOG(scsi, "PR OUT CLEAR: rkey 0x%"PRIx64"\n", rkey);
357 :
358 0 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
359 0 : if (!reg) {
360 0 : SPDK_ERRLOG("No registration\n");
361 0 : sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
362 0 : sk = SPDK_SCSI_SENSE_NOT_READY;
363 0 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
364 0 : goto error_exit;
365 : }
366 :
367 0 : if (rkey != reg->rkey) {
368 0 : SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
369 : "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
370 0 : sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
371 0 : sk = SPDK_SCSI_SENSE_NO_SENSE;
372 0 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
373 0 : goto error_exit;
374 : }
375 :
376 0 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
377 0 : scsi_pr_unregister_registrant(lun, reg);
378 0 : }
379 :
380 0 : return 0;
381 :
382 : error_exit:
383 0 : spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
384 0 : return -EINVAL;
385 0 : }
386 :
387 : static void
388 3 : scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun *lun, uint64_t sa_rkey)
389 : {
390 3 : struct spdk_scsi_pr_registrant *reg, *tmp;
391 :
392 11 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
393 8 : if (reg->rkey == sa_rkey) {
394 3 : scsi_pr_unregister_registrant(lun, reg);
395 3 : }
396 8 : }
397 3 : }
398 :
399 : static void
400 1 : scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
401 : {
402 1 : struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
403 :
404 3 : TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
405 2 : if (reg_tmp != reg) {
406 1 : scsi_pr_unregister_registrant(lun, reg_tmp);
407 1 : }
408 2 : }
409 1 : }
410 :
411 : static int
412 7 : scsi_pr_out_preempt(struct spdk_scsi_task *task,
413 : enum spdk_scsi_pr_out_service_action_code action,
414 : enum spdk_scsi_pr_type_code rtype,
415 : uint64_t rkey, uint64_t sa_rkey)
416 : {
417 7 : struct spdk_scsi_lun *lun = task->lun;
418 7 : struct spdk_scsi_pr_registrant *reg;
419 7 : bool all_regs = false;
420 :
421 7 : SPDK_DEBUGLOG(scsi, "PR OUT PREEMPT: rkey 0x%"PRIx64", sa_rkey 0x%"PRIx64" "
422 : "action %u, type %u, reservation type %u\n",
423 : rkey, sa_rkey, action, rtype, lun->reservation.rtype);
424 :
425 : /* I_T nexus is not registered */
426 7 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
427 7 : if (!reg) {
428 0 : SPDK_ERRLOG("No registration\n");
429 0 : goto conflict;
430 : }
431 7 : if (rkey != reg->rkey) {
432 0 : SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
433 : "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
434 0 : goto conflict;
435 : }
436 :
437 : /* no persistent reservation */
438 7 : if (!scsi_pr_has_reservation(lun)) {
439 1 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
440 1 : SPDK_DEBUGLOG(scsi, "PREEMPT: no persistent reservation\n");
441 1 : goto exit;
442 : }
443 :
444 6 : all_regs = scsi_pr_is_all_registrants_type(lun);
445 :
446 6 : if (all_regs) {
447 1 : if (sa_rkey != 0) {
448 0 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
449 0 : SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey\n");
450 0 : } else {
451 : /* remove all other registrants and release persistent reservation if any */
452 1 : scsi_pr_remove_all_other_regs(lun, reg);
453 : /* create persistent reservation using new type and scope */
454 1 : scsi_pr_reserve_reservation(lun, rtype, 0, reg);
455 1 : SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey zeroed\n");
456 : }
457 1 : goto exit;
458 : }
459 :
460 5 : assert(lun->reservation.crkey != 0);
461 :
462 5 : if (sa_rkey != lun->reservation.crkey) {
463 2 : if (!sa_rkey) {
464 1 : SPDK_ERRLOG("Zeroed sa_rkey\n");
465 1 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
466 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
467 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
468 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
469 1 : return -EINVAL;
470 : }
471 1 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
472 1 : goto exit;
473 : }
474 :
475 3 : if (scsi_pr_registrant_is_holder(lun, reg)) {
476 2 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
477 2 : SPDK_DEBUGLOG(scsi, "PREEMPT: preempt itself with type %u\n", rtype);
478 2 : goto exit;
479 : }
480 :
481 : /* unregister registrants if any */
482 1 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
483 1 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
484 1 : if (!reg) {
485 0 : SPDK_ERRLOG("Current I_T nexus registrant was removed\n");
486 0 : goto conflict;
487 : }
488 :
489 : /* preempt the holder */
490 1 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
491 :
492 : exit:
493 6 : lun->pr_generation++;
494 6 : return 0;
495 :
496 : conflict:
497 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
498 : SPDK_SCSI_SENSE_NO_SENSE,
499 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
500 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
501 0 : return -EINVAL;
502 7 : }
503 :
504 : int
505 0 : scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb,
506 : uint8_t *data, uint16_t data_len)
507 : {
508 0 : int rc = -1;
509 0 : uint64_t rkey, sa_rkey;
510 0 : uint8_t spec_i_pt, all_tg_pt, aptpl;
511 0 : enum spdk_scsi_pr_out_service_action_code action;
512 0 : enum spdk_scsi_pr_scope_code scope;
513 0 : enum spdk_scsi_pr_type_code rtype;
514 0 : struct spdk_scsi_pr_out_param_list *param = (struct spdk_scsi_pr_out_param_list *)data;
515 :
516 0 : action = cdb[1] & 0x0f;
517 0 : scope = (cdb[2] >> 4) & 0x0f;
518 0 : rtype = cdb[2] & 0x0f;
519 :
520 0 : rkey = from_be64(¶m->rkey);
521 0 : sa_rkey = from_be64(¶m->sa_rkey);
522 0 : aptpl = param->aptpl;
523 0 : spec_i_pt = param->spec_i_pt;
524 0 : all_tg_pt = param->all_tg_pt;
525 :
526 0 : switch (action) {
527 : case SPDK_SCSI_PR_OUT_REGISTER:
528 : case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
529 0 : rc = scsi_pr_out_register(task, action, rkey, sa_rkey,
530 0 : spec_i_pt, all_tg_pt, aptpl);
531 0 : break;
532 : case SPDK_SCSI_PR_OUT_RESERVE:
533 0 : if (scope != SPDK_SCSI_PR_LU_SCOPE) {
534 0 : goto invalid;
535 : }
536 0 : rc = scsi_pr_out_reserve(task, rtype, rkey,
537 0 : spec_i_pt, all_tg_pt, aptpl);
538 0 : break;
539 : case SPDK_SCSI_PR_OUT_RELEASE:
540 0 : if (scope != SPDK_SCSI_PR_LU_SCOPE) {
541 0 : goto invalid;
542 : }
543 0 : rc = scsi_pr_out_release(task, rtype, rkey);
544 0 : break;
545 : case SPDK_SCSI_PR_OUT_CLEAR:
546 0 : rc = scsi_pr_out_clear(task, rkey);
547 0 : break;
548 : case SPDK_SCSI_PR_OUT_PREEMPT:
549 0 : if (scope != SPDK_SCSI_PR_LU_SCOPE) {
550 0 : goto invalid;
551 : }
552 0 : rc = scsi_pr_out_preempt(task, action, rtype, rkey, sa_rkey);
553 0 : break;
554 : default:
555 0 : SPDK_ERRLOG("Invalid service action code %u\n", action);
556 0 : goto invalid;
557 : }
558 :
559 0 : return rc;
560 :
561 : invalid:
562 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
563 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
564 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
565 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
566 0 : return -EINVAL;
567 0 : }
568 :
569 : static int
570 0 : scsi_pr_in_read_keys(struct spdk_scsi_task *task, uint8_t *data,
571 : uint16_t data_len)
572 : {
573 0 : struct spdk_scsi_lun *lun = task->lun;
574 0 : struct spdk_scsi_pr_in_read_keys_data *keys;
575 0 : struct spdk_scsi_pr_registrant *reg, *tmp;
576 0 : uint16_t count = 0;
577 :
578 0 : SPDK_DEBUGLOG(scsi, "PR IN READ KEYS\n");
579 0 : keys = (struct spdk_scsi_pr_in_read_keys_data *)data;
580 :
581 0 : to_be32(&keys->header.pr_generation, lun->pr_generation);
582 0 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
583 0 : if (((count + 1) * 8 + sizeof(keys->header)) > data_len) {
584 0 : break;
585 : }
586 0 : to_be64(&keys->rkeys[count], reg->rkey);
587 0 : count++;
588 0 : }
589 0 : to_be32(&keys->header.additional_len, count * 8);
590 :
591 0 : return (sizeof(keys->header) + count * 8);
592 0 : }
593 :
594 : static int
595 0 : scsi_pr_in_read_reservations(struct spdk_scsi_task *task,
596 : uint8_t *data, uint16_t data_len)
597 : {
598 0 : struct spdk_scsi_lun *lun = task->lun;
599 0 : struct spdk_scsi_pr_in_read_reservations_data *param;
600 0 : bool all_regs = false;
601 :
602 0 : SPDK_DEBUGLOG(scsi, "PR IN READ RESERVATIONS\n");
603 0 : param = (struct spdk_scsi_pr_in_read_reservations_data *)(data);
604 :
605 0 : to_be32(¶m->header.pr_generation, lun->pr_generation);
606 0 : if (scsi_pr_has_reservation(lun)) {
607 0 : all_regs = scsi_pr_is_all_registrants_type(lun);
608 0 : if (all_regs) {
609 0 : to_be64(¶m->rkey, 0);
610 0 : } else {
611 0 : to_be64(¶m->rkey, lun->reservation.crkey);
612 : }
613 0 : to_be32(¶m->header.additional_len, 16);
614 0 : param->scope = SPDK_SCSI_PR_LU_SCOPE;
615 0 : param->type = lun->reservation.rtype;
616 0 : SPDK_DEBUGLOG(scsi, "READ RESERVATIONS with valid reservation\n");
617 0 : return sizeof(*param);
618 : }
619 :
620 : /* no reservation */
621 0 : to_be32(¶m->header.additional_len, 0);
622 0 : SPDK_DEBUGLOG(scsi, "READ RESERVATIONS no reservation\n");
623 0 : return sizeof(param->header);
624 0 : }
625 :
626 : static int
627 0 : scsi_pr_in_report_capabilities(struct spdk_scsi_task *task,
628 : uint8_t *data, uint16_t data_len)
629 : {
630 0 : struct spdk_scsi_pr_in_report_capabilities_data *param;
631 :
632 0 : SPDK_DEBUGLOG(scsi, "PR IN REPORT CAPABILITIES\n");
633 0 : param = (struct spdk_scsi_pr_in_report_capabilities_data *)data;
634 :
635 0 : memset(param, 0, sizeof(*param));
636 0 : to_be16(¶m->length, sizeof(*param));
637 : /* Compatible reservation handling to support RESERVE/RELEASE defined in SPC-2 */
638 0 : param->crh = 1;
639 0 : param->tmv = 1;
640 0 : param->wr_ex = 1;
641 0 : param->ex_ac = 1;
642 0 : param->wr_ex_ro = 1;
643 0 : param->ex_ac_ro = 1;
644 0 : param->wr_ex_ar = 1;
645 0 : param->ex_ac_ar = 1;
646 :
647 0 : return sizeof(*param);
648 0 : }
649 :
650 : static int
651 0 : scsi_pr_in_read_full_status(struct spdk_scsi_task *task,
652 : uint8_t *data, uint16_t data_len)
653 : {
654 0 : struct spdk_scsi_lun *lun = task->lun;
655 0 : struct spdk_scsi_pr_in_full_status_data *param;
656 0 : struct spdk_scsi_pr_in_full_status_desc *desc;
657 0 : struct spdk_scsi_pr_registrant *reg, *tmp;
658 0 : bool all_regs = false;
659 0 : uint32_t add_len = 0;
660 :
661 0 : SPDK_DEBUGLOG(scsi, "PR IN READ FULL STATUS\n");
662 :
663 0 : all_regs = scsi_pr_is_all_registrants_type(lun);
664 0 : param = (struct spdk_scsi_pr_in_full_status_data *)data;
665 0 : to_be32(¶m->header.pr_generation, lun->pr_generation);
666 :
667 0 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
668 0 : desc = (struct spdk_scsi_pr_in_full_status_desc *)
669 0 : ((uint8_t *)param->desc_list + add_len);
670 0 : if (add_len + sizeof(*desc) + sizeof(param->header) > data_len) {
671 0 : break;
672 : }
673 0 : add_len += sizeof(*desc);
674 0 : desc->rkey = reg->rkey;
675 0 : if (all_regs || lun->reservation.holder == reg) {
676 0 : desc->r_holder = true;
677 0 : desc->type = lun->reservation.rtype;
678 0 : } else {
679 0 : desc->r_holder = false;
680 0 : desc->type = 0;
681 : }
682 0 : desc->all_tg_pt = 0;
683 0 : desc->scope = SPDK_SCSI_PR_LU_SCOPE;
684 0 : desc->relative_target_port_id = reg->relative_target_port_id;
685 0 : if (add_len + reg->transport_id_len + sizeof(param->header) > data_len) {
686 0 : break;
687 : }
688 0 : add_len += reg->transport_id_len;
689 0 : memcpy(&desc->transport_id, reg->transport_id, reg->transport_id_len);
690 0 : to_be32(&desc->desc_len, reg->transport_id_len);
691 0 : }
692 0 : to_be32(¶m->header.additional_len, add_len);
693 :
694 0 : return (sizeof(param->header) + add_len);
695 0 : }
696 :
697 : int
698 0 : scsi_pr_in(struct spdk_scsi_task *task, uint8_t *cdb,
699 : uint8_t *data, uint16_t data_len)
700 : {
701 0 : enum spdk_scsi_pr_in_action_code action;
702 0 : int rc = 0;
703 :
704 0 : action = cdb[1] & 0x1f;
705 0 : if (data_len < sizeof(struct spdk_scsi_pr_in_read_header)) {
706 0 : goto invalid;
707 : }
708 :
709 0 : switch (action) {
710 : case SPDK_SCSI_PR_IN_READ_KEYS:
711 0 : rc = scsi_pr_in_read_keys(task, data, data_len);
712 0 : break;
713 : case SPDK_SCSI_PR_IN_READ_RESERVATION:
714 0 : if (data_len < sizeof(struct spdk_scsi_pr_in_read_reservations_data)) {
715 0 : goto invalid;
716 : }
717 0 : rc = scsi_pr_in_read_reservations(task, data, data_len);
718 0 : break;
719 : case SPDK_SCSI_PR_IN_REPORT_CAPABILITIES:
720 0 : rc = scsi_pr_in_report_capabilities(task, data, data_len);
721 0 : break;
722 : case SPDK_SCSI_PR_IN_READ_FULL_STATUS:
723 0 : rc = scsi_pr_in_read_full_status(task, data, data_len);
724 0 : break;
725 : default:
726 0 : goto invalid;
727 : }
728 :
729 0 : return rc;
730 :
731 : invalid:
732 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
733 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
734 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
735 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
736 0 : return -EINVAL;
737 0 : }
738 :
739 : int
740 13 : scsi_pr_check(struct spdk_scsi_task *task)
741 : {
742 13 : struct spdk_scsi_lun *lun = task->lun;
743 13 : uint8_t *cdb = task->cdb;
744 13 : enum spdk_scsi_pr_type_code rtype;
745 13 : enum spdk_scsi_pr_out_service_action_code action;
746 13 : struct spdk_scsi_pr_registrant *reg;
747 13 : bool dma_to_device = false;
748 :
749 : /* no reservation holders */
750 13 : if (!scsi_pr_has_reservation(lun)) {
751 0 : return 0;
752 : }
753 :
754 13 : rtype = lun->reservation.rtype;
755 13 : assert(rtype != 0);
756 :
757 13 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
758 : /* current I_T nexus hold the reservation */
759 13 : if (scsi_pr_registrant_is_holder(lun, reg)) {
760 2 : return 0;
761 : }
762 :
763 : /* reservation is held by other I_T nexus */
764 11 : switch (cdb[0]) {
765 : case SPDK_SPC_INQUIRY:
766 : case SPDK_SPC_REPORT_LUNS:
767 : case SPDK_SPC_REQUEST_SENSE:
768 : case SPDK_SPC_LOG_SENSE:
769 : case SPDK_SPC_TEST_UNIT_READY:
770 : case SPDK_SBC_START_STOP_UNIT:
771 : case SPDK_SBC_READ_CAPACITY_10:
772 : case SPDK_SPC_PERSISTENT_RESERVE_IN:
773 : case SPDK_SPC_SERVICE_ACTION_IN_16:
774 : /* CRH enabled, processed by scsi2_reserve() */
775 : case SPDK_SPC2_RESERVE_6:
776 : case SPDK_SPC2_RESERVE_10:
777 : /* CRH enabled, processed by scsi2_release() */
778 : case SPDK_SPC2_RELEASE_6:
779 : case SPDK_SPC2_RELEASE_10:
780 1 : return 0;
781 : case SPDK_SPC_MODE_SELECT_6:
782 : case SPDK_SPC_MODE_SELECT_10:
783 : case SPDK_SPC_MODE_SENSE_6:
784 : case SPDK_SPC_MODE_SENSE_10:
785 : case SPDK_SPC_LOG_SELECT:
786 : /* I_T nexus is registrant but not holder */
787 0 : if (!reg) {
788 0 : SPDK_DEBUGLOG(scsi, "CHECK: current I_T nexus "
789 : "is not registered, cdb 0x%x\n", cdb[0]);
790 0 : goto conflict;
791 : }
792 0 : return 0;
793 : case SPDK_SPC_PERSISTENT_RESERVE_OUT:
794 0 : action = cdb[1] & 0x1f;
795 0 : SPDK_DEBUGLOG(scsi, "CHECK: PR OUT action %u\n", action);
796 0 : switch (action) {
797 : case SPDK_SCSI_PR_OUT_RELEASE:
798 : case SPDK_SCSI_PR_OUT_CLEAR:
799 : case SPDK_SCSI_PR_OUT_PREEMPT:
800 : case SPDK_SCSI_PR_OUT_PREEMPT_AND_ABORT:
801 0 : if (!reg) {
802 0 : SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
803 0 : goto conflict;
804 : }
805 0 : return 0;
806 : case SPDK_SCSI_PR_OUT_REGISTER:
807 : case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
808 0 : return 0;
809 : case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
810 0 : SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
811 0 : goto conflict;
812 : default:
813 0 : SPDK_ERRLOG("CHECK: PR OUT invalid action %u\n", action);
814 0 : goto conflict;
815 : }
816 :
817 : /* For most SBC R/W commands */
818 : default:
819 10 : break;
820 : }
821 :
822 10 : switch (cdb[0]) {
823 : case SPDK_SBC_READ_6:
824 : case SPDK_SBC_READ_10:
825 : case SPDK_SBC_READ_12:
826 : case SPDK_SBC_READ_16:
827 5 : break;
828 : case SPDK_SBC_WRITE_6:
829 : case SPDK_SBC_WRITE_10:
830 : case SPDK_SBC_WRITE_12:
831 : case SPDK_SBC_WRITE_16:
832 : case SPDK_SBC_UNMAP:
833 : case SPDK_SBC_SYNCHRONIZE_CACHE_10:
834 : case SPDK_SBC_SYNCHRONIZE_CACHE_16:
835 5 : dma_to_device = true;
836 5 : break;
837 : default:
838 0 : SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
839 0 : goto conflict;
840 : }
841 :
842 10 : switch (rtype) {
843 : case SPDK_SCSI_PR_WRITE_EXCLUSIVE:
844 0 : if (dma_to_device) {
845 0 : SPDK_ERRLOG("CHECK: Write Exclusive reservation type "
846 : "rejects command 0x%x\n", cdb[0]);
847 0 : goto conflict;
848 : }
849 0 : break;
850 : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
851 4 : SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
852 : "rejects command 0x%x\n", cdb[0]);
853 4 : goto conflict;
854 : case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
855 : case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
856 4 : if (!reg && dma_to_device) {
857 1 : SPDK_ERRLOG("CHECK: Registrants only reservation "
858 : "type reject command 0x%x\n", cdb[0]);
859 1 : goto conflict;
860 : }
861 3 : break;
862 : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
863 : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
864 2 : if (!reg) {
865 2 : SPDK_ERRLOG("CHECK: All Registrants reservation "
866 : "type reject command 0x%x\n", cdb[0]);
867 2 : goto conflict;
868 : }
869 0 : break;
870 : default:
871 0 : break;
872 : }
873 :
874 3 : return 0;
875 :
876 : conflict:
877 7 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
878 : SPDK_SCSI_SENSE_NO_SENSE,
879 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
880 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
881 7 : return -1;
882 13 : }
883 :
884 : static int
885 7 : scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
886 : {
887 7 : struct spdk_scsi_lun *lun = task->lun;
888 7 : struct spdk_scsi_pr_registrant *reg;
889 7 : bool conflict = false;
890 :
891 7 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
892 7 : if (reg) {
893 : /*
894 : * From spc4r31 5.9.3 Exceptions to SPC-2 RESERVE and RELEASE
895 : * behavior
896 : *
897 : * A RESERVE(6) or RESERVE(10) command shall complete with GOOD
898 : * status, but no reservation shall be established and the
899 : * persistent reservation shall not be changed, if the command
900 : * is received from a) and b) below.
901 : *
902 : * A RELEASE(6) or RELEASE(10) command shall complete with GOOD
903 : * status, but the persistent reservation shall not be released,
904 : * if the command is received from a) and b)
905 : *
906 : * a) An I_T nexus that is a persistent reservation holder; or
907 : * b) An I_T nexus that is registered if a registrants only or
908 : * all registrants type persistent reservation is present.
909 : *
910 : * In all other cases, a RESERVE(6) command, RESERVE(10) command,
911 : * RELEASE(6) command, or RELEASE(10) command shall be processed
912 : * as defined in SPC-2.
913 : */
914 2 : if (scsi_pr_registrant_is_holder(lun, reg)) {
915 0 : return 1;
916 : }
917 :
918 2 : if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
919 0 : lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
920 2 : return 1;
921 : }
922 :
923 0 : conflict = true;
924 0 : } else {
925 : /*
926 : * From spc2r20 5.5.1 Reservations overview:
927 : *
928 : * If a logical unit has executed a PERSISTENT RESERVE OUT
929 : * command with the REGISTER or the REGISTER AND IGNORE
930 : * EXISTING KEY service action and is still registered by any
931 : * initiator, all RESERVE commands and all RELEASE commands
932 : * regardless of initiator shall conflict and shall terminate
933 : * with a RESERVATION CONFLICT status.
934 : */
935 5 : conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
936 : }
937 :
938 5 : if (conflict) {
939 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
940 : SPDK_SCSI_SENSE_NO_SENSE,
941 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
942 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
943 0 : return -1;
944 : }
945 :
946 5 : return 0;
947 7 : }
948 :
949 : int
950 3 : scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
951 : {
952 3 : struct spdk_scsi_lun *lun = task->lun;
953 3 : struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
954 3 : int ret;
955 :
956 : /* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
957 3 : if (cdb[1] & 0x3) {
958 0 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
959 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
960 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
961 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
962 0 : return -1;
963 : }
964 :
965 3 : ret = scsi2_check_reservation_conflict(task);
966 : /* PERSISTENT RESERVE is enabled */
967 3 : if (ret == 1) {
968 1 : return 0;
969 2 : } else if (ret < 0) {
970 0 : return ret;
971 : }
972 :
973 : /* SPC2 RESERVE */
974 2 : reg->initiator_port = task->initiator_port;
975 2 : if (task->initiator_port) {
976 4 : snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
977 2 : task->initiator_port->name);
978 2 : reg->transport_id_len = task->initiator_port->transport_id_len;
979 4 : memcpy(reg->transport_id, task->initiator_port->transport_id,
980 2 : reg->transport_id_len);
981 2 : }
982 2 : reg->target_port = task->target_port;
983 2 : if (task->target_port) {
984 4 : snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
985 2 : task->target_port->name);
986 2 : }
987 :
988 2 : lun->reservation.flags = SCSI_SPC2_RESERVE;
989 2 : lun->reservation.holder = &lun->scsi2_holder;
990 :
991 2 : return 0;
992 3 : }
993 :
994 : int
995 4 : scsi2_release(struct spdk_scsi_task *task)
996 : {
997 4 : struct spdk_scsi_lun *lun = task->lun;
998 4 : int ret;
999 :
1000 4 : ret = scsi2_check_reservation_conflict(task);
1001 : /* PERSISTENT RESERVE is enabled */
1002 4 : if (ret == 1) {
1003 1 : return 0;
1004 3 : } else if (ret < 0) {
1005 0 : return ret;
1006 : }
1007 :
1008 3 : if (!(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
1009 1 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
1010 : SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
1011 : SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
1012 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
1013 1 : return -EINVAL;
1014 : }
1015 :
1016 2 : memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
1017 2 : memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
1018 :
1019 2 : return 0;
1020 4 : }
1021 :
1022 : int
1023 3 : scsi2_reserve_check(struct spdk_scsi_task *task)
1024 : {
1025 3 : struct spdk_scsi_lun *lun = task->lun;
1026 3 : uint8_t *cdb = task->cdb;
1027 :
1028 3 : switch (cdb[0]) {
1029 : case SPDK_SPC_INQUIRY:
1030 : case SPDK_SPC2_RELEASE_6:
1031 : case SPDK_SPC2_RELEASE_10:
1032 1 : return 0;
1033 :
1034 : default:
1035 2 : break;
1036 : }
1037 :
1038 : /* no reservation holders */
1039 2 : if (!scsi_pr_has_reservation(lun)) {
1040 0 : return 0;
1041 : }
1042 :
1043 2 : if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
1044 1 : return 0;
1045 : }
1046 :
1047 1 : spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
1048 : SPDK_SCSI_SENSE_NO_SENSE,
1049 : SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
1050 : SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
1051 1 : return -1;
1052 3 : }
|