int core_alua_do_port_transition()

in target_core_alua.c [1051:1165]


int core_alua_do_port_transition(
	struct t10_alua_tg_pt_gp *l_tg_pt_gp,
	struct se_device *l_dev,
	struct se_lun *l_lun,
	struct se_node_acl *l_nacl,
	int new_state,
	int explicit)
{
	struct se_device *dev;
	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;
	int primary, valid_states, rc = 0;

	if (l_dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_ALUA)
		return -ENODEV;

	valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;
	if (core_alua_check_transition(new_state, valid_states, &primary,
				       explicit) != 0)
		return -EINVAL;

	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);
	spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock);
	/*
	 * For storage objects that are members of the 'default_lu_gp',
	 * we only do transition on the passed *l_tp_pt_gp, and not
	 * on all of the matching target port groups IDs in default_lu_gp.
	 */
	if (!lu_gp->lu_gp_id) {
		/*
		 * core_alua_do_transition_tg_pt() will always return
		 * success.
		 */
		l_tg_pt_gp->tg_pt_gp_alua_lun = l_lun;
		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_mb(&lu_gp->lu_gp_ref_cnt);
		return rc;
	}
	/*
	 * For all other LU groups aside from 'default_lu_gp', walk all of
	 * the associated storage objects looking for a matching target port
	 * group ID from the local target port group.
	 */
	spin_lock(&lu_gp->lu_gp_lock);
	list_for_each_entry(lu_gp_mem, &lu_gp->lu_gp_mem_list,
				lu_gp_mem_list) {

		dev = lu_gp_mem->lu_gp_mem_dev;
		atomic_inc_mb(&lu_gp_mem->lu_gp_mem_ref_cnt);
		spin_unlock(&lu_gp->lu_gp_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;
			/*
			 * If the target behavior port asymmetric access state
			 * 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
			 * in that LU group will also change.
			 */
			if (l_tg_pt_gp->tg_pt_gp_id != tg_pt_gp->tg_pt_gp_id)
				continue;

			if (l_tg_pt_gp == tg_pt_gp) {
				tg_pt_gp->tg_pt_gp_alua_lun = l_lun;
				tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl;
			} else {
				tg_pt_gp->tg_pt_gp_alua_lun = NULL;
				tg_pt_gp->tg_pt_gp_alua_nacl = NULL;
			}
			atomic_inc_mb(&tg_pt_gp->tg_pt_gp_ref_cnt);
			spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
			/*
			 * core_alua_do_transition_tg_pt() will always return
			 * success.
			 */
			rc = core_alua_do_transition_tg_pt(tg_pt_gp,
					new_state, explicit);

			spin_lock(&dev->t10_alua.tg_pt_gps_lock);
			atomic_dec_mb(&tg_pt_gp->tg_pt_gp_ref_cnt);
			if (rc)
				break;
		}
		spin_unlock(&dev->t10_alua.tg_pt_gps_lock);

		spin_lock(&lu_gp->lu_gp_lock);
		atomic_dec_mb(&lu_gp_mem->lu_gp_mem_ref_cnt);
	}
	spin_unlock(&lu_gp->lu_gp_lock);

	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_mb(&lu_gp->lu_gp_ref_cnt);
	return rc;
}