Commit 1dc5ec24 authored by James Smart's avatar James Smart Committed by Martin K. Petersen
Browse files

scsi: lpfc: add Trunking support



Add trunking support to the driver. Trunking is found on more recent
asics. In general, trunking appears as a single "port" to the driver
and overall behavior doesn't differ. Link speed is reported as an
aggregate value, while link speed control is done on a per-physical
link basis with all links in the trunk symmetrical. Some commands
returning port information are updated to additionally provide
trunking information. And new ACQEs are generated to report physical
link events relative to the trunk.

This patch contains the following modifications:

- Added link speed settings of 128GB and 256GB.

- Added handling of trunk-related ACQEs, mainly logging and trapping
  of physical link statuses.

- Added additional bsg interface to query trunk state by applications.

- Augment link_state sysfs attribtute to display trunk link status

Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 7ea92eb4
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -335,6 +335,18 @@ enum hba_state {
	LPFC_HBA_ERROR       =  -1
};

struct lpfc_trunk_link_state {
	enum hba_state state;
	uint8_t fault;
};

struct lpfc_trunk_link  {
	struct lpfc_trunk_link_state link0,
				     link1,
				     link2,
				     link3;
};

struct lpfc_vport {
	struct lpfc_hba *phba;
	struct list_head listentry;
@@ -684,6 +696,7 @@ struct lpfc_hba {
	uint32_t iocb_cmd_size;
	uint32_t iocb_rsp_size;

	struct lpfc_trunk_link  trunk_link;
	enum hba_state link_state;
	uint32_t link_flag;	/* link state flags */
#define LS_LOOPBACK_MODE      0x1	/* NPort is in Loopback mode */
+101 −0
Original line number Diff line number Diff line
@@ -883,6 +883,42 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
		}
	}

	if ((phba->sli_rev == LPFC_SLI_REV4) &&
	    ((bf_get(lpfc_sli_intf_if_type,
	     &phba->sli4_hba.sli_intf) ==
	     LPFC_SLI_INTF_IF_TYPE_6))) {
		struct lpfc_trunk_link link = phba->trunk_link;

		if (bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba))
			len += snprintf(buf + len, PAGE_SIZE - len,
				"Trunk port 0: Link %s %s\n",
				(link.link0.state == LPFC_LINK_UP) ?
				 "Up" : "Down. ",
				trunk_errmsg[link.link0.fault]);

		if (bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba))
			len += snprintf(buf + len, PAGE_SIZE - len,
				"Trunk port 1: Link %s %s\n",
				(link.link1.state == LPFC_LINK_UP) ?
				 "Up" : "Down. ",
				trunk_errmsg[link.link1.fault]);

		if (bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba))
			len += snprintf(buf + len, PAGE_SIZE - len,
				"Trunk port 2: Link %s %s\n",
				(link.link2.state == LPFC_LINK_UP) ?
				 "Up" : "Down. ",
				trunk_errmsg[link.link2.fault]);

		if (bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba))
			len += snprintf(buf + len, PAGE_SIZE - len,
				"Trunk port 3: Link %s %s\n",
				(link.link3.state == LPFC_LINK_UP) ?
				 "Up" : "Down. ",
				trunk_errmsg[link.link3.fault]);

	}

	return len;
}

@@ -1430,6 +1466,66 @@ lpfc_nport_evt_cnt_show(struct device *dev, struct device_attribute *attr,
	return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
}

int
lpfc_set_trunking(struct lpfc_hba *phba, char *buff_out)
{
	LPFC_MBOXQ_t *mbox = NULL;
	unsigned long val = 0;
	char *pval = 0;
	int rc = 0;

	if (!strncmp("enable", buff_out,
				 strlen("enable"))) {
		pval = buff_out + strlen("enable") + 1;
		rc = kstrtoul(pval, 0, &val);
		if (rc)
			return rc; /* Invalid  number */
	} else if (!strncmp("disable", buff_out,
				 strlen("disable"))) {
		val = 0;
	} else {
		return -EINVAL;  /* Invalid command */
	}

	switch (val) {
	case 0:
		val = 0x0; /* Disable */
		break;
	case 2:
		val = 0x1; /* Enable two port trunk */
		break;
	case 4:
		val = 0x2; /* Enable four port trunk */
		break;
	default:
		return -EINVAL;
	}

	lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
			"0070 Set trunk mode with val %ld ", val);

	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
	if (!mbox)
		return -ENOMEM;

	lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
			 LPFC_MBOX_OPCODE_FCOE_FC_SET_TRUNK_MODE,
			 12, LPFC_SLI4_MBX_EMBED);

	bf_set(lpfc_mbx_set_trunk_mode,
	       &mbox->u.mqe.un.set_trunk_mode,
	       val);
	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
	if (rc)
		lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
				"0071 Set trunk mode failed with status: %d",
				rc);
	if (rc != MBX_TIMEOUT)
		mempool_free(mbox, phba->mbox_mem_pool);

	return 0;
}

/**
 * lpfc_board_mode_show - Return the state of the board
 * @dev: class device that is converted into a Scsi_host.
@@ -1522,6 +1618,8 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
		status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_RESET);
	else if (strncmp(buf, "dv_reset", sizeof("dv_reset") - 1) == 0)
		status = lpfc_sli4_pdev_reg_request(phba, LPFC_DV_RESET);
	else if (strncmp(buf, "trunk", sizeof("trunk") - 1) == 0)
		status = lpfc_set_trunking(phba, (char *)buf + sizeof("trunk"));
	else
		status = -EINVAL;

@@ -6019,6 +6117,9 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
		case LPFC_LINK_SPEED_64GHZ:
			fc_host_speed(shost) = FC_PORTSPEED_64GBIT;
			break;
		case LPFC_LINK_SPEED_128GHZ:
			fc_host_speed(shost) = FC_PORTSPEED_128GBIT;
			break;
		default:
			fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
			break;
+74 −0
Original line number Diff line number Diff line
@@ -5615,6 +5615,77 @@ ras_job_error:
	return rc;
}

static int
lpfc_get_trunk_info(struct bsg_job *job)
{
	struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job));
	struct lpfc_hba *phba = vport->phba;
	struct fc_bsg_reply *bsg_reply = job->reply;
	struct lpfc_trunk_info *event_reply;
	int rc = 0;

	if (job->request_len <
	    sizeof(struct fc_bsg_request) + sizeof(struct get_trunk_info_req)) {
		lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC,
				"2744 Received GET TRUNK _INFO request below "
				"minimum size\n");
		rc = -EINVAL;
		goto job_error;
	}

	event_reply = (struct lpfc_trunk_info *)
		bsg_reply->reply_data.vendor_reply.vendor_rsp;

	if (job->reply_len <
	    sizeof(struct fc_bsg_request) + sizeof(struct lpfc_trunk_info)) {
		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
				"2728 Received GET TRUNK _INFO reply below "
				"minimum size\n");
		rc = -EINVAL;
		goto job_error;
	}
	if (event_reply == NULL) {
		rc = -EINVAL;
		goto job_error;
	}

	bsg_bf_set(lpfc_trunk_info_link_status, event_reply,
		   (phba->link_state >= LPFC_LINK_UP) ? 1 : 0);

	bsg_bf_set(lpfc_trunk_info_trunk_active0, event_reply,
		   (phba->trunk_link.link0.state == LPFC_LINK_UP) ? 1 : 0);

	bsg_bf_set(lpfc_trunk_info_trunk_active1, event_reply,
		   (phba->trunk_link.link1.state == LPFC_LINK_UP) ? 1 : 0);

	bsg_bf_set(lpfc_trunk_info_trunk_active2, event_reply,
		   (phba->trunk_link.link2.state == LPFC_LINK_UP) ? 1 : 0);

	bsg_bf_set(lpfc_trunk_info_trunk_active3, event_reply,
		   (phba->trunk_link.link3.state == LPFC_LINK_UP) ? 1 : 0);

	bsg_bf_set(lpfc_trunk_info_trunk_config0, event_reply,
		   bf_get(lpfc_conf_trunk_port0, &phba->sli4_hba));

	bsg_bf_set(lpfc_trunk_info_trunk_config1, event_reply,
		   bf_get(lpfc_conf_trunk_port1, &phba->sli4_hba));

	bsg_bf_set(lpfc_trunk_info_trunk_config2, event_reply,
		   bf_get(lpfc_conf_trunk_port2, &phba->sli4_hba));

	bsg_bf_set(lpfc_trunk_info_trunk_config3, event_reply,
		   bf_get(lpfc_conf_trunk_port3, &phba->sli4_hba));

	event_reply->port_speed = phba->sli4_hba.link_state.speed / 1000;
	event_reply->logical_speed =
				phba->sli4_hba.link_state.logical_speed / 100;
job_error:
	bsg_reply->result = rc;
	bsg_job_done(job, bsg_reply->result,
		       bsg_reply->reply_payload_rcv_len);
	return rc;

}

/**
 * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
@@ -5675,6 +5746,9 @@ lpfc_bsg_hst_vendor(struct bsg_job *job)
	case LPFC_BSG_VENDOR_RAS_SET_CONFIG:
		rc = lpfc_bsg_set_ras_config(job);
		break;
	case LPFC_BSG_VENDOR_GET_TRUNK_INFO:
		rc = lpfc_get_trunk_info(job);
		break;
	default:
		rc = -EINVAL;
		bsg_reply->reply_payload_rcv_len = 0;
+38 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#define LPFC_BSG_VENDOR_RAS_GET_FWLOG		17
#define LPFC_BSG_VENDOR_RAS_GET_CONFIG		18
#define LPFC_BSG_VENDOR_RAS_SET_CONFIG		19
#define LPFC_BSG_VENDOR_GET_TRUNK_INFO		20

struct set_ct_event {
	uint32_t command;
@@ -331,6 +332,43 @@ struct lpfc_bsg_get_ras_config_reply {
	uint32_t log_buff_sz;
};

struct lpfc_trunk_info {
	uint32_t word0;
#define lpfc_trunk_info_link_status_SHIFT      0
#define lpfc_trunk_info_link_status_MASK       1
#define lpfc_trunk_info_link_status_WORD       word0
#define lpfc_trunk_info_trunk_active0_SHIFT    8
#define lpfc_trunk_info_trunk_active0_MASK     1
#define lpfc_trunk_info_trunk_active0_WORD     word0
#define lpfc_trunk_info_trunk_active1_SHIFT    9
#define lpfc_trunk_info_trunk_active1_MASK     1
#define lpfc_trunk_info_trunk_active1_WORD     word0
#define lpfc_trunk_info_trunk_active2_SHIFT    10
#define lpfc_trunk_info_trunk_active2_MASK     1
#define lpfc_trunk_info_trunk_active2_WORD     word0
#define lpfc_trunk_info_trunk_active3_SHIFT    11
#define lpfc_trunk_info_trunk_active3_MASK     1
#define lpfc_trunk_info_trunk_active3_WORD     word0
#define lpfc_trunk_info_trunk_config0_SHIFT    12
#define lpfc_trunk_info_trunk_config0_MASK     1
#define lpfc_trunk_info_trunk_config0_WORD     word0
#define lpfc_trunk_info_trunk_config1_SHIFT    13
#define lpfc_trunk_info_trunk_config1_MASK     1
#define lpfc_trunk_info_trunk_config1_WORD     word0
#define lpfc_trunk_info_trunk_config2_SHIFT    14
#define lpfc_trunk_info_trunk_config2_MASK     1
#define lpfc_trunk_info_trunk_config2_WORD     word0
#define lpfc_trunk_info_trunk_config3_SHIFT    15
#define lpfc_trunk_info_trunk_config3_MASK     1
#define lpfc_trunk_info_trunk_config3_WORD     word0
	uint16_t    port_speed;
	uint16_t    logical_speed;
	uint32_t    reserved3;
};

struct get_trunk_info_req {
	uint32_t command;
};

/* driver only */
#define SLI_CONFIG_NOT_HANDLED		0
+5 −0
Original line number Diff line number Diff line
@@ -2340,6 +2340,8 @@ lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport,

	ae->un.AttrInt = 0;
	if (!(phba->hba_flag & HBA_FCOE_MODE)) {
		if (phba->lmt & LMT_128Gb)
			ae->un.AttrInt |= HBA_PORTSPEED_128GFC;
		if (phba->lmt & LMT_64Gb)
			ae->un.AttrInt |= HBA_PORTSPEED_64GFC;
		if (phba->lmt & LMT_32Gb)
@@ -2416,6 +2418,9 @@ lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport,
		case LPFC_LINK_SPEED_64GHZ:
			ae->un.AttrInt = HBA_PORTSPEED_64GFC;
			break;
		case LPFC_LINK_SPEED_128GHZ:
			ae->un.AttrInt = HBA_PORTSPEED_128GFC;
			break;
		default:
			ae->un.AttrInt = HBA_PORTSPEED_UNKNOWN;
			break;
Loading