diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 04d79e517..709814eec 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -164,6 +164,9 @@ srpt_set_ch_state(struct srpt_rdma_ch *ch, enum rdma_ch_state new_state) prev = ch->state; ch->state = new_state; spin_unlock_irqrestore(&ch->spinlock, flags); + + if (prev != new_state) + wake_up_process(ch->thread); return prev; } @@ -171,6 +174,7 @@ static enum rdma_ch_state srpt_set_ch_state_to_disc(struct srpt_rdma_ch *ch) { unsigned long flags; enum rdma_ch_state prev; + bool changed = false; spin_lock_irqsave(&ch->spinlock, flags); prev = ch->state; @@ -178,19 +182,23 @@ static enum rdma_ch_state srpt_set_ch_state_to_disc(struct srpt_rdma_ch *ch) case CH_CONNECTING: case CH_LIVE: ch->state = CH_DISCONNECTING; + changed = true; break; default: break; } spin_unlock_irqrestore(&ch->spinlock, flags); + if (changed) + wake_up_process(ch->thread); + return prev; } static bool srpt_set_ch_state_to_draining(struct srpt_rdma_ch *ch) { unsigned long flags; - bool changed_state = false; + bool changed = false; spin_lock_irqsave(&ch->spinlock, flags); switch (ch->state) { @@ -198,14 +206,16 @@ static bool srpt_set_ch_state_to_draining(struct srpt_rdma_ch *ch) case CH_LIVE: case CH_DISCONNECTING: ch->state = CH_DRAINING; - changed_state = true; + changed = true; break; default: break; } spin_unlock_irqrestore(&ch->spinlock, flags); - return changed_state; + if (changed) + wake_up_process(ch->thread); + return changed; } /** @@ -228,7 +238,7 @@ static bool srpt_test_and_set_ch_state(struct srpt_rdma_ch *ch, spin_unlock_irqrestore(&ch->spinlock, flags); if (changed) - wake_up(&ch->state_wq); + wake_up_process(ch->thread); return changed; } @@ -375,9 +385,10 @@ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) #endif break; case IB_EVENT_QP_LAST_WQE_REACHED: - if (!srpt_test_and_set_ch_state(ch, CH_DRAINING, CH_RELEASING)) - TRACE_DBG("%s: state %s - ignored Last WQE event.", - ch->sess_name, get_ch_state_name(ch->state)); + TRACE_DBG("%s, state %s: received Last WQE event.", + ch->sess_name, get_ch_state_name(ch->state)); + ch->last_wqe_received = true; + srpt_test_and_set_ch_state(ch, CH_DRAINING, CH_RELEASING); break; default: PRINT_ERROR("received unrecognized IB QP event %d", @@ -2178,7 +2189,6 @@ static bool __srpt_close_ch(struct srpt_rdma_ch *ch) /* fall through */ case CH_LIVE: was_live = true; - wake_up_process(ch->thread); if (ib_send_cm_dreq(ch->cm_id, NULL, 0) < 0) PRINT_ERROR("sending CM DREQ failed."); break; @@ -2229,9 +2239,10 @@ static void srpt_drain_channel(struct ib_cm_id *cm_id) if (ret < 0) PRINT_ERROR("Setting queue pair in error state" " failed: %d", ret); - } else - TRACE_DBG("Channel already in state %s", - get_ch_state_name(ch->state)); + if (ch->last_wqe_received) + srpt_test_and_set_ch_state(ch, CH_DRAINING, + CH_RELEASING); + } } static void srpt_free_ch(struct scst_session *sess) diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 54fd95c7c..e88d17653 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -334,6 +334,7 @@ struct srpt_rdma_ch { wait_queue_head_t state_wq; struct list_head list; struct list_head cmd_wait_list; + bool last_wqe_received; struct scst_session *scst_sess; u8 sess_name[36];