From 7ba44bbc51d0147583dcb9f006ba902950aef731 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 23 Nov 2012 13:44:26 +0000 Subject: [PATCH] ib_srpt: Avoid that module unloading races with closing sessions (merge r4446 from trunk) 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/branches/2.2.x@4625 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- srpt/src/ib_srpt.c | 73 ++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 92e4e9011..422eac401 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -2016,10 +2016,51 @@ 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; + + 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("%s", "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 sdev disappears + * after list_del() and before wake_up() has been invoked. + */ + spin_lock_irq(&sdev->spinlock); + list_del(&ch->list); + wake_up(&sdev->ch_releaseQ); + spin_unlock_irq(&sdev->spinlock); + + kref_put(&ch->kref, srpt_free_ch); +} + static int srpt_compl_thread(void *arg) { struct srpt_rdma_ch *ch; - struct srpt_device *sdev; /* Hibernation / freezing of the SRPT kernel thread is not supported. */ current->flags |= PF_NOFREEZE; @@ -2066,34 +2107,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); - - spin_lock_irq(&sdev->spinlock); - list_del(&ch->list); - spin_unlock_irq(&sdev->spinlock); - - 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("%s", "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(&sdev->ch_releaseQ); + while (!kthread_should_stop()) + schedule_timeout(DIV_ROUND_UP(HZ, 10)); return 0; }