Branch data 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 : 540 : 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 [ + + ]: 916 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
19 [ + + ]: 740 : if (initiator_port == reg->initiator_port &&
20 [ + - ]: 364 : target_port == reg->target_port) {
21 : 364 : return reg;
22 : : }
23 : : }
24 : :
25 : 176 : return NULL;
26 : : }
27 : :
28 : : static bool
29 : 8 : 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 : 8 : struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
34 : :
35 [ - + ]: 8 : assert(reg != NULL);
36 : :
37 [ + + ]: 8 : if ((reg->initiator_port == initiator_port) &&
38 [ + - ]: 4 : (reg->target_port == target_port)) {
39 : 4 : return true;
40 : : }
41 : :
42 : 4 : return false;
43 : : }
44 : :
45 : : /* Reservation type is all registrants or not */
46 : : static inline bool
47 : 156 : scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
48 : : {
49 [ + + ]: 264 : return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
50 [ + + ]: 108 : lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
51 : : }
52 : :
53 : : /* Registrant is reservation holder or not */
54 : : static inline bool
55 : 124 : scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
56 : : struct spdk_scsi_pr_registrant *reg)
57 : : {
58 [ + + + + ]: 124 : if (reg != NULL && scsi_pr_is_all_registrants_type(lun)) {
59 : 36 : return true;
60 : : }
61 : :
62 : 88 : return (lun->reservation.holder == reg);
63 : : }
64 : :
65 : : /* LUN holds a reservation or not */
66 : : static inline bool
67 : 25509809 : scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
68 : : {
69 : 25509809 : return !(lun->reservation.holder == NULL);
70 : : }
71 : :
72 : : static int
73 : 96 : 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 : 96 : reg = calloc(1, sizeof(*reg));
82 [ - + ]: 96 : if (!reg) {
83 : 0 : return -ENOMEM;
84 : : }
85 : :
86 [ - + - + ]: 96 : SPDK_DEBUGLOG(scsi, "REGISTER: new registrant registered "
87 : : "with key 0x%"PRIx64"\n", sa_rkey);
88 : :
89 : : /* New I_T nexus */
90 : 96 : reg->initiator_port = initiator_port;
91 [ + - ]: 96 : if (initiator_port) {
92 [ - + ]: 96 : snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
93 : 96 : initiator_port->name);
94 : 96 : reg->transport_id_len = initiator_port->transport_id_len;
95 [ - + - + ]: 96 : memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
96 : : }
97 : 96 : reg->target_port = target_port;
98 [ + - ]: 96 : if (target_port) {
99 [ - + ]: 96 : snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
100 : 96 : target_port->name);
101 : 96 : reg->relative_target_port_id = target_port->index;
102 : : }
103 : 96 : reg->rkey = sa_rkey;
104 : 96 : TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
105 : 96 : lun->pr_generation++;
106 : :
107 : 96 : return 0;
108 : : }
109 : :
110 : : static void
111 : 32 : scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
112 : : {
113 : 32 : bool all_regs = false;
114 : : struct spdk_scsi_pr_registrant *curr_reg, *tmp;
115 : :
116 [ - + - + ]: 32 : SPDK_DEBUGLOG(scsi, "REGISTER: release reservation "
117 : : "with type %u\n", lun->reservation.rtype);
118 : :
119 : : /* TODO: Unit Attention */
120 : 32 : all_regs = scsi_pr_is_all_registrants_type(lun);
121 [ + + ]: 32 : if (all_regs) {
122 [ + + ]: 32 : TAILQ_FOREACH_SAFE(curr_reg, &lun->reg_head, link, tmp) {
123 [ + + ]: 28 : if (curr_reg != reg) {
124 : 20 : lun->reservation.holder = curr_reg;
125 : 20 : lun->reservation.crkey = curr_reg->rkey;
126 : 20 : return;
127 : : }
128 : : }
129 : : }
130 : :
131 [ - + ]: 12 : memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
132 : : }
133 : :
134 : : static void
135 : 48 : 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 : 48 : lun->reservation.rtype = type;
141 : 48 : lun->reservation.crkey = rkey;
142 : 48 : lun->reservation.holder = holder;
143 : 48 : }
144 : :
145 : : static void
146 : 36 : scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
147 : : struct spdk_scsi_pr_registrant *reg)
148 : : {
149 [ - + - + ]: 36 : SPDK_DEBUGLOG(scsi, "REGISTER: unregister registrant\n");
150 : :
151 [ + + ]: 36 : TAILQ_REMOVE(&lun->reg_head, reg, link);
152 [ + + ]: 36 : if (scsi_pr_registrant_is_holder(lun, reg)) {
153 : 24 : scsi_pr_release_reservation(lun, reg);
154 : : }
155 : :
156 : 36 : free(reg);
157 : 36 : lun->pr_generation++;
158 : 36 : }
159 : :
160 : : static void
161 : 32 : scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
162 : : struct spdk_scsi_pr_registrant *reg,
163 : : uint64_t sa_rkey)
164 : : {
165 [ - + - + ]: 32 : SPDK_DEBUGLOG(scsi, "REGISTER: replace with new "
166 : : "reservation key 0x%"PRIx64"\n", sa_rkey);
167 : 32 : reg->rkey = sa_rkey;
168 : 32 : lun->pr_generation++;
169 : 32 : }
170 : :
171 : : static int
172 : 44 : 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 : 44 : struct spdk_scsi_lun *lun = task->lun;
177 : : struct spdk_scsi_pr_registrant *reg;
178 : :
179 [ - + - + ]: 44 : 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 [ + - + - : 44 : 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 : 44 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
194 : : /* No registration for the I_T nexus */
195 [ - + ]: 44 : if (!reg) {
196 : 0 : SPDK_ERRLOG("No registration\n");
197 : 0 : goto conflict;
198 : : }
199 : :
200 : : /* invalid reservation key */
201 [ - + ]: 44 : 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 [ + + ]: 44 : if (scsi_pr_has_reservation(lun)) {
209 [ + + ]: 12 : if (rtype != lun->reservation.rtype) {
210 : 4 : SPDK_ERRLOG("Reservation type doesn't match\n");
211 : 4 : goto conflict;
212 : : }
213 : :
214 [ + + ]: 8 : if (!scsi_pr_registrant_is_holder(lun, reg)) {
215 : 4 : SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
216 : 4 : goto conflict;
217 : : }
218 : : } else {
219 : : /* current I_T nexus is the first reservation holder */
220 : 32 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
221 : : }
222 : :
223 : 36 : return 0;
224 : :
225 : 8 : conflict:
226 : 8 : 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 : 8 : return -EINVAL;
231 : : }
232 : :
233 : : static int
234 : 180 : 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 : 180 : struct spdk_scsi_lun *lun = task->lun;
240 : : struct spdk_scsi_pr_registrant *reg;
241 : : int sc, sk, asc;
242 : :
243 [ - + - + ]: 180 : 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 [ + - + - : 180 : 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 : 180 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
256 : : /* an unregistered I_T nexus session */
257 [ + + ]: 180 : if (!reg) {
258 [ - + - - ]: 96 : 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 [ - + ]: 96 : 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 : 96 : return scsi_pr_register_registrant(lun, task->initiator_port,
274 : : task->target_port, sa_rkey);
275 : : } else {
276 : : /* a registered I_T nexus */
277 [ + + + - ]: 84 : if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
278 : 32 : SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
279 : : "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
280 : 32 : sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
281 : 32 : sk = SPDK_SCSI_SENSE_NO_SENSE;
282 : 32 : asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
283 : 32 : goto error_exit;
284 : : }
285 : :
286 [ + + ]: 52 : if (!sa_rkey) {
287 : : /* unregister */
288 : 20 : scsi_pr_unregister_registrant(lun, reg);
289 : : } else {
290 : : /* replace */
291 : 32 : scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
292 : : }
293 : : }
294 : :
295 : 52 : return 0;
296 : :
297 : 32 : error_exit:
298 : 32 : spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
299 : 32 : return -EINVAL;
300 : : }
301 : :
302 : : static int
303 : 8 : scsi_pr_out_release(struct spdk_scsi_task *task,
304 : : enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
305 : : {
306 : 8 : struct spdk_scsi_lun *lun = task->lun;
307 : : struct spdk_scsi_pr_registrant *reg;
308 : : int sk, asc;
309 : :
310 [ - + - + ]: 8 : SPDK_DEBUGLOG(scsi, "PR OUT RELEASE: rkey 0x%"PRIx64", "
311 : : "reservation type %u\n", rkey, rtype);
312 : :
313 : 8 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
314 [ - + ]: 8 : 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 [ - + ]: 8 : if (!scsi_pr_has_reservation(lun)) {
323 [ # # # # ]: 0 : SPDK_DEBUGLOG(scsi, "RELEASE: no reservation holder\n");
324 : 0 : return 0;
325 : : }
326 : :
327 [ + - - + ]: 8 : 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 [ - + ]: 8 : 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 : 8 : scsi_pr_release_reservation(lun, reg);
340 : :
341 : 8 : return 0;
342 : :
343 : 0 : 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 : : }
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 : : struct spdk_scsi_pr_registrant *reg, *tmp;
354 : : 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 : : }
379 : :
380 : 0 : return 0;
381 : :
382 : 0 : error_exit:
383 : 0 : spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
384 : 0 : return -EINVAL;
385 : : }
386 : :
387 : : static void
388 : 12 : scsi_pr_remove_all_regs_by_key(struct spdk_scsi_lun *lun, uint64_t sa_rkey)
389 : : {
390 : : struct spdk_scsi_pr_registrant *reg, *tmp;
391 : :
392 [ + + ]: 44 : TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
393 [ + + ]: 32 : if (reg->rkey == sa_rkey) {
394 : 12 : scsi_pr_unregister_registrant(lun, reg);
395 : : }
396 : : }
397 : 12 : }
398 : :
399 : : static void
400 : 4 : scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
401 : : {
402 : : struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
403 : :
404 [ + + ]: 12 : TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
405 [ + + ]: 8 : if (reg_tmp != reg) {
406 : 4 : scsi_pr_unregister_registrant(lun, reg_tmp);
407 : : }
408 : : }
409 : 4 : }
410 : :
411 : : static int
412 : 28 : 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 : 28 : struct spdk_scsi_lun *lun = task->lun;
418 : : struct spdk_scsi_pr_registrant *reg;
419 : 28 : bool all_regs = false;
420 : :
421 [ - + - + ]: 28 : 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 : 28 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
427 [ - + ]: 28 : if (!reg) {
428 : 0 : SPDK_ERRLOG("No registration\n");
429 : 0 : goto conflict;
430 : : }
431 [ - + ]: 28 : 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 [ + + ]: 28 : if (!scsi_pr_has_reservation(lun)) {
439 : 4 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
440 [ - + - + ]: 4 : SPDK_DEBUGLOG(scsi, "PREEMPT: no persistent reservation\n");
441 : 4 : goto exit;
442 : : }
443 : :
444 : 24 : all_regs = scsi_pr_is_all_registrants_type(lun);
445 : :
446 [ + + ]: 24 : if (all_regs) {
447 [ - + ]: 4 : 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 : : } else {
451 : : /* remove all other registrants and release persistent reservation if any */
452 : 4 : scsi_pr_remove_all_other_regs(lun, reg);
453 : : /* create persistent reservation using new type and scope */
454 : 4 : scsi_pr_reserve_reservation(lun, rtype, 0, reg);
455 [ - + - + ]: 4 : SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey zeroed\n");
456 : : }
457 : 4 : goto exit;
458 : : }
459 : :
460 [ - + ]: 20 : assert(lun->reservation.crkey != 0);
461 : :
462 [ + + ]: 20 : if (sa_rkey != lun->reservation.crkey) {
463 [ + + ]: 8 : if (!sa_rkey) {
464 : 4 : SPDK_ERRLOG("Zeroed sa_rkey\n");
465 : 4 : 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 : 4 : return -EINVAL;
470 : : }
471 : 4 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
472 : 4 : goto exit;
473 : : }
474 : :
475 [ + + ]: 12 : if (scsi_pr_registrant_is_holder(lun, reg)) {
476 : 8 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
477 [ - + - + ]: 8 : SPDK_DEBUGLOG(scsi, "PREEMPT: preempt itself with type %u\n", rtype);
478 : 8 : goto exit;
479 : : }
480 : :
481 : : /* unregister registrants if any */
482 : 4 : scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
483 : 4 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
484 [ - + ]: 4 : 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 : 4 : scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
491 : :
492 : 24 : exit:
493 : 24 : lun->pr_generation++;
494 : 24 : return 0;
495 : :
496 : 0 : 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 : : }
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 : : uint64_t rkey, sa_rkey;
510 : : uint8_t spec_i_pt, all_tg_pt, aptpl;
511 : : enum spdk_scsi_pr_out_service_action_code action;
512 : : enum spdk_scsi_pr_scope_code scope;
513 : : 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 : 0 : 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 : : spec_i_pt, all_tg_pt, aptpl);
531 : 0 : break;
532 : 0 : 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 : : spec_i_pt, all_tg_pt, aptpl);
538 : 0 : break;
539 : 0 : 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 : 0 : case SPDK_SCSI_PR_OUT_CLEAR:
546 : 0 : rc = scsi_pr_out_clear(task, rkey);
547 : 0 : break;
548 : 0 : 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 : 0 : default:
555 : 0 : SPDK_ERRLOG("Invalid service action code %u\n", action);
556 : 0 : goto invalid;
557 : : }
558 : :
559 : 0 : return rc;
560 : :
561 : 0 : 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 : : }
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 : : struct spdk_scsi_pr_in_read_keys_data *keys;
575 : : 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 : : }
589 : 0 : to_be32(&keys->header.additional_len, count * 8);
590 : :
591 : 0 : return (sizeof(keys->header) + count * 8);
592 : : }
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 : : 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 : : } 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 : : }
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 : : 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 : : }
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 : : struct spdk_scsi_pr_in_full_status_data *param;
656 : : struct spdk_scsi_pr_in_full_status_desc *desc;
657 : : 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 : : } 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 : : }
692 : 0 : to_be32(¶m->header.additional_len, add_len);
693 : :
694 : 0 : return (sizeof(param->header) + add_len);
695 : : }
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 : : 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 : 0 : case SPDK_SCSI_PR_IN_READ_KEYS:
711 : 0 : rc = scsi_pr_in_read_keys(task, data, data_len);
712 : 0 : break;
713 : 0 : 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 : 0 : case SPDK_SCSI_PR_IN_REPORT_CAPABILITIES:
720 : 0 : rc = scsi_pr_in_report_capabilities(task, data, data_len);
721 : 0 : break;
722 : 0 : 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 : 0 : default:
726 : 0 : goto invalid;
727 : : }
728 : :
729 : 0 : return rc;
730 : :
731 : 0 : 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 : : }
738 : :
739 : : int
740 : 25509721 : scsi_pr_check(struct spdk_scsi_task *task)
741 : : {
742 : 25509721 : struct spdk_scsi_lun *lun = task->lun;
743 : 25509721 : uint8_t *cdb = task->cdb;
744 : : enum spdk_scsi_pr_type_code rtype;
745 : : enum spdk_scsi_pr_out_service_action_code action;
746 : : struct spdk_scsi_pr_registrant *reg;
747 : 25509721 : bool dma_to_device = false;
748 : :
749 : : /* no reservation holders */
750 [ + + ]: 25509721 : if (!scsi_pr_has_reservation(lun)) {
751 : 25509669 : return 0;
752 : : }
753 : :
754 : 52 : rtype = lun->reservation.rtype;
755 [ - + ]: 52 : assert(rtype != 0);
756 : :
757 : 52 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
758 : : /* current I_T nexus hold the reservation */
759 [ + + ]: 52 : if (scsi_pr_registrant_is_holder(lun, reg)) {
760 : 8 : return 0;
761 : : }
762 : :
763 : : /* reservation is held by other I_T nexus */
764 [ + - - + ]: 44 : switch (cdb[0]) {
765 : 4 : 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 : 4 : return 0;
781 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : case SPDK_SCSI_PR_OUT_REGISTER:
807 : : case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
808 : 0 : return 0;
809 : 0 : case SPDK_SCSI_PR_OUT_REG_AND_MOVE:
810 : 0 : SPDK_ERRLOG("CHECK: PR OUT action %u\n", action);
811 : 0 : goto conflict;
812 : 0 : 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 : 40 : default:
819 : 40 : break;
820 : : }
821 : :
822 [ + + - ]: 40 : switch (cdb[0]) {
823 : 20 : case SPDK_SBC_READ_6:
824 : : case SPDK_SBC_READ_10:
825 : : case SPDK_SBC_READ_12:
826 : : case SPDK_SBC_READ_16:
827 : 20 : break;
828 : 20 : 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 : 20 : dma_to_device = true;
836 : 20 : break;
837 : 0 : default:
838 : 0 : SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
839 : 0 : goto conflict;
840 : : }
841 : :
842 [ - + + + : 40 : switch (rtype) {
- ]
843 : 0 : 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 : 16 : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
851 : 16 : SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
852 : : "rejects command 0x%x\n", cdb[0]);
853 : 16 : goto conflict;
854 : 16 : case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
855 : : case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
856 [ + + + + ]: 16 : if (!reg && dma_to_device) {
857 : 4 : SPDK_ERRLOG("CHECK: Registrants only reservation "
858 : : "type reject command 0x%x\n", cdb[0]);
859 : 4 : goto conflict;
860 : : }
861 : 12 : break;
862 : 8 : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
863 : : case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
864 [ + - ]: 8 : if (!reg) {
865 : 8 : SPDK_ERRLOG("CHECK: All Registrants reservation "
866 : : "type reject command 0x%x\n", cdb[0]);
867 : 8 : goto conflict;
868 : : }
869 : 0 : break;
870 : 0 : default:
871 : 0 : break;
872 : : }
873 : :
874 : 12 : return 0;
875 : :
876 : 28 : conflict:
877 : 28 : 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 : 28 : return -1;
882 : : }
883 : :
884 : : static int
885 : 28 : scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
886 : : {
887 : 28 : struct spdk_scsi_lun *lun = task->lun;
888 : : struct spdk_scsi_pr_registrant *reg;
889 : 28 : bool conflict = false;
890 : :
891 : 28 : reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
892 [ + + ]: 28 : 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 [ - + ]: 8 : if (scsi_pr_registrant_is_holder(lun, reg)) {
915 : 0 : return 1;
916 : : }
917 : :
918 [ - + ]: 8 : if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
919 [ # # ]: 0 : lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
920 : 8 : return 1;
921 : : }
922 : :
923 : 0 : conflict = true;
924 : : } 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 : 20 : conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
936 : : }
937 : :
938 [ - + ]: 20 : 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 : 20 : return 0;
947 : : }
948 : :
949 : : int
950 : 12 : scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
951 : : {
952 : 12 : struct spdk_scsi_lun *lun = task->lun;
953 : 12 : struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
954 : : int ret;
955 : :
956 : : /* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
957 [ - + ]: 12 : 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 : 12 : ret = scsi2_check_reservation_conflict(task);
966 : : /* PERSISTENT RESERVE is enabled */
967 [ + + ]: 12 : if (ret == 1) {
968 : 4 : return 0;
969 [ - + ]: 8 : } else if (ret < 0) {
970 : 0 : return ret;
971 : : }
972 : :
973 : : /* SPC2 RESERVE */
974 : 8 : reg->initiator_port = task->initiator_port;
975 [ + - ]: 8 : if (task->initiator_port) {
976 [ - + ]: 8 : snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
977 : 8 : task->initiator_port->name);
978 : 8 : reg->transport_id_len = task->initiator_port->transport_id_len;
979 [ - + - + ]: 8 : memcpy(reg->transport_id, task->initiator_port->transport_id,
980 : 8 : reg->transport_id_len);
981 : : }
982 : 8 : reg->target_port = task->target_port;
983 [ + - ]: 8 : if (task->target_port) {
984 [ - + ]: 8 : snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
985 : 8 : task->target_port->name);
986 : : }
987 : :
988 : 8 : lun->reservation.flags = SCSI_SPC2_RESERVE;
989 : 8 : lun->reservation.holder = &lun->scsi2_holder;
990 : :
991 : 8 : return 0;
992 : : }
993 : :
994 : : int
995 : 16 : scsi2_release(struct spdk_scsi_task *task)
996 : : {
997 : 16 : struct spdk_scsi_lun *lun = task->lun;
998 : : int ret;
999 : :
1000 : 16 : ret = scsi2_check_reservation_conflict(task);
1001 : : /* PERSISTENT RESERVE is enabled */
1002 [ + + ]: 16 : if (ret == 1) {
1003 : 4 : return 0;
1004 [ - + ]: 12 : } else if (ret < 0) {
1005 : 0 : return ret;
1006 : : }
1007 : :
1008 [ + + ]: 12 : if (!(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
1009 : 4 : 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 : 4 : return -EINVAL;
1014 : : }
1015 : :
1016 [ - + ]: 8 : memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
1017 [ - + ]: 8 : memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
1018 : :
1019 : 8 : return 0;
1020 : : }
1021 : :
1022 : : int
1023 : 12 : scsi2_reserve_check(struct spdk_scsi_task *task)
1024 : : {
1025 : 12 : struct spdk_scsi_lun *lun = task->lun;
1026 : 12 : uint8_t *cdb = task->cdb;
1027 : :
1028 [ + + ]: 12 : switch (cdb[0]) {
1029 : 4 : case SPDK_SPC_INQUIRY:
1030 : : case SPDK_SPC2_RELEASE_6:
1031 : : case SPDK_SPC2_RELEASE_10:
1032 : 4 : return 0;
1033 : :
1034 : 8 : default:
1035 : 8 : break;
1036 : : }
1037 : :
1038 : : /* no reservation holders */
1039 [ - + ]: 8 : if (!scsi_pr_has_reservation(lun)) {
1040 : 0 : return 0;
1041 : : }
1042 : :
1043 [ + + ]: 8 : if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
1044 : 4 : return 0;
1045 : : }
1046 : :
1047 : 4 : 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 : 4 : return -1;
1052 : : }
|