scsi: qedf: Fix crash when MFW calls for protocol stats while function is still probing
[ Upstream commit ad40f5256095c68dc17c991eb976261d5ea2daaa ] The MFW may make a call to qed and then to qedf for protocol statistics while the function is still probing. If this happens it's possible that some members of the struct qedf_ctx may not be fully initialized which can result in a NULL pointer dereference or general protection fault. To prevent this, add a new flag call QEDF_PROBING and set it when the __qedf_probe() function is active. Then in the qedf_get_protocol_tlv_data() function we can check if the function is still probing and return immediantely before any uninitialized structures can be touched. Link: https://lore.kernel.org/r/20200416084314.18851-9-skashyap@marvell.com Signed-off-by: Chad Dupuis <cdupuis@marvell.com> Signed-off-by: Saurav Kashyap <skashyap@marvell.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
e81df5b30e
commit
af59f7fdfa
@ -335,6 +335,7 @@ struct qedf_ctx {
|
|||||||
#define QEDF_GRCDUMP_CAPTURE 4
|
#define QEDF_GRCDUMP_CAPTURE 4
|
||||||
#define QEDF_IN_RECOVERY 5
|
#define QEDF_IN_RECOVERY 5
|
||||||
#define QEDF_DBG_STOP_IO 6
|
#define QEDF_DBG_STOP_IO 6
|
||||||
|
#define QEDF_PROBING 8
|
||||||
unsigned long flags; /* Miscellaneous state flags */
|
unsigned long flags; /* Miscellaneous state flags */
|
||||||
int fipvlan_retries;
|
int fipvlan_retries;
|
||||||
u8 num_queues;
|
u8 num_queues;
|
||||||
|
@ -2961,7 +2961,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
|
|||||||
{
|
{
|
||||||
int rc = -EINVAL;
|
int rc = -EINVAL;
|
||||||
struct fc_lport *lport;
|
struct fc_lport *lport;
|
||||||
struct qedf_ctx *qedf;
|
struct qedf_ctx *qedf = NULL;
|
||||||
struct Scsi_Host *host;
|
struct Scsi_Host *host;
|
||||||
bool is_vf = false;
|
bool is_vf = false;
|
||||||
struct qed_ll2_params params;
|
struct qed_ll2_params params;
|
||||||
@ -2989,6 +2989,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
|
|||||||
|
|
||||||
/* Initialize qedf_ctx */
|
/* Initialize qedf_ctx */
|
||||||
qedf = lport_priv(lport);
|
qedf = lport_priv(lport);
|
||||||
|
set_bit(QEDF_PROBING, &qedf->flags);
|
||||||
qedf->lport = lport;
|
qedf->lport = lport;
|
||||||
qedf->ctlr.lp = lport;
|
qedf->ctlr.lp = lport;
|
||||||
qedf->pdev = pdev;
|
qedf->pdev = pdev;
|
||||||
@ -3011,9 +3012,12 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
|
|||||||
} else {
|
} else {
|
||||||
/* Init pointers during recovery */
|
/* Init pointers during recovery */
|
||||||
qedf = pci_get_drvdata(pdev);
|
qedf = pci_get_drvdata(pdev);
|
||||||
|
set_bit(QEDF_PROBING, &qedf->flags);
|
||||||
lport = qedf->lport;
|
lport = qedf->lport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n");
|
||||||
|
|
||||||
host = lport->host;
|
host = lport->host;
|
||||||
|
|
||||||
/* Allocate mempool for qedf_io_work structs */
|
/* Allocate mempool for qedf_io_work structs */
|
||||||
@ -3312,6 +3316,10 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
|
|||||||
else
|
else
|
||||||
fc_fabric_login(lport);
|
fc_fabric_login(lport);
|
||||||
|
|
||||||
|
QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");
|
||||||
|
|
||||||
|
clear_bit(QEDF_PROBING, &qedf->flags);
|
||||||
|
|
||||||
/* All good */
|
/* All good */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -3337,6 +3345,11 @@ err2:
|
|||||||
err1:
|
err1:
|
||||||
scsi_host_put(lport->host);
|
scsi_host_put(lport->host);
|
||||||
err0:
|
err0:
|
||||||
|
if (qedf) {
|
||||||
|
QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");
|
||||||
|
|
||||||
|
clear_bit(QEDF_PROBING, &qedf->flags);
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3484,11 +3497,25 @@ void qedf_get_protocol_tlv_data(void *dev, void *data)
|
|||||||
{
|
{
|
||||||
struct qedf_ctx *qedf = dev;
|
struct qedf_ctx *qedf = dev;
|
||||||
struct qed_mfw_tlv_fcoe *fcoe = data;
|
struct qed_mfw_tlv_fcoe *fcoe = data;
|
||||||
struct fc_lport *lport = qedf->lport;
|
struct fc_lport *lport;
|
||||||
struct Scsi_Host *host = lport->host;
|
struct Scsi_Host *host;
|
||||||
struct fc_host_attrs *fc_host = shost_to_fc_host(host);
|
struct fc_host_attrs *fc_host;
|
||||||
struct fc_host_statistics *hst;
|
struct fc_host_statistics *hst;
|
||||||
|
|
||||||
|
if (!qedf) {
|
||||||
|
QEDF_ERR(NULL, "qedf is null.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(QEDF_PROBING, &qedf->flags)) {
|
||||||
|
QEDF_ERR(&qedf->dbg_ctx, "Function is still probing.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lport = qedf->lport;
|
||||||
|
host = lport->host;
|
||||||
|
fc_host = shost_to_fc_host(host);
|
||||||
|
|
||||||
/* Force a refresh of the fc_host stats including offload stats */
|
/* Force a refresh of the fc_host stats including offload stats */
|
||||||
hst = qedf_fc_get_host_stats(host);
|
hst = qedf_fc_get_host_stats(host);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user