Skip to contentSkip to conversation

cdrom: gdrom: fix block I/O and capacity setting

PATCH Superseded Florian Fuchs 4 participants 8 messages Apr 19 – Apr 20 SuperH

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover cdrom: gdrom: fix block I/O and capacity setting

Hi all,

This series fixes a gdrom driver Oops due to bad MMIO register access and fixes the missing updates of the block layer gendisk capacity that prevented ISO9660 mounts from working.

The change was tested on real Sega Dreamcast devices (PAL-E, NTSC-J, NTSC-U) with physical CD-R discs and with GDEMU emulated discs. Before:
Oops on mount and an unusable drive. After: Successfully able to mount and use the inserted medium.

Thanks,
Florian
---
v1->v2: for "cdrom: gdrom: replace port I/O with MMIO accessors": Don't
use helper functions with io.*_rep(), but writesw() and readsw() local in the respective functions. Improved failure case of gdrom_update_capacity() in gdrom_bdops_open().

v1: https://lore.kernel.org/linux-sh/[email protected]/

Florian Fuchs (2):
cdrom: gdrom: replace port I/O with MMIO accessors
cdrom: gdrom: update gendisk capacity on open

 drivers/cdrom/gdrom.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f

Signature · 1 line
2.43.0
#1 lore raw

PATCH v2 1/2 cdrom: gdrom: replace port I/O with MMIO accessors

GDROM_DATA_REG is a memory-mapped data register, but the driver uses outsw() and insw() only for this register. Replace this with MMIO accessors readsw() / writesw().

Before, it oopsed accessing the data register, as the io_port_base P2SEG gets added to the argument in outsw() / insw(), which leads to an unusable drive:

        BUG: unable to handle kernel paging request at 405f7080
        PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0

---
v1->v2: Don't use helper functions with io.*_rep(), but writesw() and
readsw() local in the respective functions

v1: https://lore.kernel.org/linux-sh/[email protected]/

---
 drivers/cdrom/gdrom.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/cdrom/gdrom.c +4 -4
index 4ba4dd06cbf4..89938d9b2825 100644
@@ -198,7 +198,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
 		gdrom_getsense(NULL);
 		return;
 	}
-	outsw(GDROM_DATA_REG, cmd, 6);
+	writesw((void __iomem *)GDROM_DATA_REG, cmd, 6);
 }
 
 
@@ -282,7 +282,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session)
 		err = -EINVAL;
 		goto cleanup_readtoc;
 	}
-	insw(GDROM_DATA_REG, toc, tocsize/2);
+	readsw((void __iomem *)GDROM_DATA_REG, toc, tocsize / 2);
 	if (gd.status & 0x01)
 		err = -EINVAL;
 
@@ -433,7 +433,7 @@ static int gdrom_getsense(short *bufstring)
 		GDROM_DEFAULT_TIMEOUT);
 	if (gd.pending)
 		goto cleanup_sense;
-	insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
+	readsw((void __iomem *)GDROM_DATA_REG, &sense, sense_command->buflen / 2);
 	if (sense[1] & 40) {
 		pr_info("Drive not ready - command aborted\n");
 		goto cleanup_sense;
@@ -612,7 +612,7 @@ static blk_status_t gdrom_readdisk_dma(struct request *req)
 		cpu_relax();
 	gd.pending = 1;
 	gd.transfer = 1;
-	outsw(GDROM_DATA_REG, &read_command->cmd, 6);
+	writesw((void __iomem *)GDROM_DATA_REG, read_command->cmd, 6);
 	timeout = jiffies + HZ / 2;
 	/* Wait for any pending DMA to finish */
 	while (__raw_readb(GDROM_DMA_STATUS_REG) &&
Signature · 1 line
2.43.0
Acked-by on v2 1/2
#2 lore raw

Re: [PATCH v2 1/2] cdrom: gdrom: replace port I/O with MMIO accessors

Quoted 4 lines · Florian Fuchs

GDROM_DATA_REG is a memory-mapped data register, but the driver uses outsw() and insw() only for this register. Replace this with MMIO accessors readsw() / writesw().

Quoted 8 lines

Before, it oopsed accessing the data register, as the io_port_base P2SEG gets added to the argument in outsw() / insw(), which leads to an unusable drive:

        BUG: unable to handle kernel paging request at 405f7080
        PC: [<8c28d5b4>] gdrom_spicommand+0x6c/0xb0
Quoted 53 lines

---
v1->v2: Don't use helper functions with io.*_rep(), but writesw() and
readsw() local in the respective functions

v1: https://lore.kernel.org/linux-sh/[email protected]/

---
 drivers/cdrom/gdrom.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/cdrom/gdrom.c +3 -3
index 4ba4dd06cbf4..89938d9b2825 100644
@@ -198,7 +198,7 @@ static void gdrom_spicommand(void *spi_string, int 
 buflen)
 		gdrom_getsense(NULL);
 		return;
 	}
-	outsw(GDROM_DATA_REG, cmd, 6);
+	writesw((void __iomem *)GDROM_DATA_REG, cmd, 6);
 }
 
@@ -282,7 +282,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, 
 int session)
 		err = -EINVAL;
 		goto cleanup_readtoc;
 	}
-	insw(GDROM_DATA_REG, toc, tocsize/2);
+	readsw((void __iomem *)GDROM_DATA_REG, toc, tocsize / 2);
 	if (gd.status & 0x01)
 		err = -EINVAL;
@@ -433,7 +433,7 @@ static int gdrom_getsense(short *bufstring)
 		GDROM_DEFAULT_TIMEOUT);
 	if (gd.pending)
 		goto cleanup_sense;
-	insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
+	readsw((void __iomem *)GDROM_DATA_REG, &sense, sense_command->buflen 
 / 2);
 	if (sense[1] & 40) {
 		pr_info("Drive not ready - command aborted\n");
drivers/cdrom/gdrom.c +1 -1
 		goto cleanup_sense;
@@ -612,7 +612,7 @@ static blk_status_t gdrom_readdisk_dma(struct 
 request *req)
 		cpu_relax();
 	gd.pending = 1;
 	gd.transfer = 1;
-	outsw(GDROM_DATA_REG, &read_command->cmd, 6);
+	writesw((void __iomem *)GDROM_DATA_REG, read_command->cmd, 6);
 	timeout = jiffies + HZ / 2;
 	/* Wait for any pending DMA to finish */

while (__raw_readb(GDROM_DMA_STATUS_REG) &&

#3 lore raw

Re: [PATCH v2 1/2] cdrom: gdrom: replace port I/O with MMIO accessors

In reply to Florian Fuchs
Quoted 1 line · Florian Fuchs

Not tested this on real hardware but I can confirm it addresses the identified bug and is logically correct.

Adrian McMenamin

#4 lore raw

Re: [PATCH v2 1/2] cdrom: gdrom: replace port I/O with MMIO accessors

Hi Adrian,

Quoted 4 lines · Adrian McMenamin
Quoted 1 line · Florian Fuchs

Not tested this on real hardware but I can confirm it addresses the identified bug and is logically correct.

Want to add your Reviewed-by maybe?

Adrian

Signature · 4 lines
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
#6 lore raw

PATCH v2 2/2 cdrom: gdrom: update gendisk capacity on open

In reply to Florian Fuchs

Update the gendisk capacity of the media. Without the capacity, the block reads fail before reaching the request queue, which prevented ISO9660 mounts. Refresh the capacity from the TOC leadout in gdrom_bdops_open() so it checks the inserted media.

---
v1->v2: no change for gdrom_update_capacity(), but for
gdrom_bdops_open(): handle the failure case when gdrom_update_capacity() fails but previous cdrom_open() succeeded, to cleanup the successful cdrom_open() with cdrom_release()

v1: https://lore.kernel.org/linux-sh/[email protected]/

---
 drivers/cdrom/gdrom.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
drivers/cdrom/gdrom.c +25 -0
index 89938d9b2825..fb95288fad72 100644
@@ -474,6 +474,25 @@ static const struct cdrom_device_ops gdrom_ops = {
 				  CDC_RESET | CDC_DRIVE_STATUS | CDC_CD_R,
 };
 
+static int gdrom_update_capacity(void)
+{
+	sector_t cap;
+	int ret;
+
+	if (gdrom_drivestatus(gd.cd_info, CDSL_CURRENT) != CDS_DISC_OK) {
+		set_capacity(gd.disk, 0);
+		return -ENOMEDIUM;
+	}
+	ret = gdrom_readtoc_cmd(gd.toc, 0);
+	if (ret) {
+		set_capacity(gd.disk, 0);
+		return ret;
+	}
+	cap = (sector_t)get_entry_lba(gd.toc->leadout) * GD_TO_BLK;
+	set_capacity(gd.disk, cap);
+	return 0;
+}
+
 static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 {
 	int ret;
@@ -482,6 +501,12 @@ static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 
 	mutex_lock(&gdrom_mutex);
 	ret = cdrom_open(gd.cd_info, mode);
+	if (ret)
+		goto out;
+	ret = gdrom_update_capacity();
+	if (ret)
+		cdrom_release(gd.cd_info);
+out:
 	mutex_unlock(&gdrom_mutex);
 	return ret;
 }
Signature · 1 line
2.43.0
Acked-by on v2 2/2
#7 lore raw

Re: [PATCH v2 2/2] cdrom: gdrom: update gendisk capacity on open

Quoted 61 lines · Florian Fuchs

Update the gendisk capacity of the media. Without the capacity, the block reads fail before reaching the request queue, which prevented ISO9660 mounts. Refresh the capacity from the TOC leadout in gdrom_bdops_open() so it checks the inserted media.

---
v1->v2: no change for gdrom_update_capacity(), but for
gdrom_bdops_open(): handle the failure case when gdrom_update_capacity() fails but previous cdrom_open()
succeeded,
to cleanup the successful cdrom_open() with cdrom_release()

v1: https://lore.kernel.org/linux-sh/[email protected]/

---
 drivers/cdrom/gdrom.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
drivers/cdrom/gdrom.c +25 -0
index 89938d9b2825..fb95288fad72 100644
@@ -474,6 +474,25 @@ static const struct cdrom_device_ops gdrom_ops = {
 				  CDC_RESET | CDC_DRIVE_STATUS | CDC_CD_R,
 };
 
+static int gdrom_update_capacity(void)
+{
+	sector_t cap;
+	int ret;
+
+	if (gdrom_drivestatus(gd.cd_info, CDSL_CURRENT) != CDS_DISC_OK) {
+		set_capacity(gd.disk, 0);
+		return -ENOMEDIUM;
+	}
+	ret = gdrom_readtoc_cmd(gd.toc, 0);
+	if (ret) {
+		set_capacity(gd.disk, 0);
+		return ret;
+	}
+	cap = (sector_t)get_entry_lba(gd.toc->leadout) * GD_TO_BLK;
+	set_capacity(gd.disk, cap);
+	return 0;
+}
+
 static int gdrom_bdops_open(struct gendisk *disk, blk_mode_t mode)
 {
 	int ret;
@@ -482,6 +501,12 @@ static int gdrom_bdops_open(struct gendisk *disk, 
 blk_mode_t mode)
 
 	mutex_lock(&gdrom_mutex);
 	ret = cdrom_open(gd.cd_info, mode);
+	if (ret)
+		goto out;
+	ret = gdrom_update_capacity();
+	if (ret)
+		cdrom_release(gd.cd_info);
+out:
 	mutex_unlock(&gdrom_mutex);
 	return ret;
Quoted 1 line

}

Hey Florian,

thanks for v2!

PS. I found the reason behind mount failing for the first time after a disc swap - you need to send a dummy 0x0 SPI command to "sink" the pending UNIT_ATTENTION error, otherwise it will permeate at the next command executed, which in our case is gdrom_preparedisk_cmd().

I already sent you a WIP patch via IRC. Once you test it, would you consider including it in this series and sending v3?

Cheers,
Artur