Commit 6773f079 authored by Alexander Schmidt's avatar Alexander Schmidt Committed by Roland Dreier

IB/ehca: Discard double CQE for one WR

Under rare circumstances, the ehca hardware might erroneously generate
two CQEs for the same WQE, which is not compliant to the IB spec and
will cause unpredictable errors like memory being freed twice. To
avoid this problem, the driver needs to detect the second CQE and
discard it.

For this purpose, introduce an array holding as many elements as the
SQ of the QP, called sq_map. Each sq_map entry stores a "reported"
flag for one WQE in the SQ. When a work request is posted to the SQ,
the respective "reported" flag is set to zero. After the arrival of a
CQE, the flag is set to 1, which allows to detect the occurence of a
second CQE.

The mapping between WQE / CQE and the corresponding sq_map element is
implemented by replacing the lowest 16 Bits of the wr_id with the
index in the queue map. The original 16 Bits are stored in the sq_map
entry and are restored when the CQE is passed to the application.
Signed-off-by: default avatarAlexander Schmidt <alexs@linux.vnet.ibm.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 129a10fb
...@@ -156,6 +156,14 @@ struct ehca_mod_qp_parm { ...@@ -156,6 +156,14 @@ struct ehca_mod_qp_parm {
#define EHCA_MOD_QP_PARM_MAX 4 #define EHCA_MOD_QP_PARM_MAX 4
#define QMAP_IDX_MASK 0xFFFFULL
/* struct for tracking if cqes have been reported to the application */
struct ehca_qmap_entry {
u16 app_wr_id;
u16 reported;
};
struct ehca_qp { struct ehca_qp {
union { union {
struct ib_qp ib_qp; struct ib_qp ib_qp;
...@@ -165,6 +173,7 @@ struct ehca_qp { ...@@ -165,6 +173,7 @@ struct ehca_qp {
enum ehca_ext_qp_type ext_type; enum ehca_ext_qp_type ext_type;
enum ib_qp_state state; enum ib_qp_state state;
struct ipz_queue ipz_squeue; struct ipz_queue ipz_squeue;
struct ehca_qmap_entry *sq_map;
struct ipz_queue ipz_rqueue; struct ipz_queue ipz_rqueue;
struct h_galpas galpas; struct h_galpas galpas;
u32 qkey; u32 qkey;
......
...@@ -213,6 +213,7 @@ struct ehca_wqe { ...@@ -213,6 +213,7 @@ struct ehca_wqe {
#define WC_STATUS_ERROR_BIT 0x80000000 #define WC_STATUS_ERROR_BIT 0x80000000
#define WC_STATUS_REMOTE_ERROR_FLAGS 0x0000F800 #define WC_STATUS_REMOTE_ERROR_FLAGS 0x0000F800
#define WC_STATUS_PURGE_BIT 0x10 #define WC_STATUS_PURGE_BIT 0x10
#define WC_SEND_RECEIVE_BIT 0x80
struct ehca_cqe { struct ehca_cqe {
u64 work_request_id; u64 work_request_id;
......
...@@ -412,6 +412,7 @@ static struct ehca_qp *internal_create_qp( ...@@ -412,6 +412,7 @@ static struct ehca_qp *internal_create_qp(
struct ehca_shca *shca = container_of(pd->device, struct ehca_shca, struct ehca_shca *shca = container_of(pd->device, struct ehca_shca,
ib_device); ib_device);
struct ib_ucontext *context = NULL; struct ib_ucontext *context = NULL;
u32 nr_qes;
u64 h_ret; u64 h_ret;
int is_llqp = 0, has_srq = 0; int is_llqp = 0, has_srq = 0;
int qp_type, max_send_sge, max_recv_sge, ret; int qp_type, max_send_sge, max_recv_sge, ret;
...@@ -715,6 +716,15 @@ static struct ehca_qp *internal_create_qp( ...@@ -715,6 +716,15 @@ static struct ehca_qp *internal_create_qp(
"and pages ret=%i", ret); "and pages ret=%i", ret);
goto create_qp_exit2; goto create_qp_exit2;
} }
nr_qes = my_qp->ipz_squeue.queue_length /
my_qp->ipz_squeue.qe_size;
my_qp->sq_map = vmalloc(nr_qes *
sizeof(struct ehca_qmap_entry));
if (!my_qp->sq_map) {
ehca_err(pd->device, "Couldn't allocate squeue "
"map ret=%i", ret);
goto create_qp_exit3;
}
} }
if (HAS_RQ(my_qp)) { if (HAS_RQ(my_qp)) {
...@@ -724,7 +734,7 @@ static struct ehca_qp *internal_create_qp( ...@@ -724,7 +734,7 @@ static struct ehca_qp *internal_create_qp(
if (ret) { if (ret) {
ehca_err(pd->device, "Couldn't initialize rqueue " ehca_err(pd->device, "Couldn't initialize rqueue "
"and pages ret=%i", ret); "and pages ret=%i", ret);
goto create_qp_exit3; goto create_qp_exit4;
} }
} }
...@@ -770,7 +780,7 @@ static struct ehca_qp *internal_create_qp( ...@@ -770,7 +780,7 @@ static struct ehca_qp *internal_create_qp(
if (!my_qp->mod_qp_parm) { if (!my_qp->mod_qp_parm) {
ehca_err(pd->device, ehca_err(pd->device,
"Could not alloc mod_qp_parm"); "Could not alloc mod_qp_parm");
goto create_qp_exit4; goto create_qp_exit5;
} }
} }
} }
...@@ -780,7 +790,7 @@ static struct ehca_qp *internal_create_qp( ...@@ -780,7 +790,7 @@ static struct ehca_qp *internal_create_qp(
h_ret = ehca_define_sqp(shca, my_qp, init_attr); h_ret = ehca_define_sqp(shca, my_qp, init_attr);
if (h_ret != H_SUCCESS) { if (h_ret != H_SUCCESS) {
ret = ehca2ib_return_code(h_ret); ret = ehca2ib_return_code(h_ret);
goto create_qp_exit5; goto create_qp_exit6;
} }
} }
...@@ -789,7 +799,7 @@ static struct ehca_qp *internal_create_qp( ...@@ -789,7 +799,7 @@ static struct ehca_qp *internal_create_qp(
if (ret) { if (ret) {
ehca_err(pd->device, ehca_err(pd->device,
"Couldn't assign qp to send_cq ret=%i", ret); "Couldn't assign qp to send_cq ret=%i", ret);
goto create_qp_exit5; goto create_qp_exit6;
} }
} }
...@@ -815,22 +825,26 @@ static struct ehca_qp *internal_create_qp( ...@@ -815,22 +825,26 @@ static struct ehca_qp *internal_create_qp(
if (ib_copy_to_udata(udata, &resp, sizeof resp)) { if (ib_copy_to_udata(udata, &resp, sizeof resp)) {
ehca_err(pd->device, "Copy to udata failed"); ehca_err(pd->device, "Copy to udata failed");
ret = -EINVAL; ret = -EINVAL;
goto create_qp_exit6; goto create_qp_exit7;
} }
} }
return my_qp; return my_qp;
create_qp_exit6: create_qp_exit7:
ehca_cq_unassign_qp(my_qp->send_cq, my_qp->real_qp_num); ehca_cq_unassign_qp(my_qp->send_cq, my_qp->real_qp_num);
create_qp_exit5: create_qp_exit6:
kfree(my_qp->mod_qp_parm); kfree(my_qp->mod_qp_parm);
create_qp_exit4: create_qp_exit5:
if (HAS_RQ(my_qp)) if (HAS_RQ(my_qp))
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue); ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
create_qp_exit4:
if (HAS_SQ(my_qp))
vfree(my_qp->sq_map);
create_qp_exit3: create_qp_exit3:
if (HAS_SQ(my_qp)) if (HAS_SQ(my_qp))
ipz_queue_dtor(my_pd, &my_qp->ipz_squeue); ipz_queue_dtor(my_pd, &my_qp->ipz_squeue);
...@@ -1979,8 +1993,10 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, ...@@ -1979,8 +1993,10 @@ static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
if (HAS_RQ(my_qp)) if (HAS_RQ(my_qp))
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue); ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
if (HAS_SQ(my_qp)) if (HAS_SQ(my_qp)) {
ipz_queue_dtor(my_pd, &my_qp->ipz_squeue); ipz_queue_dtor(my_pd, &my_qp->ipz_squeue);
vfree(my_qp->sq_map);
}
kmem_cache_free(qp_cache, my_qp); kmem_cache_free(qp_cache, my_qp);
atomic_dec(&shca->num_qps); atomic_dec(&shca->num_qps);
return 0; return 0;
......
...@@ -139,6 +139,7 @@ static void trace_send_wr_ud(const struct ib_send_wr *send_wr) ...@@ -139,6 +139,7 @@ static void trace_send_wr_ud(const struct ib_send_wr *send_wr)
static inline int ehca_write_swqe(struct ehca_qp *qp, static inline int ehca_write_swqe(struct ehca_qp *qp,
struct ehca_wqe *wqe_p, struct ehca_wqe *wqe_p,
const struct ib_send_wr *send_wr, const struct ib_send_wr *send_wr,
u32 sq_map_idx,
int hidden) int hidden)
{ {
u32 idx; u32 idx;
...@@ -157,7 +158,11 @@ static inline int ehca_write_swqe(struct ehca_qp *qp, ...@@ -157,7 +158,11 @@ static inline int ehca_write_swqe(struct ehca_qp *qp,
/* clear wqe header until sglist */ /* clear wqe header until sglist */
memset(wqe_p, 0, offsetof(struct ehca_wqe, u.ud_av.sg_list)); memset(wqe_p, 0, offsetof(struct ehca_wqe, u.ud_av.sg_list));
wqe_p->work_request_id = send_wr->wr_id; wqe_p->work_request_id = send_wr->wr_id & ~QMAP_IDX_MASK;
wqe_p->work_request_id |= sq_map_idx & QMAP_IDX_MASK;
qp->sq_map[sq_map_idx].app_wr_id = send_wr->wr_id & QMAP_IDX_MASK;
qp->sq_map[sq_map_idx].reported = 0;
switch (send_wr->opcode) { switch (send_wr->opcode) {
case IB_WR_SEND: case IB_WR_SEND:
...@@ -381,6 +386,7 @@ static inline int post_one_send(struct ehca_qp *my_qp, ...@@ -381,6 +386,7 @@ static inline int post_one_send(struct ehca_qp *my_qp,
{ {
struct ehca_wqe *wqe_p; struct ehca_wqe *wqe_p;
int ret; int ret;
u32 sq_map_idx;
u64 start_offset = my_qp->ipz_squeue.current_q_offset; u64 start_offset = my_qp->ipz_squeue.current_q_offset;
/* get pointer next to free WQE */ /* get pointer next to free WQE */
...@@ -393,8 +399,15 @@ static inline int post_one_send(struct ehca_qp *my_qp, ...@@ -393,8 +399,15 @@ static inline int post_one_send(struct ehca_qp *my_qp,
"qp_num=%x", my_qp->ib_qp.qp_num); "qp_num=%x", my_qp->ib_qp.qp_num);
return -ENOMEM; return -ENOMEM;
} }
/*
* Get the index of the WQE in the send queue. The same index is used
* for writing into the sq_map.
*/
sq_map_idx = start_offset / my_qp->ipz_squeue.qe_size;
/* write a SEND WQE into the QUEUE */ /* write a SEND WQE into the QUEUE */
ret = ehca_write_swqe(my_qp, wqe_p, cur_send_wr, hidden); ret = ehca_write_swqe(my_qp, wqe_p, cur_send_wr, sq_map_idx, hidden);
/* /*
* if something failed, * if something failed,
* reset the free entry pointer to the start value * reset the free entry pointer to the start value
...@@ -654,7 +667,33 @@ repoll: ...@@ -654,7 +667,33 @@ repoll:
my_cq, my_cq->cq_number); my_cq, my_cq->cq_number);
} }
/* we got a completion! */ read_lock(&ehca_qp_idr_lock);
my_qp = idr_find(&ehca_qp_idr, cqe->qp_token);
read_unlock(&ehca_qp_idr_lock);
if (!my_qp)
goto repoll;
wc->qp = &my_qp->ib_qp;
if (!(cqe->w_completion_flags & WC_SEND_RECEIVE_BIT)) {
struct ehca_qmap_entry *qmap_entry;
/*
* We got a send completion and need to restore the original
* wr_id.
*/
qmap_entry = &my_qp->sq_map[cqe->work_request_id &
QMAP_IDX_MASK];
if (qmap_entry->reported) {
ehca_warn(cq->device, "Double cqe on qp_num=%#x",
my_qp->real_qp_num);
/* found a double cqe, discard it and read next one */
goto repoll;
}
wc->wr_id = cqe->work_request_id & ~QMAP_IDX_MASK;
wc->wr_id |= qmap_entry->app_wr_id;
qmap_entry->reported = 1;
} else
/* We got a receive completion. */
wc->wr_id = cqe->work_request_id; wc->wr_id = cqe->work_request_id;
/* eval ib_wc_opcode */ /* eval ib_wc_opcode */
...@@ -678,13 +717,6 @@ repoll: ...@@ -678,13 +717,6 @@ repoll:
} else } else
wc->status = IB_WC_SUCCESS; wc->status = IB_WC_SUCCESS;
read_lock(&ehca_qp_idr_lock);
my_qp = idr_find(&ehca_qp_idr, cqe->qp_token);
read_unlock(&ehca_qp_idr_lock);
if (!my_qp)
goto repoll;
wc->qp = &my_qp->ib_qp;
wc->byte_len = cqe->nr_bytes_transferred; wc->byte_len = cqe->nr_bytes_transferred;
wc->pkey_index = cqe->pkey_index; wc->pkey_index = cqe->pkey_index;
wc->slid = cqe->rlid; wc->slid = cqe->rlid;
......
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