SCSI fixes on 20170603
This is nine fixes, seven of which are for the qedi driver (new in 4.10) the other two are a use after free in the cxgbi drivers and a potential NULL dereference in the rdac device handler. Signed-off-by: James E.J. Bottomley <jejb@linux.vnet.ibm.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJZMyq/AAoJEAVr7HOZEZN4AtUP/1oTEhpXp2epNkEBC0fMywAu T6uUUmZJhcC/k5HkaI7AJUZxYSkVcu1A1QzolJ5GoBIEesfY2W/IOwKO77hhZ1Hp l+e22ioTRXSE2AWyreyVAGSPweG0PyK66kn2vuZXht7xVmcgTelnxp8GPK2MT6vM 2Hxhf+velQxDLwLYOkLY0SAw6JAJpxkFkNcaBsuF2av3fSJtFCzTfgIqLgX1LpcA ckN8yLW0izIeOYKs0Opc65mCZu6AxKOTcvVFglFal7EB99RIU89brifBOqJIue0d KvdoeED2ReO76LZcvDObzEsfNNObHXgNZ100Dk8rF1ovY5JX1L+0fSuYZnJ2Imdg lHlgKZFVCmtLWnWUQYGizcdXHuLHt90ye2sCFK+SZx1EBBVO1maC2ME52nGVTNoU Jrk39dbg//n3ll1FNtlBIFtlFb+uD8DsrWq1KWhg98JcP2XgagCjc2VPF4Eo6Z0l v0JrxG7wATPgRNGhHXRrh1pGxvpuIXvDNVth3cVRa48RxkEJPSW+8Lg63olpCfV3 sAlbThDU/ZUwZybRLa1nbjN7awiR4GaQL+HE9kj0BdR8JpIQHe7162C9KN5NKK7u TReIFSd+oo3AyQzvSR4m0lWeHRoRarDTHkB0/NqbwjMkRUP5125onGf45IaLDI0n SaAsYtnsGBEFK/xjAai1 =MYEc -----END PGP SIGNATURE----- Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi Pull SCSI fixes from James Bottomley: "This is nine fixes, seven of which are for the qedi driver (new as of 4.10) the other two are a use after free in the cxgbi drivers and a potential NULL dereference in the rdac device handler" * tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: scsi: libcxgbi: fix skb use after free scsi: qedi: Fix endpoint NULL panic during recovery. scsi: qedi: set max_fin_rt default value scsi: qedi: Set firmware tcp msl timer value. scsi: qedi: Fix endpoint NULL panic in qedi_set_path. scsi: qedi: Set dma_boundary to 0xfff. scsi: qedi: Correctly set firmware max supported BDs. scsi: qedi: Fix bad pte call trace when iscsiuio is stopped. scsi: scsi_dh_rdac: Use ctlr directly in rdac_failover_get()
This commit is contained in:
Коммит
1f915b7fed
|
@ -1873,6 +1873,11 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
|
|||
tcp_task->dd_data = tdata;
|
||||
task->hdr = NULL;
|
||||
|
||||
if (tdata->skb) {
|
||||
kfree_skb(tdata->skb);
|
||||
tdata->skb = NULL;
|
||||
}
|
||||
|
||||
if (SKB_MAX_HEAD(cdev->skb_tx_rsvd) > (512 * MAX_SKB_FRAGS) &&
|
||||
(opcode == ISCSI_OP_SCSI_DATA_OUT ||
|
||||
(opcode == ISCSI_OP_SCSI_CMD &&
|
||||
|
@ -1890,6 +1895,7 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_get(tdata->skb);
|
||||
skb_reserve(tdata->skb, cdev->skb_tx_rsvd);
|
||||
task->hdr = (struct iscsi_hdr *)tdata->skb->data;
|
||||
task->hdr_max = SKB_TX_ISCSI_PDU_HEADER_MAX; /* BHS + AHS */
|
||||
|
@ -2035,9 +2041,9 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
|
|||
unsigned int datalen;
|
||||
int err;
|
||||
|
||||
if (!skb) {
|
||||
if (!skb || cxgbi_skcb_test_flag(skb, SKCBF_TX_DONE)) {
|
||||
log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX,
|
||||
"task 0x%p, skb NULL.\n", task);
|
||||
"task 0x%p, skb 0x%p\n", task, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2050,7 +2056,6 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
|
|||
}
|
||||
|
||||
datalen = skb->data_len;
|
||||
tdata->skb = NULL;
|
||||
|
||||
/* write ppod first if using ofldq to write ppod */
|
||||
if (ttinfo->flags & CXGBI_PPOD_INFO_FLAG_VALID) {
|
||||
|
@ -2078,6 +2083,7 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
|
|||
pdulen += ISCSI_DIGEST_SIZE;
|
||||
|
||||
task->conn->txdata_octets += pdulen;
|
||||
cxgbi_skcb_set_flag(skb, SKCBF_TX_DONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2086,7 +2092,6 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
|
|||
"task 0x%p, skb 0x%p, len %u/%u, %d EAGAIN.\n",
|
||||
task, skb, skb->len, skb->data_len, err);
|
||||
/* reset skb to send when we are called again */
|
||||
tdata->skb = skb;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -2094,7 +2099,8 @@ int cxgbi_conn_xmit_pdu(struct iscsi_task *task)
|
|||
"itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
|
||||
task->itt, skb, skb->len, skb->data_len, err);
|
||||
|
||||
kfree_skb(skb);
|
||||
__kfree_skb(tdata->skb);
|
||||
tdata->skb = NULL;
|
||||
|
||||
iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
|
||||
iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
|
||||
|
@ -2113,8 +2119,10 @@ void cxgbi_cleanup_task(struct iscsi_task *task)
|
|||
|
||||
tcp_task->dd_data = NULL;
|
||||
/* never reached the xmit task callout */
|
||||
if (tdata->skb)
|
||||
__kfree_skb(tdata->skb);
|
||||
if (tdata->skb) {
|
||||
kfree_skb(tdata->skb);
|
||||
tdata->skb = NULL;
|
||||
}
|
||||
|
||||
task_release_itt(task, task->hdr_itt);
|
||||
memset(tdata, 0, sizeof(*tdata));
|
||||
|
@ -2714,6 +2722,9 @@ EXPORT_SYMBOL_GPL(cxgbi_attr_is_visible);
|
|||
static int __init libcxgbi_init_module(void)
|
||||
{
|
||||
pr_info("%s", version);
|
||||
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, cb) <
|
||||
sizeof(struct cxgbi_skb_cb));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,8 @@ struct cxgbi_skb_rx_cb {
|
|||
};
|
||||
|
||||
struct cxgbi_skb_tx_cb {
|
||||
void *l2t;
|
||||
void *handle;
|
||||
void *arp_err_handler;
|
||||
struct sk_buff *wr_next;
|
||||
};
|
||||
|
||||
|
@ -203,6 +204,7 @@ enum cxgbi_skcb_flags {
|
|||
SKCBF_TX_NEED_HDR, /* packet needs a header */
|
||||
SKCBF_TX_MEM_WRITE, /* memory write */
|
||||
SKCBF_TX_FLAG_COMPL, /* wr completion flag */
|
||||
SKCBF_TX_DONE, /* skb tx done */
|
||||
SKCBF_RX_COALESCED, /* received whole pdu */
|
||||
SKCBF_RX_HDR, /* received pdu header */
|
||||
SKCBF_RX_DATA, /* received pdu payload */
|
||||
|
@ -215,13 +217,13 @@ enum cxgbi_skcb_flags {
|
|||
};
|
||||
|
||||
struct cxgbi_skb_cb {
|
||||
unsigned char ulp_mode;
|
||||
unsigned long flags;
|
||||
unsigned int seq;
|
||||
union {
|
||||
struct cxgbi_skb_rx_cb rx;
|
||||
struct cxgbi_skb_tx_cb tx;
|
||||
};
|
||||
unsigned char ulp_mode;
|
||||
unsigned long flags;
|
||||
unsigned int seq;
|
||||
};
|
||||
|
||||
#define CXGBI_SKB_CB(skb) ((struct cxgbi_skb_cb *)&((skb)->cb[0]))
|
||||
|
@ -374,11 +376,9 @@ static inline void cxgbi_sock_enqueue_wr(struct cxgbi_sock *csk,
|
|||
cxgbi_skcb_tx_wr_next(skb) = NULL;
|
||||
/*
|
||||
* We want to take an extra reference since both us and the driver
|
||||
* need to free the packet before it's really freed. We know there's
|
||||
* just one user currently so we use atomic_set rather than skb_get
|
||||
* to avoid the atomic op.
|
||||
* need to free the packet before it's really freed.
|
||||
*/
|
||||
atomic_set(&skb->users, 2);
|
||||
skb_get(skb);
|
||||
|
||||
if (!csk->wr_pending_head)
|
||||
csk->wr_pending_head = skb;
|
||||
|
|
|
@ -265,18 +265,16 @@ static unsigned int rdac_failover_get(struct rdac_controller *ctlr,
|
|||
struct list_head *list,
|
||||
unsigned char *cdb)
|
||||
{
|
||||
struct scsi_device *sdev = ctlr->ms_sdev;
|
||||
struct rdac_dh_data *h = sdev->handler_data;
|
||||
struct rdac_mode_common *common;
|
||||
unsigned data_size;
|
||||
struct rdac_queue_data *qdata;
|
||||
u8 *lun_table;
|
||||
|
||||
if (h->ctlr->use_ms10) {
|
||||
if (ctlr->use_ms10) {
|
||||
struct rdac_pg_expanded *rdac_pg;
|
||||
|
||||
data_size = sizeof(struct rdac_pg_expanded);
|
||||
rdac_pg = &h->ctlr->mode_select.expanded;
|
||||
rdac_pg = &ctlr->mode_select.expanded;
|
||||
memset(rdac_pg, 0, data_size);
|
||||
common = &rdac_pg->common;
|
||||
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
|
||||
|
@ -288,7 +286,7 @@ static unsigned int rdac_failover_get(struct rdac_controller *ctlr,
|
|||
struct rdac_pg_legacy *rdac_pg;
|
||||
|
||||
data_size = sizeof(struct rdac_pg_legacy);
|
||||
rdac_pg = &h->ctlr->mode_select.legacy;
|
||||
rdac_pg = &ctlr->mode_select.legacy;
|
||||
memset(rdac_pg, 0, data_size);
|
||||
common = &rdac_pg->common;
|
||||
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
|
||||
|
@ -304,7 +302,7 @@ static unsigned int rdac_failover_get(struct rdac_controller *ctlr,
|
|||
}
|
||||
|
||||
/* Prepare the command. */
|
||||
if (h->ctlr->use_ms10) {
|
||||
if (ctlr->use_ms10) {
|
||||
cdb[0] = MODE_SELECT_10;
|
||||
cdb[7] = data_size >> 8;
|
||||
cdb[8] = data_size & 0xff;
|
||||
|
|
|
@ -38,7 +38,7 @@ struct qedi_endpoint;
|
|||
#define QEDI_MAX_ISCSI_TASK 4096
|
||||
#define QEDI_MAX_TASK_NUM 0x0FFF
|
||||
#define QEDI_MAX_ISCSI_CONNS_PER_HBA 1024
|
||||
#define QEDI_ISCSI_MAX_BDS_PER_CMD 256 /* Firmware max BDs is 256 */
|
||||
#define QEDI_ISCSI_MAX_BDS_PER_CMD 255 /* Firmware max BDs is 255 */
|
||||
#define MAX_OUSTANDING_TASKS_PER_CON 1024
|
||||
|
||||
#define QEDI_MAX_BD_LEN 0xffff
|
||||
|
@ -63,6 +63,7 @@ struct qedi_endpoint;
|
|||
#define QEDI_PAGE_MASK (~((QEDI_PAGE_SIZE) - 1))
|
||||
|
||||
#define QEDI_PAGE_SIZE 4096
|
||||
#define QEDI_HW_DMA_BOUNDARY 0xfff
|
||||
#define QEDI_PATH_HANDLE 0xFE0000000UL
|
||||
|
||||
struct qedi_uio_ctrl {
|
||||
|
|
|
@ -1494,6 +1494,8 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
|
|||
tmf_hdr = (struct iscsi_tm *)mtask->hdr;
|
||||
qedi_cmd = (struct qedi_cmd *)mtask->dd_data;
|
||||
ep = qedi_conn->ep;
|
||||
if (!ep)
|
||||
return -ENODEV;
|
||||
|
||||
tid = qedi_get_task_idx(qedi);
|
||||
if (tid == -1)
|
||||
|
|
|
@ -59,6 +59,7 @@ struct scsi_host_template qedi_host_template = {
|
|||
.this_id = -1,
|
||||
.sg_tablesize = QEDI_ISCSI_MAX_BDS_PER_CMD,
|
||||
.max_sectors = 0xffff,
|
||||
.dma_boundary = QEDI_HW_DMA_BOUNDARY,
|
||||
.cmd_per_lun = 128,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
.shost_attrs = qedi_shost_attrs,
|
||||
|
@ -1223,8 +1224,12 @@ static int qedi_set_path(struct Scsi_Host *shost, struct iscsi_path *path_data)
|
|||
|
||||
iscsi_cid = (u32)path_data->handle;
|
||||
qedi_ep = qedi->ep_tbl[iscsi_cid];
|
||||
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_CONN,
|
||||
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
|
||||
"iscsi_cid=0x%x, qedi_ep=%p\n", iscsi_cid, qedi_ep);
|
||||
if (!qedi_ep) {
|
||||
ret = -EINVAL;
|
||||
goto set_path_exit;
|
||||
}
|
||||
|
||||
if (!is_valid_ether_addr(&path_data->mac_addr[0])) {
|
||||
QEDI_NOTICE(&qedi->dbg_ctx, "dst mac NOT VALID\n");
|
||||
|
|
|
@ -151,6 +151,11 @@ static int qedi_uio_close(struct uio_info *uinfo, struct inode *inode)
|
|||
|
||||
static void __qedi_free_uio_rings(struct qedi_uio_dev *udev)
|
||||
{
|
||||
if (udev->uctrl) {
|
||||
free_page((unsigned long)udev->uctrl);
|
||||
udev->uctrl = NULL;
|
||||
}
|
||||
|
||||
if (udev->ll2_ring) {
|
||||
free_page((unsigned long)udev->ll2_ring);
|
||||
udev->ll2_ring = NULL;
|
||||
|
@ -169,7 +174,6 @@ static void __qedi_free_uio(struct qedi_uio_dev *udev)
|
|||
__qedi_free_uio_rings(udev);
|
||||
|
||||
pci_dev_put(udev->pdev);
|
||||
kfree(udev->uctrl);
|
||||
kfree(udev);
|
||||
}
|
||||
|
||||
|
@ -208,6 +212,11 @@ static int __qedi_alloc_uio_rings(struct qedi_uio_dev *udev)
|
|||
if (udev->ll2_ring || udev->ll2_buf)
|
||||
return rc;
|
||||
|
||||
/* Memory for control area. */
|
||||
udev->uctrl = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!udev->uctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Allocating memory for LL2 ring */
|
||||
udev->ll2_ring_size = QEDI_PAGE_SIZE;
|
||||
udev->ll2_ring = (void *)get_zeroed_page(GFP_KERNEL | __GFP_COMP);
|
||||
|
@ -237,7 +246,6 @@ exit_alloc_ring:
|
|||
static int qedi_alloc_uio_rings(struct qedi_ctx *qedi)
|
||||
{
|
||||
struct qedi_uio_dev *udev = NULL;
|
||||
struct qedi_uio_ctrl *uctrl = NULL;
|
||||
int rc = 0;
|
||||
|
||||
list_for_each_entry(udev, &qedi_udev_list, list) {
|
||||
|
@ -258,21 +266,14 @@ static int qedi_alloc_uio_rings(struct qedi_ctx *qedi)
|
|||
goto err_udev;
|
||||
}
|
||||
|
||||
uctrl = kzalloc(sizeof(*uctrl), GFP_KERNEL);
|
||||
if (!uctrl) {
|
||||
rc = -ENOMEM;
|
||||
goto err_uctrl;
|
||||
}
|
||||
|
||||
udev->uio_dev = -1;
|
||||
|
||||
udev->qedi = qedi;
|
||||
udev->pdev = qedi->pdev;
|
||||
udev->uctrl = uctrl;
|
||||
|
||||
rc = __qedi_alloc_uio_rings(udev);
|
||||
if (rc)
|
||||
goto err_uio_rings;
|
||||
goto err_uctrl;
|
||||
|
||||
list_add(&udev->list, &qedi_udev_list);
|
||||
|
||||
|
@ -283,8 +284,6 @@ static int qedi_alloc_uio_rings(struct qedi_ctx *qedi)
|
|||
udev->rx_pkt = udev->ll2_buf + LL2_SINGLE_BUF_SIZE;
|
||||
return 0;
|
||||
|
||||
err_uio_rings:
|
||||
kfree(uctrl);
|
||||
err_uctrl:
|
||||
kfree(udev);
|
||||
err_udev:
|
||||
|
@ -828,6 +827,8 @@ static int qedi_set_iscsi_pf_param(struct qedi_ctx *qedi)
|
|||
qedi->pf_params.iscsi_pf_params.num_uhq_pages_in_ring = num_sq_pages;
|
||||
qedi->pf_params.iscsi_pf_params.num_queues = qedi->num_queues;
|
||||
qedi->pf_params.iscsi_pf_params.debug_mode = qedi_fw_debug;
|
||||
qedi->pf_params.iscsi_pf_params.two_msl_timer = 4000;
|
||||
qedi->pf_params.iscsi_pf_params.max_fin_rt = 2;
|
||||
|
||||
for (log_page_size = 0 ; log_page_size < 32 ; log_page_size++) {
|
||||
if ((1 << log_page_size) == PAGE_SIZE)
|
||||
|
|
Загрузка…
Ссылка в новой задаче