diff options
Diffstat (limited to 'drivers/target/target_core_alua.c')
| -rw-r--r-- | drivers/target/target_core_alua.c | 804 | 
1 files changed, 590 insertions, 214 deletions
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 47244102281..fbc5ebb5f76 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -41,10 +41,13 @@  #include "target_core_alua.h"  #include "target_core_ua.h" -static sense_reason_t core_alua_check_transition(int state, int *primary); +static sense_reason_t core_alua_check_transition(int state, int valid, +						 int *primary);  static int core_alua_set_tg_pt_secondary_state(  		struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, -		struct se_port *port, int explict, int offline); +		struct se_port *port, int explicit, int offline); + +static char *core_alua_dump_state(int state);  static u16 alua_lu_gps_counter;  static u32 alua_lu_gps_count; @@ -55,6 +58,86 @@ static LIST_HEAD(lu_gps_list);  struct t10_alua_lu_gp *default_lu_gp;  /* + * REPORT REFERRALS + * + * See sbc3r35 section 5.23 + */ +sense_reason_t +target_emulate_report_referrals(struct se_cmd *cmd) +{ +	struct se_device *dev = cmd->se_dev; +	struct t10_alua_lba_map *map; +	struct t10_alua_lba_map_member *map_mem; +	unsigned char *buf; +	u32 rd_len = 0, off; + +	if (cmd->data_length < 4) { +		pr_warn("REPORT REFERRALS allocation length %u too" +			" small\n", cmd->data_length); +		return TCM_INVALID_CDB_FIELD; +	} + +	buf = transport_kmap_data_sg(cmd); +	if (!buf) +		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + +	off = 4; +	spin_lock(&dev->t10_alua.lba_map_lock); +	if (list_empty(&dev->t10_alua.lba_map_list)) { +		spin_unlock(&dev->t10_alua.lba_map_lock); +		transport_kunmap_data_sg(cmd); + +		return TCM_UNSUPPORTED_SCSI_OPCODE; +	} + +	list_for_each_entry(map, &dev->t10_alua.lba_map_list, +			    lba_map_list) { +		int desc_num = off + 3; +		int pg_num; + +		off += 4; +		if (cmd->data_length > off) +			put_unaligned_be64(map->lba_map_first_lba, &buf[off]); +		off += 8; +		if (cmd->data_length > off) +			put_unaligned_be64(map->lba_map_last_lba, &buf[off]); +		off += 8; +		rd_len += 20; +		pg_num = 0; +		list_for_each_entry(map_mem, &map->lba_map_mem_list, +				    lba_map_mem_list) { +			int alua_state = map_mem->lba_map_mem_alua_state; +			int alua_pg_id = map_mem->lba_map_mem_alua_pg_id; + +			if (cmd->data_length > off) +				buf[off] = alua_state & 0x0f; +			off += 2; +			if (cmd->data_length > off) +				buf[off] = (alua_pg_id >> 8) & 0xff; +			off++; +			if (cmd->data_length > off) +				buf[off] = (alua_pg_id & 0xff); +			off++; +			rd_len += 4; +			pg_num++; +		} +		if (cmd->data_length > desc_num) +			buf[desc_num] = pg_num; +	} +	spin_unlock(&dev->t10_alua.lba_map_lock); + +	/* +	 * Set the RETURN DATA LENGTH set in the header of the DataIN Payload +	 */ +	put_unaligned_be16(rd_len, &buf[2]); + +	transport_kunmap_data_sg(cmd); + +	target_complete_cmd(cmd, GOOD); +	return 0; +} + +/*   * REPORT_TARGET_PORT_GROUPS   *   * See spc4r17 section 6.27 @@ -117,12 +200,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)  		/*  		 * Set supported ASYMMETRIC ACCESS State bits  		 */ -		buf[off] = 0x80; /* T_SUP */ -		buf[off] |= 0x40; /* O_SUP */ -		buf[off] |= 0x8; /* U_SUP */ -		buf[off] |= 0x4; /* S_SUP */ -		buf[off] |= 0x2; /* AN_SUP */ -		buf[off++] |= 0x1; /* AO_SUP */ +		buf[off++] |= tg_pt_gp->tg_pt_gp_alua_supported_states;  		/*  		 * TARGET PORT GROUP  		 */ @@ -175,7 +253,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)  	if (ext_hdr != 0) {  		buf[4] = 0x10;  		/* -		 * Set the implict transition time (in seconds) for the application +		 * Set the implicit transition time (in seconds) for the application  		 * client to use as a base for it's transition timeout value.  		 *  		 * Use the current tg_pt_gp_mem -> tg_pt_gp membership from the LUN @@ -188,7 +266,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)  			spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);  			tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;  			if (tg_pt_gp) -				buf[5] = tg_pt_gp->tg_pt_gp_implict_trans_secs; +				buf[5] = tg_pt_gp->tg_pt_gp_implicit_trans_secs;  			spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);  		}  	} @@ -199,7 +277,7 @@ target_emulate_report_target_port_groups(struct se_cmd *cmd)  }  /* - * SET_TARGET_PORT_GROUPS for explict ALUA operation. + * SET_TARGET_PORT_GROUPS for explicit ALUA operation.   *   * See spc4r17 section 6.35   */ @@ -215,7 +293,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  	unsigned char *ptr;  	sense_reason_t rc = TCM_NO_SENSE;  	u32 len = 4; /* Skip over RESERVED area in header */ -	int alua_access_state, primary = 0; +	int alua_access_state, primary = 0, valid_states;  	u16 tg_pt_id, rtpi;  	if (!l_port) @@ -232,7 +310,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  		return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;  	/* -	 * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed +	 * Determine if explicit ALUA via SET_TARGET_PORT_GROUPS is allowed  	 * for the local tg_pt_gp.  	 */  	l_tg_pt_gp_mem = l_port->sep_alua_tg_pt_gp_mem; @@ -251,12 +329,13 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  	}  	spin_unlock(&l_tg_pt_gp_mem->tg_pt_gp_mem_lock); -	if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA)) { +	if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)) {  		pr_debug("Unable to process SET_TARGET_PORT_GROUPS" -				" while TPGS_EXPLICT_ALUA is disabled\n"); +				" while TPGS_EXPLICIT_ALUA is disabled\n");  		rc = TCM_UNSUPPORTED_SCSI_OPCODE;  		goto out;  	} +	valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;  	ptr = &buf[4]; /* Skip over RESERVED area in header */ @@ -268,7 +347,8 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  		 * the state is a primary or secondary target port asymmetric  		 * access state.  		 */ -		rc = core_alua_check_transition(alua_access_state, &primary); +		rc = core_alua_check_transition(alua_access_state, +						valid_states, &primary);  		if (rc) {  			/*  			 * If the SET TARGET PORT GROUPS attempts to establish @@ -313,7 +393,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  					continue;  				atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); -				smp_mb__after_atomic_inc(); +				smp_mb__after_atomic();  				spin_unlock(&dev->t10_alua.tg_pt_gps_lock); @@ -324,13 +404,13 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd)  				spin_lock(&dev->t10_alua.tg_pt_gps_lock);  				atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); -				smp_mb__after_atomic_dec(); +				smp_mb__after_atomic();  				break;  			}  			spin_unlock(&dev->t10_alua.tg_pt_gps_lock);  		} else {  			/* -			 * Extact the RELATIVE TARGET PORT IDENTIFIER to identify +			 * Extract the RELATIVE TARGET PORT IDENTIFIER to identify  			 * the Target Port in question for the the incoming  			 * SET_TARGET_PORT_GROUPS op.  			 */ @@ -375,11 +455,26 @@ out:  	return rc;  } -static inline int core_alua_state_nonoptimized( +static inline void set_ascq(struct se_cmd *cmd, u8 alua_ascq) +{ +	/* +	 * Set SCSI additional sense code (ASC) to 'LUN Not Accessible'; +	 * The ALUA additional sense code qualifier (ASCQ) is determined +	 * by the ALUA primary or secondary access state.. +	 */ +	pr_debug("[%s]: ALUA TG Port not available, " +		"SenseKey: NOT_READY, ASC/ASCQ: " +		"0x04/0x%02x\n", +		cmd->se_tfo->get_fabric_name(), alua_ascq); + +	cmd->scsi_asc = 0x04; +	cmd->scsi_ascq = alua_ascq; +} + +static inline void core_alua_state_nonoptimized(  	struct se_cmd *cmd,  	unsigned char *cdb, -	int nonop_delay_msecs, -	u8 *alua_ascq) +	int nonop_delay_msecs)  {  	/*  	 * Set SCF_ALUA_NON_OPTIMIZED here, this value will be checked @@ -388,13 +483,85 @@ static inline int core_alua_state_nonoptimized(  	 */  	cmd->se_cmd_flags |= SCF_ALUA_NON_OPTIMIZED;  	cmd->alua_nonop_delay = nonop_delay_msecs; +} + +static inline int core_alua_state_lba_dependent( +	struct se_cmd *cmd, +	struct t10_alua_tg_pt_gp *tg_pt_gp) +{ +	struct se_device *dev = cmd->se_dev; +	u64 segment_size, segment_mult, sectors, lba; + +	/* Only need to check for cdb actually containing LBAs */ +	if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB)) +		return 0; + +	spin_lock(&dev->t10_alua.lba_map_lock); +	segment_size = dev->t10_alua.lba_map_segment_size; +	segment_mult = dev->t10_alua.lba_map_segment_multiplier; +	sectors = cmd->data_length / dev->dev_attrib.block_size; + +	lba = cmd->t_task_lba; +	while (lba < cmd->t_task_lba + sectors) { +		struct t10_alua_lba_map *cur_map = NULL, *map; +		struct t10_alua_lba_map_member *map_mem; + +		list_for_each_entry(map, &dev->t10_alua.lba_map_list, +				    lba_map_list) { +			u64 start_lba, last_lba; +			u64 first_lba = map->lba_map_first_lba; + +			if (segment_mult) { +				u64 tmp = lba; +				start_lba = do_div(tmp, segment_size * segment_mult); + +				last_lba = first_lba + segment_size - 1; +				if (start_lba >= first_lba && +				    start_lba <= last_lba) { +					lba += segment_size; +					cur_map = map; +					break; +				} +			} else { +				last_lba = map->lba_map_last_lba; +				if (lba >= first_lba && lba <= last_lba) { +					lba = last_lba + 1; +					cur_map = map; +					break; +				} +			} +		} +		if (!cur_map) { +			spin_unlock(&dev->t10_alua.lba_map_lock); +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); +			return 1; +		} +		list_for_each_entry(map_mem, &cur_map->lba_map_mem_list, +				    lba_map_mem_list) { +			if (map_mem->lba_map_mem_alua_pg_id != +			    tg_pt_gp->tg_pt_gp_id) +				continue; +			switch(map_mem->lba_map_mem_alua_state) { +			case ALUA_ACCESS_STATE_STANDBY: +				spin_unlock(&dev->t10_alua.lba_map_lock); +				set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); +				return 1; +			case ALUA_ACCESS_STATE_UNAVAILABLE: +				spin_unlock(&dev->t10_alua.lba_map_lock); +				set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); +				return 1; +			default: +				break; +			} +		} +	} +	spin_unlock(&dev->t10_alua.lba_map_lock);  	return 0;  }  static inline int core_alua_state_standby(  	struct se_cmd *cmd, -	unsigned char *cdb, -	u8 *alua_ascq) +	unsigned char *cdb)  {  	/*  	 * Allowed CDBs for ALUA_ACCESS_STATE_STANDBY as defined by @@ -409,13 +576,22 @@ static inline int core_alua_state_standby(  	case REPORT_LUNS:  	case RECEIVE_DIAGNOSTIC:  	case SEND_DIAGNOSTIC: +	case READ_CAPACITY:  		return 0; +	case SERVICE_ACTION_IN: +		switch (cdb[1] & 0x1f) { +		case SAI_READ_CAPACITY_16: +			return 0; +		default: +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); +			return 1; +		}  	case MAINTENANCE_IN:  		switch (cdb[1] & 0x1f) {  		case MI_REPORT_TARGET_PGS:  			return 0;  		default: -			*alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);  			return 1;  		}  	case MAINTENANCE_OUT: @@ -423,7 +599,7 @@ static inline int core_alua_state_standby(  		case MO_SET_TARGET_PGS:  			return 0;  		default: -			*alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);  			return 1;  		}  	case REQUEST_SENSE: @@ -433,7 +609,7 @@ static inline int core_alua_state_standby(  	case WRITE_BUFFER:  		return 0;  	default: -		*alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; +		set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);  		return 1;  	} @@ -442,8 +618,7 @@ static inline int core_alua_state_standby(  static inline int core_alua_state_unavailable(  	struct se_cmd *cmd, -	unsigned char *cdb, -	u8 *alua_ascq) +	unsigned char *cdb)  {  	/*  	 * Allowed CDBs for ALUA_ACCESS_STATE_UNAVAILABLE as defined by @@ -458,7 +633,7 @@ static inline int core_alua_state_unavailable(  		case MI_REPORT_TARGET_PGS:  			return 0;  		default: -			*alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);  			return 1;  		}  	case MAINTENANCE_OUT: @@ -466,7 +641,7 @@ static inline int core_alua_state_unavailable(  		case MO_SET_TARGET_PGS:  			return 0;  		default: -			*alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; +			set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);  			return 1;  		}  	case REQUEST_SENSE: @@ -474,7 +649,7 @@ static inline int core_alua_state_unavailable(  	case WRITE_BUFFER:  		return 0;  	default: -		*alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; +		set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);  		return 1;  	} @@ -483,11 +658,10 @@ static inline int core_alua_state_unavailable(  static inline int core_alua_state_transition(  	struct se_cmd *cmd, -	unsigned char *cdb, -	u8 *alua_ascq) +	unsigned char *cdb)  {  	/* -	 * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITIO as defined by +	 * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITION as defined by  	 * spc4r17 section 5.9.2.5  	 */  	switch (cdb[0]) { @@ -499,7 +673,7 @@ static inline int core_alua_state_transition(  		case MI_REPORT_TARGET_PGS:  			return 0;  		default: -			*alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION; +			set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);  			return 1;  		}  	case REQUEST_SENSE: @@ -507,7 +681,7 @@ static inline int core_alua_state_transition(  	case WRITE_BUFFER:  		return 0;  	default: -		*alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION; +		set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);  		return 1;  	} @@ -515,9 +689,9 @@ static inline int core_alua_state_transition(  }  /* - * return 1: Is used to signal LUN not accecsable, and check condition/not ready + * return 1: Is used to signal LUN not accessible, and check condition/not ready   * return 0: Used to signal success - * reutrn -1: Used to signal failure, and invalid cdb field + * return -1: Used to signal failure, and invalid cdb field   */  sense_reason_t  target_alua_state_check(struct se_cmd *cmd) @@ -529,8 +703,6 @@ target_alua_state_check(struct se_cmd *cmd)  	struct t10_alua_tg_pt_gp *tg_pt_gp;  	struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;  	int out_alua_state, nonop_delay_msecs; -	u8 alua_ascq; -	int ret;  	if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)  		return 0; @@ -546,9 +718,8 @@ target_alua_state_check(struct se_cmd *cmd)  	if (atomic_read(&port->sep_tg_pt_secondary_offline)) {  		pr_debug("ALUA: Got secondary offline status for local"  				" target port\n"); -		alua_ascq = ASCQ_04H_ALUA_OFFLINE; -		ret = 1; -		goto out; +		set_ascq(cmd, ASCQ_04H_ALUA_OFFLINE); +		return TCM_CHECK_CONDITION_NOT_READY;  	}  	 /*  	 * Second, obtain the struct t10_alua_tg_pt_gp_member pointer to the @@ -566,27 +737,33 @@ target_alua_state_check(struct se_cmd *cmd)  	nonop_delay_msecs = tg_pt_gp->tg_pt_gp_nonop_delay_msecs;  	spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);  	/* -	 * Process ALUA_ACCESS_STATE_ACTIVE_OPTMIZED in a separate conditional +	 * Process ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED in a separate conditional  	 * statement so the compiler knows explicitly to check this case first.  	 * For the Optimized ALUA access state case, we want to process the  	 * incoming fabric cmd ASAP..  	 */ -	if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTMIZED) +	if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED)  		return 0;  	switch (out_alua_state) {  	case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED: -		ret = core_alua_state_nonoptimized(cmd, cdb, -					nonop_delay_msecs, &alua_ascq); +		core_alua_state_nonoptimized(cmd, cdb, nonop_delay_msecs);  		break;  	case ALUA_ACCESS_STATE_STANDBY: -		ret = core_alua_state_standby(cmd, cdb, &alua_ascq); +		if (core_alua_state_standby(cmd, cdb)) +			return TCM_CHECK_CONDITION_NOT_READY;  		break;  	case ALUA_ACCESS_STATE_UNAVAILABLE: -		ret = core_alua_state_unavailable(cmd, cdb, &alua_ascq); +		if (core_alua_state_unavailable(cmd, cdb)) +			return TCM_CHECK_CONDITION_NOT_READY;  		break;  	case ALUA_ACCESS_STATE_TRANSITION: -		ret = core_alua_state_transition(cmd, cdb, &alua_ascq); +		if (core_alua_state_transition(cmd, cdb)) +			return TCM_CHECK_CONDITION_NOT_READY; +		break; +	case ALUA_ACCESS_STATE_LBA_DEPENDENT: +		if (core_alua_state_lba_dependent(cmd, tg_pt_gp)) +			return TCM_CHECK_CONDITION_NOT_READY;  		break;  	/*  	 * OFFLINE is a secondary ALUA target port group access state, that is @@ -599,41 +776,43 @@ target_alua_state_check(struct se_cmd *cmd)  		return TCM_INVALID_CDB_FIELD;  	} -out: -	if (ret > 0) { -		/* -		 * Set SCSI additional sense code (ASC) to 'LUN Not Accessible'; -		 * The ALUA additional sense code qualifier (ASCQ) is determined -		 * by the ALUA primary or secondary access state.. -		 */ -		pr_debug("[%s]: ALUA TG Port not available, " -			"SenseKey: NOT_READY, ASC/ASCQ: " -			"0x04/0x%02x\n", -			cmd->se_tfo->get_fabric_name(), alua_ascq); - -		cmd->scsi_asc = 0x04; -		cmd->scsi_ascq = alua_ascq; -		return TCM_CHECK_CONDITION_NOT_READY; -	} -  	return 0;  }  /* - * Check implict and explict ALUA state change request. + * Check implicit and explicit ALUA state change request.   */  static sense_reason_t -core_alua_check_transition(int state, int *primary) +core_alua_check_transition(int state, int valid, int *primary)  { +	/* +	 * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are +	 * defined as primary target port asymmetric access states. +	 */  	switch (state) { -	case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED: +	case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED: +		if (!(valid & ALUA_AO_SUP)) +			goto not_supported; +		*primary = 1; +		break;  	case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED: +		if (!(valid & ALUA_AN_SUP)) +			goto not_supported; +		*primary = 1; +		break;  	case ALUA_ACCESS_STATE_STANDBY: +		if (!(valid & ALUA_S_SUP)) +			goto not_supported; +		*primary = 1; +		break;  	case ALUA_ACCESS_STATE_UNAVAILABLE: -		/* -		 * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are -		 * defined as primary target port asymmetric access states. -		 */ +		if (!(valid & ALUA_U_SUP)) +			goto not_supported; +		*primary = 1; +		break; +	case ALUA_ACCESS_STATE_LBA_DEPENDENT: +		if (!(valid & ALUA_LBD_SUP)) +			goto not_supported;  		*primary = 1;  		break;  	case ALUA_ACCESS_STATE_OFFLINE: @@ -641,29 +820,46 @@ core_alua_check_transition(int state, int *primary)  		 * OFFLINE state is defined as a secondary target port  		 * asymmetric access state.  		 */ +		if (!(valid & ALUA_O_SUP)) +			goto not_supported;  		*primary = 0;  		break; +	case ALUA_ACCESS_STATE_TRANSITION: +		/* +		 * Transitioning is set internally, and +		 * cannot be selected manually. +		 */ +		goto not_supported;  	default:  		pr_err("Unknown ALUA access state: 0x%02x\n", state);  		return TCM_INVALID_PARAMETER_LIST;  	}  	return 0; + +not_supported: +	pr_err("ALUA access state %s not supported", +	       core_alua_dump_state(state)); +	return TCM_INVALID_PARAMETER_LIST;  }  static char *core_alua_dump_state(int state)  {  	switch (state) { -	case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED: +	case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:  		return "Active/Optimized";  	case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:  		return "Active/NonOptimized"; +	case ALUA_ACCESS_STATE_LBA_DEPENDENT: +		return "LBA Dependent";  	case ALUA_ACCESS_STATE_STANDBY:  		return "Standby";  	case ALUA_ACCESS_STATE_UNAVAILABLE:  		return "Unavailable";  	case ALUA_ACCESS_STATE_OFFLINE:  		return "Offline"; +	case ALUA_ACCESS_STATE_TRANSITION: +		return "Transitioning";  	default:  		return "Unknown";  	} @@ -676,10 +872,10 @@ char *core_alua_dump_status(int status)  	switch (status) {  	case ALUA_STATUS_NONE:  		return "None"; -	case ALUA_STATUS_ALTERED_BY_EXPLICT_STPG: -		return "Altered by Explict STPG"; -	case ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA: -		return "Altered by Implict ALUA"; +	case ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG: +		return "Altered by Explicit STPG"; +	case ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA: +		return "Altered by Implicit ALUA";  	default:  		return "Unknown";  	} @@ -740,58 +936,49 @@ static int core_alua_write_tpg_metadata(   * Called with tg_pt_gp->tg_pt_gp_md_mutex held   */  static int core_alua_update_tpg_primary_metadata( -	struct t10_alua_tg_pt_gp *tg_pt_gp, -	int primary_state, -	unsigned char *md_buf) +	struct t10_alua_tg_pt_gp *tg_pt_gp)  { +	unsigned char *md_buf;  	struct t10_wwn *wwn = &tg_pt_gp->tg_pt_gp_dev->t10_wwn;  	char path[ALUA_METADATA_PATH_LEN]; -	int len; +	int len, rc; + +	md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL); +	if (!md_buf) { +		pr_err("Unable to allocate buf for ALUA metadata\n"); +		return -ENOMEM; +	}  	memset(path, 0, ALUA_METADATA_PATH_LEN); -	len = snprintf(md_buf, tg_pt_gp->tg_pt_gp_md_buf_len, +	len = snprintf(md_buf, ALUA_MD_BUF_LEN,  			"tg_pt_gp_id=%hu\n"  			"alua_access_state=0x%02x\n"  			"alua_access_status=0x%02x\n", -			tg_pt_gp->tg_pt_gp_id, primary_state, +			tg_pt_gp->tg_pt_gp_id, +			tg_pt_gp->tg_pt_gp_alua_pending_state,  			tg_pt_gp->tg_pt_gp_alua_access_status);  	snprintf(path, ALUA_METADATA_PATH_LEN,  		"/var/target/alua/tpgs_%s/%s", &wwn->unit_serial[0],  		config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item)); -	return core_alua_write_tpg_metadata(path, md_buf, len); +	rc = core_alua_write_tpg_metadata(path, md_buf, len); +	kfree(md_buf); +	return rc;  } -static int core_alua_do_transition_tg_pt( -	struct t10_alua_tg_pt_gp *tg_pt_gp, -	struct se_port *l_port, -	struct se_node_acl *nacl, -	unsigned char *md_buf, -	int new_state, -	int explict) +static void core_alua_do_transition_tg_pt_work(struct work_struct *work)  { +	struct t10_alua_tg_pt_gp *tg_pt_gp = container_of(work, +		struct t10_alua_tg_pt_gp, tg_pt_gp_transition_work.work); +	struct se_device *dev = tg_pt_gp->tg_pt_gp_dev;  	struct se_dev_entry *se_deve;  	struct se_lun_acl *lacl;  	struct se_port *port;  	struct t10_alua_tg_pt_gp_member *mem; -	int old_state = 0; -	/* -	 * Save the old primary ALUA access state, and set the current state -	 * to ALUA_ACCESS_STATE_TRANSITION. -	 */ -	old_state = atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state); -	atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, -			ALUA_ACCESS_STATE_TRANSITION); -	tg_pt_gp->tg_pt_gp_alua_access_status = (explict) ? -				ALUA_STATUS_ALTERED_BY_EXPLICT_STPG : -				ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA; -	/* -	 * Check for the optional ALUA primary state transition delay -	 */ -	if (tg_pt_gp->tg_pt_gp_trans_delay_msecs != 0) -		msleep_interruptible(tg_pt_gp->tg_pt_gp_trans_delay_msecs); +	bool explicit = (tg_pt_gp->tg_pt_gp_alua_access_status == +			 ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG);  	spin_lock(&tg_pt_gp->tg_pt_gp_lock);  	list_for_each_entry(mem, &tg_pt_gp->tg_pt_gp_mem_list, @@ -802,7 +989,7 @@ static int core_alua_do_transition_tg_pt(  		 * change, a device server shall establish a unit attention  		 * condition for the initiator port associated with every I_T  		 * nexus with the additional sense code set to ASYMMETRIC -		 * ACCESS STATE CHAGED. +		 * ACCESS STATE CHANGED.  		 *  		 * After an explicit target port asymmetric access state  		 * change, a device server shall establish a unit attention @@ -812,7 +999,7 @@ static int core_alua_do_transition_tg_pt(  		 * TARGET PORT GROUPS command  		 */  		atomic_inc(&mem->tg_pt_gp_mem_ref_cnt); -		smp_mb__after_atomic_inc(); +		smp_mb__after_atomic();  		spin_unlock(&tg_pt_gp->tg_pt_gp_lock);  		spin_lock_bh(&port->sep_alua_lock); @@ -821,14 +1008,17 @@ static int core_alua_do_transition_tg_pt(  			lacl = se_deve->se_lun_acl;  			/*  			 * se_deve->se_lun_acl pointer may be NULL for a -			 * entry created without explict Node+MappedLUN ACLs +			 * entry created without explicit Node+MappedLUN ACLs  			 */  			if (!lacl)  				continue; -			if (explict && -			   (nacl != NULL) && (nacl == lacl->se_lun_nacl) && -			   (l_port != NULL) && (l_port == port)) +			if ((tg_pt_gp->tg_pt_gp_alua_access_status == +			     ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) && +			   (tg_pt_gp->tg_pt_gp_alua_nacl != NULL) && +			    (tg_pt_gp->tg_pt_gp_alua_nacl == lacl->se_lun_nacl) && +			   (tg_pt_gp->tg_pt_gp_alua_port != NULL) && +			    (tg_pt_gp->tg_pt_gp_alua_port == port))  				continue;  			core_scsi3_ua_allocate(lacl->se_lun_nacl, @@ -839,7 +1029,7 @@ static int core_alua_do_transition_tg_pt(  		spin_lock(&tg_pt_gp->tg_pt_gp_lock);  		atomic_dec(&mem->tg_pt_gp_mem_ref_cnt); -		smp_mb__after_atomic_dec(); +		smp_mb__after_atomic();  	}  	spin_unlock(&tg_pt_gp->tg_pt_gp_lock);  	/* @@ -856,20 +1046,102 @@ static int core_alua_do_transition_tg_pt(  	 */  	if (tg_pt_gp->tg_pt_gp_write_metadata) {  		mutex_lock(&tg_pt_gp->tg_pt_gp_md_mutex); -		core_alua_update_tpg_primary_metadata(tg_pt_gp, -					new_state, md_buf); +		core_alua_update_tpg_primary_metadata(tg_pt_gp);  		mutex_unlock(&tg_pt_gp->tg_pt_gp_md_mutex);  	}  	/*  	 * Set the current primary ALUA access state to the requested new state  	 */ -	atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, new_state); +	atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, +		   tg_pt_gp->tg_pt_gp_alua_pending_state);  	pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu" -		" from primary access state %s to %s\n", (explict) ? "explict" : -		"implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item), -		tg_pt_gp->tg_pt_gp_id, core_alua_dump_state(old_state), -		core_alua_dump_state(new_state)); +		" from primary access state %s to %s\n", (explicit) ? "explicit" : +		"implicit", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item), +		tg_pt_gp->tg_pt_gp_id, +		core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_previous_state), +		core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state)); +	spin_lock(&dev->t10_alua.tg_pt_gps_lock); +	atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); +	smp_mb__after_atomic(); +	spin_unlock(&dev->t10_alua.tg_pt_gps_lock); + +	if (tg_pt_gp->tg_pt_gp_transition_complete) +		complete(tg_pt_gp->tg_pt_gp_transition_complete); +} + +static int core_alua_do_transition_tg_pt( +	struct t10_alua_tg_pt_gp *tg_pt_gp, +	int new_state, +	int explicit) +{ +	struct se_device *dev = tg_pt_gp->tg_pt_gp_dev; +	DECLARE_COMPLETION_ONSTACK(wait); + +	/* Nothing to be done here */ +	if (atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state) == new_state) +		return 0; + +	if (new_state == ALUA_ACCESS_STATE_TRANSITION) +		return -EAGAIN; + +	/* +	 * Flush any pending transitions +	 */ +	if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs && +	    atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state) == +	    ALUA_ACCESS_STATE_TRANSITION) { +		/* Just in case */ +		tg_pt_gp->tg_pt_gp_alua_pending_state = new_state; +		tg_pt_gp->tg_pt_gp_transition_complete = &wait; +		flush_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work); +		wait_for_completion(&wait); +		tg_pt_gp->tg_pt_gp_transition_complete = NULL; +		return 0; +	} + +	/* +	 * Save the old primary ALUA access state, and set the current state +	 * to ALUA_ACCESS_STATE_TRANSITION. +	 */ +	tg_pt_gp->tg_pt_gp_alua_previous_state = +		atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state); +	tg_pt_gp->tg_pt_gp_alua_pending_state = new_state; + +	atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, +			ALUA_ACCESS_STATE_TRANSITION); +	tg_pt_gp->tg_pt_gp_alua_access_status = (explicit) ? +				ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG : +				ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA; + +	/* +	 * Check for the optional ALUA primary state transition delay +	 */ +	if (tg_pt_gp->tg_pt_gp_trans_delay_msecs != 0) +		msleep_interruptible(tg_pt_gp->tg_pt_gp_trans_delay_msecs); + +	/* +	 * Take a reference for workqueue item +	 */ +	spin_lock(&dev->t10_alua.tg_pt_gps_lock); +	atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); +	smp_mb__after_atomic(); +	spin_unlock(&dev->t10_alua.tg_pt_gps_lock); + +	if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) { +		unsigned long transition_tmo; + +		transition_tmo = tg_pt_gp->tg_pt_gp_implicit_trans_secs * HZ; +		queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq, +				   &tg_pt_gp->tg_pt_gp_transition_work, +				   transition_tmo); +	} else { +		tg_pt_gp->tg_pt_gp_transition_complete = &wait; +		queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq, +				   &tg_pt_gp->tg_pt_gp_transition_work, 0); +		wait_for_completion(&wait); +		tg_pt_gp->tg_pt_gp_transition_complete = NULL; +	}  	return 0;  } @@ -880,31 +1152,23 @@ int core_alua_do_port_transition(  	struct se_port *l_port,  	struct se_node_acl *l_nacl,  	int new_state, -	int explict) +	int explicit)  {  	struct se_device *dev; -	struct se_port *port; -	struct se_node_acl *nacl;  	struct t10_alua_lu_gp *lu_gp;  	struct t10_alua_lu_gp_member *lu_gp_mem, *local_lu_gp_mem;  	struct t10_alua_tg_pt_gp *tg_pt_gp; -	unsigned char *md_buf; -	int primary; +	int primary, valid_states, rc = 0; -	if (core_alua_check_transition(new_state, &primary) != 0) +	valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states; +	if (core_alua_check_transition(new_state, valid_states, &primary) != 0)  		return -EINVAL; -	md_buf = kzalloc(l_tg_pt_gp->tg_pt_gp_md_buf_len, GFP_KERNEL); -	if (!md_buf) { -		pr_err("Unable to allocate buf for ALUA metadata\n"); -		return -ENOMEM; -	} -  	local_lu_gp_mem = l_dev->dev_alua_lu_gp_mem;  	spin_lock(&local_lu_gp_mem->lu_gp_mem_lock);  	lu_gp = local_lu_gp_mem->lu_gp;  	atomic_inc(&lu_gp->lu_gp_ref_cnt); -	smp_mb__after_atomic_inc(); +	smp_mb__after_atomic();  	spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock);  	/*  	 * For storage objects that are members of the 'default_lu_gp', @@ -916,12 +1180,13 @@ int core_alua_do_port_transition(  		 * core_alua_do_transition_tg_pt() will always return  		 * success.  		 */ -		core_alua_do_transition_tg_pt(l_tg_pt_gp, l_port, l_nacl, -					md_buf, new_state, explict); +		l_tg_pt_gp->tg_pt_gp_alua_port = l_port; +		l_tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl; +		rc = core_alua_do_transition_tg_pt(l_tg_pt_gp, +						   new_state, explicit);  		atomic_dec(&lu_gp->lu_gp_ref_cnt); -		smp_mb__after_atomic_dec(); -		kfree(md_buf); -		return 0; +		smp_mb__after_atomic(); +		return rc;  	}  	/*  	 * For all other LU groups aside from 'default_lu_gp', walk all of @@ -934,7 +1199,7 @@ int core_alua_do_port_transition(  		dev = lu_gp_mem->lu_gp_mem_dev;  		atomic_inc(&lu_gp_mem->lu_gp_mem_ref_cnt); -		smp_mb__after_atomic_inc(); +		smp_mb__after_atomic();  		spin_unlock(&lu_gp->lu_gp_lock);  		spin_lock(&dev->t10_alua.tg_pt_gps_lock); @@ -946,7 +1211,7 @@ int core_alua_do_port_transition(  				continue;  			/*  			 * If the target behavior port asymmetric access state -			 * is changed for any target port group accessiable via +			 * is changed for any target port group accessible via  			 * a logical unit within a LU group, the target port  			 * behavior group asymmetric access states for the same  			 * target port group accessible via other logical units @@ -956,44 +1221,48 @@ int core_alua_do_port_transition(  				continue;  			if (l_tg_pt_gp == tg_pt_gp) { -				port = l_port; -				nacl = l_nacl; +				tg_pt_gp->tg_pt_gp_alua_port = l_port; +				tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl;  			} else { -				port = NULL; -				nacl = NULL; +				tg_pt_gp->tg_pt_gp_alua_port = NULL; +				tg_pt_gp->tg_pt_gp_alua_nacl = NULL;  			}  			atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); -			smp_mb__after_atomic_inc(); +			smp_mb__after_atomic();  			spin_unlock(&dev->t10_alua.tg_pt_gps_lock);  			/*  			 * core_alua_do_transition_tg_pt() will always return  			 * success.  			 */ -			core_alua_do_transition_tg_pt(tg_pt_gp, port, -					nacl, md_buf, new_state, explict); +			rc = core_alua_do_transition_tg_pt(tg_pt_gp, +					new_state, explicit);  			spin_lock(&dev->t10_alua.tg_pt_gps_lock);  			atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); -			smp_mb__after_atomic_dec(); +			smp_mb__after_atomic(); +			if (rc) +				break;  		}  		spin_unlock(&dev->t10_alua.tg_pt_gps_lock);  		spin_lock(&lu_gp->lu_gp_lock);  		atomic_dec(&lu_gp_mem->lu_gp_mem_ref_cnt); -		smp_mb__after_atomic_dec(); +		smp_mb__after_atomic();  	}  	spin_unlock(&lu_gp->lu_gp_lock); -	pr_debug("Successfully processed LU Group: %s all ALUA TG PT" -		" Group IDs: %hu %s transition to primary state: %s\n", -		config_item_name(&lu_gp->lu_gp_group.cg_item), -		l_tg_pt_gp->tg_pt_gp_id, (explict) ? "explict" : "implict", -		core_alua_dump_state(new_state)); +	if (!rc) { +		pr_debug("Successfully processed LU Group: %s all ALUA TG PT" +			 " Group IDs: %hu %s transition to primary state: %s\n", +			 config_item_name(&lu_gp->lu_gp_group.cg_item), +			 l_tg_pt_gp->tg_pt_gp_id, +			 (explicit) ? "explicit" : "implicit", +			 core_alua_dump_state(new_state)); +	}  	atomic_dec(&lu_gp->lu_gp_ref_cnt); -	smp_mb__after_atomic_dec(); -	kfree(md_buf); -	return 0; +	smp_mb__after_atomic(); +	return rc;  }  /* @@ -1001,13 +1270,18 @@ int core_alua_do_port_transition(   */  static int core_alua_update_tpg_secondary_metadata(  	struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, -	struct se_port *port, -	unsigned char *md_buf, -	u32 md_buf_len) +	struct se_port *port)  { +	unsigned char *md_buf;  	struct se_portal_group *se_tpg = port->sep_tpg;  	char path[ALUA_METADATA_PATH_LEN], wwn[ALUA_SECONDARY_METADATA_WWN_LEN]; -	int len; +	int len, rc; + +	md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL); +	if (!md_buf) { +		pr_err("Unable to allocate buf for ALUA metadata\n"); +		return -ENOMEM; +	}  	memset(path, 0, ALUA_METADATA_PATH_LEN);  	memset(wwn, 0, ALUA_SECONDARY_METADATA_WWN_LEN); @@ -1019,7 +1293,7 @@ static int core_alua_update_tpg_secondary_metadata(  		snprintf(wwn+len, ALUA_SECONDARY_METADATA_WWN_LEN-len, "+%hu",  				se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg)); -	len = snprintf(md_buf, md_buf_len, "alua_tg_pt_offline=%d\n" +	len = snprintf(md_buf, ALUA_MD_BUF_LEN, "alua_tg_pt_offline=%d\n"  			"alua_tg_pt_status=0x%02x\n",  			atomic_read(&port->sep_tg_pt_secondary_offline),  			port->sep_tg_pt_secondary_stat); @@ -1028,18 +1302,19 @@ static int core_alua_update_tpg_secondary_metadata(  			se_tpg->se_tpg_tfo->get_fabric_name(), wwn,  			port->sep_lun->unpacked_lun); -	return core_alua_write_tpg_metadata(path, md_buf, len); +	rc = core_alua_write_tpg_metadata(path, md_buf, len); +	kfree(md_buf); + +	return rc;  }  static int core_alua_set_tg_pt_secondary_state(  	struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,  	struct se_port *port, -	int explict, +	int explicit,  	int offline)  {  	struct t10_alua_tg_pt_gp *tg_pt_gp; -	unsigned char *md_buf; -	u32 md_buf_len;  	int trans_delay_msecs;  	spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); @@ -1060,14 +1335,13 @@ static int core_alua_set_tg_pt_secondary_state(  	else  		atomic_set(&port->sep_tg_pt_secondary_offline, 0); -	md_buf_len = tg_pt_gp->tg_pt_gp_md_buf_len; -	port->sep_tg_pt_secondary_stat = (explict) ? -			ALUA_STATUS_ALTERED_BY_EXPLICT_STPG : -			ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA; +	port->sep_tg_pt_secondary_stat = (explicit) ? +			ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG : +			ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA;  	pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu" -		" to secondary access state: %s\n", (explict) ? "explict" : -		"implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item), +		" to secondary access state: %s\n", (explicit) ? "explicit" : +		"implicit", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),  		tg_pt_gp->tg_pt_gp_id, (offline) ? "OFFLINE" : "ONLINE");  	spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); @@ -1082,23 +1356,115 @@ static int core_alua_set_tg_pt_secondary_state(  	 * secondary state and status  	 */  	if (port->sep_tg_pt_secondary_write_md) { -		md_buf = kzalloc(md_buf_len, GFP_KERNEL); -		if (!md_buf) { -			pr_err("Unable to allocate md_buf for" -				" secondary ALUA access metadata\n"); -			return -ENOMEM; -		}  		mutex_lock(&port->sep_tg_pt_md_mutex); -		core_alua_update_tpg_secondary_metadata(tg_pt_gp_mem, port, -				md_buf, md_buf_len); +		core_alua_update_tpg_secondary_metadata(tg_pt_gp_mem, port);  		mutex_unlock(&port->sep_tg_pt_md_mutex); +	} + +	return 0; +} -		kfree(md_buf); +struct t10_alua_lba_map * +core_alua_allocate_lba_map(struct list_head *list, +			   u64 first_lba, u64 last_lba) +{ +	struct t10_alua_lba_map *lba_map; + +	lba_map = kmem_cache_zalloc(t10_alua_lba_map_cache, GFP_KERNEL); +	if (!lba_map) { +		pr_err("Unable to allocate struct t10_alua_lba_map\n"); +		return ERR_PTR(-ENOMEM);  	} +	INIT_LIST_HEAD(&lba_map->lba_map_mem_list); +	lba_map->lba_map_first_lba = first_lba; +	lba_map->lba_map_last_lba = last_lba; + +	list_add_tail(&lba_map->lba_map_list, list); +	return lba_map; +} + +int +core_alua_allocate_lba_map_mem(struct t10_alua_lba_map *lba_map, +			       int pg_id, int state) +{ +	struct t10_alua_lba_map_member *lba_map_mem; +	list_for_each_entry(lba_map_mem, &lba_map->lba_map_mem_list, +			    lba_map_mem_list) { +		if (lba_map_mem->lba_map_mem_alua_pg_id == pg_id) { +			pr_err("Duplicate pg_id %d in lba_map\n", pg_id); +			return -EINVAL; +		} +	} + +	lba_map_mem = kmem_cache_zalloc(t10_alua_lba_map_mem_cache, GFP_KERNEL); +	if (!lba_map_mem) { +		pr_err("Unable to allocate struct t10_alua_lba_map_mem\n"); +		return -ENOMEM; +	} +	lba_map_mem->lba_map_mem_alua_state = state; +	lba_map_mem->lba_map_mem_alua_pg_id = pg_id; + +	list_add_tail(&lba_map_mem->lba_map_mem_list, +		      &lba_map->lba_map_mem_list);  	return 0;  } +void +core_alua_free_lba_map(struct list_head *lba_list) +{ +	struct t10_alua_lba_map *lba_map, *lba_map_tmp; +	struct t10_alua_lba_map_member *lba_map_mem, *lba_map_mem_tmp; + +	list_for_each_entry_safe(lba_map, lba_map_tmp, lba_list, +				 lba_map_list) { +		list_for_each_entry_safe(lba_map_mem, lba_map_mem_tmp, +					 &lba_map->lba_map_mem_list, +					 lba_map_mem_list) { +			list_del(&lba_map_mem->lba_map_mem_list); +			kmem_cache_free(t10_alua_lba_map_mem_cache, +					lba_map_mem); +		} +		list_del(&lba_map->lba_map_list); +		kmem_cache_free(t10_alua_lba_map_cache, lba_map); +	} +} + +void +core_alua_set_lba_map(struct se_device *dev, struct list_head *lba_map_list, +		      int segment_size, int segment_mult) +{ +	struct list_head old_lba_map_list; +	struct t10_alua_tg_pt_gp *tg_pt_gp; +	int activate = 0, supported; + +	INIT_LIST_HEAD(&old_lba_map_list); +	spin_lock(&dev->t10_alua.lba_map_lock); +	dev->t10_alua.lba_map_segment_size = segment_size; +	dev->t10_alua.lba_map_segment_multiplier = segment_mult; +	list_splice_init(&dev->t10_alua.lba_map_list, &old_lba_map_list); +	if (lba_map_list) { +		list_splice_init(lba_map_list, &dev->t10_alua.lba_map_list); +		activate = 1; +	} +	spin_unlock(&dev->t10_alua.lba_map_lock); +	spin_lock(&dev->t10_alua.tg_pt_gps_lock); +	list_for_each_entry(tg_pt_gp, &dev->t10_alua.tg_pt_gps_list, +			    tg_pt_gp_list) { + +		if (!tg_pt_gp->tg_pt_gp_valid_id) +			continue; +		supported = tg_pt_gp->tg_pt_gp_alua_supported_states; +		if (activate) +			supported |= ALUA_LBD_SUP; +		else +			supported &= ~ALUA_LBD_SUP; +		tg_pt_gp->tg_pt_gp_alua_supported_states = supported; +	} +	spin_unlock(&dev->t10_alua.tg_pt_gps_lock); +	core_alua_free_lba_map(&old_lba_map_list); +} +  struct t10_alua_lu_gp *  core_alua_allocate_lu_gp(const char *name, int def_group)  { @@ -1232,7 +1598,7 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)  		 * struct se_device is released via core_alua_free_lu_gp_mem().  		 *  		 * If the passed lu_gp does NOT match the default_lu_gp, assume -		 * we want to re-assocate a given lu_gp_mem with default_lu_gp. +		 * we want to re-associate a given lu_gp_mem with default_lu_gp.  		 */  		spin_lock(&lu_gp_mem->lu_gp_mem_lock);  		if (lu_gp != default_lu_gp) @@ -1351,21 +1717,29 @@ struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(struct se_device *dev,  	mutex_init(&tg_pt_gp->tg_pt_gp_md_mutex);  	spin_lock_init(&tg_pt_gp->tg_pt_gp_lock);  	atomic_set(&tg_pt_gp->tg_pt_gp_ref_cnt, 0); +	INIT_DELAYED_WORK(&tg_pt_gp->tg_pt_gp_transition_work, +			  core_alua_do_transition_tg_pt_work);  	tg_pt_gp->tg_pt_gp_dev = dev; -	tg_pt_gp->tg_pt_gp_md_buf_len = ALUA_MD_BUF_LEN;  	atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, -		ALUA_ACCESS_STATE_ACTIVE_OPTMIZED); +		ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED);  	/* -	 * Enable both explict and implict ALUA support by default +	 * Enable both explicit and implicit ALUA support by default  	 */  	tg_pt_gp->tg_pt_gp_alua_access_type = -			TPGS_EXPLICT_ALUA | TPGS_IMPLICT_ALUA; +			TPGS_EXPLICIT_ALUA | TPGS_IMPLICIT_ALUA;  	/*  	 * Set the default Active/NonOptimized Delay in milliseconds  	 */  	tg_pt_gp->tg_pt_gp_nonop_delay_msecs = ALUA_DEFAULT_NONOP_DELAY_MSECS;  	tg_pt_gp->tg_pt_gp_trans_delay_msecs = ALUA_DEFAULT_TRANS_DELAY_MSECS; -	tg_pt_gp->tg_pt_gp_implict_trans_secs = ALUA_DEFAULT_IMPLICT_TRANS_SECS; +	tg_pt_gp->tg_pt_gp_implicit_trans_secs = ALUA_DEFAULT_IMPLICIT_TRANS_SECS; + +	/* +	 * Enable all supported states +	 */ +	tg_pt_gp->tg_pt_gp_alua_supported_states = +	    ALUA_T_SUP | ALUA_O_SUP | +	    ALUA_U_SUP | ALUA_S_SUP | ALUA_AN_SUP | ALUA_AO_SUP;  	if (def_group) {  		spin_lock(&dev->t10_alua.tg_pt_gps_lock); @@ -1465,7 +1839,7 @@ void core_alua_free_tg_pt_gp(  	 * been called from target_core_alua_drop_tg_pt_gp().  	 *  	 * Here we remove *tg_pt_gp from the global list so that -	 * no assications *OR* explict ALUA via SET_TARGET_PORT_GROUPS +	 * no associations *OR* explicit ALUA via SET_TARGET_PORT_GROUPS  	 * can be made while we are releasing struct t10_alua_tg_pt_gp.  	 */  	spin_lock(&dev->t10_alua.tg_pt_gps_lock); @@ -1473,6 +1847,8 @@ void core_alua_free_tg_pt_gp(  	dev->t10_alua.alua_tg_pt_gps_counter--;  	spin_unlock(&dev->t10_alua.tg_pt_gps_lock); +	flush_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work); +  	/*  	 * Allow a struct t10_alua_tg_pt_gp_member * referenced by  	 * core_alua_get_tg_pt_gp_by_name() in @@ -1501,7 +1877,7 @@ void core_alua_free_tg_pt_gp(  		 * core_alua_free_tg_pt_gp_mem().  		 *  		 * If the passed tg_pt_gp does NOT match the default_tg_pt_gp, -		 * assume we want to re-assocate a given tg_pt_gp_mem with +		 * assume we want to re-associate a given tg_pt_gp_mem with  		 * default_tg_pt_gp.  		 */  		spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); @@ -1740,13 +2116,13 @@ ssize_t core_alua_show_access_type(  	struct t10_alua_tg_pt_gp *tg_pt_gp,  	char *page)  { -	if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA) && -	    (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)) -		return sprintf(page, "Implict and Explict\n"); -	else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA) -		return sprintf(page, "Implict\n"); -	else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA) -		return sprintf(page, "Explict\n"); +	if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA) && +	    (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA)) +		return sprintf(page, "Implicit and Explicit\n"); +	else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA) +		return sprintf(page, "Implicit\n"); +	else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA) +		return sprintf(page, "Explicit\n");  	else  		return sprintf(page, "None\n");  } @@ -1771,11 +2147,11 @@ ssize_t core_alua_store_access_type(  	}  	if (tmp == 3)  		tg_pt_gp->tg_pt_gp_alua_access_type = -			TPGS_IMPLICT_ALUA | TPGS_EXPLICT_ALUA; +			TPGS_IMPLICIT_ALUA | TPGS_EXPLICIT_ALUA;  	else if (tmp == 2) -		tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICT_ALUA; +		tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICIT_ALUA;  	else if (tmp == 1) -		tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICT_ALUA; +		tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICIT_ALUA;  	else  		tg_pt_gp->tg_pt_gp_alua_access_type = 0; @@ -1844,14 +2220,14 @@ ssize_t core_alua_store_trans_delay_msecs(  	return count;  } -ssize_t core_alua_show_implict_trans_secs( +ssize_t core_alua_show_implicit_trans_secs(  	struct t10_alua_tg_pt_gp *tg_pt_gp,  	char *page)  { -	return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_implict_trans_secs); +	return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_implicit_trans_secs);  } -ssize_t core_alua_store_implict_trans_secs( +ssize_t core_alua_store_implicit_trans_secs(  	struct t10_alua_tg_pt_gp *tg_pt_gp,  	const char *page,  	size_t count) @@ -1861,16 +2237,16 @@ ssize_t core_alua_store_implict_trans_secs(  	ret = kstrtoul(page, 0, &tmp);  	if (ret < 0) { -		pr_err("Unable to extract implict_trans_secs\n"); +		pr_err("Unable to extract implicit_trans_secs\n");  		return ret;  	} -	if (tmp > ALUA_MAX_IMPLICT_TRANS_SECS) { -		pr_err("Passed implict_trans_secs: %lu, exceeds" -			" ALUA_MAX_IMPLICT_TRANS_SECS: %d\n", tmp, -			ALUA_MAX_IMPLICT_TRANS_SECS); +	if (tmp > ALUA_MAX_IMPLICIT_TRANS_SECS) { +		pr_err("Passed implicit_trans_secs: %lu, exceeds" +			" ALUA_MAX_IMPLICIT_TRANS_SECS: %d\n", tmp, +			ALUA_MAX_IMPLICIT_TRANS_SECS);  		return  -EINVAL;  	} -	tg_pt_gp->tg_pt_gp_implict_trans_secs = (int)tmp; +	tg_pt_gp->tg_pt_gp_implicit_trans_secs = (int)tmp;  	return count;  } @@ -1970,8 +2346,8 @@ ssize_t core_alua_store_secondary_status(  		return ret;  	}  	if ((tmp != ALUA_STATUS_NONE) && -	    (tmp != ALUA_STATUS_ALTERED_BY_EXPLICT_STPG) && -	    (tmp != ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA)) { +	    (tmp != ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) && +	    (tmp != ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA)) {  		pr_err("Illegal value for alua_tg_pt_status: %lu\n",  				tmp);  		return -EINVAL;  | 
