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

Commit 7a470277 authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[SCSI] lpfc 8.3.11: Driver management improvements via BSG



- Add BSG support for PCI loopback testing.
- Add BSG support for extended mailbox commands.

Signed-off-by: default avatarAlex Iannicelli <alex.iannicelli@emulex.com>
Signed-off-by: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent cb5172ea
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -554,6 +554,7 @@ struct lpfc_hba {
	struct lpfc_dmabuf slim2p;

	MAILBOX_t *mbox;
	uint32_t *mbox_ext;
	uint32_t *inb_ha_copy;
	uint32_t *inb_counter;
	uint32_t inb_last_counter;
@@ -622,6 +623,7 @@ struct lpfc_hba {
	uint32_t cfg_enable_hba_reset;
	uint32_t cfg_enable_hba_heartbeat;
	uint32_t cfg_enable_bg;
	uint32_t cfg_hostmem_hgp;
	uint32_t cfg_log_verbose;
	uint32_t cfg_aer_support;
	uint32_t cfg_suppress_link_up;
+202 −55
Original line number Diff line number Diff line
@@ -79,6 +79,12 @@ struct lpfc_bsg_iocb {
struct lpfc_bsg_mbox {
	LPFC_MBOXQ_t *pmboxq;
	MAILBOX_t *mb;
	struct lpfc_dmabuf *rxbmp; /* for BIU diags */
	struct lpfc_dmabufext *dmp; /* for BIU diags */
	uint8_t *ext; /* extended mailbox data */
	uint32_t mbOffset; /* from app */
	uint32_t inExtWLen; /* from app */
	uint32_t outWxtWLen; /* from app */

	/* job waiting for this mbox command to finish */
	struct fc_bsg_job *set_job;
@@ -2377,35 +2383,68 @@ void
lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
	struct bsg_job_data *dd_data;
	MAILBOX_t *pmb;
	MAILBOX_t *mb;
	struct fc_bsg_job *job;
	uint32_t size;
	unsigned long flags;
	uint8_t *to;
	uint8_t *from;

	spin_lock_irqsave(&phba->ct_ev_lock, flags);
	dd_data = pmboxq->context1;
	/* job already timed out? */
	if (!dd_data) {
		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
		return;
	}

	pmb = &dd_data->context_un.mbox.pmboxq->u.mb;
	mb = dd_data->context_un.mbox.mb;
	/* build the outgoing buffer to do an sg copy
	 * the format is the response mailbox followed by any extended
	 * mailbox data
	 */
	from = (uint8_t *)&pmboxq->u.mb;
	to = (uint8_t *)dd_data->context_un.mbox.mb;
	memcpy(to, from, sizeof(MAILBOX_t));
	/* copy the extended data if any, count is in words */
	if (dd_data->context_un.mbox.outWxtWLen) {
		from = (uint8_t *)dd_data->context_un.mbox.ext;
		to += sizeof(MAILBOX_t);
		memcpy(to, from,
		dd_data->context_un.mbox.outWxtWLen * sizeof(uint32_t));
	}

	from = (uint8_t *)dd_data->context_un.mbox.mb;
	job = dd_data->context_un.mbox.set_job;
	memcpy(mb, pmb, sizeof(*pmb));
	size = job->request_payload.payload_len;
	size = job->reply_payload.payload_len;
	job->reply->reply_payload_rcv_len =
		sg_copy_from_buffer(job->reply_payload.sg_list,
				job->reply_payload.sg_cnt,
				mb, size);
				from, size);
	job->reply->result = 0;

	dd_data->context_un.mbox.set_job = NULL;
	job->dd_data = NULL;
	job->job_done(job);
	/* need to hold the lock until we call job done to hold off
	 * the timeout handler returning to the midlayer while
	 * we are stillprocessing the job
	 */
	spin_unlock_irqrestore(&phba->ct_ev_lock, flags);

	kfree(dd_data->context_un.mbox.mb);
	mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
	kfree(mb);
	kfree(dd_data->context_un.mbox.ext);
	if (dd_data->context_un.mbox.dmp) {
		dma_free_coherent(&phba->pcidev->dev,
			dd_data->context_un.mbox.dmp->size,
			dd_data->context_un.mbox.dmp->dma.virt,
			dd_data->context_un.mbox.dmp->dma.phys);
		kfree(dd_data->context_un.mbox.dmp);
	}
	if (dd_data->context_un.mbox.rxbmp) {
		lpfc_mbuf_free(phba, dd_data->context_un.mbox.rxbmp->virt,
			dd_data->context_un.mbox.rxbmp->phys);
		kfree(dd_data->context_un.mbox.rxbmp);
	}
	kfree(dd_data);
	return;
}
@@ -2468,6 +2507,7 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
	case MBX_WRITE_EVENT_LOG:
	case MBX_PORT_CAPABILITIES:
	case MBX_PORT_IOV_CONTROL:
	case MBX_RUN_BIU_DIAG64:
		break;
	case MBX_SET_VARIABLE:
		lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
@@ -2482,7 +2522,6 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba,
			phba->fc_topology = TOPOLOGY_PT_PT;
		}
		break;
	case MBX_RUN_BIU_DIAG64:
	case MBX_READ_EVENT_LOG:
	case MBX_READ_SPARM64:
	case MBX_READ_LA:
@@ -2518,97 +2557,199 @@ static uint32_t
lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
	struct lpfc_vport *vport)
{
	LPFC_MBOXQ_t *pmboxq;
	MAILBOX_t *pmb;
	MAILBOX_t *mb;
	struct bsg_job_data *dd_data;
	LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */
	MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */
	/* a 4k buffer to hold the mb and extended data from/to the bsg */
	MAILBOX_t *mb = NULL;
	struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */
	uint32_t size;
	struct lpfc_dmabuf *rxbmp = NULL; /* for biu diag */
	struct lpfc_dmabufext *dmp = NULL; /* for biu diag */
	struct ulp_bde64 *rxbpl = NULL;
	struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *)
		job->request->rqst_data.h_vendor.vendor_cmd;
	uint8_t *ext = NULL;
	int rc = 0;
	uint8_t *from;

	/* in case no data is transferred */
	job->reply->reply_payload_rcv_len = 0;

	/* check if requested extended data lengths are valid */
	if ((mbox_req->inExtWLen > MAILBOX_EXT_SIZE) ||
		(mbox_req->outWxtWLen > MAILBOX_EXT_SIZE)) {
		rc = -ERANGE;
		goto job_done;
	}

	/* allocate our bsg tracking structure */
	dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL);
	if (!dd_data) {
		lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
				"2727 Failed allocation of dd_data\n");
		return -ENOMEM;
		rc = -ENOMEM;
		goto job_done;
	}

	mb = kzalloc(PAGE_SIZE, GFP_KERNEL);
	if (!mb) {
		kfree(dd_data);
		return -ENOMEM;
		rc = -ENOMEM;
		goto job_done;
	}

	pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
	if (!pmboxq) {
		kfree(dd_data);
		kfree(mb);
		return -ENOMEM;
		rc = -ENOMEM;
		goto job_done;
	}
	memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));

	size = job->request_payload.payload_len;
	job->reply->reply_payload_rcv_len =
	sg_copy_to_buffer(job->request_payload.sg_list,
			job->request_payload.sg_cnt,
			mb, size);

	rc = lpfc_bsg_check_cmd_access(phba, mb, vport);
	if (rc != 0) {
		kfree(dd_data);
		kfree(mb);
		mempool_free(pmboxq, phba->mbox_mem_pool);
		return rc; /* must be negative */
	}
	if (rc != 0)
		goto job_done; /* must be negative */

	memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
	pmb = &pmboxq->u.mb;
	memcpy(pmb, mb, sizeof(*pmb));
	pmb->mbxOwner = OWN_HOST;
	pmboxq->context1 = NULL;
	pmboxq->vport = vport;

	if ((vport->fc_flag & FC_OFFLINE_MODE) ||
	    (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
		rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
		if (rc != MBX_SUCCESS) {
			if (rc != MBX_TIMEOUT) {
				kfree(dd_data);
				kfree(mb);
				mempool_free(pmboxq, phba->mbox_mem_pool);
	/* extended mailbox commands will need an extended buffer */
	if (mbox_req->inExtWLen || mbox_req->outWxtWLen) {
		ext = kzalloc(MAILBOX_EXT_SIZE, GFP_KERNEL);
		if (!ext) {
			rc = -ENOMEM;
			goto job_done;
		}

		/* any data for the device? */
		if (mbox_req->inExtWLen) {
			from = (uint8_t *)mb;
			from += sizeof(MAILBOX_t);
			memcpy((uint8_t *)ext, from,
				mbox_req->inExtWLen * sizeof(uint32_t));
		}

		pmboxq->context2 = ext;
		pmboxq->in_ext_byte_len =
			mbox_req->inExtWLen *
			sizeof(uint32_t);
		pmboxq->out_ext_byte_len =
			mbox_req->outWxtWLen *
			sizeof(uint32_t);
		pmboxq->mbox_offset_word =
			mbox_req->mbOffset;
		pmboxq->context2 = ext;
		pmboxq->in_ext_byte_len =
			mbox_req->inExtWLen * sizeof(uint32_t);
		pmboxq->out_ext_byte_len =
			mbox_req->outWxtWLen * sizeof(uint32_t);
		pmboxq->mbox_offset_word = mbox_req->mbOffset;
	}

	/* biu diag will need a kernel buffer to transfer the data
	 * allocate our own buffer and setup the mailbox command to
	 * use ours
	 */
	if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) {
		rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
		if (!rxbmp) {
			rc = -ENOMEM;
			goto job_done;
		}
			return  (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;

		rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys);
		INIT_LIST_HEAD(&rxbmp->list);
		rxbpl = (struct ulp_bde64 *) rxbmp->virt;
		dmp = diag_cmd_data_alloc(phba, rxbpl, PAGE_SIZE, 0);
		if (!dmp) {
			rc = -ENOMEM;
			goto job_done;
		}

		memcpy(mb, pmb, sizeof(*pmb));
		job->reply->reply_payload_rcv_len =
			sg_copy_from_buffer(job->reply_payload.sg_list,
					job->reply_payload.sg_cnt,
					mb, size);
		kfree(dd_data);
		kfree(mb);
		mempool_free(pmboxq, phba->mbox_mem_pool);
		/* not waiting mbox already done */
		return 0;
		dmp->size = PAGE_SIZE;
		INIT_LIST_HEAD(&dmp->dma.list);
		pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh =
			putPaddrHigh(dmp->dma.phys);
		pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow =
			putPaddrLow(dmp->dma.phys);

		pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh =
			putPaddrHigh(dmp->dma.phys +
				pmb->un.varBIUdiag.un.s2.
					xmit_bde64.tus.f.bdeSize);
		pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow =
			putPaddrLow(dmp->dma.phys +
				pmb->un.varBIUdiag.un.s2.
					xmit_bde64.tus.f.bdeSize);
		dd_data->context_un.mbox.rxbmp = rxbmp;
		dd_data->context_un.mbox.dmp = dmp;
	} else {
		dd_data->context_un.mbox.rxbmp = NULL;
		dd_data->context_un.mbox.dmp = NULL;
	}

	/* setup wake call as IOCB callback */
	pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait;

	/* setup context field to pass wait_queue pointer to wake function */
	pmboxq->context1 = dd_data;
	dd_data->type = TYPE_MBOX;
	dd_data->context_un.mbox.pmboxq = pmboxq;
	dd_data->context_un.mbox.mb = mb;
	dd_data->context_un.mbox.set_job = job;
	dd_data->context_un.mbox.ext = ext;
	dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset;
	dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen;
	dd_data->context_un.mbox.outWxtWLen = mbox_req->outWxtWLen;
	job->dd_data = dd_data;

	if ((vport->fc_flag & FC_OFFLINE_MODE) ||
	    (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
		rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
		if (rc != MBX_SUCCESS) {
			rc = (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV;
			goto job_done;
		}

		/* job finished, copy the data */
		memcpy(mb, pmb, sizeof(*pmb));
		job->reply->reply_payload_rcv_len =
			sg_copy_from_buffer(job->reply_payload.sg_list,
					job->reply_payload.sg_cnt,
					mb, size);
		/* not waiting mbox already done */
		rc = 0;
		goto job_done;
	}

	rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
	if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
		kfree(dd_data);
	if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY))
		return 1; /* job started */

job_done:
	/* common exit for error or job completed inline */
	kfree(mb);
	if (pmboxq)
		mempool_free(pmboxq, phba->mbox_mem_pool);
		return -EIO;
	kfree(ext);
	if (dmp) {
		dma_free_coherent(&phba->pcidev->dev,
			dmp->size, dmp->dma.virt,
				dmp->dma.phys);
		kfree(dmp);
	}
	if (rxbmp) {
		lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys);
		kfree(rxbmp);
	}
	kfree(dd_data);

	return 1;
	return rc;
}

/**
@@ -2638,6 +2779,11 @@ lpfc_bsg_mbox_cmd(struct fc_bsg_job *job)
		goto job_error;
	}

	if (job->reply_payload.payload_len != PAGE_SIZE) {
		rc = -EINVAL;
		goto job_error;
	}

	if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
		rc = -EAGAIN;
		goto job_error;
@@ -3094,6 +3240,7 @@ lpfc_bsg_timeout(struct fc_bsg_job *job)
		job->dd_data = NULL;
		job->reply->reply_payload_rcv_len = 0;
		job->reply->result = -EAGAIN;
		/* the mbox completion handler can now be run */
		spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
		job->job_done(job);
		break;
+2 −2
Original line number Diff line number Diff line
@@ -93,9 +93,9 @@ struct get_mgmt_rev_reply {

struct dfc_mbox_req {
	uint32_t command;
	uint32_t mbOffset;
	uint32_t inExtWLen;
	uint32_t outExtWLen;
	uint8_t mbOffset;
	uint32_t outWxtWLen;
};

/* Used for menlo command or menlo data. The xri is only used for menlo data */
+9 −1
Original line number Diff line number Diff line
@@ -2934,6 +2934,12 @@ typedef struct {
/* Union of all Mailbox Command types */
#define MAILBOX_CMD_WSIZE	32
#define MAILBOX_CMD_SIZE	(MAILBOX_CMD_WSIZE * sizeof(uint32_t))
/* ext_wsize times 4 bytes should not be greater than max xmit size */
#define MAILBOX_EXT_WSIZE	512
#define MAILBOX_EXT_SIZE	(MAILBOX_EXT_WSIZE * sizeof(uint32_t))
#define MAILBOX_HBA_EXT_OFFSET  0x100
/* max mbox xmit size is a page size for sysfs IO operations */
#define MAILBOX_MAX_XMIT_SIZE   PAGE_SIZE

typedef union {
	uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; /* first word is type/
@@ -3652,7 +3658,8 @@ typedef struct _IOCB { /* IOCB structure */
/* Maximum IOCBs that will fit in SLI2 slim */
#define MAX_SLI2_IOCB    498
#define MAX_SLIM_IOCB_SIZE (SLI2_SLIM_SIZE - \
			    (sizeof(MAILBOX_t) + sizeof(PCB_t)))
			    (sizeof(MAILBOX_t) + sizeof(PCB_t) + \
			    sizeof(uint32_t) * MAILBOX_EXT_WSIZE))

/* HBQ entries are 4 words each = 4k */
#define LPFC_TOTAL_HBQ_SIZE (sizeof(struct lpfc_hbq_entry) *  \
@@ -3660,6 +3667,7 @@ typedef struct _IOCB { /* IOCB structure */

struct lpfc_sli2_slim {
	MAILBOX_t mbx;
	uint32_t  mbx_ext_words[MAILBOX_EXT_WSIZE];
	PCB_t pcb;
	IOCB_t IOCBs[MAX_SLIM_IOCB_SIZE];
};
+2 −0
Original line number Diff line number Diff line
@@ -5059,6 +5059,8 @@ lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)

	memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
	phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
	phba->mbox_ext = (phba->slim2p.virt +
		offsetof(struct lpfc_sli2_slim, mbx_ext_words));
	phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
	phba->IOCBs = (phba->slim2p.virt +
		       offsetof(struct lpfc_sli2_slim, IOCBs));
Loading