commit 46e80fa875722245d01e77bf515488fba90c5a41
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Thu May 28 03:01:29 2026 +0200

    linux: Pass DEV_FLUSH_CACHE set_status to drivers

diff --git a/linux/dev/glue/block.c b/linux/dev/glue/block.c
index 59462a86..91b1599a 100644
--- a/linux/dev/glue/block.c
+++ b/linux/dev/glue/block.c
@@ -64,6 +64,7 @@
 #include <device/disk_status.h>
 #include <device/device_reply.user.h>
 #include <device/device_emul.h>
+#include <device/device_types.h>
 #include <device/ds_routines.h>
 
 /* TODO.  This should be fixed to not be i386 specific.  */
@@ -1729,6 +1730,7 @@ device_set_status (void *d, dev_flavor_t flavor, dev_status_t status,
 
   switch (flavor)
     {
+      case DEV_FLUSH_CACHE:
       case BLKRRPART:
 	{
 	  DECL_DATA;
commit e9a35a23ecc9e128ef525ecf6c97e065835d2d7b
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Thu May 28 03:01:51 2026 +0200

    ide: Support cache flush

diff --git a/linux/src/drivers/block/ide.c b/linux/src/drivers/block/ide.c
index c8dee846..73d2f96e 100644
--- a/linux/src/drivers/block/ide.c
+++ b/linux/src/drivers/block/ide.c
@@ -302,6 +302,8 @@
 #include <linux/genhd.h>
 #include <linux/malloc.h>
 
+#include <device/device_types.h>
+
 #include <ahci.h>
 
 #include <asm/byteorder.h>
@@ -2385,6 +2387,13 @@ static int ide_ioctl (struct inode *inode, struct file *file,
 			restore_flags(flags);
 			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
 			return 0;
+		case DEV_FLUSH_CACHE:
+		{
+			byte flush[] = {WIN_FLUSH_CACHE,0,0,0};
+			rq.buffer = (char *)flush;
+			err = ide_do_drive_cmd (drive, &rq, ide_wait);
+			return err;
+		}
 
 		RO_IOCTLS(inode->i_rdev, arg);
 
diff --git a/linux/src/include/linux/hdreg.h b/linux/src/include/linux/hdreg.h
index 4a388c5d..9a9fe7e0 100644
--- a/linux/src/include/linux/hdreg.h
+++ b/linux/src/include/linux/hdreg.h
@@ -58,6 +58,8 @@
 #define WIN_MULTREAD		0xC4	/* read sectors using multiple mode */
 #define WIN_MULTWRITE		0xC5	/* write sectors using multiple mode */
 #define WIN_SETMULT		0xC6	/* enable/disable multiple mode */
+#define WIN_FLUSH_CACHE		0xE7
+#define WIN_FLUSH_CACHE_EXT	0xEA	/* 48-Bit */
 #define WIN_IDENTIFY		0xEC	/* ask drive to identify itself	*/
 #define WIN_SETFEATURES		0xEF	/* set special drive features */
 #define WIN_READDMA		0xc8	/* read sectors using DMA transfers */
commit 9d10f486be9d328870fab5979de48695d824e0c4
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Thu May 28 03:02:10 2026 +0200

    ahci: Support cache flush

diff --git a/linux/dev/drivers/block/ahci.c b/linux/dev/drivers/block/ahci.c
index addcc3e5..776dcd2d 100644
--- a/linux/dev/drivers/block/ahci.c
+++ b/linux/dev/drivers/block/ahci.c
@@ -28,6 +28,8 @@
 #include <linux/genhd.h>
 #include <asm/io.h>
 
+#include <device/device_types.h>
+
 #define MAJOR_NR SCSI_DISK_MAJOR
 #include <linux/blk.h>
 
@@ -250,9 +252,20 @@ static struct port {
 	unsigned lba48;			/* Whether LBA48 is supported */
 	unsigned identify;		/* Whether we are just identifying
 					   at boot */
+	unsigned flush;			/* Whether we are flushing */
 	struct gendisk *gd;
 } ports[MAX_PORTS];
 
+/* Disk timed out while processing command, interrupt operation */
+static void command_timeout(unsigned long data)
+{
+	struct port *port = (void*) data;
+
+	wake_up(&port->q);
+}
+
+static struct timer_list command_timer = { .function = command_timeout };
+
 
 /* do_request() gets called by the block layer to push a request to the disk.
    We just push one, and when an interrupt tells it's over, we call do_request()
@@ -428,11 +441,73 @@ kill_rq:
 	ahci_end_request(0);
 }
 
+/* Push the request to the controler port */
+static int ahci_do_flush(struct port *port)
+{
+	struct ahci_command *command = port->command;
+	struct ahci_cmd_tbl *prdtl = port->prdtl;
+	struct ahci_fis_h2d *fis_h2d;
+	unsigned slot = 1;
+	unsigned long flags;
+	unsigned long long timeout;
+
+	fis_h2d = (void*) &prdtl[slot].cfis;
+	fis_h2d->fis_type = FIS_TYPE_REG_H2D;
+	fis_h2d->flags = 128;
+	if (port->lba48) {
+		fis_h2d->command = WIN_FLUSH_CACHE_EXT;
+	} else {
+		fis_h2d->command = WIN_FLUSH_CACHE;
+	}
+
+	fis_h2d->device = 0;
+
+	command[slot].opts = sizeof(*fis_h2d) / sizeof(u32);
+
+	port->flush = 1;
+
+	save_flags(flags);
+	cli();
+
+	/* Make sure main memory buffers are up to date */
+	mb();
+	/* Issue command */
+	writel(1 << slot, &port->ahci_port->ci);
+
+	timeout = jiffies + WAIT_MAX;
+	command_timer.expires = timeout;
+	command_timer.data = (unsigned long) port;
+	add_timer(&command_timer);
+	while (readl(&port->ahci_port->ci) & (1 << slot)) {
+		if (jiffies >= timeout) {
+			printk("sd%u: timeout waiting for flush\n", port-ports);
+			port->ahci_host = NULL;
+			port->ahci_port = NULL;
+			del_timer(&command_timer);
+			port->flush = 0;
+			restore_flags(flags);
+			return -EIO;
+		}
+		sleep_on(&port->q);
+	}
+	del_timer(&command_timer);
+	restore_flags(flags);
+
+	port->flush = 0;
+
+	return 0;
+}
+
 /* The given port got an interrupt, terminate the current request if any */
 static void ahci_port_interrupt(struct port *port, u32 status)
 {
 	unsigned slot = 0;
 
+	if (port->flush && !(readl(&port->ahci_port->ci) & (1 << 1))) {
+		/* Flush done */
+		wake_up(&port->q);
+	}
+
 	if (readl(&port->ahci_port->ci) & (1 << slot)) {
 		/* Command still pending */
 		return;
@@ -502,7 +577,7 @@ static int ahci_ioctl (struct inode *inode, struct file *file,
 	if (major != MAJOR_NR)
 		return -ENOTTY;
 
-	unit = DEVICE_NR(inode->i_rdev);
+	unit = MINOR(inode->i_rdev) >> PARTN_BITS;
 	if (unit >= MAX_PORTS)
 		return -EINVAL;
 
@@ -513,6 +588,11 @@ static int ahci_ioctl (struct inode *inode, struct file *file,
 				return -EINVAL;
 			resetup_one_dev(ports[unit].gd, unit);
 			return 0;
+		case DEV_FLUSH_CACHE:
+			if (!suser()) return -EACCES;
+			if (!ports[unit].gd)
+				return -EINVAL;
+			return ahci_do_flush(&ports[unit]);
 		default:
 			return -EPERM;
 	}
@@ -541,8 +621,7 @@ static void ahci_release (struct inode *inode, struct file *file)
 
 static int ahci_fsync (struct inode *inode, struct file *file)
 {
-	printk("fsync\n");
-	return -ENOSYS;
+	return ahci_ioctl(inode, file, DEV_FLUSH_CACHE, 0);
 }
 
 static struct file_operations ahci_fops = {
@@ -561,16 +640,6 @@ static struct file_operations ahci_fops = {
 	.revalidate = NULL,
 };
 
-/* Disk timed out while processing identify, interrupt ahci_probe_port */
-static void identify_timeout(unsigned long data)
-{
-	struct port *port = (void*) data;
-
-	wake_up(&port->q);
-}
-
-static struct timer_list identify_timer = { .function = identify_timeout };
-
 static int ahci_identify(const volatile struct ahci_host *ahci_host, const volatile struct ahci_port *ahci_port, struct port *port, unsigned cmd)
 {
 	struct hd_driveid id;
@@ -642,22 +711,24 @@ static int ahci_identify(const volatile struct ahci_host *ahci_host, const volat
 	writel(1 << slot, &ahci_port->ci);
 
 	timeout = jiffies + WAIT_MAX;
-	identify_timer.expires = timeout;
-	identify_timer.data = (unsigned long) port;
-	add_timer(&identify_timer);
+	command_timer.expires = timeout;
+	command_timer.data = (unsigned long) port;
+	add_timer(&command_timer);
 	while (!port->status) {
 		if (jiffies >= timeout) {
-                       printk("sd%u: timeout waiting for identify\n", port-ports);
+			printk("sd%u: timeout waiting for identify\n", port-ports);
 			port->ahci_host = NULL;
 			port->ahci_port = NULL;
-			del_timer(&identify_timer);
+			port->identify = 0;
+			del_timer(&command_timer);
 			restore_flags(flags);
 			return 3;
 		}
 		sleep_on(&port->q);
 	}
-	del_timer(&identify_timer);
+	del_timer(&command_timer);
 	restore_flags(flags);
+	port->identify = 0;
 
 	if ((port->status & PORT_IRQ_TF_ERR) || readl(&ahci_port->is) & PORT_IRQ_TF_ERR)
 	{
@@ -719,7 +790,6 @@ static int ahci_identify(const volatile struct ahci_host *ahci_host, const volat
 		else
 			printk("sd%u: %s, %uMB w/%dkB Cache\n", (unsigned) (port - ports), id.model, (unsigned) (port->capacity/2048), id.buf_size/2);
 	}
-	port->identify = 0;
 
 	return ret;
 }
