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