LCOV - code coverage report
Current view: top level - spdk/lib/scsi - scsi_pr.c (source / functions) Hit Total Coverage
Test: Combined Lines: 247 514 48.1 %
Date: 2024-07-14 04:14:37 Functions: 20 28 71.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 136 341 39.9 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2019 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "scsi_internal.h"
       7                 :            : 
       8                 :            : #include "spdk/endian.h"
       9                 :            : 
      10                 :            : /* Get registrant by I_T nexus */
      11                 :            : static struct spdk_scsi_pr_registrant *
      12                 :        606 : 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         [ +  + ]:       1020 :         TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
      19         [ +  + ]:        822 :                 if (initiator_port == reg->initiator_port &&
      20         [ +  - ]:        408 :                     target_port == reg->target_port) {
      21                 :        408 :                         return reg;
      22                 :            :                 }
      23                 :            :         }
      24                 :            : 
      25                 :        198 :         return NULL;
      26                 :            : }
      27                 :            : 
      28                 :            : static bool
      29                 :         12 : 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                 :         12 :         struct spdk_scsi_pr_registrant *reg = lun->reservation.holder;
      34                 :            : 
      35         [ -  + ]:         12 :         assert(reg != NULL);
      36                 :            : 
      37         [ +  + ]:         12 :         if ((reg->initiator_port == initiator_port) &&
      38         [ +  - ]:          6 :             (reg->target_port == target_port)) {
      39                 :          6 :                 return true;
      40                 :            :         }
      41                 :            : 
      42                 :          6 :         return false;
      43                 :            : }
      44                 :            : 
      45                 :            : /* Reservation type is all registrants or not */
      46                 :            : static inline bool
      47                 :        186 : scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
      48                 :            : {
      49         [ +  + ]:        348 :         return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
      50         [ -  + ]:        162 :                 lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
      51                 :            : }
      52                 :            : 
      53                 :            : /* Registrant is reservation holder or not */
      54                 :            : static inline bool
      55                 :        132 : scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
      56                 :            :                              struct spdk_scsi_pr_registrant *reg)
      57                 :            : {
      58         [ +  + ]:        132 :         if (scsi_pr_is_all_registrants_type(lun)) {
      59                 :         12 :                 return true;
      60                 :            :         }
      61                 :            : 
      62                 :        120 :         return (lun->reservation.holder == reg);
      63                 :            : }
      64                 :            : 
      65                 :            : /* LUN holds a reservation or not */
      66                 :            : static inline bool
      67                 :   30183361 : scsi_pr_has_reservation(struct spdk_scsi_lun *lun)
      68                 :            : {
      69                 :   30183361 :         return !(lun->reservation.holder == NULL);
      70                 :            : }
      71                 :            : 
      72                 :            : static int
      73                 :        108 : 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                 :        108 :         reg = calloc(1, sizeof(*reg));
      82         [ -  + ]:        108 :         if (!reg) {
      83                 :          0 :                 return -ENOMEM;
      84                 :            :         }
      85                 :            : 
      86   [ -  +  -  + ]:        108 :         SPDK_DEBUGLOG(scsi, "REGISTER: new registrant registered "
      87                 :            :                       "with key 0x%"PRIx64"\n", sa_rkey);
      88                 :            : 
      89                 :            :         /* New I_T nexus */
      90                 :        108 :         reg->initiator_port = initiator_port;
      91         [ +  - ]:        108 :         if (initiator_port) {
      92         [ -  + ]:        108 :                 snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
      93                 :        108 :                          initiator_port->name);
      94                 :        108 :                 reg->transport_id_len = initiator_port->transport_id_len;
      95   [ -  +  -  + ]:        108 :                 memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
      96                 :            :         }
      97                 :        108 :         reg->target_port = target_port;
      98         [ +  - ]:        108 :         if (target_port) {
      99         [ -  + ]:        108 :                 snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
     100                 :        108 :                          target_port->name);
     101                 :        108 :                 reg->relative_target_port_id = target_port->index;
     102                 :            :         }
     103                 :        108 :         reg->rkey = sa_rkey;
     104                 :        108 :         TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
     105                 :        108 :         lun->pr_generation++;
     106                 :            : 
     107                 :        108 :         return 0;
     108                 :            : }
     109                 :            : 
     110                 :            : static void
     111                 :         18 : scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
     112                 :            : {
     113                 :         18 :         bool all_regs = false;
     114                 :            : 
     115   [ -  +  -  + ]:         18 :         SPDK_DEBUGLOG(scsi, "REGISTER: release reservation "
     116                 :            :                       "with type %u\n", lun->reservation.rtype);
     117                 :            : 
     118                 :            :         /* TODO: Unit Attention */
     119                 :         18 :         all_regs = scsi_pr_is_all_registrants_type(lun);
     120   [ +  +  +  - ]:         18 :         if (all_regs && !TAILQ_EMPTY(&lun->reg_head)) {
     121                 :          6 :                 lun->reservation.holder = TAILQ_FIRST(&lun->reg_head);
     122                 :          6 :                 return;
     123                 :            :         }
     124                 :            : 
     125         [ -  + ]:         12 :         memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
     126                 :            : }
     127                 :            : 
     128                 :            : static void
     129                 :         60 : 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                 :         60 :         lun->reservation.rtype = type;
     135                 :         60 :         lun->reservation.crkey = rkey;
     136                 :         60 :         lun->reservation.holder = holder;
     137                 :         60 : }
     138                 :            : 
     139                 :            : static void
     140                 :         36 : scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
     141                 :            :                               struct spdk_scsi_pr_registrant *reg)
     142                 :            : {
     143   [ -  +  -  + ]:         36 :         SPDK_DEBUGLOG(scsi, "REGISTER: unregister registrant\n");
     144                 :            : 
     145         [ +  + ]:         36 :         TAILQ_REMOVE(&lun->reg_head, reg, link);
     146         [ +  + ]:         36 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     147                 :         18 :                 scsi_pr_release_reservation(lun, reg);
     148                 :            :         }
     149                 :            : 
     150                 :         36 :         free(reg);
     151                 :         36 :         lun->pr_generation++;
     152                 :         36 : }
     153                 :            : 
     154                 :            : static void
     155                 :         36 : scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
     156                 :            :                                struct spdk_scsi_pr_registrant *reg,
     157                 :            :                                uint64_t sa_rkey)
     158                 :            : {
     159   [ -  +  -  + ]:         36 :         SPDK_DEBUGLOG(scsi, "REGISTER: replace with new "
     160                 :            :                       "reservation key 0x%"PRIx64"\n", sa_rkey);
     161                 :         36 :         reg->rkey = sa_rkey;
     162                 :         36 :         lun->pr_generation++;
     163                 :         36 : }
     164                 :            : 
     165                 :            : static int
     166                 :         54 : 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                 :         54 :         struct spdk_scsi_lun *lun = task->lun;
     171                 :            :         struct spdk_scsi_pr_registrant *reg;
     172                 :            : 
     173   [ -  +  -  + ]:         54 :         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   [ +  -  +  -  :         54 :         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                 :         54 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     188                 :            :         /* No registration for the I_T nexus */
     189         [ -  + ]:         54 :         if (!reg) {
     190                 :          0 :                 SPDK_ERRLOG("No registration\n");
     191                 :          0 :                 goto conflict;
     192                 :            :         }
     193                 :            : 
     194                 :            :         /* invalid reservation key */
     195         [ -  + ]:         54 :         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         [ +  + ]:         54 :         if (scsi_pr_has_reservation(lun)) {
     203         [ +  + ]:         18 :                 if (rtype != lun->reservation.rtype) {
     204                 :          6 :                         SPDK_ERRLOG("Reservation type doesn't match\n");
     205                 :          6 :                         goto conflict;
     206                 :            :                 }
     207                 :            : 
     208         [ +  + ]:         12 :                 if (!scsi_pr_registrant_is_holder(lun, reg)) {
     209                 :          6 :                         SPDK_ERRLOG("Only 1 holder is allowed for type %u\n", rtype);
     210                 :          6 :                         goto conflict;
     211                 :            :                 }
     212                 :            :         } else {
     213                 :            :                 /* current I_T nexus is the first reservation holder */
     214                 :         36 :                 scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     215                 :            :         }
     216                 :            : 
     217                 :         42 :         return 0;
     218                 :            : 
     219                 :         12 : conflict:
     220                 :         12 :         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                 :         12 :         return -EINVAL;
     225                 :            : }
     226                 :            : 
     227                 :            : static int
     228                 :        192 : 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                 :        192 :         struct spdk_scsi_lun *lun = task->lun;
     234                 :            :         struct spdk_scsi_pr_registrant *reg;
     235                 :            :         int sc, sk, asc;
     236                 :            : 
     237   [ -  +  -  + ]:        192 :         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   [ +  -  +  -  :        192 :         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                 :        192 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     250                 :            :         /* an unregistered I_T nexus session */
     251         [ +  + ]:        192 :         if (!reg) {
     252   [ -  +  -  - ]:        108 :                 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         [ -  + ]:        108 :                 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                 :        108 :                 return scsi_pr_register_registrant(lun, task->initiator_port,
     268                 :            :                                                    task->target_port, sa_rkey);
     269                 :            :         } else {
     270                 :            :                 /* a registered I_T nexus */
     271   [ +  +  +  - ]:         84 :                 if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
     272                 :         36 :                         SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
     273                 :            :                                     "registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
     274                 :         36 :                         sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
     275                 :         36 :                         sk = SPDK_SCSI_SENSE_NO_SENSE;
     276                 :         36 :                         asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
     277                 :         36 :                         goto error_exit;
     278                 :            :                 }
     279                 :            : 
     280         [ +  + ]:         48 :                 if (!sa_rkey) {
     281                 :            :                         /* unregister */
     282                 :         12 :                         scsi_pr_unregister_registrant(lun, reg);
     283                 :            :                 } else {
     284                 :            :                         /* replace */
     285                 :         36 :                         scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
     286                 :            :                 }
     287                 :            :         }
     288                 :            : 
     289                 :         48 :         return 0;
     290                 :            : 
     291                 :         36 : error_exit:
     292                 :         36 :         spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
     293                 :         36 :         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                 :         18 : 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         [ +  + ]:         66 :         TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
     387         [ +  + ]:         48 :                 if (reg->rkey == sa_rkey) {
     388                 :         18 :                         scsi_pr_unregister_registrant(lun, reg);
     389                 :            :                 }
     390                 :            :         }
     391                 :         18 : }
     392                 :            : 
     393                 :            : static void
     394                 :          6 : 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         [ +  + ]:         18 :         TAILQ_FOREACH_SAFE(reg_tmp, &lun->reg_head, link, reg_tmp2) {
     399         [ +  + ]:         12 :                 if (reg_tmp != reg) {
     400                 :          6 :                         scsi_pr_unregister_registrant(lun, reg_tmp);
     401                 :            :                 }
     402                 :            :         }
     403                 :          6 : }
     404                 :            : 
     405                 :            : static int
     406                 :         42 : 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                 :         42 :         struct spdk_scsi_lun *lun = task->lun;
     412                 :            :         struct spdk_scsi_pr_registrant *reg;
     413                 :         42 :         bool all_regs = false;
     414                 :            : 
     415   [ -  +  -  + ]:         42 :         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                 :         42 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     421         [ -  + ]:         42 :         if (!reg) {
     422                 :          0 :                 SPDK_ERRLOG("No registration\n");
     423                 :          0 :                 goto conflict;
     424                 :            :         }
     425         [ -  + ]:         42 :         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         [ +  + ]:         42 :         if (!scsi_pr_has_reservation(lun)) {
     433                 :          6 :                 scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     434   [ -  +  -  + ]:          6 :                 SPDK_DEBUGLOG(scsi, "PREEMPT: no persistent reservation\n");
     435                 :          6 :                 goto exit;
     436                 :            :         }
     437                 :            : 
     438                 :         36 :         all_regs = scsi_pr_is_all_registrants_type(lun);
     439                 :            : 
     440         [ +  + ]:         36 :         if (all_regs) {
     441         [ -  + ]:          6 :                 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                 :          6 :                         scsi_pr_remove_all_other_regs(lun, reg);
     447                 :            :                         /* create persistent reservation using new type and scope */
     448                 :          6 :                         scsi_pr_reserve_reservation(lun, rtype, 0, reg);
     449   [ -  +  -  + ]:          6 :                         SPDK_DEBUGLOG(scsi, "PREEMPT: All registrants type with sa_rkey zeroed\n");
     450                 :            :                 }
     451                 :          6 :                 goto exit;
     452                 :            :         }
     453                 :            : 
     454         [ -  + ]:         30 :         assert(lun->reservation.crkey != 0);
     455                 :            : 
     456         [ +  + ]:         30 :         if (sa_rkey != lun->reservation.crkey) {
     457         [ +  + ]:         12 :                 if (!sa_rkey) {
     458                 :          6 :                         SPDK_ERRLOG("Zeroed sa_rkey\n");
     459                 :          6 :                         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                 :          6 :                         return -EINVAL;
     464                 :            :                 }
     465                 :          6 :                 scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     466                 :          6 :                 goto exit;
     467                 :            :         }
     468                 :            : 
     469         [ +  + ]:         18 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     470                 :         12 :                 scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     471   [ -  +  -  + ]:         12 :                 SPDK_DEBUGLOG(scsi, "PREEMPT: preempt itself with type %u\n", rtype);
     472                 :         12 :                 goto exit;
     473                 :            :         }
     474                 :            : 
     475                 :            :         /* unregister registrants if any */
     476                 :          6 :         scsi_pr_remove_all_regs_by_key(lun, sa_rkey);
     477                 :          6 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     478         [ -  + ]:          6 :         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                 :          6 :         scsi_pr_reserve_reservation(lun, rtype, rkey, reg);
     485                 :            : 
     486                 :         36 : exit:
     487                 :         36 :         lun->pr_generation++;
     488                 :         36 :         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                 :   30183253 : scsi_pr_check(struct spdk_scsi_task *task)
     735                 :            : {
     736                 :   30183253 :         struct spdk_scsi_lun *lun = task->lun;
     737                 :   30183253 :         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                 :   30183253 :         bool dma_to_device = false;
     742                 :            : 
     743                 :            :         /* no reservation holders */
     744         [ +  + ]:   30183253 :         if (!scsi_pr_has_reservation(lun)) {
     745                 :   30183199 :                 return 0;
     746                 :            :         }
     747                 :            : 
     748                 :         54 :         rtype = lun->reservation.rtype;
     749         [ -  + ]:         54 :         assert(rtype != 0);
     750                 :            : 
     751                 :         54 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     752                 :            :         /* current I_T nexus hold the reservation */
     753         [ -  + ]:         54 :         if (scsi_pr_registrant_is_holder(lun, reg)) {
     754                 :          0 :                 return 0;
     755                 :            :         }
     756                 :            : 
     757                 :            :         /* reservation is held by other I_T nexus */
     758   [ +  -  -  + ]:         54 :         switch (cdb[0]) {
     759                 :          6 :         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                 :          6 :                 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                 :         48 :         default:
     813                 :         48 :                 break;
     814                 :            :         }
     815                 :            : 
     816      [ +  +  - ]:         48 :         switch (cdb[0]) {
     817                 :         24 :         case SPDK_SBC_READ_6:
     818                 :            :         case SPDK_SBC_READ_10:
     819                 :            :         case SPDK_SBC_READ_12:
     820                 :            :         case SPDK_SBC_READ_16:
     821                 :         24 :                 break;
     822                 :         24 :         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                 :         24 :                 dma_to_device = true;
     830                 :         24 :                 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   [ -  +  +  -  :         48 :         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                 :         24 :         case SPDK_SCSI_PR_EXCLUSIVE_ACCESS:
     845                 :         24 :                 SPDK_ERRLOG("CHECK: Exclusive Access reservation type "
     846                 :            :                             "rejects command 0x%x\n", cdb[0]);
     847                 :         24 :                 goto conflict;
     848                 :         24 :         case SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY:
     849                 :            :         case SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
     850   [ +  +  +  + ]:         24 :                 if (!reg && dma_to_device) {
     851                 :          6 :                         SPDK_ERRLOG("CHECK: Registrants only reservation "
     852                 :            :                                     "type  reject command 0x%x\n", cdb[0]);
     853                 :          6 :                         goto conflict;
     854                 :            :                 }
     855                 :         18 :                 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                 :         18 :         return 0;
     869                 :            : 
     870                 :         30 : conflict:
     871                 :         30 :         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                 :         30 :         return -1;
     876                 :            : }
     877                 :            : 
     878                 :            : static int
     879                 :         42 : scsi2_check_reservation_conflict(struct spdk_scsi_task *task)
     880                 :            : {
     881                 :         42 :         struct spdk_scsi_lun *lun = task->lun;
     882                 :            :         struct spdk_scsi_pr_registrant *reg;
     883                 :         42 :         bool conflict = false;
     884                 :            : 
     885                 :         42 :         reg = scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
     886         [ +  + ]:         42 :         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         [ -  + ]:         12 :                 if (scsi_pr_registrant_is_holder(lun, reg)) {
     909                 :          0 :                         return 1;
     910                 :            :                 }
     911                 :            : 
     912         [ -  + ]:         12 :                 if (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_REGS_ONLY ||
     913         [ #  # ]:          0 :                     lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_REGS_ONLY) {
     914                 :         12 :                         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                 :         30 :                 conflict = TAILQ_EMPTY(&lun->reg_head) ? false : true;
     930                 :            :         }
     931                 :            : 
     932         [ -  + ]:         30 :         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                 :         30 :         return 0;
     941                 :            : }
     942                 :            : 
     943                 :            : int
     944                 :         18 : scsi2_reserve(struct spdk_scsi_task *task, uint8_t *cdb)
     945                 :            : {
     946                 :         18 :         struct spdk_scsi_lun *lun = task->lun;
     947                 :         18 :         struct spdk_scsi_pr_registrant *reg = &lun->scsi2_holder;
     948                 :            :         int ret;
     949                 :            : 
     950                 :            :         /* Obsolete Bits and LongID set, returning ILLEGAL_REQUEST */
     951         [ -  + ]:         18 :         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                 :         18 :         ret = scsi2_check_reservation_conflict(task);
     960                 :            :         /* PERSISTENT RESERVE is enabled */
     961         [ +  + ]:         18 :         if (ret == 1) {
     962                 :          6 :                 return 0;
     963         [ -  + ]:         12 :         } else if (ret < 0) {
     964                 :          0 :                 return ret;
     965                 :            :         }
     966                 :            : 
     967                 :            :         /* SPC2 RESERVE */
     968                 :         12 :         reg->initiator_port = task->initiator_port;
     969         [ +  - ]:         12 :         if (task->initiator_port) {
     970         [ -  + ]:         12 :                 snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
     971                 :         12 :                          task->initiator_port->name);
     972                 :         12 :                 reg->transport_id_len = task->initiator_port->transport_id_len;
     973   [ -  +  -  + ]:         12 :                 memcpy(reg->transport_id, task->initiator_port->transport_id,
     974                 :         12 :                        reg->transport_id_len);
     975                 :            :         }
     976                 :         12 :         reg->target_port = task->target_port;
     977         [ +  - ]:         12 :         if (task->target_port) {
     978         [ -  + ]:         12 :                 snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
     979                 :         12 :                          task->target_port->name);
     980                 :            :         }
     981                 :            : 
     982                 :         12 :         lun->reservation.flags = SCSI_SPC2_RESERVE;
     983                 :         12 :         lun->reservation.holder = &lun->scsi2_holder;
     984                 :            : 
     985                 :         12 :         return 0;
     986                 :            : }
     987                 :            : 
     988                 :            : int
     989                 :         24 : scsi2_release(struct spdk_scsi_task *task)
     990                 :            : {
     991                 :         24 :         struct spdk_scsi_lun *lun = task->lun;
     992                 :            :         int ret;
     993                 :            : 
     994                 :         24 :         ret = scsi2_check_reservation_conflict(task);
     995                 :            :         /* PERSISTENT RESERVE is enabled */
     996         [ +  + ]:         24 :         if (ret == 1) {
     997                 :          6 :                 return 0;
     998         [ -  + ]:         18 :         } else if (ret < 0) {
     999                 :          0 :                 return ret;
    1000                 :            :         }
    1001                 :            : 
    1002         [ +  + ]:         18 :         if (!(lun->reservation.flags & SCSI_SPC2_RESERVE)) {
    1003                 :          6 :                 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                 :          6 :                 return -EINVAL;
    1008                 :            :         }
    1009                 :            : 
    1010         [ -  + ]:         12 :         memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
    1011         [ -  + ]:         12 :         memset(&lun->scsi2_holder, 0, sizeof(struct spdk_scsi_pr_registrant));
    1012                 :            : 
    1013                 :         12 :         return 0;
    1014                 :            : }
    1015                 :            : 
    1016                 :            : int
    1017                 :         18 : scsi2_reserve_check(struct spdk_scsi_task *task)
    1018                 :            : {
    1019                 :         18 :         struct spdk_scsi_lun *lun = task->lun;
    1020                 :         18 :         uint8_t *cdb = task->cdb;
    1021                 :            : 
    1022         [ +  + ]:         18 :         switch (cdb[0]) {
    1023                 :          6 :         case SPDK_SPC_INQUIRY:
    1024                 :            :         case SPDK_SPC2_RELEASE_6:
    1025                 :            :         case SPDK_SPC2_RELEASE_10:
    1026                 :          6 :                 return 0;
    1027                 :            : 
    1028                 :         12 :         default:
    1029                 :         12 :                 break;
    1030                 :            :         }
    1031                 :            : 
    1032                 :            :         /* no reservation holders */
    1033         [ -  + ]:         12 :         if (!scsi_pr_has_reservation(lun)) {
    1034                 :          0 :                 return 0;
    1035                 :            :         }
    1036                 :            : 
    1037         [ +  + ]:         12 :         if (scsi2_it_nexus_is_holder(lun, task->initiator_port, task->target_port)) {
    1038                 :          6 :                 return 0;
    1039                 :            :         }
    1040                 :            : 
    1041                 :          6 :         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                 :          6 :         return -1;
    1046                 :            : }

Generated by: LCOV version 1.14