Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0645cb83 authored by Quinn Tran's avatar Quinn Tran Committed by Martin K. Petersen
Browse files

scsi: qla2xxx: Add mode control for each physical port



Add ability to allow each physical port to control operating mode.  Current
code forces all ports to behave in one mode (i.e. initiator, target or
dual). This patch allows user to select the operating mode for each port.

- Driver must be loaded in dual mode to allow resource allocation

modprobe qla2xxx qlini_mode=dual

- In addition user can make adjustment to exchange resources using following
  command

echo 1024 > /sys/class/scsi_host/host<x>/ql2xiniexchg
echo 1024 > /sys/class/scsi_host/host<x>/ql2xexchoffld

- trigger mode change and new setting of ql2xexchoffld|ql2xiniexchg

echo [<value>] > /sys/class/scsi_host/host<x>/qlini_mode

where, value can be one of following
  - enabled
  - disabled
  - dual
  - exclusive

Signed-off-by: default avatarQuinn Tran <quinn.tran@cavium.com>
Signed-off-by: default avatarHimanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 0e324e94
Loading
Loading
Loading
Loading
+449 −0
Original line number Diff line number Diff line
@@ -1632,6 +1632,433 @@ qla2x00_max_speed_sup_show(struct device *dev, struct device_attribute *attr,
	    ha->max_speed_sup ? "32Gps" : "16Gps");
}

/* ----- */

static ssize_t
qlini_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int len = 0;

	len += scnprintf(buf + len, PAGE_SIZE-len,
	    "Supported options: enabled | disabled | dual | exclusive\n");

	/* --- */
	len += scnprintf(buf + len, PAGE_SIZE-len, "Current selection: ");

	switch (vha->qlini_mode) {
	case QLA2XXX_INI_MODE_EXCLUSIVE:
		len += scnprintf(buf + len, PAGE_SIZE-len,
		    QLA2XXX_INI_MODE_STR_EXCLUSIVE);
		break;
	case QLA2XXX_INI_MODE_DISABLED:
		len += scnprintf(buf + len, PAGE_SIZE-len,
		    QLA2XXX_INI_MODE_STR_DISABLED);
		break;
	case QLA2XXX_INI_MODE_ENABLED:
		len += scnprintf(buf + len, PAGE_SIZE-len,
		    QLA2XXX_INI_MODE_STR_ENABLED);
		break;
	case QLA2XXX_INI_MODE_DUAL:
		len += scnprintf(buf + len, PAGE_SIZE-len,
		    QLA2XXX_INI_MODE_STR_DUAL);
		break;
	}
	len += scnprintf(buf + len, PAGE_SIZE-len, "\n");

	return len;
}

static char *mode_to_str[] = {
	"exclusive",
	"disabled",
	"enabled",
	"dual",
};

#define NEED_EXCH_OFFLOAD(_exchg) ((_exchg) > FW_DEF_EXCHANGES_CNT)
static int qla_set_ini_mode(scsi_qla_host_t *vha, int op)
{
	int rc = 0;
	enum {
		NO_ACTION,
		MODE_CHANGE_ACCEPT,
		MODE_CHANGE_NO_ACTION,
		TARGET_STILL_ACTIVE,
	};
	int action = NO_ACTION;
	int set_mode = 0;
	u8  eo_toggle = 0;	/* exchange offload flipped */

	switch (vha->qlini_mode) {
	case QLA2XXX_INI_MODE_DISABLED:
		switch (op) {
		case QLA2XXX_INI_MODE_DISABLED:
			if (qla_tgt_mode_enabled(vha)) {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;
				if (((vha->ql2xexchoffld !=
				    vha->u_ql2xexchoffld) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld)) ||
				    eo_toggle) {
					/*
					 * The number of exchange to be offload
					 * was tweaked or offload option was
					 * flipped
					 */
					action = MODE_CHANGE_ACCEPT;
				} else {
					action = MODE_CHANGE_NO_ACTION;
				}
			} else {
				action = MODE_CHANGE_NO_ACTION;
			}
			break;
		case QLA2XXX_INI_MODE_EXCLUSIVE:
			if (qla_tgt_mode_enabled(vha)) {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;
				if (((vha->ql2xexchoffld !=
				    vha->u_ql2xexchoffld) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld)) ||
				    eo_toggle) {
					/*
					 * The number of exchange to be offload
					 * was tweaked or offload option was
					 * flipped
					 */
					action = MODE_CHANGE_ACCEPT;
				} else {
					action = MODE_CHANGE_NO_ACTION;
				}
			} else {
				action = MODE_CHANGE_ACCEPT;
			}
			break;
		case QLA2XXX_INI_MODE_DUAL:
			action = MODE_CHANGE_ACCEPT;
			/* active_mode is target only, reset it to dual */
			if (qla_tgt_mode_enabled(vha)) {
				set_mode = 1;
				action = MODE_CHANGE_ACCEPT;
			} else {
				action = MODE_CHANGE_NO_ACTION;
			}
			break;

		case QLA2XXX_INI_MODE_ENABLED:
			if (qla_tgt_mode_enabled(vha))
				action = TARGET_STILL_ACTIVE;
			else {
				action = MODE_CHANGE_ACCEPT;
				set_mode = 1;
			}
			break;
		}
		break;

	case QLA2XXX_INI_MODE_EXCLUSIVE:
		switch (op) {
		case QLA2XXX_INI_MODE_EXCLUSIVE:
			if (qla_tgt_mode_enabled(vha)) {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;
				if (((vha->ql2xexchoffld !=
				    vha->u_ql2xexchoffld) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld)) ||
				    eo_toggle)
					/*
					 * The number of exchange to be offload
					 * was tweaked or offload option was
					 * flipped
					 */
					action = MODE_CHANGE_ACCEPT;
				else
					action = NO_ACTION;
			} else
				action = NO_ACTION;

			break;

		case QLA2XXX_INI_MODE_DISABLED:
			if (qla_tgt_mode_enabled(vha)) {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;
				if (((vha->ql2xexchoffld !=
				      vha->u_ql2xexchoffld) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld)) ||
				    eo_toggle)
					action = MODE_CHANGE_ACCEPT;
				else
					action = MODE_CHANGE_NO_ACTION;
			} else
				action = MODE_CHANGE_NO_ACTION;
			break;

		case QLA2XXX_INI_MODE_DUAL: /* exclusive -> dual */
			if (qla_tgt_mode_enabled(vha)) {
				action = MODE_CHANGE_ACCEPT;
				set_mode = 1;
			} else
				action = MODE_CHANGE_ACCEPT;
			break;

		case QLA2XXX_INI_MODE_ENABLED:
			if (qla_tgt_mode_enabled(vha))
				action = TARGET_STILL_ACTIVE;
			else {
				if (vha->hw->flags.fw_started)
					action = MODE_CHANGE_NO_ACTION;
				else
					action = MODE_CHANGE_ACCEPT;
			}
			break;
		}
		break;

	case QLA2XXX_INI_MODE_ENABLED:
		switch (op) {
		case QLA2XXX_INI_MODE_ENABLED:
			if (NEED_EXCH_OFFLOAD(vha->u_ql2xiniexchg) !=
			    vha->hw->flags.exchoffld_enabled)
				eo_toggle = 1;
			if (((vha->ql2xiniexchg != vha->u_ql2xiniexchg) &&
				NEED_EXCH_OFFLOAD(vha->u_ql2xiniexchg)) ||
			    eo_toggle)
				action = MODE_CHANGE_ACCEPT;
			else
				action = NO_ACTION;
			break;
		case QLA2XXX_INI_MODE_DUAL:
		case QLA2XXX_INI_MODE_DISABLED:
			action = MODE_CHANGE_ACCEPT;
			break;
		default:
			action = MODE_CHANGE_NO_ACTION;
			break;
		}
		break;

	case QLA2XXX_INI_MODE_DUAL:
		switch (op) {
		case QLA2XXX_INI_MODE_DUAL:
			if (qla_tgt_mode_enabled(vha) ||
			    qla_dual_mode_enabled(vha)) {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld +
					vha->u_ql2xiniexchg) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;

				if ((((vha->ql2xexchoffld +
				       vha->ql2xiniexchg) !=
				    (vha->u_ql2xiniexchg +
				     vha->u_ql2xexchoffld)) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xiniexchg +
					vha->u_ql2xexchoffld)) || eo_toggle)
					action = MODE_CHANGE_ACCEPT;
				else
					action = NO_ACTION;
			} else {
				if (NEED_EXCH_OFFLOAD(vha->u_ql2xexchoffld +
					vha->u_ql2xiniexchg) !=
				    vha->hw->flags.exchoffld_enabled)
					eo_toggle = 1;

				if ((((vha->ql2xexchoffld + vha->ql2xiniexchg)
				    != (vha->u_ql2xiniexchg +
					vha->u_ql2xexchoffld)) &&
				    NEED_EXCH_OFFLOAD(vha->u_ql2xiniexchg +
					vha->u_ql2xexchoffld)) || eo_toggle)
					action = MODE_CHANGE_NO_ACTION;
				else
					action = NO_ACTION;
			}
			break;

		case QLA2XXX_INI_MODE_DISABLED:
			if (qla_tgt_mode_enabled(vha) ||
			    qla_dual_mode_enabled(vha)) {
				/* turning off initiator mode */
				set_mode = 1;
				action = MODE_CHANGE_ACCEPT;
			} else {
				action = MODE_CHANGE_NO_ACTION;
			}
			break;

		case QLA2XXX_INI_MODE_EXCLUSIVE:
			if (qla_tgt_mode_enabled(vha) ||
			    qla_dual_mode_enabled(vha)) {
				set_mode = 1;
				action = MODE_CHANGE_ACCEPT;
			} else {
				action = MODE_CHANGE_ACCEPT;
			}
			break;

		case QLA2XXX_INI_MODE_ENABLED:
			if (qla_tgt_mode_enabled(vha) ||
			    qla_dual_mode_enabled(vha)) {
				action = TARGET_STILL_ACTIVE;
			} else {
				action = MODE_CHANGE_ACCEPT;
			}
		}
		break;
	}

	switch (action) {
	case MODE_CHANGE_ACCEPT:
		ql_log(ql_log_warn, vha, 0xffff,
		    "Mode change accepted. From %s to %s, Tgt exchg %d|%d. ini exchg %d|%d\n",
		    mode_to_str[vha->qlini_mode], mode_to_str[op],
		    vha->ql2xexchoffld, vha->u_ql2xexchoffld,
		    vha->ql2xiniexchg, vha->u_ql2xiniexchg);

		vha->qlini_mode = op;
		vha->ql2xexchoffld = vha->u_ql2xexchoffld;
		vha->ql2xiniexchg = vha->u_ql2xiniexchg;
		if (set_mode)
			qlt_set_mode(vha);
		vha->flags.online = 1;
		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
		break;

	case MODE_CHANGE_NO_ACTION:
		ql_log(ql_log_warn, vha, 0xffff,
		    "Mode is set. No action taken. From %s to %s, Tgt exchg %d|%d. ini exchg %d|%d\n",
		    mode_to_str[vha->qlini_mode], mode_to_str[op],
		    vha->ql2xexchoffld, vha->u_ql2xexchoffld,
		    vha->ql2xiniexchg, vha->u_ql2xiniexchg);
		vha->qlini_mode = op;
		vha->ql2xexchoffld = vha->u_ql2xexchoffld;
		vha->ql2xiniexchg = vha->u_ql2xiniexchg;
		break;

	case TARGET_STILL_ACTIVE:
		ql_log(ql_log_warn, vha, 0xffff,
		    "Target Mode is active. Unable to change Mode.\n");
		break;

	case NO_ACTION:
	default:
		ql_log(ql_log_warn, vha, 0xffff,
		    "Mode unchange. No action taken. %d|%d pct %d|%d.\n",
		    vha->qlini_mode, op,
		    vha->ql2xexchoffld, vha->u_ql2xexchoffld);
		break;
	}

	return rc;
}

static ssize_t
qlini_mode_store(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int ini;

	if (!buf)
		return -EINVAL;

	if (strncasecmp(QLA2XXX_INI_MODE_STR_EXCLUSIVE, buf,
		strlen(QLA2XXX_INI_MODE_STR_EXCLUSIVE)) == 0)
		ini = QLA2XXX_INI_MODE_EXCLUSIVE;
	else if (strncasecmp(QLA2XXX_INI_MODE_STR_DISABLED, buf,
		strlen(QLA2XXX_INI_MODE_STR_DISABLED)) == 0)
		ini = QLA2XXX_INI_MODE_DISABLED;
	else if (strncasecmp(QLA2XXX_INI_MODE_STR_ENABLED, buf,
		  strlen(QLA2XXX_INI_MODE_STR_ENABLED)) == 0)
		ini = QLA2XXX_INI_MODE_ENABLED;
	else if (strncasecmp(QLA2XXX_INI_MODE_STR_DUAL, buf,
		strlen(QLA2XXX_INI_MODE_STR_DUAL)) == 0)
		ini = QLA2XXX_INI_MODE_DUAL;
	else
		return -EINVAL;

	qla_set_ini_mode(vha, ini);
	return strlen(buf);
}

static ssize_t
ql2xexchoffld_show(struct device *dev, struct device_attribute *attr,
    char *buf)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int len = 0;

	len += scnprintf(buf + len, PAGE_SIZE-len,
		"target exchange: new %d : current: %d\n\n",
		vha->u_ql2xexchoffld, vha->ql2xexchoffld);

	len += scnprintf(buf + len, PAGE_SIZE-len,
	    "Please (re)set operating mode via \"/sys/class/scsi_host/host%ld/qlini_mode\" to load new setting.\n",
	    vha->host_no);

	return len;
}

static ssize_t
ql2xexchoffld_store(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int val = 0;

	if (sscanf(buf, "%d", &val) != 1)
		return -EINVAL;

	if (val > FW_MAX_EXCHANGES_CNT)
		val = FW_MAX_EXCHANGES_CNT;
	else if (val < 0)
		val = 0;

	vha->u_ql2xexchoffld = val;
	return strlen(buf);
}

static ssize_t
ql2xiniexchg_show(struct device *dev, struct device_attribute *attr,
    char *buf)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int len = 0;

	len += scnprintf(buf + len, PAGE_SIZE-len,
		"target exchange: new %d : current: %d\n\n",
		vha->u_ql2xiniexchg, vha->ql2xiniexchg);

	len += scnprintf(buf + len, PAGE_SIZE-len,
	    "Please (re)set operating mode via \"/sys/class/scsi_host/host%ld/qlini_mode\" to load new setting.\n",
	    vha->host_no);

	return len;
}

static ssize_t
ql2xiniexchg_store(struct device *dev, struct device_attribute *attr,
    const char *buf, size_t count)
{
	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
	int val = 0;

	if (sscanf(buf, "%d", &val) != 1)
		return -EINVAL;

	if (val > FW_MAX_EXCHANGES_CNT)
		val = FW_MAX_EXCHANGES_CNT;
	else if (val < 0)
		val = 0;

	vha->u_ql2xiniexchg = val;
	return strlen(buf);
}

static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
@@ -1682,6 +2109,10 @@ static DEVICE_ATTR(max_speed_sup, S_IRUGO, qla2x00_max_speed_sup_show, NULL);
static DEVICE_ATTR(zio_threshold, 0644,
    qla_zio_threshold_show,
    qla_zio_threshold_store);
static DEVICE_ATTR_RW(qlini_mode);
static DEVICE_ATTR_RW(ql2xexchoffld);
static DEVICE_ATTR_RW(ql2xiniexchg);


struct device_attribute *qla2x00_host_attrs[] = {
	&dev_attr_driver_version,
@@ -1719,9 +2150,27 @@ struct device_attribute *qla2x00_host_attrs[] = {
	&dev_attr_min_link_speed,
	&dev_attr_max_speed_sup,
	&dev_attr_zio_threshold,
	NULL, /* reserve for qlini_mode */
	NULL, /* reserve for ql2xiniexchg */
	NULL, /* reserve for ql2xexchoffld */
	NULL,
};

void qla_insert_tgt_attrs(void)
{
	struct device_attribute **attr;

	/* advance to empty slot */
	for (attr = &qla2x00_host_attrs[0]; *attr; ++attr)
		continue;

	*attr = &dev_attr_qlini_mode;
	attr++;
	*attr = &dev_attr_ql2xiniexchg;
	attr++;
	*attr = &dev_attr_ql2xexchoffld;
}

/* Host attributes. */

static void
+7 −0
Original line number Diff line number Diff line
@@ -4376,6 +4376,13 @@ typedef struct scsi_qla_host {
	atomic_t	vref_count;
	struct qla8044_reset_template reset_tmplt;
	uint16_t	bbcr;

	uint16_t u_ql2xexchoffld;
	uint16_t u_ql2xiniexchg;
	uint16_t qlini_mode;
	uint16_t ql2xexchoffld;
	uint16_t ql2xiniexchg;

	struct name_list_extended gnl;
	/* Count of active session/fcport */
	int fcport_count;
+4 −1
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ extern int ql2xnvmeenable;
extern int ql2xautodetectsfp;
extern int ql2xenablemsix;
extern int qla2xuseresexchforels;
extern int ql2xexlogins;

extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -675,6 +676,7 @@ void qla_scan_work_fn(struct work_struct *);
 */
struct device_attribute;
extern struct device_attribute *qla2x00_host_attrs[];
extern struct device_attribute *qla2x00_host_attrs_dm[];
struct fc_function_template;
extern struct fc_function_template qla2xxx_transport_functions;
extern struct fc_function_template qla2xxx_transport_vport_functions;
@@ -688,7 +690,7 @@ extern int qla2x00_echo_test(scsi_qla_host_t *,
extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *);
extern int qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *,
	struct qla_fcp_prio_cfg *, uint8_t);

void qla_insert_tgt_attrs(void);
/*
 * Global Function Prototypes in qla_dfs.c source file.
 */
@@ -895,5 +897,6 @@ void qlt_unknown_atio_work_fn(struct work_struct *);
void qlt_update_host_map(struct scsi_qla_host *, port_id_t);
void qlt_remove_target_resources(struct qla_hw_data *);
void qlt_clr_qp_table(struct scsi_qla_host *vha);
void qlt_set_mode(struct scsi_qla_host *);

#endif /* _QLA_GBL_H */
+15 −0
Original line number Diff line number Diff line
@@ -4056,6 +4056,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
		ql_dbg(ql_dbg_init, vha, 0x00d3,
		    "Init Firmware -- success.\n");
		QLA_FW_STARTED(ha);
		vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0;
	}

	return (rval);
@@ -6702,6 +6703,20 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
			return status;
		}

		switch (vha->qlini_mode) {
		case QLA2XXX_INI_MODE_DISABLED:
			if (!qla_tgt_mode_enabled(vha))
				return 0;
			break;
		case QLA2XXX_INI_MODE_DUAL:
			if (!qla_dual_mode_enabled(vha))
				return 0;
			break;
		case QLA2XXX_INI_MODE_ENABLED:
		default:
			break;
		}

		ha->isp_ops->get_flash_version(vha, req->ring);

		ha->isp_ops->nvram_config(vha);
+3 −3
Original line number Diff line number Diff line
@@ -318,13 +318,13 @@ static inline bool
qla_is_exch_offld_enabled(struct scsi_qla_host *vha)
{
	if (qla_ini_mode_enabled(vha) &&
	    (ql2xiniexchg > FW_DEF_EXCHANGES_CNT))
	    (vha->ql2xiniexchg > FW_DEF_EXCHANGES_CNT))
		return true;
	else if (qla_tgt_mode_enabled(vha) &&
	    (ql2xexchoffld > FW_DEF_EXCHANGES_CNT))
	    (vha->ql2xexchoffld > FW_DEF_EXCHANGES_CNT))
		return true;
	else if (qla_dual_mode_enabled(vha) &&
	    ((ql2xiniexchg + ql2xexchoffld) > FW_DEF_EXCHANGES_CNT))
	    ((vha->ql2xiniexchg + vha->ql2xexchoffld) > FW_DEF_EXCHANGES_CNT))
		return true;
	else
		return false;
Loading