LCOV - code coverage report
Current view: top level - lib/scsi - scsi_pr.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 246 514 47.9 %
Date: 2024-07-15 14:06:47 Functions: 20 28 71.4 %

          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(&param->rkey);
     515           0 :         sa_rkey = from_be64(&param->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(&param->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(&param->rkey, 0);
     604             :                 } else {
     605           0 :                         to_be64(&param->rkey, lun->reservation.crkey);
     606             :                 }
     607           0 :                 to_be32(&param->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(&param->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(&param->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(&param->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(&param->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             : }

Generated by: LCOV version 1.15