From 36a77c3708e142e649b41a21f5dcb82b3ad508ea Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 4 Aug 2012 08:36:57 +0000 Subject: [PATCH] ib_srpt: Avoid that module unloading races with closing sessions Fixed issue: BUG: unable to handle kernel paging request at ffffffffa04dc479 IP: [] 0xffffffffa04dc478 Oops: 0010 [#1] SMP Pid: 29313, comm: srpt_mlx4_0-2 Tainted: G O 3.6.0-rc1-debug+ #1 Process srpt_mlx4_0-2 Call Trace: [] ? kthread+0xae/0xc0 [] ? kernel_thread_helper+0x4/0x10 [] ? _raw_spin_unlock_irq+0x30/0x50 [] ? trace_hardirqs_on+0xd/0x10 [] ? retint_restore_args+0xe/0xe [] ? __init_kthread_worker+0x70/0x70 [] ? gs_change+0xb/0xb Code: Bad RIP value. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4446 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- srpt/src/ib_srpt.c | 81 ++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 1bf820c4c..0ff66d35f 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -2040,11 +2040,52 @@ static void srpt_free_ch(struct kref *kref) kfree(ch); } +static void srpt_unreg_sess(struct scst_session *scst_sess) +{ + struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(scst_sess); + struct srpt_device *sdev = ch->sport->sdev; + struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + + kthread_stop(ch->thread); + + srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, + sdev, ch->rq_size, + ch->max_rsp_size, DMA_TO_DEVICE); + + /* + * Note: if a DREQ is received after ch->dreq_received has been read, + * ib_destroy_cm_id() will send a DREP. + * + */ + if (ch->dreq_received) { + if (ib_send_cm_drep(ch->cm_id, NULL, 0) >= 0) + PRINT_INFO("Received DREQ and sent DREP for session %s", + ch->sess_name); + else + PRINT_ERROR("Sending DREP failed"); + } + + /* + * If the connection is still established, ib_destroy_cm_id() will + * send a DREQ. + */ + ib_destroy_cm_id(ch->cm_id); + + /* + * Invoke wake_up() inside the lock to avoid that srpt_tgt disappears + * after list_del() and before wake_up() has been invoked. + */ + spin_lock_irq(&srpt_tgt->spinlock); + list_del(&ch->list); + wake_up(&srpt_tgt->ch_releaseQ); + spin_unlock_irq(&srpt_tgt->spinlock); + + kref_put(&ch->kref, srpt_free_ch); +} + static int srpt_compl_thread(void *arg) { struct srpt_rdma_ch *ch; - struct srpt_device *sdev; - struct srpt_tgt *srpt_tgt; /* Hibernation / freezing of the SRPT kernel thread is not supported. */ current->flags |= PF_NOFREEZE; @@ -2098,40 +2139,10 @@ static int srpt_compl_thread(void *arg) TRACE_DBG("ch %s: about to invoke scst_unregister_session()", ch->sess_name); - scst_unregister_session(ch->scst_sess, true, NULL); + scst_unregister_session(ch->scst_sess, false, srpt_unreg_sess); - sdev = ch->sport->sdev; - srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, - sdev, ch->rq_size, - ch->max_rsp_size, DMA_TO_DEVICE); - - srpt_tgt = ch->srpt_tgt; - spin_lock_irq(&srpt_tgt->spinlock); - list_del(&ch->list); - spin_unlock_irq(&srpt_tgt->spinlock); - - /* - * Note: if a DREQ is received after ch->dreq_received has been read, - * ib_destroy_cm_id() will send a DREP. - * - */ - if (ch->dreq_received) { - if (ib_send_cm_drep(ch->cm_id, NULL, 0) >= 0) - PRINT_INFO("Received DREQ and sent DREP for session %s", - ch->sess_name); - else - PRINT_ERROR("Sending DREP failed"); - } - - /* - * If the connection is still established, ib_destroy_cm_id() will - * send a DREQ. - */ - ib_destroy_cm_id(ch->cm_id); - - kref_put(&ch->kref, srpt_free_ch); - - wake_up(&srpt_tgt->ch_releaseQ); + while (!kthread_should_stop()) + schedule_timeout(DIV_ROUND_UP(HZ, 10)); return 0; }