Commit 938f372c authored by Jitendra Bhivare's avatar Jitendra Bhivare Committed by Martin K. Petersen

scsi: be2iscsi: Fix async PDU handling path

BUG: unable to handle kernel NULL pointer dereference at 000000000000015e
IP: [<ffffffffa0081700>]
hwi_get_async_handle.isra.23.constprop.39+0x90/0x1d0 [be2iscsi]
PGD 0
Oops: 0000 [#1] SMP
...
Call Trace:
 <IRQ>
 [<ffffffffa00818bc>] hwi_process_default_pdu_ring+0x7c/0x280 [be2iscsi]
 [<ffffffffa0088f51>] beiscsi_process_cq+0x321/0xb90 [be2iscsi]
 [<ffffffff810af028>] ? __wake_up_common+0x58/0x90
 [<ffffffff810b0d84>] ? __wake_up+0x44/0x50
 [<ffffffffa0089a2d>] be_iopoll+0x1d/0xb0 [be2iscsi]
 [<ffffffff812d1f61>] blk_iopoll_softirq+0xc1/0x100
 [<ffffffff81084b0f>] __do_softirq+0xef/0x280

The symptom observed is multiple async handles get queued for same index
thus causing leak in buffers posted to FW.

The root cause is:
- async handle is continued to be used even if it does not match the
completion.
- list_move operation done on already filled index.

1. Remove use of writables, host_write_ptr and ep_read_ptr.
2. Remove consumed logic to update writables. Instead, use only
free_entries to do the accounting of handles to be posted back.
3. Remove busy_list, instead use simple slot to index handles.
4. Added check no data, header less and overflow to make sure
all async_handles are flushed in error cases.
5. Added code to verify gathering of handles to form PDU by
checking final bit before forwarding PDU.
6. Added code to catch mismatch with CQE and handle gracefully.
7. Use AMAP, traverse cri_wait_queue list to post buffers, log
"async PDU" related errors.
8. Rearranged few data structures and added comments in init &
processing path.
9. Added WARN_ONs to catch any HD ring corruption.
Signed-off-by: default avatarJitendra Bhivare <jitendra.bhivare@broadcom.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4ee1ec42
This diff is collapsed.
...@@ -608,80 +608,81 @@ struct amap_beiscsi_offload_params { ...@@ -608,80 +608,81 @@ struct amap_beiscsi_offload_params {
u8 max_recv_data_segment_length[32]; u8 max_recv_data_segment_length[32];
}; };
/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, struct hd_async_handle {
struct beiscsi_hba *phba, struct sol_cqe *psol);*/
struct async_pdu_handle {
struct list_head link; struct list_head link;
struct be_bus_address pa; struct be_bus_address pa;
void *pbuffer; void *pbuffer;
unsigned int consumed; u32 buffer_len;
unsigned char index; u16 index;
unsigned char is_header; u16 cri;
unsigned short cri; u8 is_header;
unsigned long buffer_len; u8 is_final;
}; };
struct hwi_async_entry { /**
struct { * This has list of async PDUs that are waiting to be processed.
unsigned char hdr_received; * Buffers live in this list for a brief duration before they get
unsigned char hdr_len; * processed and posted back to hardware.
unsigned short bytes_received; * Note that we don't really need one cri_wait_queue per async_entry.
* We need one cri_wait_queue per CRI. Its easier to manage if this
* is tagged along with the async_entry.
*/
struct hd_async_entry {
struct cri_wait_queue {
unsigned short hdr_len;
unsigned int bytes_received;
unsigned int bytes_needed; unsigned int bytes_needed;
struct list_head list; struct list_head list;
} wait_queue; } wq;
/* handles posted to FW resides here */
struct list_head header_busy_list; struct hd_async_handle *header;
struct list_head data_busy_list; struct hd_async_handle *data;
}; };
struct hwi_async_pdu_context { struct hd_async_buf_context {
struct {
struct be_bus_address pa_base;
void *va_base;
void *ring_base;
struct async_pdu_handle *handle_base;
unsigned int host_write_ptr;
unsigned int ep_read_ptr;
unsigned int writables;
unsigned int free_entries;
unsigned int busy_entries;
struct list_head free_list;
} async_header;
struct {
struct be_bus_address pa_base; struct be_bus_address pa_base;
void *va_base; void *va_base;
void *ring_base; void *ring_base;
struct async_pdu_handle *handle_base; struct hd_async_handle *handle_base;
u16 free_entries;
unsigned int host_write_ptr; u32 buffer_size;
unsigned int ep_read_ptr; /**
unsigned int writables; * Once iSCSI layer finishes processing an async PDU, the
* handles used for the PDU are added to this list.
unsigned int free_entries; * They are posted back to FW in groups of 8.
unsigned int busy_entries; */
struct list_head free_list; struct list_head free_list;
} async_data; };
unsigned int buffer_size; /**
unsigned int num_entries; * hd_async_context is declared for each ULP supporting iSCSI function.
*/
struct hd_async_context {
struct hd_async_buf_context async_header;
struct hd_async_buf_context async_data;
u16 num_entries;
/**
* When unsol PDU is in, it needs to be chained till all the bytes are
* received and then processing is done. hd_async_entry is created
* based on the cid_count for each ULP. When unsol PDU comes in based
* on the conn_id it needs to be added to the correct async_entry wq.
* Below defined cid_to_async_cri_map is used to reterive the
* async_cri_map for a particular connection.
*
* This array is initialized after beiscsi_create_wrb_rings returns.
*
* - this method takes more memory space, fixed to 2K
* - any support for connections greater than this the array size needs
* to be incremented
*/
#define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid]) #define BE_GET_ASYNC_CRI_FROM_CID(cid) (pasync_ctx->cid_to_async_cri_map[cid])
unsigned short cid_to_async_cri_map[BE_MAX_SESSION]; unsigned short cid_to_async_cri_map[BE_MAX_SESSION];
/** /**
* This is a varying size list! Do not add anything * This is a variable size array. Don`t add anything after this field!!
* after this entry!!
*/ */
struct hwi_async_entry *async_entry; struct hd_async_entry *async_entry;
}; };
#define PDUCQE_CODE_MASK 0x0000003F
#define PDUCQE_DPL_MASK 0xFFFF0000
#define PDUCQE_INDEX_MASK 0x0000FFFF
struct i_t_dpdu_cqe { struct i_t_dpdu_cqe {
u32 dw[4]; u32 dw[4];
} __packed; } __packed;
...@@ -1077,9 +1078,14 @@ struct hwi_context_memory { ...@@ -1077,9 +1078,14 @@ struct hwi_context_memory {
struct be_queue_info be_cq[MAX_CPUS - 1]; struct be_queue_info be_cq[MAX_CPUS - 1];
struct be_queue_info *be_wrbq; struct be_queue_info *be_wrbq;
/**
* Create array of ULP number for below entries as DEFQ
* will be created for both ULP if iSCSI Protocol is
* loaded on both ULP.
*/
struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_hdrq[BEISCSI_ULP_COUNT];
struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT]; struct be_queue_info be_def_dataq[BEISCSI_ULP_COUNT];
struct hwi_async_pdu_context *pasync_ctx[BEISCSI_ULP_COUNT]; struct hd_async_context *pasync_ctx[BEISCSI_ULP_COUNT];
}; };
void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle); void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment