From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Runyu Xiao <runyu.xiao@seu.edu.cn>, Jens Axboe <axboe@kernel.dk>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 5.15.y] block: amiflop: synchronize flush timer before track flush
Date: Tue, 10 Nov 2026 18:10:19 -0500 [thread overview]
Message-ID: <20261110230918.76289-1-sashal@kernel.org> (raw)
In-Reply-To: <20261107060127-failed-5.15-9690943353ce@kernel.org>
From: Runyu Xiao <runyu.xiao@seu.edu.cn>
[ Upstream commit 9690943353cec1690a715e000ba6a74e5139f530 ]
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: 1da177e4c3f4 ("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>
[ sashal: adapted blk_mq_freeze_queue() and blk_mq_unfreeze_queue() calls to the older API without memflags ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/block/amiflop.c | 69 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 61 insertions(+), 8 deletions(-)
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 1ed557cb5ed23..46e134259e19c 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -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) {
- del_timer (flush_track_timer + drive);
- non_int_flush_track (drive);
+ flush_track_timer_sync(drive);
}
errcnt = 0;
while (errcnt < MAX_ERRORS) {
@@ -1563,6 +1604,12 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_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,
@@ -1571,6 +1618,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
}
floppy_off(drive);
rel_fdc();
+ flush_track_timer_start(drive);
}
else
return -EINVAL;
@@ -1592,8 +1640,7 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
case FDDEFPRM:
return -EINVAL;
case FDFLUSH: /* unconditionally, even if not needed */
- del_timer (flush_track_timer + drive);
- non_int_flush_track(drive);
+ flush_track_timer_sync(drive);
break;
#ifdef RAW_IOCTL
case IOCTL_RAW_TRACK:
@@ -1612,9 +1659,14 @@ static int fd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
+ bool freeze_queue = cmd == FDFMTTRK || cmd == FDFLUSH;
mutex_lock(&amiflop_mutex);
+ if (freeze_queue)
+ 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);
mutex_unlock(&amiflop_mutex);
return ret;
@@ -1716,9 +1768,9 @@ static void floppy_release(struct gendisk *disk, fmode_t mode)
int drive = p - unit;
mutex_lock(&amiflop_mutex);
+ blk_mq_freeze_queue(disk->queue);
if (unit[drive].dirty == 1) {
- del_timer (flush_track_timer + drive);
- non_int_flush_track (drive);
+ flush_track_timer_sync(drive);
}
if (!fd_ref[drive]--) {
@@ -1728,6 +1780,7 @@ static void floppy_release(struct gendisk *disk, fmode_t mode)
#ifdef MODULE
floppy_off (drive);
#endif
+ blk_mq_unfreeze_queue(disk->queue);
mutex_unlock(&amiflop_mutex);
}
--
2.53.0
prev parent reply other threads:[~2026-11-10 23:10 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 11:01 FAILED: patch "[PATCH] block: amiflop: synchronize flush timer before track flush" failed to apply to 5.15-stable tree Sasha Levin
2026-11-10 23:10 ` Sasha Levin [this message]
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=20261110230918.76289-1-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=axboe@kernel.dk \
--cc=runyu.xiao@seu.edu.cn \
--cc=stable@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