Re: [PATCH] scsi: scsi_debug: fix zone transition to full condition

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jun 07, 2022 at 06:40:14PM +0900, Damien Le Moal wrote:
> On 6/7/22 18:37, Niklas Cassel wrote:
> > On Tue, Jun 07, 2022 at 06:25:45PM +0900, Damien Le Moal wrote:
> > > On 6/7/22 18:16, Niklas Cassel wrote:
> > > > On Tue, Jun 07, 2022 at 10:49:42AM +0900, Damien Le Moal wrote:
> > > > > When a write command to a sequential write required or sequential write
> > > > > preferred zone result in the zone write pointer reaching the end of the
> > > > > zone, the zone condition must be set to full AND the number of
> > > > > implicitly or explicitly open zones updated to have a correct accounting
> > > > > for zone resources. However, the function zbc_inc_wp() only sets the
> > > > > zone condition to full without updating the open zone counters,
> > > > > resulting in a zone state machine breakage.
> > > > > 
> > > > > Factor out the correct code from zbc_finish_zone() to transition a zone
> > > > > to the full condition and introduce the helper zbc_set_zone_full(). Use
> > > > > this helper in zbc_finish_zone() and zbc_inc_wp() to correctly
> > > > > transition zones to the full condition.
> > > > > 
> > > > > Fixes: 0d1cf9378bd4 ("scsi: scsi_debug: Add ZBC zone commands")
> > > > > Signed-off-by: Damien Le Moal <damien.lemoal@xxxxxxxxxxxxxxxxxx>
> > > > > ---
> > > > >    drivers/scsi/scsi_debug.c | 27 +++++++++++++++++----------
> > > > >    1 file changed, 17 insertions(+), 10 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> > > > > index 1f423f723d06..6c2bb02a42d8 100644
> > > > > --- a/drivers/scsi/scsi_debug.c
> > > > > +++ b/drivers/scsi/scsi_debug.c
> > > > > @@ -2826,6 +2826,19 @@ static void zbc_open_zone(struct sdebug_dev_info *devip,
> > > > > 	}
> > > > >    }
> > > > > 
> > > > > +static inline void zbc_set_zone_full(struct sdebug_dev_info *devip,
> > > > > +				     struct sdeb_zone_state *zsp)
> > > > > +{
> > > > > +	enum sdebug_z_cond zc = zsp->z_cond;
> > > > > +
> > > > > +	if (zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN)
> > > > > +		zbc_close_zone(devip, zsp);
> > > > > +	if (zsp->z_cond == ZC4_CLOSED)
> > > > > +		devip->nr_closed--;
> > > > > +	zsp->z_wp = zsp->z_start + zsp->z_size;
> > > > > +	zsp->z_cond = ZC5_FULL;
> > > > > +}
> > > > > +
> > > > >    static void zbc_inc_wp(struct sdebug_dev_info *devip,
> > > > > 		       unsigned long long lba, unsigned int num)
> > > > >    {
> > > > > @@ -2838,7 +2851,7 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
> > > > > 	if (zsp->z_type == ZBC_ZTYPE_SWR) {
> > > > > 		zsp->z_wp += num;
> > > > > 		if (zsp->z_wp >= zend)
> > > > > -			zsp->z_cond = ZC5_FULL;
> > > > > +			zbc_set_zone_full(devip, zsp);
> > > > > 		return;
> > > > > 	}
> > > > > 
> > > > > @@ -2857,7 +2870,7 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
> > > > > 			n = num;
> > > > > 		}
> > > > > 		if (zsp->z_wp >= zend)
> > > > > -			zsp->z_cond = ZC5_FULL;
> > > > > +			zbc_set_zone_full(devip, zsp);
> > > > 
> > > > Hello Damien,
> > > > 
> > > > In the equivalent function (null_zone_write()) in null_blk,
> > > > we instead do this:
> > > > 
> > > > 	if (zone->wp == zone->start + zone->capacity) {
> > > > 		null_lock_zone_res(dev);
> > > > 		if (zone->cond == BLK_ZONE_COND_EXP_OPEN)
> > > > 			dev->nr_zones_exp_open--;
> > > > 		else if (zone->cond == BLK_ZONE_COND_IMP_OPEN)
> > > > 			dev->nr_zones_imp_open--;
> > > > 		zone->cond = BLK_ZONE_COND_FULL;
> > > > 		null_unlock_zone_res(dev);
> > > > 	}
> > > > 
> > > > Isn't it more clear to do the same here?
> > > > i.e. set the state to FULL, like before, and simply decrease the
> > > > imp/exp open counters.
> > > > 
> > > > zbc_set_zone_full() does some things that are not applicable in
> > > > the write path, specifically this:
> > > > > +     if (zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN)
> > > > > +             zbc_close_zone(devip, zsp);
> > > > > +     if (zsp->z_cond == ZC4_CLOSED)
> > > > > +             devip->nr_closed--;
> > > > 
> > > > e.g. with this new helper, if we are in e.g. IMP OPEN, we will now
> > > > set the zone state first to CLOSED, increase the nr_closed counter,
> > > > decrease the nr_closed counter, and then set the zone state to FULL.
> > > 
> > > Yes. I am aware of this. It is indeed a bit inefficient, but this makes for
> > > a simple bug fix by covering all call sites (finish and write). If you look
> > > at zbc_rwp_zone() for zone reset, something similar end up being done, the
> > > closed condition is used as an intermediate one. So that one should be
> > > cleaned up too.
> > > 
> > > We should improve this, but I think this should be done in a followup
> > > patch(es) and I prefer to keep this bug fix patch small.
> > > Unless you insist :)
> > 
> > I just saw that zbc_rwp_zone() does the same after sending my email.
> > 
> > I also saw that zbc_close_zone() does a:
> > 
> > 	if (!zbc_zone_is_seq(zsp))
> > 		return;
> > 
> > (Although I don't see a similar check in zbc_finish_zone())
> > 
> > So one has to ensure that both SWR and SWP are still handled correctly
> > when doing this cleanup, so considering that this fix solves the problem,
> > it is probably better to leave the cleanup to remove the extra (and at
> > least in my opinion, confusing) state transition in a follow up series.
> > 
> > Therefore:
> > Reviewed-by: Niklas Cassel <niklas.cassel@xxxxxxx>
> 
> Thanks. Could do a fix like this which avoids the closed intermediate state:
> 
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index 1f423f723d06..6ff4e03ad521 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -2826,6 +2826,27 @@ static void zbc_open_zone(struct sdebug_dev_info
> *devip,
>  	}
>  }
> 
> +static void zbc_set_zone_full(struct sdebug_dev_info *devip,
> +			      struct sdeb_zone_state *zsp)
> +{
> +	switch (zsp->z_cond) {
> +	case ZC2_IMPLICIT_OPEN:
> +		devip->nr_imp_open--;
> +		break;
> +	case ZC3_EXPLICIT_OPEN:
> +		devip->nr_exp_open--;
> +		break;
> +	case ZC4_CLOSED:
> +		devip->nr_closed--;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	zsp->z_wp = zsp->z_start + zsp->z_size;
> +	zsp->z_cond = ZC5_FULL;
> +}
> +
>  static void zbc_inc_wp(struct sdebug_dev_info *devip,
>  		       unsigned long long lba, unsigned int num)
>  {
> @@ -2838,7 +2859,7 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
>  	if (zsp->z_type == ZBC_ZTYPE_SWR) {
>  		zsp->z_wp += num;
>  		if (zsp->z_wp >= zend)
> -			zsp->z_cond = ZC5_FULL;
> +			zbc_set_zone_full(devip, zsp);
>  		return;
>  	}
> 
> @@ -2857,7 +2878,7 @@ static void zbc_inc_wp(struct sdebug_dev_info *devip,
>  			n = num;
>  		}
>  		if (zsp->z_wp >= zend)
> -			zsp->z_cond = ZC5_FULL;
> +			zbc_set_zone_full(devip, zsp);
> 
>  		num -= n;
>  		lba += n;
> @@ -4731,14 +4752,8 @@ static void zbc_finish_zone(struct sdebug_dev_info
> *devip,
>  	enum sdebug_z_cond zc = zsp->z_cond;
> 
>  	if (zc == ZC4_CLOSED || zc == ZC2_IMPLICIT_OPEN ||
> -	    zc == ZC3_EXPLICIT_OPEN || (empty && zc == ZC1_EMPTY)) {
> -		if (zc == ZC2_IMPLICIT_OPEN || zc == ZC3_EXPLICIT_OPEN)
> -			zbc_close_zone(devip, zsp);
> -		if (zsp->z_cond == ZC4_CLOSED)
> -			devip->nr_closed--;
> -		zsp->z_wp = zsp->z_start + zsp->z_size;
> -		zsp->z_cond = ZC5_FULL;
> -	}
> +	    zc == ZC3_EXPLICIT_OPEN || (empty && zc == ZC1_EMPTY))
> +		zbc_set_zone_full(devip, zsp);
>  }
> 
>  static void zbc_finish_all(struct sdebug_dev_info *devip)
> 
> Can send a v2 with that. Tested. It works fine too.

Looks good to me.

But if you send it, I think that you should send a 2/2 that cleans up
zbc_rwp_zone() as well, so that we remove the intermediate closed state
everywhere.

The reason why I really disliked the intermediate closed state when
transitioning to FULL/EMPTY, is because it is not in the spec.

For closed and empty zones, we do have an intermediate implicit open state,
e.g. for a finish zone operation, both in the spec and in the code
(check_zbc_access_params()), so this intermediate state was represented in
the code for a reason.

Someone reading the code could thus think that the intermediate closed state
also exists for a reason (is described in the spec), but that isn't the case.


Kind regards,
Niklas



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux