block: amiflop: synchronize flush timer before track flush

The amiflop track buffer is shared with the timer callback. timer_delete()
does not wait for an in-flight callback, and that callback can rearm itself
when it cannot acquire the floppy controller. A synchronous track flush can
therefore race with the callback.

Stop the timer before synchronous track writes and use timer_delete_sync()
to wait for any callback already running. Keep callback rearming disabled
until the write is complete. Apply this to the FDFMTTRK path as well.

FDFLUSH, FDFMTTRK and close-time flushes run outside the blk-mq
request callback. Freeze their queues so an in-flight request cannot
change the shared track buffer while it is encoded and written. Restart
the timer if a track remains dirty after a failed flush.

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
Link: https://patch.msgid.link/20260930065951.2932739-1-runyu.xiao@seu.edu.cn
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Runyu Xiao 2026-09-30 14:59:51 +08:00 committed by Jens Axboe
parent e0f2224433
commit 9690943353

View File

@ -265,6 +265,8 @@ static struct fd_data_type data_types[] = {
static struct amiga_floppy_struct unit[FD_MAX_UNITS];
static struct timer_list flush_track_timer[FD_MAX_UNITS];
static DEFINE_SPINLOCK(flush_track_timer_lock);
static bool flush_track_timer_stopped[FD_MAX_UNITS];
static struct timer_list post_write_timer;
static unsigned long post_write_timer_drive;
static struct timer_list motor_on_timer;
@ -1370,11 +1372,22 @@ static void flush_track_callback(struct timer_list *timer)
sizeof(flush_track_timer[0]);
nr&=3;
spin_lock(&flush_track_timer_lock);
if (flush_track_timer_stopped[nr]) {
spin_unlock(&flush_track_timer_lock);
return;
}
spin_unlock(&flush_track_timer_lock);
writefromint = 1;
if (!try_fdc(nr)) {
/* we might block in an interrupt, so try again later */
flush_track_timer[nr].expires = jiffies + 1;
add_timer(flush_track_timer + nr);
spin_lock(&flush_track_timer_lock);
if (!flush_track_timer_stopped[nr]) {
flush_track_timer[nr].expires = jiffies + 1;
add_timer(flush_track_timer + nr);
}
spin_unlock(&flush_track_timer_lock);
return;
}
get_fdc(nr);
@ -1421,6 +1434,35 @@ static int non_int_flush_track (unsigned long nr)
return 1;
}
static void flush_track_timer_stop(unsigned int drive)
{
unsigned long flags;
spin_lock_irqsave(&flush_track_timer_lock, flags);
flush_track_timer_stopped[drive] = true;
spin_unlock_irqrestore(&flush_track_timer_lock, flags);
timer_delete_sync(flush_track_timer + drive);
}
static void flush_track_timer_start(unsigned int drive)
{
unsigned long flags;
spin_lock_irqsave(&flush_track_timer_lock, flags);
flush_track_timer_stopped[drive] = false;
if (unit[drive].dirty)
mod_timer(flush_track_timer + drive, jiffies + 1);
spin_unlock_irqrestore(&flush_track_timer_lock, flags);
}
static void flush_track_timer_sync(unsigned int drive)
{
flush_track_timer_stop(drive);
non_int_flush_track(drive);
flush_track_timer_start(drive);
}
static int get_track(int drive, int track)
{
int error, errcnt;
@ -1435,8 +1477,7 @@ static int get_track(int drive, int track)
}
if (unit[drive].dirty == 1) {
timer_delete(flush_track_timer + drive);
non_int_flush_track (drive);
flush_track_timer_sync(drive);
}
errcnt = 0;
while (errcnt < MAX_ERRORS) {
@ -1562,6 +1603,12 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
case FDFMTTRK:
if (param < p->type->tracks * p->type->heads)
{
flush_track_timer_stop(drive);
if (unit[drive].dirty == 1 &&
!non_int_flush_track(drive)) {
flush_track_timer_start(drive);
return -EIO;
}
get_fdc(drive);
if (fd_seek(drive,param) != 0){
memset(p->trackbuf, FD_FILL_BYTE,
@ -1570,6 +1617,7 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
}
floppy_off(drive);
rel_fdc();
flush_track_timer_start(drive);
}
else
return -EINVAL;
@ -1591,8 +1639,7 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
case FDDEFPRM:
return -EINVAL;
case FDFLUSH: /* unconditionally, even if not needed */
timer_delete(flush_track_timer + drive);
non_int_flush_track(drive);
flush_track_timer_sync(drive);
break;
#ifdef RAW_IOCTL
case IOCTL_RAW_TRACK:
@ -1611,9 +1658,15 @@ static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
unsigned int memflags = 0;
bool freeze_queue = cmd == FDFMTTRK || cmd == FDFLUSH;
mutex_lock(&amiflop_mutex);
if (freeze_queue)
memflags = blk_mq_freeze_queue(bdev->bd_disk->queue);
ret = fd_locked_ioctl(bdev, mode, cmd, param);
if (freeze_queue)
blk_mq_unfreeze_queue(bdev->bd_disk->queue, memflags);
mutex_unlock(&amiflop_mutex);
return ret;
@ -1711,11 +1764,12 @@ static void floppy_release(struct gendisk *disk)
{
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
unsigned int memflags;
mutex_lock(&amiflop_mutex);
memflags = blk_mq_freeze_queue(disk->queue);
if (unit[drive].dirty == 1) {
timer_delete(flush_track_timer + drive);
non_int_flush_track (drive);
flush_track_timer_sync(drive);
}
if (!fd_ref[drive]--) {
@ -1725,6 +1779,7 @@ static void floppy_release(struct gendisk *disk)
#ifdef MODULE
floppy_off (drive);
#endif
blk_mq_unfreeze_queue(disk->queue, memflags);
mutex_unlock(&amiflop_mutex);
}