// SPDX-License-Identifier: GPL-2.0-or-later/******************************************************************************* * Filename: target_core_pr.c * * This file contains SPC-3 compliant persistent reservations and * legacy SPC-2 reservations with compatible reservation handling (CRH=1) * * (c) Copyright 2009-2013 Datera, Inc. * * Nicholas A. Bellinger <nab@kernel.org> * ******************************************************************************/ #include <linux/slab.h>#include <linux/spinlock.h>#include <linux/list.h>#include <linux/vmalloc.h>#include <linux/file.h>#include <linux/fcntl.h>#include <linux/fs.h>#include <linux/fs_struct.h>#include <scsi/scsi_proto.h>#include <linux/unaligned.h> #include <target/target_core_base.h>#include <target/target_core_backend.h>#include <target/target_core_fabric.h> #include "target_core_internal.h"#include "target_core_pr.h"#include "target_core_ua.h" /* * Used for Specify Initiator Ports Capable Bit (SPEC_I_PT) */struct pr_transport_id_holder { struct t10_pr_registration *dest_pr_reg; struct se_portal_group *dest_tpg; struct se_node_acl *dest_node_acl; struct se_dev_entry *dest_se_deve; struct list_head dest_list;}; void core_pr_dump_initiator_port( struct t10_pr_registration *pr_reg, char *buf, u32 size){ if (!pr_reg->isid_present_at_reg) { buf[0] = '\0'; return; } snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid);} enum register_type { REGISTER, REGISTER_AND_IGNORE_EXISTING_KEY, REGISTER_AND_MOVE,}; enum preempt_type { PREEMPT, PREEMPT_AND_ABORT,}; static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *, struct t10_pr_registration *, int, int); static int is_reservation_holder( struct t10_pr_registration *pr_res_holder, struct t10_pr_registration *pr_reg){ int pr_res_type; if (pr_res_holder) { pr_res_type = pr_res_holder->pr_res_type; return pr_res_holder == pr_reg || pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG; } return 0;} static sense_reason_ttarget_scsi2_reservation_check(struct se_cmd *cmd){ struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; switch (cmd->t_task_cdb[0]) { case INQUIRY: case RELEASE_6: case RELEASE_10: return 0; default: break; } if (!dev->reservation_holder || !sess) return 0; if (dev->reservation_holder->se_node_acl != sess->se_node_acl) return TCM_RESERVATION_CONFLICT; if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) { if (dev->dev_res_bin_isid != sess->sess_bin_isid) return TCM_RESERVATION_CONFLICT; } return 0;} static struct t10_pr_registration *core_scsi3_locate_pr_reg(struct se_device *, struct se_node_acl *, struct se_session *);static void core_scsi3_put_pr_reg(struct t10_pr_registration *); static int target_check_scsi2_reservation_conflict(struct se_cmd *cmd){ struct se_session *se_sess = cmd->se_sess; struct se_device *dev = cmd->se_dev; struct t10_pr_registration *pr_reg; struct t10_reservation *pr_tmpl = &dev->t10_pr; int conflict = 0; pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl, se_sess); if (pr_reg) { /* * From spc4r17 5.7.3 Exceptions to SPC-2 RESERVE and RELEASE * behavior * * A RESERVE(6) or RESERVE(10) command shall complete with GOOD * status, but no reservation shall be established and the * persistent reservation shall not be changed, if the command * is received from a) and b) below. * * A RELEASE(6) or RELEASE(10) command shall complete with GOOD * status, but the persistent reservation shall not be released, * if the command is received from a) and b) * * a) An I_T nexus that is a persistent reservation holder; or * b) An I_T nexus that is registered if a registrants only or * all registrants type persistent reservation is present. * * In all other cases, a RESERVE(6) command, RESERVE(10) command, * RELEASE(6) command, or RELEASE(10) command shall be processed * as defined in SPC-2. */ if (pr_reg->pr_res_holder) { core_scsi3_put_pr_reg(pr_reg); return 1; } if ((pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY) || (pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY) || (pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) || (pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) { core_scsi3_put_pr_reg(pr_reg); return 1; } core_scsi3_put_pr_reg(pr_reg); conflict = 1; } else { /* * Following spc2r20 5.5.1 Reservations overview: * * If a logical unit has executed a PERSISTENT RESERVE OUT * command with the REGISTER or the REGISTER AND IGNORE * EXISTING KEY service action and is still registered by any * initiator, all RESERVE commands and all RELEASE commands * regardless of initiator shall conflict and shall terminate * with a RESERVATION CONFLICT status. */ spin_lock(&pr_tmpl->registration_lock); conflict = (list_empty(&pr_tmpl->registration_list)) ? 0 : 1; spin_unlock(&pr_tmpl->registration_lock); } if (conflict) { pr_err("Received legacy SPC-2 RESERVE/RELEASE" " while active SPC-3 registrations exist," " returning RESERVATION_CONFLICT\n"); return -EBUSY; } return 0;} void target_release_reservation(struct se_device *dev){ dev->reservation_holder = NULL; dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS; if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) { dev->dev_res_bin_isid = 0; dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS_WITH_ISID; }} sense_reason_ttarget_scsi2_reservation_release(struct se_cmd *cmd){ struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; struct se_portal_group *tpg; int rc; if (!sess || !sess->se_tpg) goto out; rc = target_check_scsi2_reservation_conflict(cmd); if (rc == 1) goto out; if (rc < 0) return TCM_RESERVATION_CONFLICT; spin_lock(&dev->dev_reservation_lock); if (!dev->reservation_holder || !sess) goto out_unlock; if (dev->reservation_holder->se_node_acl != sess->se_node_acl) goto out_unlock; if (dev->dev_res_bin_isid != sess->sess_bin_isid) goto out_unlock; target_release_reservation(dev); tpg = sess->se_tpg; pr_debug("SCSI-2 Released reservation for %s LUN: %llu ->" " MAPPED LUN: %llu for %s\n", tpg->se_tpg_tfo->fabric_name, cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); out_unlock: spin_unlock(&dev->dev_reservation_lock);out: target_complete_cmd(cmd, SAM_STAT_GOOD); return 0;} sense_reason_ttarget_scsi2_reservation_reserve(struct se_cmd *cmd){ struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; struct se_portal_group *tpg; sense_reason_t ret = 0; int rc; if ((cmd->t_task_cdb[1] & 0x01) && (cmd->t_task_cdb[1] & 0x02)) { pr_err("LongIO and Obsolete Bits set, returning ILLEGAL_REQUEST\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } /* * This is currently the case for target_core_mod passthrough struct se_cmd * ops */ if (!sess || !sess->se_tpg) goto out; rc = target_check_scsi2_reservation_conflict(cmd); if (rc == 1) goto out; if (rc < 0) return TCM_RESERVATION_CONFLICT; tpg = sess->se_tpg; spin_lock(&dev->dev_reservation_lock); if (dev->reservation_holder && dev->reservation_holder->se_node_acl != sess->se_node_acl) { pr_err("SCSI-2 RESERVATION CONFLICT for %s fabric\n", tpg->se_tpg_tfo->fabric_name); pr_err("Original reserver LUN: %llu %s\n", cmd->se_lun->unpacked_lun, dev->reservation_holder->se_node_acl->initiatorname); pr_err("Current attempt - LUN: %llu -> MAPPED LUN: %llu" " from %s \n", cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); ret = TCM_RESERVATION_CONFLICT; goto out_unlock; } dev->reservation_holder = sess; dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS; if (sess->sess_bin_isid != 0) { dev->dev_res_bin_isid = sess->sess_bin_isid; dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS_WITH_ISID; } pr_debug("SCSI-2 Reserved %s LUN: %llu -> MAPPED LUN: %llu" " for %s\n", tpg->se_tpg_tfo->fabric_name, cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); out_unlock: spin_unlock(&dev->dev_reservation_lock);out: if (!ret) target_complete_cmd(cmd, SAM_STAT_GOOD); return ret;} /* * Begin SPC-3/SPC-4 Persistent Reservations emulation support * * This function is called by those initiator ports who are *NOT* * the active PR reservation holder when a reservation is present. */static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type, bool isid_mismatch){ unsigned char *cdb = cmd->t_task_cdb; struct se_session *se_sess = cmd->se_sess; struct se_node_acl *nacl = se_sess->se_node_acl; int other_cdb = 0; int registered_nexus = 0, ret = 1; /* Conflict by default */ int all_reg = 0, reg_only = 0; /* ALL_REG, REG_ONLY */ int we = 0; /* Write Exclusive */ int legacy = 0; /* Act like a legacy device and return * RESERVATION CONFLICT on some CDBs */ if (isid_mismatch) { registered_nexus = 0; } else { struct se_dev_entry *se_deve; rcu_read_lock(); se_deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun); if (se_deve) registered_nexus = test_bit(DEF_PR_REG_ACTIVE, &se_deve->deve_flags); rcu_read_unlock(); } switch (pr_reg_type) { case PR_TYPE_WRITE_EXCLUSIVE: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS: /* * Some commands are only allowed for the persistent reservation * holder. */ break; case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: /* * Some commands are only allowed for registered I_T Nexuses. */ reg_only = 1; break; case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: /* * Each registered I_T Nexus is a reservation holder. */ all_reg = 1; break; default: return -EINVAL; } /* * Referenced from spc4r17 table 45 for *NON* PR holder access */ switch (cdb[0]) { case SECURITY_PROTOCOL_IN: if (registered_nexus) return 0; ret = (we) ? 0 : 1; break; case MODE_SENSE: case MODE_SENSE_10: case READ_ATTRIBUTE: case READ_BUFFER: case RECEIVE_DIAGNOSTIC: if (legacy) { ret = 1; break; } if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case PERSISTENT_RESERVE_OUT: /* * This follows PERSISTENT_RESERVE_OUT service actions that * are allowed in the presence of various reservations. * See spc4r17, table 46 */ switch (cdb[1] & 0x1f) { case PRO_CLEAR: case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: ret = (registered_nexus) ? 0 : 1; break; case PRO_REGISTER: case PRO_REGISTER_AND_IGNORE_EXISTING_KEY: ret = 0; break; case PRO_REGISTER_AND_MOVE: case PRO_RESERVE: ret = 1; break; case PRO_RELEASE: ret = (registered_nexus) ? 0 : 1; break; default: pr_err("Unknown PERSISTENT_RESERVE_OUT service" " action: 0x%02x\n", cdb[1] & 0x1f); return -EINVAL; } break; case RELEASE_6: case RELEASE_10: /* Handled by CRH=1 in target_scsi2_reservation_release() */ ret = 0; break; case RESERVE_6: case RESERVE_10: /* Handled by CRH=1 in target_scsi2_reservation_reserve() */ ret = 0; break; case TEST_UNIT_READY: ret = (legacy) ? 1 : 0; /* Conflict for legacy */ break; case MAINTENANCE_IN: switch (cdb[1] & 0x1f) { case MI_MANAGEMENT_PROTOCOL_IN: if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case MI_REPORT_SUPPORTED_OPERATION_CODES: case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: if (legacy) { ret = 1; break; } if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case MI_REPORT_ALIASES: case MI_REPORT_IDENTIFYING_INFORMATION: case MI_REPORT_PRIORITY: case MI_REPORT_TARGET_PGS: case MI_REPORT_TIMESTAMP: ret = 0; /* Allowed */ break; default: pr_err("Unknown MI Service Action: 0x%02x\n", (cdb[1] & 0x1f)); return -EINVAL; } break; case ACCESS_CONTROL_IN: case ACCESS_CONTROL_OUT: case INQUIRY: case LOG_SENSE: case SERVICE_ACTION_IN_12: case READ_CAPACITY: case REPORT_LUNS: case REQUEST_SENSE: case PERSISTENT_RESERVE_IN: ret = 0; /*/ Allowed CDBs */ break; default: other_cdb = 1; break; } /* * Case where the CDB is explicitly allowed in the above switch * statement. */ if (!ret && !other_cdb) { pr_debug("Allowing explicit CDB: 0x%02x for %s" " reservation holder\n", cdb[0], core_scsi3_pr_dump_type(pr_reg_type)); return ret; } /* * Check if write exclusive initiator ports *NOT* holding the * WRITE_EXCLUSIVE_* reservation. */ if (we && !registered_nexus) { if (cmd->data_direction == DMA_TO_DEVICE) {Lines 1–500 of 4200. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * Filename: target_core_pr.c * * This file contains SPC-3 compliant persistent reservations and * legacy SPC-2 reservations with compatible reservation handling (CRH=1) * * (c) Copyright 2009-2013 Datera, Inc. * * Nicholas A. Bellinger <nab@kernel.org> * ******************************************************************************/ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/list.h> #include <linux/vmalloc.h> #include <linux/file.h> #include <linux/fcntl.h> #include <linux/fs.h> #include <linux/fs_struct.h> #include <scsi/scsi_proto.h> #include <linux/unaligned.h> #include <target/target_core_base.h> #include <target/target_core_backend.h> #include <target/target_core_fabric.h> #include "target_core_internal.h" #include "target_core_pr.h" #include "target_core_ua.h" /* * Used for Specify Initiator Ports Capable Bit (SPEC_I_PT) */ struct pr_transport_id_holder { struct t10_pr_registration *dest_pr_reg; struct se_portal_group *dest_tpg; struct se_node_acl *dest_node_acl; struct se_dev_entry *dest_se_deve; struct list_head dest_list; }; void core_pr_dump_initiator_port( struct t10_pr_registration *pr_reg, char *buf, u32 size) { if (!pr_reg->isid_present_at_reg) { buf[0] = '\0'; return; } snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid); } enum register_type { REGISTER, REGISTER_AND_IGNORE_EXISTING_KEY, REGISTER_AND_MOVE, }; enum preempt_type { PREEMPT, PREEMPT_AND_ABORT, }; static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *, struct t10_pr_registration *, int, int); static int is_reservation_holder( struct t10_pr_registration *pr_res_holder, struct t10_pr_registration *pr_reg) { int pr_res_type; if (pr_res_holder) { pr_res_type = pr_res_holder->pr_res_type; return pr_res_holder == pr_reg || pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG; } return 0; } static sense_reason_t target_scsi2_reservation_check(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; switch (cmd->t_task_cdb[0]) { case INQUIRY: case RELEASE_6: case RELEASE_10: return 0; default: break; } if (!dev->reservation_holder || !sess) return 0; if (dev->reservation_holder->se_node_acl != sess->se_node_acl) return TCM_RESERVATION_CONFLICT; if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) { if (dev->dev_res_bin_isid != sess->sess_bin_isid) return TCM_RESERVATION_CONFLICT; } return 0; } static struct t10_pr_registration *core_scsi3_locate_pr_reg(struct se_device *, struct se_node_acl *, struct se_session *); static void core_scsi3_put_pr_reg(struct t10_pr_registration *); static int target_check_scsi2_reservation_conflict(struct se_cmd *cmd) { struct se_session *se_sess = cmd->se_sess; struct se_device *dev = cmd->se_dev; struct t10_pr_registration *pr_reg; struct t10_reservation *pr_tmpl = &dev->t10_pr; int conflict = 0; pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl, se_sess); if (pr_reg) { /* * From spc4r17 5.7.3 Exceptions to SPC-2 RESERVE and RELEASE * behavior * * A RESERVE(6) or RESERVE(10) command shall complete with GOOD * status, but no reservation shall be established and the * persistent reservation shall not be changed, if the command * is received from a) and b) below. * * A RELEASE(6) or RELEASE(10) command shall complete with GOOD * status, but the persistent reservation shall not be released, * if the command is received from a) and b) * * a) An I_T nexus that is a persistent reservation holder; or * b) An I_T nexus that is registered if a registrants only or * all registrants type persistent reservation is present. * * In all other cases, a RESERVE(6) command, RESERVE(10) command, * RELEASE(6) command, or RELEASE(10) command shall be processed * as defined in SPC-2. */ if (pr_reg->pr_res_holder) { core_scsi3_put_pr_reg(pr_reg); return 1; } if ((pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY) || (pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY) || (pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) || (pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) { core_scsi3_put_pr_reg(pr_reg); return 1; } core_scsi3_put_pr_reg(pr_reg); conflict = 1; } else { /* * Following spc2r20 5.5.1 Reservations overview: * * If a logical unit has executed a PERSISTENT RESERVE OUT * command with the REGISTER or the REGISTER AND IGNORE * EXISTING KEY service action and is still registered by any * initiator, all RESERVE commands and all RELEASE commands * regardless of initiator shall conflict and shall terminate * with a RESERVATION CONFLICT status. */ spin_lock(&pr_tmpl->registration_lock); conflict = (list_empty(&pr_tmpl->registration_list)) ? 0 : 1; spin_unlock(&pr_tmpl->registration_lock); } if (conflict) { pr_err("Received legacy SPC-2 RESERVE/RELEASE" " while active SPC-3 registrations exist," " returning RESERVATION_CONFLICT\n"); return -EBUSY; } return 0; } void target_release_reservation(struct se_device *dev) { dev->reservation_holder = NULL; dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS; if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) { dev->dev_res_bin_isid = 0; dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS_WITH_ISID; } } sense_reason_t target_scsi2_reservation_release(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; struct se_portal_group *tpg; int rc; if (!sess || !sess->se_tpg) goto out; rc = target_check_scsi2_reservation_conflict(cmd); if (rc == 1) goto out; if (rc < 0) return TCM_RESERVATION_CONFLICT; spin_lock(&dev->dev_reservation_lock); if (!dev->reservation_holder || !sess) goto out_unlock; if (dev->reservation_holder->se_node_acl != sess->se_node_acl) goto out_unlock; if (dev->dev_res_bin_isid != sess->sess_bin_isid) goto out_unlock; target_release_reservation(dev); tpg = sess->se_tpg; pr_debug("SCSI-2 Released reservation for %s LUN: %llu ->" " MAPPED LUN: %llu for %s\n", tpg->se_tpg_tfo->fabric_name, cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); out_unlock: spin_unlock(&dev->dev_reservation_lock); out: target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; } sense_reason_t target_scsi2_reservation_reserve(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; struct se_portal_group *tpg; sense_reason_t ret = 0; int rc; if ((cmd->t_task_cdb[1] & 0x01) && (cmd->t_task_cdb[1] & 0x02)) { pr_err("LongIO and Obsolete Bits set, returning ILLEGAL_REQUEST\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } /* * This is currently the case for target_core_mod passthrough struct se_cmd * ops */ if (!sess || !sess->se_tpg) goto out; rc = target_check_scsi2_reservation_conflict(cmd); if (rc == 1) goto out; if (rc < 0) return TCM_RESERVATION_CONFLICT; tpg = sess->se_tpg; spin_lock(&dev->dev_reservation_lock); if (dev->reservation_holder && dev->reservation_holder->se_node_acl != sess->se_node_acl) { pr_err("SCSI-2 RESERVATION CONFLICT for %s fabric\n", tpg->se_tpg_tfo->fabric_name); pr_err("Original reserver LUN: %llu %s\n", cmd->se_lun->unpacked_lun, dev->reservation_holder->se_node_acl->initiatorname); pr_err("Current attempt - LUN: %llu -> MAPPED LUN: %llu" " from %s \n", cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); ret = TCM_RESERVATION_CONFLICT; goto out_unlock; } dev->reservation_holder = sess; dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS; if (sess->sess_bin_isid != 0) { dev->dev_res_bin_isid = sess->sess_bin_isid; dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS_WITH_ISID; } pr_debug("SCSI-2 Reserved %s LUN: %llu -> MAPPED LUN: %llu" " for %s\n", tpg->se_tpg_tfo->fabric_name, cmd->se_lun->unpacked_lun, cmd->orig_fe_lun, sess->se_node_acl->initiatorname); out_unlock: spin_unlock(&dev->dev_reservation_lock); out: if (!ret) target_complete_cmd(cmd, SAM_STAT_GOOD); return ret; } /* * Begin SPC-3/SPC-4 Persistent Reservations emulation support * * This function is called by those initiator ports who are *NOT* * the active PR reservation holder when a reservation is present. */ static int core_scsi3_pr_seq_non_holder(struct se_cmd *cmd, u32 pr_reg_type, bool isid_mismatch) { unsigned char *cdb = cmd->t_task_cdb; struct se_session *se_sess = cmd->se_sess; struct se_node_acl *nacl = se_sess->se_node_acl; int other_cdb = 0; int registered_nexus = 0, ret = 1; /* Conflict by default */ int all_reg = 0, reg_only = 0; /* ALL_REG, REG_ONLY */ int we = 0; /* Write Exclusive */ int legacy = 0; /* Act like a legacy device and return * RESERVATION CONFLICT on some CDBs */ if (isid_mismatch) { registered_nexus = 0; } else { struct se_dev_entry *se_deve; rcu_read_lock(); se_deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun); if (se_deve) registered_nexus = test_bit(DEF_PR_REG_ACTIVE, &se_deve->deve_flags); rcu_read_unlock(); } switch (pr_reg_type) { case PR_TYPE_WRITE_EXCLUSIVE: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS: /* * Some commands are only allowed for the persistent reservation * holder. */ break; case PR_TYPE_WRITE_EXCLUSIVE_REGONLY: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY: /* * Some commands are only allowed for registered I_T Nexuses. */ reg_only = 1; break; case PR_TYPE_WRITE_EXCLUSIVE_ALLREG: we = 1; fallthrough; case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG: /* * Each registered I_T Nexus is a reservation holder. */ all_reg = 1; break; default: return -EINVAL; } /* * Referenced from spc4r17 table 45 for *NON* PR holder access */ switch (cdb[0]) { case SECURITY_PROTOCOL_IN: if (registered_nexus) return 0; ret = (we) ? 0 : 1; break; case MODE_SENSE: case MODE_SENSE_10: case READ_ATTRIBUTE: case READ_BUFFER: case RECEIVE_DIAGNOSTIC: if (legacy) { ret = 1; break; } if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case PERSISTENT_RESERVE_OUT: /* * This follows PERSISTENT_RESERVE_OUT service actions that * are allowed in the presence of various reservations. * See spc4r17, table 46 */ switch (cdb[1] & 0x1f) { case PRO_CLEAR: case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: ret = (registered_nexus) ? 0 : 1; break; case PRO_REGISTER: case PRO_REGISTER_AND_IGNORE_EXISTING_KEY: ret = 0; break; case PRO_REGISTER_AND_MOVE: case PRO_RESERVE: ret = 1; break; case PRO_RELEASE: ret = (registered_nexus) ? 0 : 1; break; default: pr_err("Unknown PERSISTENT_RESERVE_OUT service" " action: 0x%02x\n", cdb[1] & 0x1f); return -EINVAL; } break; case RELEASE_6: case RELEASE_10: /* Handled by CRH=1 in target_scsi2_reservation_release() */ ret = 0; break; case RESERVE_6: case RESERVE_10: /* Handled by CRH=1 in target_scsi2_reservation_reserve() */ ret = 0; break; case TEST_UNIT_READY: ret = (legacy) ? 1 : 0; /* Conflict for legacy */ break; case MAINTENANCE_IN: switch (cdb[1] & 0x1f) { case MI_MANAGEMENT_PROTOCOL_IN: if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case MI_REPORT_SUPPORTED_OPERATION_CODES: case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: if (legacy) { ret = 1; break; } if (registered_nexus) { ret = 0; break; } ret = (we) ? 0 : 1; /* Allowed Write Exclusive */ break; case MI_REPORT_ALIASES: case MI_REPORT_IDENTIFYING_INFORMATION: case MI_REPORT_PRIORITY: case MI_REPORT_TARGET_PGS: case MI_REPORT_TIMESTAMP: ret = 0; /* Allowed */ break; default: pr_err("Unknown MI Service Action: 0x%02x\n", (cdb[1] & 0x1f)); return -EINVAL; } break; case ACCESS_CONTROL_IN: case ACCESS_CONTROL_OUT: case INQUIRY: case LOG_SENSE: case SERVICE_ACTION_IN_12: case READ_CAPACITY: case REPORT_LUNS: case REQUEST_SENSE: case PERSISTENT_RESERVE_IN: ret = 0; /*/ Allowed CDBs */ break; default: other_cdb = 1; break; } /* * Case where the CDB is explicitly allowed in the above switch * statement. */ if (!ret && !other_cdb) { pr_debug("Allowing explicit CDB: 0x%02x for %s" " reservation holder\n", cdb[0], core_scsi3_pr_dump_type(pr_reg_type)); return ret; } /* * Check if write exclusive initiator ports *NOT* holding the * WRITE_EXCLUSIVE_* reservation. */ if (we && !registered_nexus) { if (cmd->data_direction == DMA_TO_DEVICE) {