LCOV - code coverage report
Current view: top level - lib/scsi - scsi_pr.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 264 517 51.1 %
Date: 2024-12-08 02:23:54 Functions: 21 28 75.0 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2019 Intel Corporation.
       3             :  *   All rights reserved.
       4             :  */
       5             : 
       6             : #include "scsi_internal.h"
       7             : 
       8             : #include "spdk/endian.h"
       9             : 
      10             : /* Get registrant by I_T nexus */
      11             : static struct spdk_scsi_pr_registrant *
      12         135 : scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
      13             :                        struct spdk_scsi_port *initiator_port,
      14             :                        struct spdk_scsi_port *target_port)
      15             : {
      16             :         struct spdk_scsi_pr_registrant *reg, *tmp;
      17             : 
      18         229 :         TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
      19         185 :                 if (initiator_port == reg->initiator_port &&
      20          91 :                     target_port == reg->target_port) {
      21          91 :                         return reg;
      22             :                 }
      23             :         }
      24             : 
      25          44 :         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          39 : scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
      48             : {
      49          66 :         return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
      50          27 :                 lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
      51             : }
      52             : 
      53             : /* Registrant is reservation holder or not */
      54             : static inline bool
      55          31 : scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
      56             :                              struct spdk_scsi_pr_registrant *reg)
      57             : {
      58          31 :         if (reg != NULL && scsi_pr_is_all_registrants_type(lun)) {
      59           9 :                 return true;
      60             :         }
      61             : 
      62          22 :         return (lun->reservation.holder == reg);
      63             : }
      64             : 
      65             : /* LUN holds a reservation or not */
      66             : static inline bool
      67          35 : scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
      68             : {
      69          35 :         return !(lun->reservation.holder == NULL);
      70             : }
      71             : 
      72             : static int
      73          24 : scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
      74             :                             struct spdk_scsi_port *initiator_port,
      75             :                             struct spdk_scsi_port *target_port,
      76             :                             uint64_t sa_rkey)
      77             : {
      78             :         struct spdk_scsi_pr_registrant *reg;
      79             : 
      80             :         /* Register sa_rkey with the I_T nexus */
      81          24 :         reg = calloc(1, sizeof(*reg));
      82          24 :         if (!reg) {
      83           0 :                 return -ENOMEM;
      84             :         }
      85             : 
      86          24 :         SPDK_DEBUGLOG(scsi, "REGISTER: new registrant registered "
      87             :                       "with key 0x%"PRIx64"\n", sa_rkey);
      88             : 
      89             :         /* New I_T nexus */
      90          24 :         reg->initiator_port = initiator_port;
      91          24 :         if (initiator_port) {
      92          24 :                 snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
      93          24 :                          initiator_port->name);
      94          24 :                 reg->transport_id_len = initiator_port->transport_id_len;
      95          24 :                 memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
      96             :         }
      97          24 :         reg->target_port = target_port;
      98          24 :         if (target_port) {
      99          24 :                 snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
     100          24 :                          target_port->name);
     101          24 :                 reg->relative_target_port_id = target_port->index;
     102             :         }
     103          24 :         reg->rkey = sa_rkey;
     104          24 :         TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
     105          24 :         lun->pr_generation++;
     106             : 
     107          24 :         return 0;
     108             : }
     109             : 
     110             : static void
     111           8 : scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
     112             : {
     113           8 :         bool all_regs = false;
     114             :         struct spdk_scsi_pr_registrant *curr_reg, *tmp;
     115             : 
     116           8 :         SPDK_DEBUGLOG(scsi, "REGISTER: release reservation "
     117             :                       "with type %u\n", lun->reservation.rtype);
     118             : 
     119             :         /* TODO: Unit Attention */
     120           8 :         all_regs = scsi_pr_is_all_registrants_type(lun);
     121           8 :         if (all_regs) {
     122           8 :                 TAILQ_FOREACH_SAFE(curr_reg, &lun->reg_head, link, tmp) {
     123           7 :                         if (curr_reg != reg) {
     124           5 :                                 lun->reservation.holder = curr_reg;
     125           5 :                                 lun->reservation.crkey = curr_reg->rkey;
     126           5 :                                 return;
     127             :                         }
     128             :                 }
     129             :         }
     130             : 
     131           3 :         memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
     132             : }
     133             : 
     134             : static void
     135          12 : scsi_pr_reserve_reservation(struct spdk_scsi_lun *lun,
     136             :                             enum spdk_scsi_pr_type_code type,
     137             :                             uint64_t rkey,
     138             :                             struct spdk_scsi_pr_registrant *holder)
     139             : {
     140          12 :         lun->reservation.rtype = type;
     141          12 :         lun->reservation.crkey = rkey;
     142          12 :         lun->reservation.holder = holder;
     143          12 : }
     144             : 
     145             : static void
     146           9 : scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
     147             :                               struct spdk_scsi_pr_registrant *reg)
     148             : {
     149           9 :         SPDK_DEBUGLOG(scsi, "REGISTER: unregister registrant\n");
     150             : 
     151           9 :         TAILQ_REMOVE(&lun->reg_head, reg, link);
     152           9 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     153           6 :                 scsi_pr_release_reservation(lun, reg);
     154             :         }
     155             : 
     156           9 :         free(reg);
     157           9 :         lun->pr_generation++;
     158           9 : }
     159             : 
     160             : static void
     161           8 : scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
     162             :                                struct spdk_scsi_pr_registrant *reg,
     163             :                                uint64_t sa_rkey)
     164             : {
     165           8 :         SPDK_DEBUGLOG(scsi, "REGISTER: replace with new "
     166             :                       "reservation key 0x%"PRIx64"\n", sa_rkey);
     167           8 :         reg->rkey = sa_rkey;
     168           8 :         lun->pr_generation++;
     169           8 : }
     170             : 
     171             : static int
     172          11 : scsi_pr_out_reserve(struct spdk_scsi_task *task,
     173             :                     enum spdk_scsi_pr_type_code rtype, uint64_t rkey,
     174             :                     uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
     175             : {
     176          11 :         struct spdk_scsi_lun *lun = task->lun;
     177             :         struct spdk_scsi_pr_registrant *reg;
     178             : 
     179          11 :         SPDK_DEBUGLOG(scsi, "PR OUT RESERVE: rkey 0x%"PRIx64", requested "
     180             :                       "reservation type %u, type %u\n", rkey, rtype, lun->reservation.rtype);
     181             : 
     182             :         /* TODO: don't support now */
     183          11 :         if (spec_i_pt || all_tg_pt || aptpl) {
     184           0 :                 SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt fields "
     185             :                             "or invalid aptpl field\n");
     186           0 :                 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
     187             :                                           SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
     188             :                                           SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
     189             :                                           SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     190           0 :                 return -EINVAL;
     191             :         }
     192             : 
     193          11 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     194             :         /* No registration for the I_T nexus */
     195          11 :         if (!reg) {
     196           0 :                 SPDK_ERRLOG("No registration\n");
     197           0 :                 goto conflict;
     198             :         }
     199             : 
     200             :         /* invalid reservation key */
     201          11 :         if (reg->rkey != rkey) {
     202           0 :                 SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match 0x%"PRIx64"\n",
     203             :                             rkey, reg->rkey);
     204           0 :                 goto conflict;
     205             :         }
     206             : 
     207             :         /* reservation holder already exists */
     208          11 :         if (scsi_pr_has_reservation(lun)) {
     209           3 :                 if (rtype != lun->reservation.rtype) {
     210           1 :                         SPDK_ERRLOG("Reservation type doesn't match\n");
     211           1 :                         goto conflict;
     212             :                 }
     213             : 
     214           2 :                 if (!scsi_pr_registrant_is_holder(lun, reg)) {
     215           1 :                         SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
     216           1 :                         goto conflict;
     217             :                 }
     218             :         } else {
     219             :                 /* current I_T nexus is the first reservation holder */
     220           8 :                 scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     221             :         }
     222             : 
     223           9 :         return 0;
     224             : 
     225           2 : conflict:
     226           2 :         spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
     227             :                                   SPDK_SCSI_SENSE_NO_SENSE,
     228             :                                   SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
     229             :                                   SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     230           2 :         return -EINVAL;
     231             : }
     232             : 
     233             : static int
     234          45 : scsi_pr_out_register(struct spdk_scsi_task *task,
     235             :                      enum spdk_scsi_pr_out_service_action_code action,
     236             :                      uint64_t rkey, uint64_t sa_rkey,
     237             :                      uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
     238             : {
     239          45 :         struct spdk_scsi_lun *lun = task->lun;
     240             :         struct spdk_scsi_pr_registrant *reg;
     241             :         int sc, sk, asc;
     242             : 
     243          45 :         SPDK_DEBUGLOG(scsi, "PR OUT REGISTER: rkey 0x%"PRIx64", "
     244             :                       "sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
     245             : 
     246             :         /* TODO: don't support now */
     247          45 :         if (spec_i_pt || all_tg_pt || aptpl) {
     248           0 :                 SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
     249           0 :                 sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
     250           0 :                 sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
     251           0 :                 asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
     252           0 :                 goto error_exit;
     253             :         }
     254             : 
     255          45 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     256             :         /* an unregistered I_T nexus session */
     257          45 :         if (!reg) {
     258          24 :                 if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
     259           0 :                         SPDK_ERRLOG("Reservation key field is not empty\n");
     260           0 :                         sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
     261           0 :                         sk = SPDK_SCSI_SENSE_NO_SENSE;
     262           0 :                         asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
     263           0 :                         goto error_exit;
     264             :                 }
     265             : 
     266          24 :                 if (!sa_rkey) {
     267             :                         /* Do nothing except return GOOD status */
     268           0 :                         SPDK_DEBUGLOG(scsi, "REGISTER: service action "
     269             :                                       "reservation key is zero, do noting\n");
     270           0 :                         return 0;
     271             :                 }
     272             :                 /* Add a new registrant for the I_T nexus */
     273          24 :                 return scsi_pr_register_registrant(lun, task->initiator_port,
     274             :                                                    task->target_port, sa_rkey);
     275             :         } else {
     276             :                 /* a registered I_T nexus */
     277          21 :                 if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
     278           8 :                         SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
     279             :                                     "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
     280           8 :                         sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
     281           8 :                         sk = SPDK_SCSI_SENSE_NO_SENSE;
     282           8 :                         asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
     283           8 :                         goto error_exit;
     284             :                 }
     285             : 
     286          13 :                 if (!sa_rkey) {
     287             :                         /* unregister */
     288           5 :                         scsi_pr_unregister_registrant(lun, reg);
     289             :                 } else {
     290             :                         /* replace */
     291           8 :                         scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
     292             :                 }
     293             :         }
     294             : 
     295          13 :         return 0;
     296             : 
     297           8 : error_exit:
     298           8 :         spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
     299           8 :         return -EINVAL;
     300             : }
     301             : 
     302             : static int
     303           2 : scsi_pr_out_release(struct spdk_scsi_task *task,
     304             :                     enum spdk_scsi_pr_type_code rtype, uint64_t rkey)
     305             : {
     306           2 :         struct spdk_scsi_lun *lun = task->lun;
     307             :         struct spdk_scsi_pr_registrant *reg;
     308             :         int sk, asc;
     309             : 
     310           2 :         SPDK_DEBUGLOG(scsi, "PR OUT RELEASE: rkey 0x%"PRIx64", "
     311             :                       "reservation type %u\n", rkey, rtype);
     312             : 
     313           2 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     314           2 :         if (!reg) {
     315           0 :                 SPDK_ERRLOG("No registration\n");
     316           0 :                 sk = SPDK_SCSI_SENSE_NOT_READY;
     317           0 :                 asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
     318           0 :                 goto check_condition;
     319             :         }
     320             : 
     321             :         /* no reservation holder */
     322           2 :         if (!scsi_pr_has_reservation(lun)) {
     323           0 :                 SPDK_DEBUGLOG(scsi, "RELEASE: no reservation holder\n");
     324           0 :                 return 0;
     325             :         }
     326             : 
     327           2 :         if (lun->reservation.rtype != rtype || rkey != lun->reservation.crkey) {
     328           0 :                 sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
     329           0 :                 asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
     330           0 :                 goto check_condition;
     331             :         }
     332             : 
     333             :         /* I_T nexus is not a persistent reservation holder */
     334           2 :         if (!scsi_pr_registrant_is_holder(lun, reg)) {
     335           0 :                 SPDK_DEBUGLOG(scsi, "RELEASE: current I_T nexus is not holder\n");
     336           0 :                 return 0;
     337             :         }
     338             : 
     339           2 :         scsi_pr_release_reservation(lun, reg);
     340             : 
     341           2 :         return 0;
     342             : 
     343           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           3 : 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          11 :         TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
     393           8 :                 if (reg->rkey == sa_rkey) {
     394           3 :                         scsi_pr_unregister_registrant(lun, reg);
     395             :                 }
     396             :         }
     397           3 : }
     398             : 
     399             : static void
     400           1 : scsi_pr_remove_all_other_regs(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
     401             : {
     402             :         struct spdk_scsi_pr_registrant *reg_tmp, *reg_tmp2;
     403             : 
     404           3 :         TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
     405           2 :                 if (reg_tmp != reg) {
     406           1 :                         scsi_pr_unregister_registrant(lun, reg_tmp);
     407             :                 }
     408             :         }
     409           1 : }
     410             : 
     411             : static int
     412           7 : scsi_pr_out_preempt(struct spdk_scsi_task *task,
     413             :                     enum spdk_scsi_pr_out_service_action_code action,
     414             :                     enum spdk_scsi_pr_type_code rtype,
     415             :                     uint64_t rkey, uint64_t sa_rkey)
     416             : {
     417           7 :         struct spdk_scsi_lun *lun = task->lun;
     418             :         struct spdk_scsi_pr_registrant *reg;
     419           7 :         bool all_regs = false;
     420             : 
     421           7 :         SPDK_DEBUGLOG(scsi, "PR OUT PREEMPT: rkey 0x%"PRIx64", sa_rkey 0x%"PRIx64" "
     422             :                       "action %u, type %u, reservation type %u\n",
     423             :                       rkey, sa_rkey, action, rtype, lun->reservation.rtype);
     424             : 
     425             :         /* I_T nexus is not registered */
     426           7 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     427           7 :         if (!reg) {
     428           0 :                 SPDK_ERRLOG("No registration\n");
     429           0 :                 goto conflict;
     430             :         }
     431           7 :         if (rkey != reg->rkey) {
     432           0 :                 SPDK_ERRLOG("Reservation key 0x%"PRIx64" doesn't match "
     433             :                             "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
     434           0 :                 goto conflict;
     435             :         }
     436             : 
     437             :         /* no persistent reservation */
     438           7 :         if (!scsi_pr_has_reservation(lun)) {
     439           1 :                 scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     440           1 :                 SPDK_DEBUGLOG(scsi, "PREEMPT: no persistent reservation\n");
     441           1 :                 goto exit;
     442             :         }
     443             : 
     444           6 :         all_regs = scsi_pr_is_all_registrants_type(lun);
     445             : 
     446           6 :         if (all_regs) {
     447           1 :                 if (sa_rkey != 0) {
     448           0 :                         scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     449           0 :                         SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey\n");
     450             :                 } else {
     451             :                         /* remove all other registrants and release persistent reservation if any */
     452           1 :                         scsi_pr_remove_all_other_regs(lun, reg);
     453             :                         /* create persistent reservation using new type and scope */
     454           1 :                         scsi_pr_reserve_reservation(lun, rtype, 0, reg);
     455           1 :                         SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey zeroed\n");
     456             :                 }
     457           1 :                 goto exit;
     458             :         }
     459             : 
     460           5 :         assert(lun->reservation.crkey != 0);
     461             : 
     462           5 :         if (sa_rkey != lun->reservation.crkey) {
     463           2 :                 if (!sa_rkey) {
     464           1 :                         SPDK_ERRLOG("Zeroed sa_rkey\n");
     465           1 :                         spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
     466             :                                                   SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
     467             :                                                   SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
     468             :                                                   SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     469           1 :                         return -EINVAL;
     470             :                 }
     471           1 :                 scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     472           1 :                 goto exit;
     473             :         }
     474             : 
     475           3 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     476           2 :                 scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     477           2 :                 SPDK_DEBUGLOG(scsi, "PREEMPT: preempt itself with type %u\n", rtype);
     478           2 :                 goto exit;
     479             :         }
     480             : 
     481             :         /* unregister registrants if any */
     482           1 :         scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     483           1 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     484           1 :         if (!reg) {
     485           0 :                 SPDK_ERRLOG("Current I_T nexus registrant was removed\n");
     486           0 :                 goto conflict;
     487             :         }
     488             : 
     489             :         /* preempt the holder */
     490           1 :         scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     491             : 
     492           6 : exit:
     493           6 :         lun->pr_generation++;
     494           6 :         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(&param->rkey);
     521           0 :         sa_rkey = from_be64(&param->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(&param->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(&param->rkey, 0);
     610             :                 } else {
     611           0 :                         to_be64(&param->rkey, lun->reservation.crkey);
     612             :                 }
     613           0 :                 to_be32(&param->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(&param->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(&param->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(&param->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(&param->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          13 : scsi_pr_check(struct spdk_scsi_task *task)
     741             : {
     742          13 :         struct spdk_scsi_lun *lun = task->lun;
     743          13 :         uint8_t *cdb = task->cdb;
     744             :         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          13 :         bool dma_to_device = false;
     748             : 
     749             :         /* no reservation holders */
     750          13 :         if (!scsi_pr_has_reservation(lun)) {
     751           0 :                 return 0;
     752             :         }
     753             : 
     754          13 :         rtype = lun->reservation.rtype;
     755          13 :         assert(rtype != 0);
     756             : 
     757          13 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     758             :         /* current I_T nexus hold the reservation */
     759          13 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     760           2 :                 return 0;
     761             :         }
     762             : 
     763             :         /* reservation is held by other I_T nexus */
     764          11 :         switch (cdb[0]) {
     765           1 :         case SPDK_SPC_INQUIRY:
     766             :         case SPDK_SPC_REPORT_LUNS:
     767             :         case SPDK_SPC_REQUEST_SENSE:
     768             :         case SPDK_SPC_LOG_SENSE:
     769             :         case SPDK_SPC_TEST_UNIT_READY:
     770             :         case SPDK_SBC_START_STOP_UNIT:
     771             :         case SPDK_SBC_READ_CAPACITY_10:
     772             :         case SPDK_SPC_PERSISTENT_RESERVE_IN:
     773             :         case SPDK_SPC_SERVICE_ACTION_IN_16:
     774             :         /* CRH enabled, processed by scsi2_reserve() */
     775             :         case SPDK_SPC2_RESERVE_6:
     776             :         case SPDK_SPC2_RESERVE_10:
     777             :         /* CRH enabled, processed by scsi2_release() */
     778             :         case SPDK_SPC2_RELEASE_6:
     779             :         case SPDK_SPC2_RELEASE_10:
     780           1 :                 return 0;
     781           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          10 :         default:
     819          10 :                 break;
     820             :         }
     821             : 
     822          10 :         switch (cdb[0]) {
     823           5 :         case SPDK_SBC_READ_6:
     824             :         case SPDK_SBC_READ_10:
     825             :         case SPDK_SBC_READ_12:
     826             :         case SPDK_SBC_READ_16:
     827           5 :                 break;
     828           5 :         case SPDK_SBC_WRITE_6:
     829             :         case SPDK_SBC_WRITE_10:
     830             :         case SPDK_SBC_WRITE_12:
     831             :         case SPDK_SBC_WRITE_16:
     832             :         case SPDK_SBC_UNMAP:
     833             :         case SPDK_SBC_SYNCHRONIZE_CACHE_10:
     834             :         case SPDK_SBC_SYNCHRONIZE_CACHE_16:
     835           5 :                 dma_to_device = true;
     836           5 :                 break;
     837           0 :         default:
     838           0 :                 SPDK_ERRLOG("CHECK: unsupported SCSI command cdb 0x%x\n", cdb[0]);
     839           0 :                 goto conflict;
     840             :         }
     841             : 
     842          10 :         switch (rtype) {
     843           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           4 :         case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
     851           4 :                 SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
     852             :                             "rejects command 0x%x\n", cdb[0]);
     853           4 :                 goto conflict;
     854           4 :         case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
     855             :         case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
     856           4 :                 if (!reg && dma_to_device) {
     857           1 :                         SPDK_ERRLOG("CHECK: Registrants only reservation "
     858             :                                     "type  reject command 0x%x\n", cdb[0]);
     859           1 :                         goto conflict;
     860             :                 }
     861           3 :                 break;
     862           2 :         case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY:
     863             :         case SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
     864           2 :                 if (!reg) {
     865           2 :                         SPDK_ERRLOG("CHECK: All Registrants reservation "
     866             :                                     "type  reject command 0x%x\n", cdb[0]);
     867           2 :                         goto conflict;
     868             :                 }
     869           0 :                 break;
     870           0 :         default:
     871           0 :                 break;
     872             :         }
     873             : 
     874           3 :         return 0;
     875             : 
     876           7 : conflict:
     877           7 :         spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
     878             :                                   SPDK_SCSI_SENSE_NO_SENSE,
     879             :                                   SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
     880             :                                   SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     881           7 :         return -1;
     882             : }
     883             : 
     884             : static int
     885           7 : scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
     886             : {
     887           7 :         struct spdk_scsi_lun *lun = task->lun;
     888             :         struct spdk_scsi_pr_registrant *reg;
     889           7 :         bool conflict = false;
     890             : 
     891           7 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     892           7 :         if (reg) {
     893             :                 /*
     894             :                  * From spc4r31 5.9.3 Exceptions to SPC-2 RESERVE and RELEASE
     895             :                  * behavior
     896             :                  *
     897             :                  * A RESERVE(6) or RESERVE(10) command shall complete with GOOD
     898             :                  * status, but no reservation shall be established and the
     899             :                  * persistent reservation shall not be changed, if the command
     900             :                  * is received from a) and b) below.
     901             :                  *
     902             :                  * A RELEASE(6) or RELEASE(10) command shall complete with GOOD
     903             :                  * status, but the persistent reservation shall not be released,
     904             :                  * if the command is received from a) and b)
     905             :                  *
     906             :                  * a) An I_T nexus that is a persistent reservation holder; or
     907             :                  * b) An I_T nexus that is registered if a registrants only or
     908             :                  *    all registrants type persistent reservation is present.
     909             :                  *
     910             :                  * In all other cases, a RESERVE(6) command, RESERVE(10) command,
     911             :                  * RELEASE(6) command, or RELEASE(10) command shall be processed
     912             :                  * as defined in SPC-2.
     913             :                  */
     914           2 :                 if (scsi_pr_registrant_is_holder(lun, reg)) {
     915           0 :                         return 1;
     916             :                 }
     917             : 
     918           2 :                 if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
     919           0 :                     lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
     920           2 :                         return 1;
     921             :                 }
     922             : 
     923           0 :                 conflict = true;
     924             :         } else {
     925             :                 /*
     926             :                  * From spc2r20 5.5.1 Reservations overview:
     927             :                  *
     928             :                  * If a logical unit has executed a PERSISTENT RESERVE OUT
     929             :                  * command with the REGISTER or the REGISTER AND IGNORE
     930             :                  * EXISTING KEY service action and is still registered by any
     931             :                  * initiator, all RESERVE commands and all RELEASE commands
     932             :                  * regardless of initiator shall conflict and shall terminate
     933             :                  * with a RESERVATION CONFLICT status.
     934             :                  */
     935           5 :                 conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
     936             :         }
     937             : 
     938           5 :         if (conflict) {
     939           0 :                 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
     940             :                                           SPDK_SCSI_SENSE_NO_SENSE,
     941             :                                           SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
     942             :                                           SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     943           0 :                 return -1;
     944             :         }
     945             : 
     946           5 :         return 0;
     947             : }
     948             : 
     949             : int
     950           3 : scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
     951             : {
     952           3 :         struct spdk_scsi_lun *lun = task->lun;
     953           3 :         struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
     954             :         int ret;
     955             : 
     956             :         /* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
     957           3 :         if (cdb[1] & 0x3) {
     958           0 :                 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
     959             :                                           SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
     960             :                                           SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
     961             :                                           SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
     962           0 :                 return -1;
     963             :         }
     964             : 
     965           3 :         ret = scsi2_check_reservation_conflict(task);
     966             :         /* PERSISTENT RESERVE is enabled */
     967           3 :         if (ret == 1) {
     968           1 :                 return 0;
     969           2 :         } else if (ret < 0) {
     970           0 :                 return ret;
     971             :         }
     972             : 
     973             :         /* SPC2 RESERVE */
     974           2 :         reg->initiator_port = task->initiator_port;
     975           2 :         if (task->initiator_port) {
     976           2 :                 snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
     977           2 :                          task->initiator_port->name);
     978           2 :                 reg->transport_id_len = task->initiator_port->transport_id_len;
     979           2 :                 memcpy(reg->transport_id, task->initiator_port->transport_id,
     980           2 :                        reg->transport_id_len);
     981             :         }
     982           2 :         reg->target_port = task->target_port;
     983           2 :         if (task->target_port) {
     984           2 :                 snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
     985           2 :                          task->target_port->name);
     986             :         }
     987             : 
     988           2 :         lun->reservation.flags = SCSI_SPC2_RESERVE;
     989           2 :         lun->reservation.holder = &lun->scsi2_holder;
     990             : 
     991           2 :         return 0;
     992             : }
     993             : 
     994             : int
     995           4 : scsi2_release(struct spdk_scsi_task *task)
     996             : {
     997           4 :         struct spdk_scsi_lun *lun = task->lun;
     998             :         int ret;
     999             : 
    1000           4 :         ret = scsi2_check_reservation_conflict(task);
    1001             :         /* PERSISTENT RESERVE is enabled */
    1002           4 :         if (ret == 1) {
    1003           1 :                 return 0;
    1004           3 :         } else if (ret < 0) {
    1005           0 :                 return ret;
    1006             :         }
    1007             : 
    1008           3 :         if (!(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
    1009           1 :                 spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
    1010             :                                           SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
    1011             :                                           SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
    1012             :                                           SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
    1013           1 :                 return -EINVAL;
    1014             :         }
    1015             : 
    1016           2 :         memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
    1017           2 :         memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
    1018             : 
    1019           2 :         return 0;
    1020             : }
    1021             : 
    1022             : int
    1023           3 : scsi2_reserve_check(struct spdk_scsi_task *task)
    1024             : {
    1025           3 :         struct spdk_scsi_lun *lun = task->lun;
    1026           3 :         uint8_t *cdb = task->cdb;
    1027             : 
    1028           3 :         switch (cdb[0]) {
    1029           1 :         case SPDK_SPC_INQUIRY:
    1030             :         case SPDK_SPC2_RELEASE_6:
    1031             :         case SPDK_SPC2_RELEASE_10:
    1032           1 :                 return 0;
    1033             : 
    1034           2 :         default:
    1035           2 :                 break;
    1036             :         }
    1037             : 
    1038             :         /* no reservation holders */
    1039           2 :         if (!scsi_pr_has_reservation(lun)) {
    1040           0 :                 return 0;
    1041             :         }
    1042             : 
    1043           2 :         if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
    1044           1 :                 return 0;
    1045             :         }
    1046             : 
    1047           1 :         spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_RESERVATION_CONFLICT,
    1048             :                                   SPDK_SCSI_SENSE_NO_SENSE,
    1049             :                                   SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE,
    1050             :                                   SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
    1051           1 :         return -1;
    1052             : }

Generated by: LCOV version 1.15