4200 lines · 124.8 KB · C
RawDownload
// 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.