public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: Alok Kataria <akataria@vmware.com>
To: Eric.Moore@lsi.com, James.Bottomley@HansenPartnership.com
Cc: DL-MPTFusionLinux@lsi.com, linux-scsi@vger.kernel.org
Subject: Re: [PATCH] Fix double free of MPT request frames.
Date: Wed, 27 May 2009 15:54:43 -0700	[thread overview]
Message-ID: <1243464883.20345.56.camel@alok-dev1> (raw)
In-Reply-To: <1243365953.20345.24.camel@alok-dev1>

Ccing James...any comments on the patch below ? 

Thanks,
Alok

On Tue, 2009-05-26 at 12:25 -0700, Alok Kataria wrote:
> Hi, 
> 
> While testing scsi path failover for disks using MPT drivers we hit a
> kernel oops on RHEL 5.1-64bit, while analyzing the problem we noticed
> that this is due to a race present in the mpt scsi code path. The same
> race seems to be present in the latest git kernel code too.
> 
> Here is the description of the race,
> If a command has run for too long - which can happen often in the
> failover case - the SCSI stack decides to abort that command and invokes
> a device's abort handler. 
> The code flow is...
> 
> mptscsih_abort ==> mptscsih_TMHandle (ABORT_TASK) ==>
> mptscsih_IssueTaskMgmt (submits the command ) ==>
> mptscsih_tm_wait_for_completion()
> 
> the mptscsih_tm_wait_for_completion code looks like this
> 
> do {
>                 spin_lock_irqsave(&ioc->FreeQlock, flags);
>                 if(hd->tmPending == 0) {
>                         status = SUCCESS;
>                         spin_unlock_irqrestore(&ioc->FreeQlock, flags);
>                         break;
>                 }
>                 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
>                 msleep(250);                        <<==================
>         } while (--loop_count);
> 
> where its looping on hd->tmPending (SUCCESS) or until timeout expires
> (FAILURE).
> 
> Every thing works fine in the success case, in the failure case we
> return to mptscsih_IssueTaskMgmt, and call mpt_HardResetHandler, where
> we throw all inflight commands (hard reset) and free request via
> mpt_free_msg_frame. Which also seems fine.
> 
> Now notice in the loop above that in the last iteration we sleep for
> 250ms after the last read to tmPending, the TM command could complete
> between this check of tmPending and before the adapter is hard reset. 
> In such a case the request is double freed once when the command
> completes (interrupt gets delivered and mpt_reply releases request
> frame), and once after the mpt_HardResetHandler. This results in
> corruption of  the linked list of empty request frames and causes a oops
> on next list access.
> 
> The patch below attempts to fix this race by freeing the request_frame
> before mpt_HardReset and doing that atomically after checking for
> tmPending value. Also the mptscsih_taskmgmt_complete function returns
> "1" irrespective of the tmPending value which results in always freeing
> of the request frame from mpt_reply, so I have modified that function to
> look at the tmPending value before returning. 
> 
> While I am at it, I have also added a BUG_ON statement in
> mpt_free_msg_frame to check for double free, instead of silently
> corrupting the list.
> 
> Please consider the patch below for inclusion.
> 
> 
> Signed-off-by: Alok N Kataria <akataria@vmware.com>
> Cc: <stable@kernel.org>
> ---
> 
>  drivers/message/fusion/mptbase.c  |   11 ++++-------
>  drivers/message/fusion/mptbase.h  |   10 +++++++++-
>  drivers/message/fusion/mptscsih.c |   25 +++++++++++++++++++++----
>  3 files changed, 34 insertions(+), 12 deletions(-)
> 
> 
> diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
> index 5d496a9..a1637bf 100644
> --- a/drivers/message/fusion/mptbase.c
> +++ b/drivers/message/fusion/mptbase.c
> @@ -974,7 +974,7 @@ mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
>  
>  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
>  /**
> - *	mpt_free_msg_frame - Place MPT request frame back on FreeQ.
> + *	__mpt_free_msg_frame - Place MPT request frame back on FreeQ.
>   *	@ioc: Pointer to MPT adapter structure
>   *	@mf: Pointer to MPT request frame
>   *
> @@ -982,18 +982,15 @@ mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
>   *	FreeQ.
>   */
>  void
> -mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
> +__mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
>  {
> -	unsigned long flags;
> -
> +	BUG_ON(mf->u.frame.linkage.arg1 == 0xdeadbeaf);
>  	/*  Put Request back on FreeQ!  */
> -	spin_lock_irqsave(&ioc->FreeQlock, flags);
>  	mf->u.frame.linkage.arg1 = 0xdeadbeaf; /* signature to know if this mf is freed */
>  	list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ);
>  #ifdef MFCNT
>  	ioc->mfcnt--;
>  #endif
> -	spin_unlock_irqrestore(&ioc->FreeQlock, flags);
>  }
>  
>  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
> @@ -7612,7 +7609,7 @@ EXPORT_SYMBOL(mpt_device_driver_deregister);
>  EXPORT_SYMBOL(mpt_get_msg_frame);
>  EXPORT_SYMBOL(mpt_put_msg_frame);
>  EXPORT_SYMBOL(mpt_put_msg_frame_hi_pri);
> -EXPORT_SYMBOL(mpt_free_msg_frame);
> +EXPORT_SYMBOL(__mpt_free_msg_frame);
>  EXPORT_SYMBOL(mpt_add_sge);
>  EXPORT_SYMBOL(mpt_send_handshake_request);
>  EXPORT_SYMBOL(mpt_verify_adapter);
> diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
> index b3e981d..4e4ee53 100644
> --- a/drivers/message/fusion/mptbase.h
> +++ b/drivers/message/fusion/mptbase.h
> @@ -906,7 +906,7 @@ extern void	 mpt_reset_deregister(u8 cb_idx);
>  extern int	 mpt_device_driver_register(struct mpt_pci_driver * dd_cbfunc, u8 cb_idx);
>  extern void	 mpt_device_driver_deregister(u8 cb_idx);
>  extern MPT_FRAME_HDR	*mpt_get_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc);
> -extern void	 mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
> +extern void	 __mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
>  extern void	 mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
>  extern void	 mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
>  extern void	 mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr);
> @@ -924,6 +924,14 @@ extern int	 mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode);
>  extern int	 mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk);
>  extern void     mpt_halt_firmware(MPT_ADAPTER *ioc);
>  
> +static inline void
> +mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
> +{
> +	unsigned long flags;
> +	spin_lock_irqsave(&ioc->FreeQlock, flags);
> +	__mpt_free_msg_frame(ioc, mf);
> +	spin_unlock_irqrestore(&ioc->FreeQlock, flags);
> +}
>  
>  /*
>   *  Public data decl's...
> diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
> index e62c6bc..afe7fc0 100644
> --- a/drivers/message/fusion/mptscsih.c
> +++ b/drivers/message/fusion/mptscsih.c
> @@ -1719,6 +1719,18 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
>  	}
>  
>  	if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&ioc->FreeQlock, flags);
> +		if(hd->tmPending == 0) {
> +                        spin_unlock_irqrestore(&ioc->FreeQlock, flags);
> +                        goto out_success;
> +                }
> +		__mpt_free_msg_frame(ioc, mf);
> +		hd->tmPending = 0;
> +		hd->tmState = TM_STATE_NONE;
> +		spin_unlock_irqrestore(&ioc->FreeQlock, flags);
> +
>  		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "task management request TIMED OUT!"
>  			" (hd %p, ioc %p, mf %p) \n", ioc->name, hd,
>  			ioc, mf));
> @@ -1727,9 +1739,10 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
>  		retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
>  		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "rc=%d \n",
>  			 ioc->name, retval));
> -		goto fail_out;
> +		return FAILED;
>  	}
>  
> +out_success:
>  	/*
>  	 * Handle success case, see if theres a non-zero ioc_status.
>  	 */
> @@ -2159,6 +2172,7 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *m
>  	u16			 iocstatus;
>  	u8			 tmType;
>  	u32			 termination_count;
> +	int			 retval = 1;
>  
>  	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
>  	    ioc->name, mf, mr));
> @@ -2235,12 +2249,15 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *m
>  
>   out:
>  	spin_lock_irqsave(&ioc->FreeQlock, flags);
> -	hd->tmPending = 0;
> -	hd->tmState = TM_STATE_NONE;
> +	if (hd->tmPending) {
> +		hd->tmPending = 0;
> +		hd->tmState = TM_STATE_NONE;
> +	} else
> +		retval = 0;
>  	hd->tm_iocstatus = iocstatus;
>  	spin_unlock_irqrestore(&ioc->FreeQlock, flags);
>  
> -	return 1;
> +	return retval;
>  }
>  
>  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
> 


  reply	other threads:[~2009-05-27 22:54 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-26 19:25 [PATCH] Fix double free of MPT request frames Alok Kataria
2009-05-27 22:54 ` Alok Kataria [this message]
2009-05-30  5:56   ` Desai, Kashyap
2009-05-30 18:34     ` Alok Kataria
2009-06-03  9:24       ` Desai, Kashyap
  -- strict thread matches above, loose matches on Subject: below --
2009-12-10 22:36 Darius S. Naqvi
2009-12-15  4:51 ` Desai, Kashyap

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1243464883.20345.56.camel@alok-dev1 \
    --to=akataria@vmware.com \
    --cc=DL-MPTFusionLinux@lsi.com \
    --cc=Eric.Moore@lsi.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=linux-scsi@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox