ib_srpt: Fix "rejected SRP_LOGIN_REQ because creating a new RDMA channel failed"

Fix two race conditions that could trigger this failure:
- Completion thread not woken up after channel state change.
- Reception of Last WQE event before the channel queue pair was reset.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@3870 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2011-09-18 11:15:17 +00:00
parent afe7e9c6ec
commit 1c358f4222
2 changed files with 23 additions and 11 deletions

View File

@@ -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)

View File

@@ -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];