Re: [PATCH 23/31] swim: Don't needlessly re-read sectors

Laurent Vivier <[email protected]> Sun, 26 Jul 2026 18:01:08 +0200
Newsgroups org.kernel.vger.linux-m68k,org.kernel.vger.linux-block,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Le 16/07/2026 =C3=A0 12:02, Finn Thain a =C3=A9crit=C2=A0:
> floppy_read_sectors() is confusing because variable names seem to confla=
te
> tracks and cylinders. Rename the track variable, eliminate a division
> operation and adopt suitable integer types.
>=20
> For readahead to work effectively, small sequential reads should not
> require waiting for spindle rotation. Unfortunately, the present algorit=
hm
> is very inefficient and does a lot of unnecessary waiting.
>=20
> E.g. if the device is asked to read sectors 1 thru 18, and if sector 9
> happens to be under the heads, the driver will proceed to read sectors 9
> thru 18, but discard the results, while it waits for sector 1 to arrive.
>=20
> If sector 1 couldn't be read on the first attempt, and needs a retry, th=
e
> driver will proceed to read sectors 2 thru 18, but discard the results,
> while it waits for sector 1 to come around again.
>=20
> In between reading sector 1 and sector 2, the driver needlessly calls
> swim_track() and swim_head() again. But what's worse is that, on a 68030
> system, re-enabling interrupts after each sector read can result in a fu=
ll
> rotation between sectors (that is a 3 ms wait).
>=20
> Floppy drivers often implement a track cache, that can be filled in a
> single rotation, to solve problems like these. But I think this solution
> is much simpler.
>=20
> For each request, use a sector bitmap to keep a record of sectors read
> successfully. Read or retry, as necessary, the requested sectors in
> whatever sequence they arrive in. Keep interrupts disabled until the
> whole track has passed under the read head.
>=20
> Fixes: 8852ecd97488 ("m68k: mac - Add SWIM floppy support")
> Signed-off-by: Finn Thain <[email protected]>
> ---
>   drivers/block/swim.c | 91 +++++++++++++++++++++++++-------------------
>   1 file changed, 52 insertions(+), 39 deletions(-)
>=20
> diff --git a/drivers/block/swim.c b/drivers/block/swim.c
> index 0bab7ed3daa8..dae401e649d7 100644
> --- a/drivers/block/swim.c
> +++ b/drivers/block/swim.c
> @@ -180,9 +180,9 @@ struct floppy_state {
>   	enum media_type	 type;
>   	int		 write_protected;
>  =20
> -	int		 total_secs;
> -	int		 secpercyl;
> -	int		 secpertrack;
> +	unsigned int	 total_secs;
> +	unsigned int	 secpercyl;
> +	unsigned int	 secpertrack;
>  =20
>   	/* in-use information */
>  =20
> @@ -455,68 +455,81 @@ static int floppy_eject(struct floppy_state *fs)
>   	return 0;
>   }
>  =20
> -static inline int swim_read_sector(struct floppy_state *fs,
> -				   int side, int track,
> -				   int sector, unsigned char *buffer)
> +static unsigned int swim_read_sector_range(struct floppy_state *fs,
> +					unsigned int side, unsigned int track,
> +					unsigned int start, unsigned int count,
> +					unsigned char *buffer)
>   {
>   	struct swim __iomem *base =3D fs->swd->base;
>   	unsigned long flags;
>   	struct sector_header header;
> -	int ret =3D -1;
> -	short i;
> +	int i, bits =3D 0;
>  =20
> -	swim_track(fs, track);
> -	swim_head(base, side);
> +	for (i =3D 0; i < 18; i++) { /* A track has at most 18 sectors */
> +		if (i >=3D count)
> +			break;
> +		bits |=3D BIT(i);
> +	}
>  =20
>   	local_irq_save(flags);
>   	for (i =3D 0; i < 36; i++) {
> +		if (bits =3D=3D 0) /* All count sectors were read ok */
> +			break;
> +
>   		if (swim_read_sector_header(base, &header) ||
>   		    swim_read(base, error) || header.track !=3D track ||
>   		    header.side !=3D side || header.size !=3D 2)
>   			continue;
> -		if (header.sector =3D=3D sector) {
> -			/* found */
>  =20
> -			ret =3D swim_read_sector_data(base, buffer);
> -			if (swim_read(base, error))
> -				ret =3D -EIO;
> -			break;
> +		if (header.sector >=3D start && header.sector < start + count) {
> +			unsigned int offset =3D header.sector - start;
> +			int ret;
> +
> +			if ((bits & BIT(offset)) =3D=3D 0)
> +				continue; /* This sector was already read ok */
> +
> +			ret =3D swim_read_sector_data(base, buffer + 512 * offset);
> +			if (ret =3D=3D 512 && swim_read(base, error) =3D=3D 0)
> +				bits &=3D ~BIT(offset);
>   		}
>   	}
>   	local_irq_restore(flags);
>  =20
> -	return ret;
> +	return bits ? ffs(bits) - 1 : count; /* No. of contiguous ok sectors *=
/
>   }
>  =20
>   static blk_status_t floppy_read_sectors(struct floppy_state *fs,
> -			       int req_sector, int sectors_nb,
> -			       unsigned char *buffer)
> +					unsigned int req_sector,
> +					unsigned int sectors_nb,
> +					unsigned char *buffer)
>   {
>   	struct swim __iomem *base =3D fs->swd->base;
> -	int ret;
> -	int side, track, sector;
> -	int i, try;
> -
> +	unsigned int failures =3D 0;
>  =20
>   	swim_drive(base, fs->location);
>   	swim_READY_timeout(base);
>  =20
> -	for (i =3D req_sector; i < req_sector + sectors_nb; i++) {
> -		int x;
> -		track =3D i / fs->secpercyl;
> -		x =3D i % fs->secpercyl;
> -		side =3D x / fs->secpertrack;
> -		sector =3D x % fs->secpertrack + 1;
> -
> -		try =3D 5;
> -		do {
> -			ret =3D swim_read_sector(fs, side, track, sector,
> -						buffer);
> -			if (try-- =3D=3D 0)
> -				return BLK_STS_IOERR;
> -		} while (ret !=3D 512);
> -
> -		buffer +=3D ret;
> +	while (sectors_nb) {
> +		unsigned int cyl, x, head, sector, n, ret;
> +
> +		cyl =3D req_sector / fs->secpercyl;
> +		x =3D req_sector % fs->secpercyl;
> +		head =3D (x >=3D fs->secpertrack) ? 1 : 0;
> +		sector =3D x % fs->secpertrack;
> +		n =3D min(sectors_nb, fs->secpertrack - sector);
> +
> +		swim_track(fs, cyl);
> +		swim_head(base, head);
> +
> +		ret =3D swim_read_sector_range(fs, head, cyl, sector + 1, n, buffer);
> +		if (ret !=3D n)
> +			++failures;
> +		if (failures >=3D 5)
> +			return BLK_STS_IOERR;

Old code failed after 5 retries on the same sector, new code fails after 5=
 incomplete ranges=20
anywhere in it, even if earlier errors were recovered and request made pro=
gress.

Is this what you want?

Thanks,
Laurent

> +
> +		buffer +=3D 512 * ret;
> +		sectors_nb -=3D ret;
> +		req_sector +=3D ret;
>   	}
>  =20
>   	return 0;