linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] pata_parport: add driver (PARIDE replacement)
@ 2022-03-12 14:44 Ondrej Zary
  2022-03-13 19:15 ` Ondrej Zary
  2022-03-13 20:38 ` Sergey Shtylyov
  0 siblings, 2 replies; 31+ messages in thread
From: Ondrej Zary @ 2022-03-12 14:44 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

The pata_parport is a libata-based replacement of the old PARIDE
subsystem - driver for parallel port IDE devices.
It uses the original paride low-level protocol drivers but does not
need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
behind parallel port adapters are handled by the ATA layer.

This will allow paride and its high-level drivers to be removed.

paride and pata_parport are mutually exclusive because the compiled
protocol drivers are incompatible.

Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
chip).

Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
drivers - they don't handle non-multiple-of-4 block transfers
correctly. This causes problems with LS-120 drive.
There is also another bug in EPAT: EPP modes don't work unless a 4-bit
or 8-bit mode is used first (probably some initialization missing?).
Once the device is initialized, EPP works until power cycle.

So after device power on, you have to:
echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
echo pata_parport.0 >/sys/bus/pata_parport/delete_device
echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
(autoprobe will initialize correctly as it tries the slowest modes
first but you'll get the broken EPP-32 mode)

Signed-off-by: Ondrej Zary <linux@zary.sk>
---
 Documentation/admin-guide/blockdev/paride.rst |  52 ++
 drivers/Makefile                              |   2 +-
 drivers/ata/Kconfig                           |  14 +
 drivers/ata/Makefile                          |   2 +
 drivers/ata/pata_parport.c                    | 819 ++++++++++++++++++
 drivers/block/paride/Kconfig                  |  32 +-
 drivers/block/paride/paride.h                 |  13 +
 include/linux/pata_parport.h                  | 108 +++
 8 files changed, 1025 insertions(+), 17 deletions(-)
 create mode 100644 drivers/ata/pata_parport.c
 create mode 100644 include/linux/pata_parport.h

diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
index e1ce90af602a..e431a1ef41eb 100644
--- a/Documentation/admin-guide/blockdev/paride.rst
+++ b/Documentation/admin-guide/blockdev/paride.rst
@@ -2,6 +2,9 @@
 Linux and parallel port IDE devices
 ===================================
 
+Most of this document describes the old paride driver. For the new libata
+pata_parport drivrer, jump to the section 4 at the end.
+
 PARIDE v1.03   (c) 1997-8  Grant Guenther <grant@torque.net>
 
 1. Introduction
@@ -437,3 +440,52 @@ You might also find some useful information on the linux-parport
 web pages (although they are not always up to date) at
 
 	http://web.archive.org/web/%2E/http://www.torque.net/parport/
+
+4. pata_parport driver
+======================
+pata_parport is a libata-based driver that uses the same low-level protocol
+drivers as PARIDE but there are no high-level drivers (pd, pcd, pf, pt, pg).
+The IDE devices behind parallel port adapters are handled by the ATA layer.
+
+The device creation is also changed - no protocol numbers or parport I/O
+addresses are used.
+
+All parports and all protocol drivers are probed automatically unless probe=0
+parameter is used. So just "modprobe epat" is enough for a Imation SuperDisk
+drive to work.
+
+Manual device creation::
+
+	# echo "port protocol mode unit delay" >/sys/bus/pata_parport/new_device
+
+where:
+
+	======== ================================================
+	port	 parport name (or "auto" for all parports)
+	protocol protocol name (or "auto" for all protocols)
+	mode	 mode number (protocol-specific) or -1 for probe
+	unit	 unit number (see the paride documentation above)
+	delay	 I/O delay (see the paride documentation above)
+	======== ================================================
+
+If you omit the parameters from the end, defaults will be used, e.g.:
+
+Probe all parports with all protocols::
+
+	# echo auto >/sys/bus/pata_parport/new_device
+
+Probe parport0 using protocol epat and mode 4 (EPP-16)::
+
+	# echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
+
+Probe parport0 using all protocols::
+
+	# echo "parport0 auto" >/sys/bus/pata_parport/new_device
+
+Probe all parports using protoocol epat::
+
+	# echo "auto epat" >/sys/bus/pata_parport/new_device
+
+Deleting devices::
+
+	# echo pata_parport.0 >/sys/bus/pata_parport/delete_device
diff --git a/drivers/Makefile b/drivers/Makefile
index a110338c860c..8ec515f3614e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -98,7 +98,7 @@ obj-$(CONFIG_DIO)		+= dio/
 obj-$(CONFIG_SBUS)		+= sbus/
 obj-$(CONFIG_ZORRO)		+= zorro/
 obj-$(CONFIG_ATA_OVER_ETH)	+= block/aoe/
-obj-$(CONFIG_PARIDE) 		+= block/paride/
+obj-y		 		+= block/paride/
 obj-$(CONFIG_TC)		+= tc/
 obj-$(CONFIG_USB_PHY)		+= usb/
 obj-$(CONFIG_USB)		+= usb/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index e5641e6c52ee..ccbf6114fd53 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -1161,6 +1161,20 @@ config PATA_WINBOND_VLB
 	  Support for the Winbond W83759A controller on Vesa Local Bus
 	  systems.
 
+config PATA_PARPORT
+	tristate "Parallel port IDE device support"
+	depends on PARPORT_PC && PARIDE=n
+	help
+	  There are many external CD-ROM and disk devices that connect through
+	  your computer's parallel port. Most of them are actually IDE devices
+	  using a parallel port IDE adapter. This option enables the
+	  PATA_PARPORT subsystem which contains drivers for many of these
+	  external drives.
+	  Read <file:Documentation/admin-guide/blockdev/paride.rst> for more
+	  information.
+
+	  Use the old PARIDE protocol modules.
+
 comment "Generic fallback / legacy drivers"
 
 config PATA_ACPI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b8aebfb14e82..77145834a585 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -114,6 +114,8 @@ obj-$(CONFIG_PATA_SAMSUNG_CF)	+= pata_samsung_cf.o
 
 obj-$(CONFIG_PATA_PXA)		+= pata_pxa.o
 
+obj-$(CONFIG_PATA_PARPORT)	+= pata_parport.o
+
 # Should be last but two libata driver
 obj-$(CONFIG_PATA_ACPI)		+= pata_acpi.o
 # Should be last but one libata driver
diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
new file mode 100644
index 000000000000..783764626a27
--- /dev/null
+++ b/drivers/ata/pata_parport.c
@@ -0,0 +1,819 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022 Ondrej Zary
+ * based on paride.c by Grant R. Guenther <grant@torque.net>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/pata_parport.h>
+
+#define DRV_NAME "pata_parport"
+
+static DEFINE_IDR(parport_list);
+static DEFINE_IDR(protocols);
+static DEFINE_IDA(pata_parport_bus_dev_ids);
+static DEFINE_MUTEX(pi_mutex);
+
+static bool probe = true;
+module_param(probe, bool, 0644);
+MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
+
+static bool verbose;
+module_param(verbose, bool, 0644);
+MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
+
+#define DISCONNECT_TIMEOUT	(HZ / 10)
+
+static void pi_connect(struct pi_adapter *pi)
+{
+	del_timer_sync(&pi->timer);
+	if (!pi->claimed) {
+		pi->claimed = true;
+		parport_claim_or_block(pi->pardev);
+		pi->proto->connect(pi);
+	}
+}
+
+static void pi_disconnect_timer(struct timer_list *t)
+{
+	struct pi_adapter *pi = from_timer(pi, t, timer);
+
+	if (pi->claimed) {
+		pi->proto->disconnect(pi);
+		parport_release(pi->pardev);
+		pi->claimed = false;
+	}
+}
+
+/* functions taken from libata-sff.c and converted from direct port I/O */
+static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+	u8 tmp;
+
+	if (device == 0)
+		tmp = ATA_DEVICE_OBS;
+	else
+		tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+	pi_connect(pi);
+	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+	ata_sff_pause(ap);
+}
+
+static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+	u8 nsect, lbal;
+
+	pata_parport_dev_select(ap, device);
+
+	pi_connect(pi);
+	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
+	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
+	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);
+
+	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 055);
+	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
+
+	nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+	lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+
+	if ((nsect == 0x55) && (lbal == 0xaa))
+		return true;	/* we found a device */
+
+	return false;		/* nothing found */
+}
+
+static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
+				      unsigned long deadline)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	/* software reset.  causes dev0 to be selected */
+	pi->proto->write_regr(pi, 1, 6, ap->ctl);
+	udelay(20);
+	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
+	udelay(20);
+	pi->proto->write_regr(pi, 1, 6, ap->ctl);
+	ap->last_ctl = ap->ctl;
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+
+	/* wait the port to become ready */
+	return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
+				  unsigned long deadline)
+{
+	struct ata_port *ap = link->ap;
+	unsigned int devmask = 0;
+	int rc;
+	u8 err;
+
+	/* determine if device 0/1 are present */
+	if (pata_parport_devchk(ap, 0))
+		devmask |= (1 << 0);
+	if (pata_parport_devchk(ap, 1))
+		devmask |= (1 << 1);
+
+	/* select device 0 again */
+	pata_parport_dev_select(ap, 0);
+
+	/* issue bus reset */
+	rc = pata_parport_bus_softreset(ap, devmask, deadline);
+	if (rc && rc != -ENODEV) {
+		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
+		return rc;
+	}
+
+	/* determine by signature whether we have ATA or ATAPI devices */
+	classes[0] = ata_sff_dev_classify(&link->device[0],
+					  devmask & (1 << 0), &err);
+	if (err != 0x81)
+		classes[1] = ata_sff_dev_classify(&link->device[1],
+						  devmask & (1 << 1), &err);
+
+	return 0;
+}
+
+static u8 pata_parport_check_status(struct ata_port *ap)
+{
+	u8 status;
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+
+	return status;
+}
+
+static u8 pata_parport_check_altstatus(struct ata_port *ap)
+{
+	u8 altstatus;
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	altstatus = pi->proto->read_regr(pi, 1, 6);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+
+	return altstatus;
+}
+
+static void pata_parport_tf_load(struct ata_port *ap,
+				 const struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	if (tf->ctl != ap->last_ctl) {
+		pi->proto->write_regr(pi, 1, 6, tf->ctl);
+		ap->last_ctl = tf->ctl;
+		ata_wait_idle(ap);
+	}
+
+	if (tf->flags & ATA_TFLAG_ISADDR) {
+		if (tf->flags & ATA_TFLAG_LBA48) {
+			pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
+					      tf->hob_feature);
+			pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
+					      tf->hob_nsect);
+			pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
+					      tf->hob_lbal);
+			pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
+					      tf->hob_lbam);
+			pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
+					      tf->hob_lbah);
+		}
+		pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
+		pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
+		pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
+		pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
+		pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
+	}
+
+	if (tf->flags & ATA_TFLAG_DEVICE)
+		pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);
+
+	ata_wait_idle(ap);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+}
+
+static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
+	tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+	tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+	tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+	tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+	tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+	tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);
+
+	if (tf->flags & ATA_TFLAG_LBA48) {
+		pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
+		tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
+		tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+		tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+		tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
+		tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
+		pi->proto->write_regr(pi, 1, 6, tf->ctl);
+		ap->last_ctl = tf->ctl;
+	}
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+}
+
+static void pata_parport_exec_command(struct ata_port *ap,
+				      const struct ata_taskfile *tf)
+{
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
+	ata_sff_pause(ap);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+}
+
+static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
+				unsigned char *buf, unsigned int buflen, int rw)
+{
+	struct ata_port *ap = qc->dev->link->ap;
+	struct pi_adapter *pi = ap->host->private_data;
+
+	pi_connect(pi);
+	if (rw == READ)
+		pi->proto->read_block(pi, buf, buflen);
+	else
+		pi->proto->write_block(pi, buf, buflen);
+	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+
+	return buflen;
+}
+
+static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
+{
+	int count;
+	struct ata_port *ap;
+	struct pi_adapter *pi;
+	char junk[2];
+
+	/* We only need to flush incoming data when a command was running */
+	if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
+		return;
+
+	ap = qc->ap;
+	pi = ap->host->private_data;
+	/* Drain up to 64K of data before we give up this recovery method */
+	for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
+						&& count < 65536; count += 2) {
+		pi_connect(pi);
+		pi->proto->read_block(pi, junk, 2);
+		mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
+	}
+
+	if (count)
+		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
+}
+
+static void pata_parport_lost_interrupt(struct ata_port *ap)
+{
+	u8 status;
+	struct ata_queued_cmd *qc;
+
+	/* Only one outstanding command per SFF channel */
+	qc = ata_qc_from_tag(ap, ap->link.active_tag);
+	/* We cannot lose an interrupt on a non-existent or polled command */
+	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
+		return;
+	/*
+	 * See if the controller thinks it is still busy - if so the command
+	 * isn't a lost IRQ but is still in progress
+	 */
+	status = pata_parport_check_altstatus(ap);
+	if (status & ATA_BUSY)
+		return;
+
+	/*
+	 * There was a command running, we are no longer busy and we have
+	 * no interrupt.
+	 */
+	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
+	/* Run the host interrupt logic as if the interrupt had not been lost */
+	ata_sff_port_intr(ap, qc);
+}
+
+static struct ata_port_operations pata_parport_port_ops = {
+	.inherits		= &ata_sff_port_ops,
+
+	.softreset		= pata_parport_softreset,
+	.hardreset		= NULL,
+
+	.sff_dev_select		= pata_parport_dev_select,
+	.sff_check_status	= pata_parport_check_status,
+	.sff_check_altstatus	= pata_parport_check_altstatus,
+	.sff_tf_load		= pata_parport_tf_load,
+	.sff_tf_read		= pata_parport_tf_read,
+	.sff_exec_command	= pata_parport_exec_command,
+	.sff_data_xfer		= pata_parport_data_xfer,
+	.sff_drain_fifo		= pata_parport_drain_fifo,
+
+	.lost_interrupt		= pata_parport_lost_interrupt,
+};
+
+static const struct ata_port_info pata_parport_port_info = {
+	.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
+	.pio_mask	= ATA_PIO0,
+	/* No DMA */
+	.port_ops	= &pata_parport_port_ops,
+};
+
+static void pi_release(struct pi_adapter *pi)
+{
+	parport_unregister_device(pi->pardev);
+	if (pi->proto->release_proto)
+		pi->proto->release_proto(pi);
+	module_put(pi->proto->owner);
+}
+
+static int default_test_proto(struct pi_adapter *pi, char *scratch)
+{
+	int j, k;
+	int e[2] = { 0, 0 };
+
+	pi->proto->connect(pi);
+
+	for (j = 0; j < 2; j++) {
+		pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
+		for (k = 0; k < 256; k++) {
+			pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
+			pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
+			if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
+				e[j]++;
+		}
+	}
+	pi->proto->disconnect(pi);
+
+	if (verbose)
+		dev_info(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
+		       pi->proto->name, pi->port,
+		       pi->mode, e[0], e[1]);
+
+	return e[0] && e[1];	/* not here if both > 0 */
+}
+
+static int pi_test_proto(struct pi_adapter *pi, char *scratch)
+{
+	int res;
+
+	parport_claim_or_block(pi->pardev);
+	if (pi->proto->test_proto)
+		res = pi->proto->test_proto(pi, scratch, verbose);
+	else
+		res = default_test_proto(pi, scratch);
+	parport_release(pi->pardev);
+
+	return res;
+}
+
+static bool pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
+{
+	int best, range;
+
+	if (pi->mode != -1) {
+		if (pi->mode >= max)
+			return false;
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first)
+			range = 8;
+		if (range == 8 && pi->port % 8)
+			return false;
+		return !pi_test_proto(pi, scratch);
+	}
+	best = -1;
+	for (pi->mode = 0; pi->mode < max; pi->mode++) {
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first)
+			range = 8;
+		if (range == 8 && pi->port % 8)
+			break;
+		if (!pi_test_proto(pi, scratch))
+			best = pi->mode;
+	}
+	pi->mode = best;
+	return best > -1;
+}
+
+static bool pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
+{
+	int max, s, e;
+
+	s = unit;
+	e = s + 1;
+
+	if (s == -1) {
+		s = 0;
+		e = pi->proto->max_units;
+	}
+
+	if (pi->proto->test_port) {
+		parport_claim_or_block(pi->pardev);
+		max = pi->proto->test_port(pi);
+		parport_release(pi->pardev);
+	} else {
+		max = pi->proto->max_mode;
+	}
+
+	if (pi->proto->probe_unit) {
+		parport_claim_or_block(pi->pardev);
+		for (pi->unit = s; pi->unit < e; pi->unit++) {
+			if (pi->proto->probe_unit(pi)) {
+				parport_release(pi->pardev);
+				return pi_probe_mode(pi, max, scratch);
+			}
+		}
+		parport_release(pi->pardev);
+		return false;
+	}
+
+	return pi_probe_mode(pi, max, scratch);
+}
+
+static void pata_parport_dev_release(struct device *dev)
+{
+	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+
+	kfree(pi);
+}
+
+static void pata_parport_bus_release(struct device *dev)
+{
+	/* nothing to do here but required to avoid warning on device removal */
+}
+
+static struct bus_type pata_parport_bus_type = {
+	.name = DRV_NAME,
+};
+
+static struct device pata_parport_bus = {
+	.init_name = DRV_NAME,
+	.release = pata_parport_bus_release,
+};
+
+/* temporary for old paride protocol modules */
+static struct scsi_host_template pata_parport_sht = {
+	PATA_PARPORT_SHT("pata_parport")
+};
+
+static struct pi_adapter *pi_init_one(struct parport *parport,
+			struct pi_protocol *pr, int mode, int unit, int delay)
+{
+	struct pardev_cb par_cb = { };
+	char scratch[512];
+	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
+	struct ata_host *host;
+	struct pi_adapter *pi;
+
+	pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
+	if (!pi)
+		return NULL;
+
+	/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
+	pi->dev.parent = &pata_parport_bus;
+	pi->dev.bus = &pata_parport_bus_type;
+	pi->dev.driver = &pr->driver;
+	pi->dev.release = pata_parport_dev_release;
+	pi->dev.id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
+	if (pi->dev.id < 0)
+		return NULL; /* pata_parport_dev_release will do kfree(pi) */
+	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
+	if (device_register(&pi->dev)) {
+		put_device(&pi->dev);
+		goto out_ida_free;
+	}
+
+	pi->proto = pr;
+
+	if (!try_module_get(pi->proto->owner))
+		goto out_unreg_dev;
+	if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
+		goto out_module_put;
+
+	pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
+	pi->mode = mode;
+	pi->port = parport->base;
+
+	par_cb.private = pi;
+	pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb,
+						pi->dev.id);
+	if (!pi->pardev)
+		goto out_module_put;
+
+	if (!pi_probe_unit(pi, unit, scratch)) {
+		dev_info(&pi->dev, "Adapter not found\n");
+		goto out_unreg_parport;
+	}
+
+	pi->proto->log_adapter(pi, scratch, verbose);
+
+	host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
+	if (!host)
+		goto out_unreg_parport;
+	dev_set_drvdata(&pi->dev, host);
+	host->private_data = pi;
+
+	ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
+	ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
+
+	timer_setup(&pi->timer, pi_disconnect_timer, 0);
+
+	if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
+		goto out_unreg_parport;
+
+	return pi;
+
+out_unreg_parport:
+	parport_unregister_device(pi->pardev);
+	if (pi->proto->release_proto)
+		pi->proto->release_proto(pi);
+out_module_put:
+	module_put(pi->proto->owner);
+out_unreg_dev:
+	device_unregister(&pi->dev);
+out_ida_free:
+	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
+	return NULL;
+}
+
+int pata_parport_register_driver(struct pi_protocol *pr)
+{
+	int error;
+	struct parport *parport;
+	int port_num;
+
+	pr->driver.bus = &pata_parport_bus_type;
+	pr->driver.name = pr->name;
+	error = driver_register(&pr->driver);
+	if (error)
+		return error;
+
+	mutex_lock(&pi_mutex);
+	error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
+	if (error < 0) {
+		driver_unregister(&pr->driver);
+		mutex_unlock(&pi_mutex);
+		return error;
+	}
+
+	pr_info("pata_parport: protocol %s registered\n", pr->name);
+
+	if (probe) {
+		/* probe all parports using this protocol */
+		idr_for_each_entry(&parport_list, parport, port_num)
+			pi_init_one(parport, pr, -1, 0, -1);
+	}
+	mutex_unlock(&pi_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(pata_parport_register_driver);
+
+void pata_parport_unregister_driver(struct pi_protocol *pr)
+{
+	struct pi_protocol *pr_iter;
+	int id = -1;
+
+	mutex_lock(&pi_mutex);
+	idr_for_each_entry(&protocols, pr_iter, id) {
+		if (pr_iter == pr)
+			break;
+	}
+	idr_remove(&protocols, id);
+	mutex_unlock(&pi_mutex);
+	driver_unregister(&pr->driver);
+}
+EXPORT_SYMBOL(pata_parport_unregister_driver);
+
+static ssize_t new_device_store(struct bus_type *bus, const char *buf,
+				size_t count)
+{
+	char port[12] = "auto";
+	char protocol[8] = "auto";
+	int mode = -1, unit = -1, delay = -1;
+	struct pi_protocol *pr, *pr_wanted;
+	struct device_driver *drv;
+	struct parport *parport;
+	int port_num, port_wanted, pr_num;
+	bool ok = false;
+
+	if (sscanf(buf, "%11s %7s %d %d %d",
+			port, protocol, &mode, &unit, &delay) < 1)
+		return -EINVAL;
+
+	if (sscanf(port, "parport%u", &port_wanted) < 1) {
+		if (!strcmp(port, "auto")) {
+			port_wanted = -1;
+		} else {
+			pr_err("invalid port name %s\n", port);
+			return -EINVAL;
+		}
+	}
+
+	drv = driver_find(protocol, &pata_parport_bus_type);
+	if (!drv) {
+		if (!strcmp(protocol, "auto")) {
+			pr_wanted = NULL;
+		} else {
+			pr_err("protocol %s not found\n", protocol);
+			return -EINVAL;
+		}
+	} else {
+		pr_wanted = container_of(drv, struct pi_protocol, driver);
+	}
+
+	mutex_lock(&pi_mutex);
+	/* walk all parports */
+	idr_for_each_entry(&parport_list, parport, port_num) {
+		if (port_num == port_wanted || port_wanted == -1) {
+			parport = parport_find_number(port_num);
+			if (!parport) {
+				pr_err("no such port %s\n", port);
+				mutex_unlock(&pi_mutex);
+				return -ENODEV;
+			}
+			/* walk all protocols */
+			idr_for_each_entry(&protocols, pr, pr_num) {
+				if (pr == pr_wanted || !pr_wanted)
+					if (pi_init_one(parport, pr, mode, unit,
+							delay))
+						ok = true;
+			}
+			parport_put_port(parport);
+		}
+	}
+	mutex_unlock(&pi_mutex);
+	if (!ok)
+		return -ENODEV;
+
+	return count;
+}
+static BUS_ATTR_WO(new_device);
+
+static void pi_remove_one(struct device *dev)
+{
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct pi_adapter *pi = host->private_data;
+
+	ata_host_detach(host);
+	del_timer_sync(&pi->timer);
+	if (pi->claimed) {
+		pi->proto->disconnect(pi);
+		parport_release(pi->pardev);
+	}
+	pi_release(pi);
+	device_unregister(dev);
+	ida_free(&pata_parport_bus_dev_ids, dev->id);
+	/* pata_parport_dev_release will do kfree(pi) */
+}
+
+static ssize_t delete_device_store(struct bus_type *bus, const char *buf,
+				   size_t count)
+{
+	struct device *dev;
+	char device_name[32];
+
+	if (sscanf(buf, "%31s", device_name) < 1)
+		return -EINVAL;
+
+	mutex_lock(&pi_mutex);
+	dev = bus_find_device_by_name(bus, NULL, device_name);
+	if (!dev) {
+		mutex_unlock(&pi_mutex);
+		return -ENODEV;
+	}
+
+	pi_remove_one(dev);
+	mutex_unlock(&pi_mutex);
+
+	return count;
+}
+static BUS_ATTR_WO(delete_device);
+
+static void pata_parport_attach(struct parport *port)
+{
+	struct pi_protocol *pr;
+	int pr_num, id;
+
+	mutex_lock(&pi_mutex);
+	id = idr_alloc(&parport_list, port, port->number, port->number,
+		       GFP_KERNEL);
+	if (id < 0) {
+		mutex_unlock(&pi_mutex);
+		return;
+	}
+
+	if (probe) {
+		/* probe this port using all protocols */
+		idr_for_each_entry(&protocols, pr, pr_num)
+			pi_init_one(port, pr, -1, 0, -1);
+	}
+	mutex_unlock(&pi_mutex);
+}
+
+static int pi_remove_port(struct device *dev, void *p)
+{
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct pi_adapter *pi = host->private_data;
+
+	if (pi->pardev->port == p)
+		pi_remove_one(dev);
+
+	return 0;
+}
+
+static void pata_parport_detach(struct parport *port)
+{
+	mutex_lock(&pi_mutex);
+	bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
+	idr_remove(&parport_list, port->number);
+	mutex_unlock(&pi_mutex);
+}
+
+static struct parport_driver pata_parport_driver = {
+	.name = DRV_NAME,
+	.match_port = pata_parport_attach,
+	.detach = pata_parport_detach,
+	.devmodel = true,
+};
+
+static __init int pata_parport_init(void)
+{
+	int error;
+
+	error = bus_register(&pata_parport_bus_type);
+	if (error) {
+		pr_err("failed to register pata_parport bus, error: %d\n", error);
+		return error;
+	}
+
+	error = device_register(&pata_parport_bus);
+	if (error) {
+		pr_err("failed to register pata_parport bus, error: %d\n", error);
+		goto out_unregister_bus;
+	}
+
+	error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
+	if (error) {
+		pr_err("unable to create sysfs file, error: %d\n", error);
+		goto out_unregister_dev;
+	}
+
+	error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
+	if (error) {
+		pr_err("unable to create sysfs file, error: %d\n", error);
+		goto out_remove_new;
+	}
+
+	error = parport_register_driver(&pata_parport_driver);
+	if (error) {
+		pr_err("unable to register parport driver, error: %d\n", error);
+		goto out_remove_del;
+	}
+
+	return 0;
+
+out_remove_del:
+	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+out_remove_new:
+	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+out_unregister_dev:
+	device_unregister(&pata_parport_bus);
+out_unregister_bus:
+	bus_unregister(&pata_parport_bus_type);
+	return error;
+}
+
+static __exit void pata_parport_exit(void)
+{
+	parport_unregister_driver(&pata_parport_driver);
+	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
+	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
+	device_unregister(&pata_parport_bus);
+	bus_unregister(&pata_parport_bus_type);
+}
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("driver for parallel port ATA adapters");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("paride");
+
+module_init(pata_parport_init);
+module_exit(pata_parport_exit);
diff --git a/drivers/block/paride/Kconfig b/drivers/block/paride/Kconfig
index a295634597ba..01e4ef3655c1 100644
--- a/drivers/block/paride/Kconfig
+++ b/drivers/block/paride/Kconfig
@@ -92,11 +92,11 @@ config PARIDE_PG
 	  later fully support this driver.
 
 comment "Parallel IDE protocol modules"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 
 config PARIDE_ATEN
 	tristate "ATEN EH-100 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the ATEN EH-100 parallel port IDE
 	  protocol. This protocol is used in some inexpensive low performance
@@ -109,7 +109,7 @@ config PARIDE_ATEN
 
 config PARIDE_BPCK
 	tristate "MicroSolutions backpack (Series 5) protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the Micro Solutions BACKPACK
 	  parallel port Series 5 IDE protocol.  (Most BACKPACK drives made
@@ -127,7 +127,7 @@ config PARIDE_BPCK
 
 config PARIDE_BPCK6
 	tristate "MicroSolutions backpack (Series 6) protocol"
-	depends on PARIDE && !64BIT
+	depends on (PARIDE || PATA_PARPORT) && !64BIT
 	help
 	  This option enables support for the Micro Solutions BACKPACK
 	  parallel port Series 6 IDE protocol.  (Most BACKPACK drives made
@@ -146,7 +146,7 @@ config PARIDE_BPCK6
 
 config PARIDE_COMM
 	tristate "DataStor Commuter protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the Commuter parallel port IDE
 	  protocol from DataStor. If you chose to build PARIDE support
@@ -157,7 +157,7 @@ config PARIDE_COMM
 
 config PARIDE_DSTR
 	tristate "DataStor EP-2000 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the EP-2000 parallel port IDE
 	  protocol from DataStor. If you chose to build PARIDE support
@@ -168,7 +168,7 @@ config PARIDE_DSTR
 
 config PARIDE_FIT2
 	tristate "FIT TD-2000 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the TD-2000 parallel port IDE
 	  protocol from Fidelity International Technology. This is a simple
@@ -181,7 +181,7 @@ config PARIDE_FIT2
 
 config PARIDE_FIT3
 	tristate "FIT TD-3000 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the TD-3000 parallel port IDE
 	  protocol from Fidelity International Technology. This protocol is
@@ -194,7 +194,7 @@ config PARIDE_FIT3
 
 config PARIDE_EPAT
 	tristate "Shuttle EPAT/EPEZ protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the EPAT parallel port IDE protocol.
 	  EPAT is a parallel port IDE adapter manufactured by Shuttle
@@ -216,7 +216,7 @@ config PARIDE_EPATC8
 
 config PARIDE_EPIA
 	tristate "Shuttle EPIA protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the (obsolete) EPIA parallel port
 	  IDE protocol from Shuttle Technology. This adapter can still be
@@ -228,7 +228,7 @@ config PARIDE_EPIA
 
 config PARIDE_FRIQ
 	tristate "Freecom IQ ASIC-2 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for version 2 of the Freecom IQ parallel
 	  port IDE adapter.  This adapter is used by the Maxell Superdisk
@@ -240,7 +240,7 @@ config PARIDE_FRIQ
 
 config PARIDE_FRPW
 	tristate "FreeCom power protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the Freecom power parallel port IDE
 	  protocol. If you chose to build PARIDE support into your kernel, you
@@ -251,7 +251,7 @@ config PARIDE_FRPW
 
 config PARIDE_KBIC
 	tristate "KingByte KBIC-951A/971A protocols"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the KBIC-951A and KBIC-971A parallel
 	  port IDE protocols from KingByte Information Corp. KingByte's
@@ -264,7 +264,7 @@ config PARIDE_KBIC
 
 config PARIDE_KTTI
 	tristate "KT PHd protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the "PHd" parallel port IDE protocol
 	  from KT Technology. This is a simple (low speed) adapter that is
@@ -277,7 +277,7 @@ config PARIDE_KTTI
 
 config PARIDE_ON20
 	tristate "OnSpec 90c20 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the (obsolete) 90c20 parallel port
 	  IDE protocol from OnSpec (often marketed under the ValuStore brand
@@ -289,7 +289,7 @@ config PARIDE_ON20
 
 config PARIDE_ON26
 	tristate "OnSpec 90c26 protocol"
-	depends on PARIDE
+	depends on PARIDE || PATA_PARPORT
 	help
 	  This option enables support for the 90c26 parallel port IDE protocol
 	  from OnSpec Electronics (often marketed under the ValuStore brand
diff --git a/drivers/block/paride/paride.h b/drivers/block/paride/paride.h
index ddb9e589da7f..24dcfadb782d 100644
--- a/drivers/block/paride/paride.h
+++ b/drivers/block/paride/paride.h
@@ -1,3 +1,15 @@
+/*
+ * The low-level protocol modules are used by either paride or pata_parport.
+ * These two are mutually exclusive because the compiled low-level protocol
+ * modules are not compatible.
+ * When PATA_PARPORT is enabled, include pata_parport.h instead of the rest
+ * of this file.
+ */
+
+#if IS_ENABLED(CONFIG_PATA_PARPORT)
+#include <linux/pata_parport.h>
+
+#else
 #ifndef __DRIVERS_PARIDE_H__
 #define __DRIVERS_PARIDE_H__
 
@@ -170,3 +182,4 @@ void pi_unregister_driver(void *);
 
 #endif /* __DRIVERS_PARIDE_H__ */
 /* end of paride.h */
+#endif /* IS_ENABLED(CONFIG_PATA_PARPORT) */
diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
new file mode 100644
index 000000000000..f1ba57bb319c
--- /dev/null
+++ b/include/linux/pata_parport.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *	pata_parport.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
+ *				    Under the terms of the GPL.
+ *
+ * This file defines the interface for parallel port IDE adapter chip drivers.
+ */
+
+#include <linux/libata.h>
+
+#define PI_PCD	1	/* dummy for paride protocol modules */
+
+struct pi_adapter {
+	struct device dev;
+	struct pi_protocol *proto;	/* adapter protocol */
+	int port;			/* base address of parallel port */
+	int mode;			/* transfer mode in use */
+	int delay;			/* adapter delay setting */
+	int devtype;			/* dummy for paride protocol modules */
+	char *device;			/* dummy for paride protocol modules */
+	int unit;			/* unit number for chained adapters */
+	int saved_r0;			/* saved port state */
+	int saved_r2;			/* saved port state */
+	unsigned long private;		/* for protocol module */
+	struct pardevice *pardev;	/* pointer to pardevice */
+	bool claimed;			/* parport has already been claimed */
+	struct timer_list timer;	/* disconnect timer */
+};
+
+typedef struct pi_adapter PIA;	/* for paride protocol modules */
+
+/* registers are addressed as (cont,regr)
+ *	cont: 0 for command register file, 1 for control register(s)
+ *	regr: 0-7 for register number.
+ */
+
+/* macros and functions exported to the protocol modules */
+#define delay_p			(pi->delay ? udelay(pi->delay) : (void)0)
+#define out_p(offs, byte)	do { outb(byte, pi->port + offs); delay_p; } while (0)
+#define in_p(offs)		(delay_p, inb(pi->port + offs))
+
+#define w0(byte)		out_p(0, byte)
+#define r0()			(in_p(0) & 0xff)
+#define w1(byte)		out_p(1, byte)
+#define r1()			(in_p(1) & 0xff)
+#define w2(byte)		out_p(2, byte)
+#define r2()			(in_p(2) & 0xff)
+#define w3(byte)		out_p(3, byte)
+#define w4(byte)		out_p(4, byte)
+#define r4()			(in_p(4) & 0xff)
+#define w4w(data)		do { outw(data, pi->port + 4); delay_p; } while (0)
+#define w4l(data)		do { outl(data, pi->port + 4); delay_p; } while (0)
+#define r4w()			(delay_p, inw(pi->port + 4) & 0xffff)
+#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
+
+static inline u16 pi_swab16(char *b, int k)
+{
+	union { u16 u; char t[2]; } r;
+
+	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
+	return r.u;
+}
+
+static inline u32 pi_swab32(char *b, int k)
+{
+	union { u32 u; char f[4]; } r;
+
+	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
+	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
+	return r.u;
+}
+
+struct pi_protocol {
+	char name[8];
+
+	int max_mode;
+	int epp_first;		/* modes >= this use 8 ports */
+
+	int default_delay;
+	int max_units;		/* max chained units probed for */
+
+	void (*write_regr)(struct pi_adapter *pi, int cont, int regr, int val);
+	int (*read_regr)(struct pi_adapter *pi, int cont, int regr);
+	void (*write_block)(struct pi_adapter *pi, char *buf, int count);
+	void (*read_block)(struct pi_adapter *pi, char *buf, int count);
+
+	void (*connect)(struct pi_adapter *pi);
+	void (*disconnect)(struct pi_adapter *pi);
+
+	int (*test_port)(struct pi_adapter *pi);
+	int (*probe_unit)(struct pi_adapter *pi);
+	int (*test_proto)(struct pi_adapter *pi, char *scratch, int verbose);
+	void (*log_adapter)(struct pi_adapter *pi, char *scratch, int verbose);
+
+	int (*init_proto)(struct pi_adapter *pi);
+	void (*release_proto)(struct pi_adapter *pi);
+	struct module *owner;
+	struct device_driver driver;
+	struct scsi_host_template sht;
+};
+
+#define PATA_PARPORT_SHT ATA_PIO_SHT
+
+int pata_parport_register_driver(struct pi_protocol *pr);
+void pata_parport_unregister_driver(struct pi_protocol *pr);
+/* defines for old paride protocol modules */
+#define paride_register pata_parport_register_driver
+#define paride_unregister pata_parport_unregister_driver
-- 
Ondrej Zary


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-12 14:44 [PATCH] pata_parport: add driver (PARIDE replacement) Ondrej Zary
@ 2022-03-13 19:15 ` Ondrej Zary
  2022-03-13 23:19   ` Jens Axboe
  2022-03-13 20:38 ` Sergey Shtylyov
  1 sibling, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-13 19:15 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
> The pata_parport is a libata-based replacement of the old PARIDE
> subsystem - driver for parallel port IDE devices.
> It uses the original paride low-level protocol drivers but does not
> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> behind parallel port adapters are handled by the ATA layer.
> 
> This will allow paride and its high-level drivers to be removed.
> 
> paride and pata_parport are mutually exclusive because the compiled
> protocol drivers are incompatible.
> 
> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> chip).
> 
> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> drivers - they don't handle non-multiple-of-4 block transfers
> correctly. This causes problems with LS-120 drive.
> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> or 8-bit mode is used first (probably some initialization missing?).
> Once the device is initialized, EPP works until power cycle.
> 
> So after device power on, you have to:
> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> (autoprobe will initialize correctly as it tries the slowest modes
> first but you'll get the broken EPP-32 mode)

Found a bug - the same device can be registered multiple times. Fix will be in
v2. But this revealed a bigger problem: pi_connect can sleep (uses
parport_claim_or_block) and libata does not like that.
Any ideas how to fix this?

Mar 12 18:47:13 test kernel: [  348.863534] pata_parport: protocol epat registered
Mar 12 18:47:16 test kernel: [  352.226932] (null): epat 1.02, Shuttle EPAT chip c6 at 0x378,
Mar 12 18:47:16 test kernel: [  352.226943] mode 2 (8-bit), delay 1
Mar 12 18:47:16 test kernel: [  352.255210] scsi host4: pata_parport
Mar 12 18:47:16 test kernel: [  352.255359] ata5: PATA max PIO0 port parport0 protocol epat
Mar 12 18:47:17 test kernel: [  352.489441] ata5.00: ATAPI: LS-120 COSM   04              UHD Floppy, 0270M09T, max PIO2
Mar 12 18:47:17 test kernel: [  352.572733] scsi 4:0:0:0: Direct-Access     MATSHITA LS-120 COSM   04 0270 PQ: 0 ANSI: 5
Mar 12 18:47:17 test kernel: [  352.608160] sd 4:0:0:0: Attached scsi generic sg1 type 0
Mar 12 18:47:17 test kernel: [  352.635053] sd 4:0:0:0: [sdb] Media removed, stopped polling
Mar 12 18:47:17 test kernel: [  352.658924] sd 4:0:0:0: [sdb] Attached SCSI removable disk
Mar 12 18:47:31 test kernel: [  366.811817] (null): epat 1.02, Shuttle EPAT chip c6 at 0x378,
Mar 12 18:47:31 test kernel: [  366.811828] mode 2 (8-bit), delay 1
Mar 12 18:47:31 test kernel: [  366.846397] scsi host5: pata_parport
Mar 12 18:47:31 test kernel: [  366.846557] ata6: PATA max PIO0 port parport0 protocol epat
Mar 12 18:47:31 test kernel: [  367.076726] ata6.00: ATAPI: LS-120 COSM   04              UHD Floppy, 0270M09T, max PIO2
Mar 12 18:47:31 test kernel: [  367.157050] scsi 5:0:0:0: Direct-Access     MATSHITA LS-120 COSM   04 0270 PQ: 0 ANSI: 5
Mar 12 18:47:31 test kernel: [  367.192614] sd 5:0:0:0: Attached scsi generic sg2 type 0
Mar 12 18:47:31 test kernel: [  367.219398] sd 5:0:0:0: [sdc] Media removed, stopped polling
Mar 12 18:47:31 test kernel: [  367.243247] sd 5:0:0:0: [sdc] Attached SCSI removable disk
Mar 12 18:47:32 test kernel: [  367.542155] Modules linked in: epat pata_parport nouveau snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm psmouse snd_timer snd soundcore wmi
hwmon serio_raw drm_ttm_helper 8139cp sg intel_agp i2c_dev parport_pc parport fuse
Mar 12 18:47:32 test kernel: [  367.542219] CPU: 0 PID: 17 Comm: kworker/0:1 Not tainted 5.17.0-rc2+ #441
Mar 12 18:47:32 test kernel: [  367.542227] Hardware name:  /848P-ICH5, BIOS 6.00 PG 02/03/2005
Mar 12 18:47:32 test kernel: [  367.542232] Workqueue: events_freezable_power_ disk_events_workfn
Mar 12 18:47:32 test kernel: [  367.542242] Call Trace:
Mar 12 18:47:32 test kernel: [  367.542250]  dump_stack_lvl+0x32/0x41
Mar 12 18:47:32 test kernel: [  367.542259]  ? preempt_count_add+0x4c/0x51
Mar 12 18:47:32 test kernel: [  367.542266]  dump_stack+0xd/0x10
Mar 12 18:47:32 test kernel: [  367.542271]  __schedule_bug+0x90/0xa1
Mar 12 18:47:32 test kernel: [  367.542281]  __schedule+0x3b/0x456
Mar 12 18:47:32 test kernel: [  367.542289]  schedule+0x68/0xa3
Mar 12 18:47:32 test kernel: [  367.542296]  parport_claim_or_block+0x68/0xa5 [parport]
Mar 12 18:47:32 test kernel: [  367.542307]  ? init_wait_entry+0x21/0x21
Mar 12 18:47:32 test kernel: [  367.542315]  pi_connect+0x2b/0x3e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542323]  pata_parport_check_status+0x16/0x4e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542329]  ? pata_parport_lost_interrupt+0x7c/0x7c [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542336]  ata_sff_busy_wait+0x27/0x3b
Mar 12 18:47:32 test kernel: [  367.542345]  ata_wait_idle+0x12/0x14
Mar 12 18:47:32 test kernel: [  367.542350]  ata_sff_qc_issue+0x27/0x106
Mar 12 18:47:32 test kernel: [  367.542357]  ata_qc_issue+0x14b/0x16b
Mar 12 18:47:32 test kernel: [  367.542364]  __ata_scsi_queuecmd+0x1c7/0x1d4
Mar 12 18:47:32 test kernel: [  367.542369]  ? ata_scsiop_inq_83+0xb3/0xb3
Mar 12 18:47:32 test kernel: [  367.542375]  ata_scsi_queuecmd+0x36/0x5f
Mar 12 18:47:32 test kernel: [  367.542380]  scsi_queue_rq+0x577/0x69e
Mar 12 18:47:32 test kernel: [  367.542389]  blk_mq_dispatch_rq_list+0x229/0x3e4
Mar 12 18:47:32 test kernel: [  367.542395]  ? __sbitmap_get_word+0x28/0x5a
Mar 12 18:47:32 test kernel: [  367.542405]  __blk_mq_sched_dispatch_requests+0x7b/0xdb
Mar 12 18:47:32 test kernel: [  367.542411]  blk_mq_sched_dispatch_requests+0x25/0x46
Mar 12 18:47:32 test kernel: [  367.542417]  __blk_mq_run_hw_queue+0x36/0x6b
Mar 12 18:47:32 test kernel: [  367.542424]  __blk_mq_delay_run_hw_queue+0x54/0x11d
Mar 12 18:47:32 test kernel: [  367.542431]  blk_mq_run_hw_queue+0xa5/0xab
Mar 12 18:47:32 test kernel: [  367.542437]  blk_mq_sched_insert_request+0xa2/0xaa
Mar 12 18:47:32 test kernel: [  367.542443]  ? update_io_ticks+0x2a/0x5b
Mar 12 18:47:32 test kernel: [  367.542451]  blk_execute_rq_nowait+0x3f/0x47
Mar 12 18:47:32 test kernel: [  367.542457]  blk_execute_rq+0x3a/0xa4
Mar 12 18:47:32 test kernel: [  367.542463]  __scsi_execute+0x90/0x133
Mar 12 18:47:32 test kernel: [  367.542470]  scsi_test_unit_ready+0x43/0x95
Mar 12 18:47:32 test kernel: [  367.542477]  sd_check_events+0x8b/0xf6
Mar 12 18:47:32 test kernel: [  367.542483]  ? media_not_present+0x3b/0x3b
Mar 12 18:47:32 test kernel: [  367.542489]  disk_check_events+0x26/0xa3
Mar 12 18:47:32 test kernel: [  367.542495]  disk_events_workfn+0x10/0x12
Mar 12 18:47:32 test kernel: [  367.542499]  process_one_work+0x107/0x198
Mar 12 18:47:32 test kernel: [  367.542508]  worker_thread+0x151/0x1e3
Mar 12 18:47:32 test kernel: [  367.542514]  kthread+0xd2/0xd7
Mar 12 18:47:32 test kernel: [  367.542519]  ? rescuer_thread+0x1ec/0x1ec
Mar 12 18:47:32 test kernel: [  367.542525]  ? kthread_complete_and_exit+0x16/0x16
Mar 12 18:47:32 test kernel: [  367.542531]  ret_from_fork+0x19/0x28
Mar 12 18:47:32 test kernel: [  367.542540] ------------[ cut here ]------------
Mar 12 18:47:32 test kernel: [  367.542543] Voluntary context switch within RCU read-side critical section!
Mar 12 18:47:32 test kernel: [  367.542552] WARNING: CPU: 0 PID: 17 at kernel/rcu/tree_plugin.h:318 rcu_note_context_switch+0x4e/0x27f
Mar 12 18:47:32 test kernel: [  367.542568] Modules linked in: epat pata_parport nouveau snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm psmouse snd_timer snd soundcore wmi hwmon serio_raw drm_ttm_helper 8139cp sg intel_agp i2c_dev parport_pc parport fuse
Mar 12 18:47:32 test kernel: [  367.542605] CPU: 0 PID: 17 Comm: kworker/0:1 Tainted: G        W         5.17.0-rc2+ #441
Mar 12 18:47:32 test kernel: [  367.542612] Hardware name:  /848P-ICH5, BIOS 6.00 PG 02/03/2005
Mar 12 18:47:32 test kernel: [  367.542616] Workqueue: events_freezable_power_ disk_events_workfn
Mar 12 18:47:32 test kernel: [  367.542624] EIP: rcu_note_context_switch+0x4e/0x27f
Mar 12 18:47:32 test kernel: [  367.542631] Code: 80 7d e3 00 75 27 8b 86 fc 01 00 00 85 c0 7e 1d 80 3d 95 06 87 d8 00 75 14 68 43 ad 6e d8 c6 05 95 06 87 d8 01 e8 ce e9 50 00 <0f> 0b 58 64 a1 cc 86 90 d8 8b 80 fc 01 00 00 85 c0 0f 8e dc 01 00
Mar 12 18:47:32 test kernel: [  367.542637] EAX: 0000003f EBX: c10bb780 ECX: ef9939cc EDX: 00000002
Mar 12 18:47:32 test kernel: [  367.542643] ESI: c10bb780 EDI: ef997840 EBP: c1093c5c ESP: c1093c38
Mar 12 18:47:32 test kernel: [  367.542648] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 EFLAGS: 00010096
Mar 12 18:47:32 test kernel: [  367.542654] CR0: 80050033 CR2: b5126000 CR3: 0d97c000 CR4: 00000690
Mar 12 18:47:32 test kernel: [  367.542660] Call Trace:
Mar 12 18:47:32 test kernel: [  367.542665]  ? preempt_count_add+0x4c/0x51
Mar 12 18:47:32 test kernel: [  367.542672]  ? dump_stack+0xd/0x10
Mar 12 18:47:32 test kernel: [  367.542677]  ? preempt_count_add+0x4c/0x51
Mar 12 18:47:32 test kernel: [  367.542683]  __schedule+0x63/0x456
Mar 12 18:47:32 test kernel: [  367.542691]  schedule+0x68/0xa3
Mar 12 18:47:32 test kernel: [  367.542697]  parport_claim_or_block+0x68/0xa5 [parport]
Mar 12 18:47:32 test kernel: [  367.542706]  ? init_wait_entry+0x21/0x21
Mar 12 18:47:32 test kernel: [  367.542713]  pi_connect+0x2b/0x3e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542720]  pata_parport_check_status+0x16/0x4e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542726]  ? pata_parport_lost_interrupt+0x7c/0x7c [pata_parport]
Mar 12 18:47:32 test kernel: [  367.542733]  ata_sff_busy_wait+0x27/0x3b
Mar 12 18:47:32 test kernel: [  367.542740]  ata_wait_idle+0x12/0x14
Mar 12 18:47:32 test kernel: [  367.542745]  ata_sff_qc_issue+0x27/0x106
Mar 12 18:47:32 test kernel: [  367.542752]  ata_qc_issue+0x14b/0x16b
Mar 12 18:47:32 test kernel: [  367.542758]  __ata_scsi_queuecmd+0x1c7/0x1d4
Mar 12 18:47:32 test kernel: [  367.542763]  ? ata_scsiop_inq_83+0xb3/0xb3
Mar 12 18:47:32 test kernel: [  367.542769]  ata_scsi_queuecmd+0x36/0x5f
Mar 12 18:47:32 test kernel: [  367.542774]  scsi_queue_rq+0x577/0x69e
Mar 12 18:47:32 test kernel: [  367.542782]  blk_mq_dispatch_rq_list+0x229/0x3e4
Mar 12 18:47:32 test kernel: [  367.542789]  ? __sbitmap_get_word+0x28/0x5a
Mar 12 18:47:32 test kernel: [  367.542797]  __blk_mq_sched_dispatch_requests+0x7b/0xdb
Mar 12 18:47:32 test kernel: [  367.542804]  blk_mq_sched_dispatch_requests+0x25/0x46
Mar 12 18:47:32 test kernel: [  367.542809]  __blk_mq_run_hw_queue+0x36/0x6b
Mar 12 18:47:32 test kernel: [  367.542816]  __blk_mq_delay_run_hw_queue+0x54/0x11d
Mar 12 18:47:32 test kernel: [  367.542823]  blk_mq_run_hw_queue+0xa5/0xab
Mar 12 18:47:32 test kernel: [  367.542829]  blk_mq_sched_insert_request+0xa2/0xaa
Mar 12 18:47:32 test kernel: [  367.542835]  ? update_io_ticks+0x2a/0x5b
Mar 12 18:47:32 test kernel: [  367.542842]  blk_execute_rq_nowait+0x3f/0x47
Mar 12 18:47:32 test kernel: [  367.542848]  blk_execute_rq+0x3a/0xa4
Mar 12 18:47:32 test kernel: [  367.542854]  __scsi_execute+0x90/0x133
Mar 12 18:47:32 test kernel: [  367.542861]  scsi_test_unit_ready+0x43/0x95
Mar 12 18:47:32 test kernel: [  367.542868]  sd_check_events+0x8b/0xf6
Mar 12 18:47:32 test kernel: [  367.542874]  ? media_not_present+0x3b/0x3b
Mar 12 18:47:32 test kernel: [  367.542879]  disk_check_events+0x26/0xa3
Mar 12 18:47:32 test kernel: [  367.542885]  disk_events_workfn+0x10/0x12
Mar 12 18:47:32 test kernel: [  367.542890]  process_one_work+0x107/0x198
Mar 12 18:47:32 test kernel: [  367.542898]  worker_thread+0x151/0x1e3
Mar 12 18:47:32 test kernel: [  367.542904]  kthread+0xd2/0xd7
Mar 12 18:47:32 test kernel: [  367.542909]  ? rescuer_thread+0x1ec/0x1ec
Mar 12 18:47:32 test kernel: [  367.542915]  ? kthread_complete_and_exit+0x16/0x16
Mar 12 18:47:32 test kernel: [  367.542921]  ret_from_fork+0x19/0x28
Mar 12 18:47:32 test kernel: [  367.542928] ---[ end trace 0000000000000000 ]---
Mar 12 18:47:32 test kernel: [  367.650552] ------------[ cut here ]------------
Mar 12 18:47:32 test kernel: [  367.650568] DEBUG_LOCKS_WARN_ON(val > preempt_count())
Mar 12 18:47:32 test kernel: [  367.650576] WARNING: CPU: 0 PID: 17 at kernel/sched/core.c:5474 preempt_count_sub+0x3e/0x7f
Mar 12 18:47:32 test kernel: [  367.650595] Modules linked in: epat pata_parport nouveau snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm psmouse snd_timer snd soundcore wmi hwmon serio_raw drm_ttm_helper 8139cp sg intel_agp i2c_dev parport_pc parport fuse
Mar 12 18:47:32 test kernel: [  367.650641] CPU: 0 PID: 17 Comm: kworker/0:1 Tainted: G        W         5.17.0-rc2+ #441
Mar 12 18:47:32 test kernel: [  367.650649] Hardware name:  /848P-ICH5, BIOS 6.00 PG 02/03/2005
Mar 12 18:47:32 test kernel: [  367.650655] Workqueue: events_freezable_power_ disk_events_workfn
Mar 12 18:47:32 test kernel: [  367.650665] EIP: preempt_count_sub+0x3e/0x7f
Mar 12 18:47:32 test kernel: [  367.650672] Code: e2 ff ff ff 7f 39 c2 7d 25 e8 9d f4 19 00 85 c0 74 57 83 3d b4 2c 87 d8 00 75 4e 68 0a 6c 6e d8 68 db a7 6d d8 e8 3d b4 53 00 <0f> 0b eb 2e 3d fe 00 00 00 77 2b 84 d2 75 27 e8 6d f4 19 00 85 c0
Mar 12 18:47:32 test kernel: [  367.650679] EAX: 0000002a EBX: 00000000 ECX: ef9939cc EDX: 00000001
Mar 12 18:47:32 test kernel: [  367.650685] ESI: d4974000 EDI: 00000293 EBP: c1093d30 ESP: c1093d28
Mar 12 18:47:32 test kernel: [  367.650690] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 EFLAGS: 00010292
Mar 12 18:47:32 test kernel: [  367.650697] CR0: 80050033 CR2: b0eae6ec CR3: 035b5000 CR4: 00000690
Mar 12 18:47:32 test kernel: [  367.650704] Call Trace:
Mar 12 18:47:32 test kernel: [  367.650711]  _raw_spin_unlock_irqrestore+0x17/0x28
Mar 12 18:47:32 test kernel: [  367.650720]  ata_scsi_queuecmd+0x57/0x5f
Mar 12 18:47:32 test kernel: [  367.650728]  scsi_queue_rq+0x577/0x69e
Mar 12 18:47:32 test kernel: [  367.650737]  blk_mq_dispatch_rq_list+0x229/0x3e4
Mar 12 18:47:32 test kernel: [  367.650743]  ? __sbitmap_get_word+0x28/0x5a
Mar 12 18:47:32 test kernel: [  367.650753]  __blk_mq_sched_dispatch_requests+0x7b/0xdb
Mar 12 18:47:32 test kernel: [  367.650760]  blk_mq_sched_dispatch_requests+0x25/0x46
Mar 12 18:47:32 test kernel: [  367.650766]  __blk_mq_run_hw_queue+0x36/0x6b
Mar 12 18:47:32 test kernel: [  367.650774]  __blk_mq_delay_run_hw_queue+0x54/0x11d
Mar 12 18:47:32 test kernel: [  367.650781]  blk_mq_run_hw_queue+0xa5/0xab
Mar 12 18:47:32 test kernel: [  367.650788]  blk_mq_sched_insert_request+0xa2/0xaa
Mar 12 18:47:32 test kernel: [  367.650793]  ? update_io_ticks+0x2a/0x5b
Mar 12 18:47:32 test kernel: [  367.650801]  blk_execute_rq_nowait+0x3f/0x47
Mar 12 18:47:32 test kernel: [  367.650808]  blk_execute_rq+0x3a/0xa4
Mar 12 18:47:32 test kernel: [  367.650815]  __scsi_execute+0x90/0x133
Mar 12 18:47:32 test kernel: [  367.650822]  scsi_test_unit_ready+0x43/0x95
Mar 12 18:47:32 test kernel: [  367.650830]  sd_check_events+0x8b/0xf6
Mar 12 18:47:32 test kernel: [  367.650836]  ? media_not_present+0x3b/0x3b
Mar 12 18:47:32 test kernel: [  367.650842]  disk_check_events+0x26/0xa3
Mar 12 18:47:32 test kernel: [  367.650848]  disk_events_workfn+0x10/0x12
Mar 12 18:47:32 test kernel: [  367.650853]  process_one_work+0x107/0x198
Mar 12 18:47:32 test kernel: [  367.650862]  worker_thread+0x151/0x1e3
Mar 12 18:47:32 test kernel: [  367.650868]  kthread+0xd2/0xd7
Mar 12 18:47:32 test kernel: [  367.650874]  ? rescuer_thread+0x1ec/0x1ec
Mar 12 18:47:32 test kernel: [  367.650880]  ? kthread_complete_and_exit+0x16/0x16
Mar 12 18:47:32 test kernel: [  367.650886]  ret_from_fork+0x19/0x28
Mar 12 18:47:32 test kernel: [  367.650895] ---[ end trace 0000000000000000 ]---
Mar 12 18:47:32 test kernel: [  367.658169] Modules linked in: epat pata_parport nouveau snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm psmouse snd_timer snd soundcore wmi hwmon serio_raw drm_ttm_helper 8139cp sg intel_agp i2c_dev parport_pc parport fuse
Mar 12 18:47:32 test kernel: [  367.658232] CPU: 0 PID: 561 Comm: scsi_eh_5 Tainted: G        W         5.17.0-rc2+ #441
Mar 12 18:47:32 test kernel: [  367.658240] Hardware name:  /848P-ICH5, BIOS 6.00 PG 02/03/2005
Mar 12 18:47:32 test kernel: [  367.658244] Call Trace:
Mar 12 18:47:32 test kernel: [  367.658251]  dump_stack_lvl+0x32/0x41
Mar 12 18:47:32 test kernel: [  367.658260]  ? preempt_count_add+0x4c/0x51
Mar 12 18:47:32 test kernel: [  367.658266]  dump_stack+0xd/0x10
Mar 12 18:47:32 test kernel: [  367.658272]  __schedule_bug+0x90/0xa1
Mar 12 18:47:32 test kernel: [  367.658278]  __schedule+0x3b/0x456
Mar 12 18:47:32 test kernel: [  367.658286]  ? _raw_spin_lock_irqsave+0x1b/0x21
Mar 12 18:47:32 test kernel: [  367.658293]  schedule+0x68/0xa3
Mar 12 18:47:32 test kernel: [  367.658300]  parport_claim_or_block+0x68/0xa5 [parport]
Mar 12 18:47:32 test kernel: [  367.658313]  ? init_wait_entry+0x21/0x21
Mar 12 18:47:32 test kernel: [  367.658321]  pi_connect+0x2b/0x3e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.658329]  pata_parport_check_status+0x16/0x4e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.658338]  pata_parport_drain_fifo+0x2b/0x7c [pata_parport]
Mar 12 18:47:32 test kernel: [  367.658345]  ? ata_sff_prereset+0x7e/0x7e
Mar 12 18:47:32 test kernel: [  367.658354]  ata_sff_error_handler+0x68/0xb6
Mar 12 18:47:32 test kernel: [  367.658361]  ? pata_parport_devchk+0x11e/0x11e [pata_parport]
Mar 12 18:47:32 test kernel: [  367.658369]  ata_scsi_port_error_handler+0x1cf/0x46a
Mar 12 18:47:32 test kernel: [  367.658376]  ata_scsi_error+0x74/0x85
Mar 12 18:47:32 test kernel: [  367.658383]  scsi_error_handler+0x11a/0x461
Mar 12 18:47:32 test kernel: [  367.658391]  ? _raw_spin_lock_irqsave+0x1b/0x21
Mar 12 18:47:32 test kernel: [  367.658397]  ? _raw_spin_unlock_irqrestore+0x17/0x28
Mar 12 18:47:32 test kernel: [  367.658402]  ? __kthread_parkme+0x2c/0x70
Mar 12 18:47:32 test kernel: [  367.658409]  kthread+0xd2/0xd7
Mar 12 18:47:32 test kernel: [  367.658415]  ? scsi_eh_get_sense+0x176/0x176
Mar 12 18:47:32 test kernel: [  367.658421]  ? kthread_complete_and_exit+0x16/0x16
Mar 12 18:47:32 test kernel: [  367.658427]  ret_from_fork+0x19/0x28
Mar 12 18:47:32 test kernel: [  367.815293] sd 5:0:0:0: [sdc] Media removed, stopped polling


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-12 14:44 [PATCH] pata_parport: add driver (PARIDE replacement) Ondrej Zary
  2022-03-13 19:15 ` Ondrej Zary
@ 2022-03-13 20:38 ` Sergey Shtylyov
  2022-03-13 21:19   ` Ondrej Zary
  1 sibling, 1 reply; 31+ messages in thread
From: Sergey Shtylyov @ 2022-03-13 20:38 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

Hello!

On 3/12/22 5:44 PM, Ondrej Zary wrote:

> The pata_parport is a libata-based replacement of the old PARIDE
> subsystem - driver for parallel port IDE devices.
> It uses the original paride low-level protocol drivers but does not
> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> behind parallel port adapters are handled by the ATA layer.
> 
> This will allow paride and its high-level drivers to be removed.
> 
> paride and pata_parport are mutually exclusive because the compiled
> protocol drivers are incompatible.
> 
> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> chip).
> 
> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> drivers - they don't handle non-multiple-of-4 block transfers
> correctly. This causes problems with LS-120 drive.
> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> or 8-bit mode is used first (probably some initialization missing?).
> Once the device is initialized, EPP works until power cycle.
> 
> So after device power on, you have to:
> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> (autoprobe will initialize correctly as it tries the slowest modes
> first but you'll get the broken EPP-32 mode)
> 
> Signed-off-by: Ondrej Zary <linux@zary.sk>
[...]
> diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
> index e1ce90af602a..e431a1ef41eb 100644
> --- a/Documentation/admin-guide/blockdev/paride.rst
> +++ b/Documentation/admin-guide/blockdev/paride.rst
[...]
> diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
> new file mode 100644
> index 000000000000..783764626a27
> --- /dev/null
> +++ b/drivers/ata/pata_parport.c
> @@ -0,0 +1,819 @@
[...]
> +static void pata_parport_lost_interrupt(struct ata_port *ap)
> +{
> +	u8 status;
> +	struct ata_queued_cmd *qc;
> +
> +	/* Only one outstanding command per SFF channel */
> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> +	/* We cannot lose an interrupt on a non-existent or polled command */
> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> +		return;
> +	/*
> +	 * See if the controller thinks it is still busy - if so the command
> +	 * isn't a lost IRQ but is still in progress
> +	 */
> +	status = pata_parport_check_altstatus(ap);
> +	if (status & ATA_BUSY)
> +		return;
> +
> +	/*
> +	 * There was a command running, we are no longer busy and we have
> +	 * no interrupt.
> +	 */
> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> +	/* Run the host interrupt logic as if the interrupt had not been lost */
> +	ata_sff_port_intr(ap, qc);
> +}

   As I said, ata_sff_lost_interrupt() could be used instead...

[...]
> +static void pi_remove_one(struct device *dev)
> +{
> +	struct ata_host *host = dev_get_drvdata(dev);
> +	struct pi_adapter *pi = host->private_data;
> +
> +	ata_host_detach(host);
> +	del_timer_sync(&pi->timer);
> +	if (pi->claimed) {
> +		pi->proto->disconnect(pi);
> +		parport_release(pi->pardev);
> +	}

   This duplicates most of pci_disconnect_timer(), worth factoring out?

> +	pi_release(pi);
> +	device_unregister(dev);
> +	ida_free(&pata_parport_bus_dev_ids, dev->id);
> +	/* pata_parport_dev_release will do kfree(pi) */
> +}
[...]
> diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
> new file mode 100644
> index 000000000000..f1ba57bb319c
> --- /dev/null
> +++ b/include/linux/pata_parport.h
> @@ -0,0 +1,108 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + *	pata_parport.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
> + *				    Under the terms of the GPL.
> + *
> + * This file defines the interface for parallel port IDE adapter chip drivers.
> + */
> +
> +#include <linux/libata.h>
> +
> +#define PI_PCD	1	/* dummy for paride protocol modules */
> +
> +struct pi_adapter {
> +	struct device dev;
> +	struct pi_protocol *proto;	/* adapter protocol */
> +	int port;			/* base address of parallel port */
> +	int mode;			/* transfer mode in use */
> +	int delay;			/* adapter delay setting */
> +	int devtype;			/* dummy for paride protocol modules */
> +	char *device;			/* dummy for paride protocol modules */
> +	int unit;			/* unit number for chained adapters */
> +	int saved_r0;			/* saved port state */
> +	int saved_r2;			/* saved port state */
> +	unsigned long private;		/* for protocol module */
> +	struct pardevice *pardev;	/* pointer to pardevice */
> +	bool claimed;			/* parport has already been claimed */
> +	struct timer_list timer;	/* disconnect timer */
> +};
> +
> +typedef struct pi_adapter PIA;	/* for paride protocol modules */
> +
> +/* registers are addressed as (cont,regr)
> + *	cont: 0 for command register file, 1 for control register(s)
> + *	regr: 0-7 for register number.
> + */
> +
> +/* macros and functions exported to the protocol modules */
> +#define delay_p			(pi->delay ? udelay(pi->delay) : (void)0)
> +#define out_p(offs, byte)	do { outb(byte, pi->port + offs); delay_p; } while (0)
> +#define in_p(offs)		(delay_p, inb(pi->port + offs))
> +
> +#define w0(byte)		out_p(0, byte)
> +#define r0()			(in_p(0) & 0xff)
> +#define w1(byte)		out_p(1, byte)
> +#define r1()			(in_p(1) & 0xff)
> +#define w2(byte)		out_p(2, byte)
> +#define r2()			(in_p(2) & 0xff)
> +#define w3(byte)		out_p(3, byte)
> +#define w4(byte)		out_p(4, byte)
> +#define r4()			(in_p(4) & 0xff)
> +#define w4w(data)		do { outw(data, pi->port + 4); delay_p; } while (0)
> +#define w4l(data)		do { outl(data, pi->port + 4); delay_p; } while (0)
> +#define r4w()			(delay_p, inw(pi->port + 4) & 0xffff)
> +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
> +

   I still don't think all this masking achieves anything...

> +static inline u16 pi_swab16(char *b, int k)
> +{
> +	union { u16 u; char t[2]; } r;
> +
> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> +	return r.u;
> +}
> +
> +static inline u32 pi_swab32(char *b, int k)
> +{
> +	union { u32 u; char f[4]; } r;
> +
> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> +	return r.u;

   Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
swapping...

[...]

MBR, Sergey

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-13 20:38 ` Sergey Shtylyov
@ 2022-03-13 21:19   ` Ondrej Zary
  2022-03-16  8:50     ` Sergey Shtylyov
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-13 21:19 UTC (permalink / raw)
  To: Sergey Shtylyov
  Cc: Damien Le Moal, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Sunday 13 March 2022 21:38:10 Sergey Shtylyov wrote:
> Hello!
> 
> On 3/12/22 5:44 PM, Ondrej Zary wrote:
> 
> > The pata_parport is a libata-based replacement of the old PARIDE
> > subsystem - driver for parallel port IDE devices.
> > It uses the original paride low-level protocol drivers but does not
> > need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> > behind parallel port adapters are handled by the ATA layer.
> > 
> > This will allow paride and its high-level drivers to be removed.
> > 
> > paride and pata_parport are mutually exclusive because the compiled
> > protocol drivers are incompatible.
> > 
> > Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> > chip).
> > 
> > Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> > drivers - they don't handle non-multiple-of-4 block transfers
> > correctly. This causes problems with LS-120 drive.
> > There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> > or 8-bit mode is used first (probably some initialization missing?).
> > Once the device is initialized, EPP works until power cycle.
> > 
> > So after device power on, you have to:
> > echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> > echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> > echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> > (autoprobe will initialize correctly as it tries the slowest modes
> > first but you'll get the broken EPP-32 mode)
> > 
> > Signed-off-by: Ondrej Zary <linux@zary.sk>
> [...]
> > diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
> > index e1ce90af602a..e431a1ef41eb 100644
> > --- a/Documentation/admin-guide/blockdev/paride.rst
> > +++ b/Documentation/admin-guide/blockdev/paride.rst
> [...]
> > diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
> > new file mode 100644
> > index 000000000000..783764626a27
> > --- /dev/null
> > +++ b/drivers/ata/pata_parport.c
> > @@ -0,0 +1,819 @@
> [...]
> > +static void pata_parport_lost_interrupt(struct ata_port *ap)
> > +{
> > +	u8 status;
> > +	struct ata_queued_cmd *qc;
> > +
> > +	/* Only one outstanding command per SFF channel */
> > +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> > +	/* We cannot lose an interrupt on a non-existent or polled command */
> > +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> > +		return;
> > +	/*
> > +	 * See if the controller thinks it is still busy - if so the command
> > +	 * isn't a lost IRQ but is still in progress
> > +	 */
> > +	status = pata_parport_check_altstatus(ap);
> > +	if (status & ATA_BUSY)
> > +		return;
> > +
> > +	/*
> > +	 * There was a command running, we are no longer busy and we have
> > +	 * no interrupt.
> > +	 */
> > +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> > +	/* Run the host interrupt logic as if the interrupt had not been lost */
> > +	ata_sff_port_intr(ap, qc);
> > +}
> 
>    As I said, ata_sff_lost_interrupt() could be used instead...

It couldn't be used because it calls ata_sff_altstatus().

> [...]
> > +static void pi_remove_one(struct device *dev)
> > +{
> > +	struct ata_host *host = dev_get_drvdata(dev);
> > +	struct pi_adapter *pi = host->private_data;
> > +
> > +	ata_host_detach(host);
> > +	del_timer_sync(&pi->timer);
> > +	if (pi->claimed) {
> > +		pi->proto->disconnect(pi);
> > +		parport_release(pi->pardev);
> > +	}
> 
>    This duplicates most of pci_disconnect_timer(), worth factoring out?
> 
> > +	pi_release(pi);
> > +	device_unregister(dev);
> > +	ida_free(&pata_parport_bus_dev_ids, dev->id);
> > +	/* pata_parport_dev_release will do kfree(pi) */
> > +}
> [...]
> > diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
> > new file mode 100644
> > index 000000000000..f1ba57bb319c
> > --- /dev/null
> > +++ b/include/linux/pata_parport.h
> > @@ -0,0 +1,108 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + *	pata_parport.h	(c) 1997-8  Grant R. Guenther <grant@torque.net>
> > + *				    Under the terms of the GPL.
> > + *
> > + * This file defines the interface for parallel port IDE adapter chip drivers.
> > + */
> > +
> > +#include <linux/libata.h>
> > +
> > +#define PI_PCD	1	/* dummy for paride protocol modules */
> > +
> > +struct pi_adapter {
> > +	struct device dev;
> > +	struct pi_protocol *proto;	/* adapter protocol */
> > +	int port;			/* base address of parallel port */
> > +	int mode;			/* transfer mode in use */
> > +	int delay;			/* adapter delay setting */
> > +	int devtype;			/* dummy for paride protocol modules */
> > +	char *device;			/* dummy for paride protocol modules */
> > +	int unit;			/* unit number for chained adapters */
> > +	int saved_r0;			/* saved port state */
> > +	int saved_r2;			/* saved port state */
> > +	unsigned long private;		/* for protocol module */
> > +	struct pardevice *pardev;	/* pointer to pardevice */
> > +	bool claimed;			/* parport has already been claimed */
> > +	struct timer_list timer;	/* disconnect timer */
> > +};
> > +
> > +typedef struct pi_adapter PIA;	/* for paride protocol modules */
> > +
> > +/* registers are addressed as (cont,regr)
> > + *	cont: 0 for command register file, 1 for control register(s)
> > + *	regr: 0-7 for register number.
> > + */
> > +
> > +/* macros and functions exported to the protocol modules */
> > +#define delay_p			(pi->delay ? udelay(pi->delay) : (void)0)
> > +#define out_p(offs, byte)	do { outb(byte, pi->port + offs); delay_p; } while (0)
> > +#define in_p(offs)		(delay_p, inb(pi->port + offs))
> > +
> > +#define w0(byte)		out_p(0, byte)
> > +#define r0()			(in_p(0) & 0xff)
> > +#define w1(byte)		out_p(1, byte)
> > +#define r1()			(in_p(1) & 0xff)
> > +#define w2(byte)		out_p(2, byte)
> > +#define r2()			(in_p(2) & 0xff)
> > +#define w3(byte)		out_p(3, byte)
> > +#define w4(byte)		out_p(4, byte)
> > +#define r4()			(in_p(4) & 0xff)
> > +#define w4w(data)		do { outw(data, pi->port + 4); delay_p; } while (0)
> > +#define w4l(data)		do { outl(data, pi->port + 4); delay_p; } while (0)
> > +#define r4w()			(delay_p, inw(pi->port + 4) & 0xffff)
> > +#define r4l()			(delay_p, inl(pi->port + 4) & 0xffffffff)
> > +
> 
>    I still don't think all this masking achieves anything...

It comes from old paride.h. I'll drop the masking. I will delete this completely after paride removal.

> > +static inline u16 pi_swab16(char *b, int k)
> > +{
> > +	union { u16 u; char t[2]; } r;
> > +
> > +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> > +	return r.u;
> > +}
> > +
> > +static inline u32 pi_swab32(char *b, int k)
> > +{
> > +	union { u32 u; char f[4]; } r;
> > +
> > +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> > +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> > +	return r.u;
> 
>    Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
> swapping...

This crap comes from old paride.h and we can't get rid of it without touching the protocol drivers (comm.c and kbic.c). Maybe use something like:

#define pi_swab16(char *b, int k) 	swab16p((u16 *)&b[2 * k])

but I'm not sure it's equivalent on a big-endian machine.
 
> [...]
> 
> MBR, Sergey
> 


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-13 19:15 ` Ondrej Zary
@ 2022-03-13 23:19   ` Jens Axboe
  2022-03-14 20:25     ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Jens Axboe @ 2022-03-13 23:19 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: Christoph Hellwig, Tim Waugh, linux-block, linux-parport,
	linux-ide, linux-kernel

On 3/13/22 1:15 PM, Ondrej Zary wrote:
> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
>> The pata_parport is a libata-based replacement of the old PARIDE
>> subsystem - driver for parallel port IDE devices.
>> It uses the original paride low-level protocol drivers but does not
>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>> behind parallel port adapters are handled by the ATA layer.
>>
>> This will allow paride and its high-level drivers to be removed.
>>
>> paride and pata_parport are mutually exclusive because the compiled
>> protocol drivers are incompatible.
>>
>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>> chip).
>>
>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>> drivers - they don't handle non-multiple-of-4 block transfers
>> correctly. This causes problems with LS-120 drive.
>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>> or 8-bit mode is used first (probably some initialization missing?).
>> Once the device is initialized, EPP works until power cycle.
>>
>> So after device power on, you have to:
>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>> (autoprobe will initialize correctly as it tries the slowest modes
>> first but you'll get the broken EPP-32 mode)
> 
> Found a bug - the same device can be registered multiple times. Fix
> will be in v2. But this revealed a bigger problem: pi_connect can
> sleep (uses parport_claim_or_block) and libata does not like that. Any
> ideas how to fix this?

I think you'd need two things here:

- The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
  will allow blocking off the queue_rq path.

- You need to look at making libata safe wrt calling ata_qc_issue()
  outside the lock. Should probably be fine if you just gate that on
  whether or not the queue was setup in blocking mode, as that doesn't
  currently exist in libata.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-13 23:19   ` Jens Axboe
@ 2022-03-14 20:25     ` Ondrej Zary
  2022-03-14 20:29       ` Jens Axboe
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-14 20:25 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Damien Le Moal, Christoph Hellwig, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
> On 3/13/22 1:15 PM, Ondrej Zary wrote:
> > On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
> >> The pata_parport is a libata-based replacement of the old PARIDE
> >> subsystem - driver for parallel port IDE devices.
> >> It uses the original paride low-level protocol drivers but does not
> >> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> >> behind parallel port adapters are handled by the ATA layer.
> >>
> >> This will allow paride and its high-level drivers to be removed.
> >>
> >> paride and pata_parport are mutually exclusive because the compiled
> >> protocol drivers are incompatible.
> >>
> >> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> >> chip).
> >>
> >> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> >> drivers - they don't handle non-multiple-of-4 block transfers
> >> correctly. This causes problems with LS-120 drive.
> >> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> >> or 8-bit mode is used first (probably some initialization missing?).
> >> Once the device is initialized, EPP works until power cycle.
> >>
> >> So after device power on, you have to:
> >> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> >> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> >> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> >> (autoprobe will initialize correctly as it tries the slowest modes
> >> first but you'll get the broken EPP-32 mode)
> > 
> > Found a bug - the same device can be registered multiple times. Fix
> > will be in v2. But this revealed a bigger problem: pi_connect can
> > sleep (uses parport_claim_or_block) and libata does not like that. Any
> > ideas how to fix this?
> 
> I think you'd need two things here:
> 
> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
>   will allow blocking off the queue_rq path.

My knowledge about blk-mq is exactly zero. After grepping the code, I guess that BLK_MQ_F_BLOCKING should be used by the block device drivers - sd and sr?
 
> - You need to look at making libata safe wrt calling ata_qc_issue()
>   outside the lock. Should probably be fine if you just gate that on
>   whether or not the queue was setup in blocking mode, as that doesn't
>   currently exist in libata.
> 


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-14 20:25     ` Ondrej Zary
@ 2022-03-14 20:29       ` Jens Axboe
  2022-03-15  4:22         ` Damien Le Moal
  2022-03-15  8:24         ` Christoph Hellwig
  0 siblings, 2 replies; 31+ messages in thread
From: Jens Axboe @ 2022-03-14 20:29 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Christoph Hellwig, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On 3/14/22 2:25 PM, Ondrej Zary wrote:
> On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
>> On 3/13/22 1:15 PM, Ondrej Zary wrote:
>>> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
>>>> The pata_parport is a libata-based replacement of the old PARIDE
>>>> subsystem - driver for parallel port IDE devices.
>>>> It uses the original paride low-level protocol drivers but does not
>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>>>> behind parallel port adapters are handled by the ATA layer.
>>>>
>>>> This will allow paride and its high-level drivers to be removed.
>>>>
>>>> paride and pata_parport are mutually exclusive because the compiled
>>>> protocol drivers are incompatible.
>>>>
>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>>>> chip).
>>>>
>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>>>> drivers - they don't handle non-multiple-of-4 block transfers
>>>> correctly. This causes problems with LS-120 drive.
>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>>>> or 8-bit mode is used first (probably some initialization missing?).
>>>> Once the device is initialized, EPP works until power cycle.
>>>>
>>>> So after device power on, you have to:
>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>>>> (autoprobe will initialize correctly as it tries the slowest modes
>>>> first but you'll get the broken EPP-32 mode)
>>>
>>> Found a bug - the same device can be registered multiple times. Fix
>>> will be in v2. But this revealed a bigger problem: pi_connect can
>>> sleep (uses parport_claim_or_block) and libata does not like that. Any
>>> ideas how to fix this?
>>
>> I think you'd need two things here:
>>
>> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
>>   will allow blocking off the queue_rq path.
> 
> My knowledge about blk-mq is exactly zero. After grepping the code, I
> guess that BLK_MQ_F_BLOCKING should be used by the block device
> drivers - sd and sr?

The controller would set

->needs_blocking_queue_rq = true;

or something, and we'd default to false. And if that is set, when the
blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
if that flag is true.

That's the block layer side. Then in libata you'd need to ensure that
you check that same setting and invoke ata_qc_issue() appropriately.

Very top level stuff, there might be more things lurking below. But
you'll probably find them as you test this stuff...

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-14 20:29       ` Jens Axboe
@ 2022-03-15  4:22         ` Damien Le Moal
  2022-03-15 18:44           ` Ondrej Zary
  2022-03-15  8:24         ` Christoph Hellwig
  1 sibling, 1 reply; 31+ messages in thread
From: Damien Le Moal @ 2022-03-15  4:22 UTC (permalink / raw)
  To: Jens Axboe, Ondrej Zary
  Cc: Christoph Hellwig, Tim Waugh, linux-block, linux-parport,
	linux-ide, linux-kernel

On 3/15/22 05:29, Jens Axboe wrote:
> On 3/14/22 2:25 PM, Ondrej Zary wrote:
>> On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
>>> On 3/13/22 1:15 PM, Ondrej Zary wrote:
>>>> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
>>>>> The pata_parport is a libata-based replacement of the old PARIDE
>>>>> subsystem - driver for parallel port IDE devices.
>>>>> It uses the original paride low-level protocol drivers but does not
>>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>>>>> behind parallel port adapters are handled by the ATA layer.
>>>>>
>>>>> This will allow paride and its high-level drivers to be removed.
>>>>>
>>>>> paride and pata_parport are mutually exclusive because the compiled
>>>>> protocol drivers are incompatible.
>>>>>
>>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>>>>> chip).
>>>>>
>>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>>>>> drivers - they don't handle non-multiple-of-4 block transfers
>>>>> correctly. This causes problems with LS-120 drive.
>>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>>>>> or 8-bit mode is used first (probably some initialization missing?).
>>>>> Once the device is initialized, EPP works until power cycle.
>>>>>
>>>>> So after device power on, you have to:
>>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>>>>> (autoprobe will initialize correctly as it tries the slowest modes
>>>>> first but you'll get the broken EPP-32 mode)
>>>>
>>>> Found a bug - the same device can be registered multiple times. Fix
>>>> will be in v2. But this revealed a bigger problem: pi_connect can
>>>> sleep (uses parport_claim_or_block) and libata does not like that. Any
>>>> ideas how to fix this?
>>>
>>> I think you'd need two things here:
>>>
>>> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
>>>   will allow blocking off the queue_rq path.
>>
>> My knowledge about blk-mq is exactly zero. After grepping the code, I
>> guess that BLK_MQ_F_BLOCKING should be used by the block device
>> drivers - sd and sr?
> 
> The controller would set
> 
> ->needs_blocking_queue_rq = true;
> 
> or something, and we'd default to false. And if that is set, when the
> blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
> if that flag is true.
> 
> That's the block layer side. Then in libata you'd need to ensure that
> you check that same setting and invoke ata_qc_issue() appropriately.
> 
> Very top level stuff, there might be more things lurking below. But
> you'll probably find them as you test this stuff...

Yes, the ata_port spinlock being held when calling ata_qc_issue() is
mandatory. But since I am assuming that all the IDE devices connected to
this adapter are QD=1 maximum, there can only be only one command in
flight. So it may be OK to release that lock before calling pi_connect()
and retake it right after it. libsas actually does something similar
(for no good reasons in that case though).

Jens point remain though that since pi_connect() can sleep, marking the
device queue with BLK_MQ_F_BLOCKING is mandatory.

-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-14 20:29       ` Jens Axboe
  2022-03-15  4:22         ` Damien Le Moal
@ 2022-03-15  8:24         ` Christoph Hellwig
  1 sibling, 0 replies; 31+ messages in thread
From: Christoph Hellwig @ 2022-03-15  8:24 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Ondrej Zary, Damien Le Moal, Christoph Hellwig, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Mon, Mar 14, 2022 at 02:29:20PM -0600, Jens Axboe wrote:
> The controller would set
> 
> ->needs_blocking_queue_rq = true;
> 
> or something, and we'd default to false. And if that is set, when the
> blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
> if that flag is true.
> 
> That's the block layer side. Then in libata you'd need to ensure that
> you check that same setting and invoke ata_qc_issue() appropriately.
> 
> Very top level stuff, there might be more things lurking below. But
> you'll probably find them as you test this stuff...

FYI, this somewhat mistitled series:

https://lore.kernel.org/all/20220308003957.123312-1-michael.christie@oracle.com/

adds BLK_MQ_F_BLOCKING support to the scsi core.  Doing libata
should be fairly easy and can built ontop of that.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-15  4:22         ` Damien Le Moal
@ 2022-03-15 18:44           ` Ondrej Zary
  2022-03-15 18:47             ` Jens Axboe
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-15 18:44 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Jens Axboe, Christoph Hellwig, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Tuesday 15 March 2022 05:22:47 Damien Le Moal wrote:
> On 3/15/22 05:29, Jens Axboe wrote:
> > On 3/14/22 2:25 PM, Ondrej Zary wrote:
> >> On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
> >>> On 3/13/22 1:15 PM, Ondrej Zary wrote:
> >>>> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
> >>>>> The pata_parport is a libata-based replacement of the old PARIDE
> >>>>> subsystem - driver for parallel port IDE devices.
> >>>>> It uses the original paride low-level protocol drivers but does not
> >>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> >>>>> behind parallel port adapters are handled by the ATA layer.
> >>>>>
> >>>>> This will allow paride and its high-level drivers to be removed.
> >>>>>
> >>>>> paride and pata_parport are mutually exclusive because the compiled
> >>>>> protocol drivers are incompatible.
> >>>>>
> >>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> >>>>> chip).
> >>>>>
> >>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> >>>>> drivers - they don't handle non-multiple-of-4 block transfers
> >>>>> correctly. This causes problems with LS-120 drive.
> >>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> >>>>> or 8-bit mode is used first (probably some initialization missing?).
> >>>>> Once the device is initialized, EPP works until power cycle.
> >>>>>
> >>>>> So after device power on, you have to:
> >>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> >>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> >>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> >>>>> (autoprobe will initialize correctly as it tries the slowest modes
> >>>>> first but you'll get the broken EPP-32 mode)
> >>>>
> >>>> Found a bug - the same device can be registered multiple times. Fix
> >>>> will be in v2. But this revealed a bigger problem: pi_connect can
> >>>> sleep (uses parport_claim_or_block) and libata does not like that. Any
> >>>> ideas how to fix this?
> >>>
> >>> I think you'd need two things here:
> >>>
> >>> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
> >>>   will allow blocking off the queue_rq path.
> >>
> >> My knowledge about blk-mq is exactly zero. After grepping the code, I
> >> guess that BLK_MQ_F_BLOCKING should be used by the block device
> >> drivers - sd and sr?
> > 
> > The controller would set
> > 
> > ->needs_blocking_queue_rq = true;
> > 
> > or something, and we'd default to false. And if that is set, when the
> > blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
> > if that flag is true.
> > 
> > That's the block layer side. Then in libata you'd need to ensure that
> > you check that same setting and invoke ata_qc_issue() appropriately.
> > 
> > Very top level stuff, there might be more things lurking below. But
> > you'll probably find them as you test this stuff...
> 
> Yes, the ata_port spinlock being held when calling ata_qc_issue() is
> mandatory. But since I am assuming that all the IDE devices connected to
> this adapter are QD=1 maximum, there can only be only one command in
> flight. So it may be OK to release that lock before calling pi_connect()
> and retake it right after it. libsas actually does something similar
> (for no good reasons in that case though).
> 
> Jens point remain though that since pi_connect() can sleep, marking the
> device queue with BLK_MQ_F_BLOCKING is mandatory.
 
Something like this? Requires Mike's SCSI BLK_MQ_F_BLOCKING patch:
https://lore.kernel.org/all/20220308003957.123312-2-michael.christie%40oracle.com/

#define PATA_PARPORT_SHT(drv_name)      \
        ATA_PIO_SHT(drv_name),          \
        .queuecommand_blocks    = true,

static void pi_connect(struct ata_port *ap)
{
	struct pi_adapter *pi = ap->host->private_data;

	del_timer_sync(&pi->timer);
	if (!pi->claimed) {
		bool locked = spin_is_locked(ap->lock);
		pi->claimed = true;
		if (locked)
			spin_unlock(ap->lock);
		parport_claim_or_block(pi->pardev);
		if (locked)
			spin_lock(ap->lock);
		pi->proto->connect(pi);
	}
}

spin_is_locked is needed because the lock is not always held.
It seems to work - no more stack traces after device double registration (only ATA errors but that's expected).

-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-15 18:44           ` Ondrej Zary
@ 2022-03-15 18:47             ` Jens Axboe
  2022-03-15 21:17               ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Jens Axboe @ 2022-03-15 18:47 UTC (permalink / raw)
  To: Ondrej Zary, Damien Le Moal
  Cc: Christoph Hellwig, Tim Waugh, linux-block, linux-parport,
	linux-ide, linux-kernel

On 3/15/22 12:44 PM, Ondrej Zary wrote:
> On Tuesday 15 March 2022 05:22:47 Damien Le Moal wrote:
>> On 3/15/22 05:29, Jens Axboe wrote:
>>> On 3/14/22 2:25 PM, Ondrej Zary wrote:
>>>> On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
>>>>> On 3/13/22 1:15 PM, Ondrej Zary wrote:
>>>>>> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
>>>>>>> The pata_parport is a libata-based replacement of the old PARIDE
>>>>>>> subsystem - driver for parallel port IDE devices.
>>>>>>> It uses the original paride low-level protocol drivers but does not
>>>>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>>>>>>> behind parallel port adapters are handled by the ATA layer.
>>>>>>>
>>>>>>> This will allow paride and its high-level drivers to be removed.
>>>>>>>
>>>>>>> paride and pata_parport are mutually exclusive because the compiled
>>>>>>> protocol drivers are incompatible.
>>>>>>>
>>>>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>>>>>>> chip).
>>>>>>>
>>>>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>>>>>>> drivers - they don't handle non-multiple-of-4 block transfers
>>>>>>> correctly. This causes problems with LS-120 drive.
>>>>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>>>>>>> or 8-bit mode is used first (probably some initialization missing?).
>>>>>>> Once the device is initialized, EPP works until power cycle.
>>>>>>>
>>>>>>> So after device power on, you have to:
>>>>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>>>>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>>>>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>>>>>>> (autoprobe will initialize correctly as it tries the slowest modes
>>>>>>> first but you'll get the broken EPP-32 mode)
>>>>>>
>>>>>> Found a bug - the same device can be registered multiple times. Fix
>>>>>> will be in v2. But this revealed a bigger problem: pi_connect can
>>>>>> sleep (uses parport_claim_or_block) and libata does not like that. Any
>>>>>> ideas how to fix this?
>>>>>
>>>>> I think you'd need two things here:
>>>>>
>>>>> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
>>>>>   will allow blocking off the queue_rq path.
>>>>
>>>> My knowledge about blk-mq is exactly zero. After grepping the code, I
>>>> guess that BLK_MQ_F_BLOCKING should be used by the block device
>>>> drivers - sd and sr?
>>>
>>> The controller would set
>>>
>>> ->needs_blocking_queue_rq = true;
>>>
>>> or something, and we'd default to false. And if that is set, when the
>>> blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
>>> if that flag is true.
>>>
>>> That's the block layer side. Then in libata you'd need to ensure that
>>> you check that same setting and invoke ata_qc_issue() appropriately.
>>>
>>> Very top level stuff, there might be more things lurking below. But
>>> you'll probably find them as you test this stuff...
>>
>> Yes, the ata_port spinlock being held when calling ata_qc_issue() is
>> mandatory. But since I am assuming that all the IDE devices connected to
>> this adapter are QD=1 maximum, there can only be only one command in
>> flight. So it may be OK to release that lock before calling pi_connect()
>> and retake it right after it. libsas actually does something similar
>> (for no good reasons in that case though).
>>
>> Jens point remain though that since pi_connect() can sleep, marking the
>> device queue with BLK_MQ_F_BLOCKING is mandatory.
>  
> Something like this? Requires Mike's SCSI BLK_MQ_F_BLOCKING patch:
> https://lore.kernel.org/all/20220308003957.123312-2-michael.christie%40oracle.com/
> 
> #define PATA_PARPORT_SHT(drv_name)      \
>         ATA_PIO_SHT(drv_name),          \
>         .queuecommand_blocks    = true,
> 
> static void pi_connect(struct ata_port *ap)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 
> 	del_timer_sync(&pi->timer);
> 	if (!pi->claimed) {
> 		bool locked = spin_is_locked(ap->lock);
> 		pi->claimed = true;
> 		if (locked)
> 			spin_unlock(ap->lock);
> 		parport_claim_or_block(pi->pardev);
> 		if (locked)
> 			spin_lock(ap->lock);
> 		pi->proto->connect(pi);
> 	}
> }
> 
> spin_is_locked is needed because the lock is not always held. It seems
> to work - no more stack traces after device double registration (only
> ATA errors but that's expected).

That's a very bad paradigm. What if it is locked, but the caller isn't
the one that locked it? Would be better to either make the locking state
consistent, or provide an unlocked variant (if feasible, doesn't always
work if it's a provided helper already in a struct of ops), or even
resorting to passing in locking state as a last resort.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-15 18:47             ` Jens Axboe
@ 2022-03-15 21:17               ` Ondrej Zary
  2022-03-16  0:19                 ` Damien Le Moal
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-15 21:17 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Damien Le Moal, Christoph Hellwig, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On Tuesday 15 March 2022 19:47:32 Jens Axboe wrote:
> On 3/15/22 12:44 PM, Ondrej Zary wrote:
> > On Tuesday 15 March 2022 05:22:47 Damien Le Moal wrote:
> >> On 3/15/22 05:29, Jens Axboe wrote:
> >>> On 3/14/22 2:25 PM, Ondrej Zary wrote:
> >>>> On Monday 14 March 2022 00:19:30 Jens Axboe wrote:
> >>>>> On 3/13/22 1:15 PM, Ondrej Zary wrote:
> >>>>>> On Saturday 12 March 2022 15:44:15 Ondrej Zary wrote:
> >>>>>>> The pata_parport is a libata-based replacement of the old PARIDE
> >>>>>>> subsystem - driver for parallel port IDE devices.
> >>>>>>> It uses the original paride low-level protocol drivers but does not
> >>>>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> >>>>>>> behind parallel port adapters are handled by the ATA layer.
> >>>>>>>
> >>>>>>> This will allow paride and its high-level drivers to be removed.
> >>>>>>>
> >>>>>>> paride and pata_parport are mutually exclusive because the compiled
> >>>>>>> protocol drivers are incompatible.
> >>>>>>>
> >>>>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> >>>>>>> chip).
> >>>>>>>
> >>>>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> >>>>>>> drivers - they don't handle non-multiple-of-4 block transfers
> >>>>>>> correctly. This causes problems with LS-120 drive.
> >>>>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> >>>>>>> or 8-bit mode is used first (probably some initialization missing?).
> >>>>>>> Once the device is initialized, EPP works until power cycle.
> >>>>>>>
> >>>>>>> So after device power on, you have to:
> >>>>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> >>>>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> >>>>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> >>>>>>> (autoprobe will initialize correctly as it tries the slowest modes
> >>>>>>> first but you'll get the broken EPP-32 mode)
> >>>>>>
> >>>>>> Found a bug - the same device can be registered multiple times. Fix
> >>>>>> will be in v2. But this revealed a bigger problem: pi_connect can
> >>>>>> sleep (uses parport_claim_or_block) and libata does not like that. Any
> >>>>>> ideas how to fix this?
> >>>>>
> >>>>> I think you'd need two things here:
> >>>>>
> >>>>> - The blk-mq queue should be registered with BLK_MQ_F_BLOCKING, which
> >>>>>   will allow blocking off the queue_rq path.
> >>>>
> >>>> My knowledge about blk-mq is exactly zero. After grepping the code, I
> >>>> guess that BLK_MQ_F_BLOCKING should be used by the block device
> >>>> drivers - sd and sr?
> >>>
> >>> The controller would set
> >>>
> >>> ->needs_blocking_queue_rq = true;
> >>>
> >>> or something, and we'd default to false. And if that is set, when the
> >>> blk-mq queue is created, then we'd set BLK_MQ_F_BLOCKING upon creation
> >>> if that flag is true.
> >>>
> >>> That's the block layer side. Then in libata you'd need to ensure that
> >>> you check that same setting and invoke ata_qc_issue() appropriately.
> >>>
> >>> Very top level stuff, there might be more things lurking below. But
> >>> you'll probably find them as you test this stuff...
> >>
> >> Yes, the ata_port spinlock being held when calling ata_qc_issue() is
> >> mandatory. But since I am assuming that all the IDE devices connected to
> >> this adapter are QD=1 maximum, there can only be only one command in
> >> flight. So it may be OK to release that lock before calling pi_connect()
> >> and retake it right after it. libsas actually does something similar
> >> (for no good reasons in that case though).
> >>
> >> Jens point remain though that since pi_connect() can sleep, marking the
> >> device queue with BLK_MQ_F_BLOCKING is mandatory.
> >  
> > Something like this? Requires Mike's SCSI BLK_MQ_F_BLOCKING patch:
> > https://lore.kernel.org/all/20220308003957.123312-2-michael.christie%40oracle.com/
> > 
> > #define PATA_PARPORT_SHT(drv_name)      \
> >         ATA_PIO_SHT(drv_name),          \
> >         .queuecommand_blocks    = true,
> > 
> > static void pi_connect(struct ata_port *ap)
> > {
> > 	struct pi_adapter *pi = ap->host->private_data;
> > 
> > 	del_timer_sync(&pi->timer);
> > 	if (!pi->claimed) {
> > 		bool locked = spin_is_locked(ap->lock);
> > 		pi->claimed = true;
> > 		if (locked)
> > 			spin_unlock(ap->lock);
> > 		parport_claim_or_block(pi->pardev);
> > 		if (locked)
> > 			spin_lock(ap->lock);
> > 		pi->proto->connect(pi);
> > 	}
> > }
> > 
> > spin_is_locked is needed because the lock is not always held. It seems
> > to work - no more stack traces after device double registration (only
> > ATA errors but that's expected).
> 
> That's a very bad paradigm. What if it is locked, but the caller isn't
> the one that locked it? Would be better to either make the locking state
> consistent, or provide an unlocked variant (if feasible, doesn't always
> work if it's a provided helper already in a struct of ops), or even
> resorting to passing in locking state as a last resort.
 
libata locking seems to be very complex and our functions seem to be called with various lock states. I'm lost.

Might be easier to add connect() and disconnect() to struct ata_port_operations...

-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-15 21:17               ` Ondrej Zary
@ 2022-03-16  0:19                 ` Damien Le Moal
  0 siblings, 0 replies; 31+ messages in thread
From: Damien Le Moal @ 2022-03-16  0:19 UTC (permalink / raw)
  To: Ondrej Zary, Jens Axboe
  Cc: Christoph Hellwig, Tim Waugh, linux-block, linux-parport,
	linux-ide, linux-kernel

On 3/16/22 06:17, Ondrej Zary wrote:
>>> Something like this? Requires Mike's SCSI BLK_MQ_F_BLOCKING patch:
>>> https://lore.kernel.org/all/20220308003957.123312-2-michael.christie%40oracle.com/
>>>
>>> #define PATA_PARPORT_SHT(drv_name)      \
>>>         ATA_PIO_SHT(drv_name),          \
>>>         .queuecommand_blocks    = true,
>>>
>>> static void pi_connect(struct ata_port *ap)
>>> {
>>> 	struct pi_adapter *pi = ap->host->private_data;
>>>
>>> 	del_timer_sync(&pi->timer);
>>> 	if (!pi->claimed) {
>>> 		bool locked = spin_is_locked(ap->lock);

For the pi_connect() call in the ata_qc_issue() context, ap->lock is
always held, so this is not necessary.

If you have other pi_connect() calls in different contexts, we will need
to address these too. For internal commands during scan, ap->lock is
also always held.

>>> 		pi->claimed = true;
>>> 		if (locked)
>>> 			spin_unlock(ap->lock);

You need spin_unlock_irqrestore(). See the locking done in
ata_scsi_queuecmd() which is the starting point for issuing a command
through libata.

>>> 		parport_claim_or_block(pi->pardev);
>>> 		if (locked)
>>> 			spin_lock(ap->lock);
>>> 		pi->proto->connect(pi);
>>> 	}
>>> }
>>>
>>> spin_is_locked is needed because the lock is not always held. It seems
>>> to work - no more stack traces after device double registration (only
>>> ATA errors but that's expected).
>>
>> That's a very bad paradigm. What if it is locked, but the caller isn't
>> the one that locked it? Would be better to either make the locking state
>> consistent, or provide an unlocked variant (if feasible, doesn't always
>> work if it's a provided helper already in a struct of ops), or even
>> resorting to passing in locking state as a last resort.
>  
> libata locking seems to be very complex and our functions seem to be called with various lock states. I'm lost.
> 
> Might be easier to add connect() and disconnect() to struct ata_port_operations...

But you would not be able to call these within the ata_qc_issue()
context, which I think is necessary in your case. Also, these
connect/disconnect operations are not something defined by the ATA
protocol, so we should try to keep these hidden in the LLDD. It is
better I think to find a solution about the locking, if necessary using
a different qc_issue operation or using the queuecommand_blocks
attribute to have libata call the LLDD qc_issue without lock helds,
which should be OK (need to check).

Ideally, we should refine this ap->lock big lock to avoid it being held
throughout the entire submission path. I will try to have a look at this.


-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-13 21:19   ` Ondrej Zary
@ 2022-03-16  8:50     ` Sergey Shtylyov
  2022-03-16 11:28       ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Sergey Shtylyov @ 2022-03-16  8:50 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

Hello!

On 3/14/22 12:19 AM, Ondrej Zary wrote:

[...]
>>> The pata_parport is a libata-based replacement of the old PARIDE
>>> subsystem - driver for parallel port IDE devices.
>>> It uses the original paride low-level protocol drivers but does not
>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>>> behind parallel port adapters are handled by the ATA layer.
>>>
>>> This will allow paride and its high-level drivers to be removed.
>>>
>>> paride and pata_parport are mutually exclusive because the compiled
>>> protocol drivers are incompatible.
>>>
>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>>> chip).
>>>
>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>>> drivers - they don't handle non-multiple-of-4 block transfers
>>> correctly. This causes problems with LS-120 drive.
>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>>> or 8-bit mode is used first (probably some initialization missing?).
>>> Once the device is initialized, EPP works until power cycle.
>>>
>>> So after device power on, you have to:
>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>>> (autoprobe will initialize correctly as it tries the slowest modes
>>> first but you'll get the broken EPP-32 mode)
>>>
>>> Signed-off-by: Ondrej Zary <linux@zary.sk>
>> [...]
>>> diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
>>> index e1ce90af602a..e431a1ef41eb 100644
>>> --- a/Documentation/admin-guide/blockdev/paride.rst
>>> +++ b/Documentation/admin-guide/blockdev/paride.rst
>> [...]
>>> diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
>>> new file mode 100644
>>> index 000000000000..783764626a27
>>> --- /dev/null
>>> +++ b/drivers/ata/pata_parport.c
>>> @@ -0,0 +1,819 @@
>> [...]
>>> +static void pata_parport_lost_interrupt(struct ata_port *ap)
>>> +{
>>> +	u8 status;
>>> +	struct ata_queued_cmd *qc;
>>> +
>>> +	/* Only one outstanding command per SFF channel */
>>> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
>>> +	/* We cannot lose an interrupt on a non-existent or polled command */
>>> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
>>> +		return;
>>> +	/*
>>> +	 * See if the controller thinks it is still busy - if so the command
>>> +	 * isn't a lost IRQ but is still in progress
>>> +	 */
>>> +	status = pata_parport_check_altstatus(ap);
>>> +	if (status & ATA_BUSY)
>>> +		return;
>>> +
>>> +	/*
>>> +	 * There was a command running, we are no longer busy and we have
>>> +	 * no interrupt.
>>> +	 */
>>> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
>>> +	/* Run the host interrupt logic as if the interrupt had not been lost */
>>> +	ata_sff_port_intr(ap, qc);
>>> +}
>>
>>    As I said, ata_sff_lost_interrupt() could be used instead...
> 
> It couldn't be used because it calls ata_sff_altstatus().

   And? That one used to call the sff_check_altstatus() method (which you define)
even before my patch:

https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=03c0e84f9c1e166d57d06b04497e11205f48e9a8

[...]
>>> diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
>>> new file mode 100644
>>> index 000000000000..f1ba57bb319c
>>> --- /dev/null
>>> +++ b/include/linux/pata_parport.h
>>> @@ -0,0 +1,108 @@
[...]
>>> +static inline u16 pi_swab16(char *b, int k)
>>> +{
>>> +	union { u16 u; char t[2]; } r;
>>> +
>>> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
>>> +	return r.u;
>>> +}
>>> +
>>> +static inline u32 pi_swab32(char *b, int k)
>>> +{
>>> +	union { u32 u; char f[4]; } r;
>>> +
>>> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
>>> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
>>> +	return r.u;
>>
>>    Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
>> swapping...
> 
> This crap comes from old paride.h and we can't get rid of it without touching the protocol drivers

   I don't argue about the *inline*s themselves, just about the ineffective code inside them.

> (comm.c and kbic.c). Maybe use something like:
> 
> #define pi_swab16(char *b, int k) 	swab16p((u16 *)&b[2 * k])

> but I'm not sure it's equivalent on a big-endian machine.

   These functions are endian-agnostic -- they swap always.
   If you only need to swab the bytes on big-endian machines, you should use cpu_to_le*() and/or
le*_to_cpu()...

[...]

MBR, Sergey

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-16  8:50     ` Sergey Shtylyov
@ 2022-03-16 11:28       ` Ondrej Zary
  2022-03-16 11:44         ` Damien Le Moal
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-16 11:28 UTC (permalink / raw)
  To: Sergey Shtylyov
  Cc: Damien Le Moal, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Wednesday 16 March 2022, Sergey Shtylyov wrote:
> Hello!
> 
> On 3/14/22 12:19 AM, Ondrej Zary wrote:
> 
> [...]
> >>> The pata_parport is a libata-based replacement of the old PARIDE
> >>> subsystem - driver for parallel port IDE devices.
> >>> It uses the original paride low-level protocol drivers but does not
> >>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> >>> behind parallel port adapters are handled by the ATA layer.
> >>>
> >>> This will allow paride and its high-level drivers to be removed.
> >>>
> >>> paride and pata_parport are mutually exclusive because the compiled
> >>> protocol drivers are incompatible.
> >>>
> >>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> >>> chip).
> >>>
> >>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> >>> drivers - they don't handle non-multiple-of-4 block transfers
> >>> correctly. This causes problems with LS-120 drive.
> >>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> >>> or 8-bit mode is used first (probably some initialization missing?).
> >>> Once the device is initialized, EPP works until power cycle.
> >>>
> >>> So after device power on, you have to:
> >>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> >>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> >>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> >>> (autoprobe will initialize correctly as it tries the slowest modes
> >>> first but you'll get the broken EPP-32 mode)
> >>>
> >>> Signed-off-by: Ondrej Zary <linux@zary.sk>
> >> [...]
> >>> diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
> >>> index e1ce90af602a..e431a1ef41eb 100644
> >>> --- a/Documentation/admin-guide/blockdev/paride.rst
> >>> +++ b/Documentation/admin-guide/blockdev/paride.rst
> >> [...]
> >>> diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
> >>> new file mode 100644
> >>> index 000000000000..783764626a27
> >>> --- /dev/null
> >>> +++ b/drivers/ata/pata_parport.c
> >>> @@ -0,0 +1,819 @@
> >> [...]
> >>> +static void pata_parport_lost_interrupt(struct ata_port *ap)
> >>> +{
> >>> +	u8 status;
> >>> +	struct ata_queued_cmd *qc;
> >>> +
> >>> +	/* Only one outstanding command per SFF channel */
> >>> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> >>> +	/* We cannot lose an interrupt on a non-existent or polled command */
> >>> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> >>> +		return;
> >>> +	/*
> >>> +	 * See if the controller thinks it is still busy - if so the command
> >>> +	 * isn't a lost IRQ but is still in progress
> >>> +	 */
> >>> +	status = pata_parport_check_altstatus(ap);
> >>> +	if (status & ATA_BUSY)
> >>> +		return;
> >>> +
> >>> +	/*
> >>> +	 * There was a command running, we are no longer busy and we have
> >>> +	 * no interrupt.
> >>> +	 */
> >>> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> >>> +	/* Run the host interrupt logic as if the interrupt had not been lost */
> >>> +	ata_sff_port_intr(ap, qc);
> >>> +}
> >>
> >>    As I said, ata_sff_lost_interrupt() could be used instead...
> > 
> > It couldn't be used because it calls ata_sff_altstatus().
> 
>    And? That one used to call the sff_check_altstatus() method (which you define)
> even before my patch:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=03c0e84f9c1e166d57d06b04497e11205f48e9a8

OK, I was probably confused by ata_sff_check_status which uses ioread directly.

> [...]
> >>> diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
> >>> new file mode 100644
> >>> index 000000000000..f1ba57bb319c
> >>> --- /dev/null
> >>> +++ b/include/linux/pata_parport.h
> >>> @@ -0,0 +1,108 @@
> [...]
> >>> +static inline u16 pi_swab16(char *b, int k)
> >>> +{
> >>> +	union { u16 u; char t[2]; } r;
> >>> +
> >>> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> >>> +	return r.u;
> >>> +}
> >>> +
> >>> +static inline u32 pi_swab32(char *b, int k)
> >>> +{
> >>> +	union { u32 u; char f[4]; } r;
> >>> +
> >>> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> >>> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> >>> +	return r.u;
> >>
> >>    Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
> >> swapping...
> > 
> > This crap comes from old paride.h and we can't get rid of it without touching the protocol drivers
> 
>    I don't argue about the *inline*s themselves, just about the ineffective code inside them.
> 
> > (comm.c and kbic.c). Maybe use something like:
> > 
> > #define pi_swab16(char *b, int k) 	swab16p((u16 *)&b[2 * k])
> 
> > but I'm not sure it's equivalent on a big-endian machine.
> 
>    These functions are endian-agnostic -- they swap always.
>    If you only need to swab the bytes on big-endian machines, you should use cpu_to_le*() and/or
> le*_to_cpu()...

swab16 swaps always but pi_swab16 does not on big-endian. It's probably a bug but doing the correct thing by accident. Other protocol drivers completely ignore endianness, probably because PARIDE was meant for x86 only.

> [...]
> 
> MBR, Sergey
> 



-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-16 11:28       ` Ondrej Zary
@ 2022-03-16 11:44         ` Damien Le Moal
  2022-03-16 12:58           ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Damien Le Moal @ 2022-03-16 11:44 UTC (permalink / raw)
  To: Ondrej Zary, Sergey Shtylyov
  Cc: Christoph Hellwig, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On 3/16/22 20:28, Ondrej Zary wrote:
> On Wednesday 16 March 2022, Sergey Shtylyov wrote:
>> Hello!
>>
>> On 3/14/22 12:19 AM, Ondrej Zary wrote:
>>
>> [...]
>>>>> The pata_parport is a libata-based replacement of the old PARIDE
>>>>> subsystem - driver for parallel port IDE devices.
>>>>> It uses the original paride low-level protocol drivers but does not
>>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
>>>>> behind parallel port adapters are handled by the ATA layer.
>>>>>
>>>>> This will allow paride and its high-level drivers to be removed.
>>>>>
>>>>> paride and pata_parport are mutually exclusive because the compiled
>>>>> protocol drivers are incompatible.
>>>>>
>>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
>>>>> chip).
>>>>>
>>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
>>>>> drivers - they don't handle non-multiple-of-4 block transfers
>>>>> correctly. This causes problems with LS-120 drive.
>>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
>>>>> or 8-bit mode is used first (probably some initialization missing?).
>>>>> Once the device is initialized, EPP works until power cycle.
>>>>>
>>>>> So after device power on, you have to:
>>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
>>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
>>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
>>>>> (autoprobe will initialize correctly as it tries the slowest modes
>>>>> first but you'll get the broken EPP-32 mode)
>>>>>
>>>>> Signed-off-by: Ondrej Zary <linux@zary.sk>
>>>> [...]
>>>>> diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
>>>>> index e1ce90af602a..e431a1ef41eb 100644
>>>>> --- a/Documentation/admin-guide/blockdev/paride.rst
>>>>> +++ b/Documentation/admin-guide/blockdev/paride.rst
>>>> [...]
>>>>> diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
>>>>> new file mode 100644
>>>>> index 000000000000..783764626a27
>>>>> --- /dev/null
>>>>> +++ b/drivers/ata/pata_parport.c
>>>>> @@ -0,0 +1,819 @@
>>>> [...]
>>>>> +static void pata_parport_lost_interrupt(struct ata_port *ap)
>>>>> +{
>>>>> +	u8 status;
>>>>> +	struct ata_queued_cmd *qc;
>>>>> +
>>>>> +	/* Only one outstanding command per SFF channel */
>>>>> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
>>>>> +	/* We cannot lose an interrupt on a non-existent or polled command */
>>>>> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
>>>>> +		return;
>>>>> +	/*
>>>>> +	 * See if the controller thinks it is still busy - if so the command
>>>>> +	 * isn't a lost IRQ but is still in progress
>>>>> +	 */
>>>>> +	status = pata_parport_check_altstatus(ap);
>>>>> +	if (status & ATA_BUSY)
>>>>> +		return;
>>>>> +
>>>>> +	/*
>>>>> +	 * There was a command running, we are no longer busy and we have
>>>>> +	 * no interrupt.
>>>>> +	 */
>>>>> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
>>>>> +	/* Run the host interrupt logic as if the interrupt had not been lost */
>>>>> +	ata_sff_port_intr(ap, qc);
>>>>> +}
>>>>
>>>>    As I said, ata_sff_lost_interrupt() could be used instead...
>>>
>>> It couldn't be used because it calls ata_sff_altstatus().
>>
>>    And? That one used to call the sff_check_altstatus() method (which you define)
>> even before my patch:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=03c0e84f9c1e166d57d06b04497e11205f48e9a8
> 
> OK, I was probably confused by ata_sff_check_status which uses ioread directly.
> 
>> [...]
>>>>> diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
>>>>> new file mode 100644
>>>>> index 000000000000..f1ba57bb319c
>>>>> --- /dev/null
>>>>> +++ b/include/linux/pata_parport.h
>>>>> @@ -0,0 +1,108 @@
>> [...]
>>>>> +static inline u16 pi_swab16(char *b, int k)
>>>>> +{
>>>>> +	union { u16 u; char t[2]; } r;
>>>>> +
>>>>> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
>>>>> +	return r.u;
>>>>> +}
>>>>> +
>>>>> +static inline u32 pi_swab32(char *b, int k)
>>>>> +{
>>>>> +	union { u32 u; char f[4]; } r;
>>>>> +
>>>>> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
>>>>> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
>>>>> +	return r.u;
>>>>
>>>>    Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
>>>> swapping...
>>>
>>> This crap comes from old paride.h and we can't get rid of it without touching the protocol drivers
>>
>>    I don't argue about the *inline*s themselves, just about the ineffective code inside them.
>>
>>> (comm.c and kbic.c). Maybe use something like:
>>>
>>> #define pi_swab16(char *b, int k) 	swab16p((u16 *)&b[2 * k])
>>
>>> but I'm not sure it's equivalent on a big-endian machine.
>>
>>    These functions are endian-agnostic -- they swap always.
>>    If you only need to swab the bytes on big-endian machines, you should use cpu_to_le*() and/or
>> le*_to_cpu()...
> 
> swab16 swaps always but pi_swab16 does not on big-endian. It's probably a bug but doing the correct thing by accident. Other protocol drivers completely ignore endianness, probably because PARIDE was meant for x86 only.

Fix that. ATA/IDE uses little endian. So all command & replies fields
should be handled with put_unaligned_lexx()/get_unaligned_lexx(), or
cpu_to_lexx() and lexx_to_cpu().

> 
>> [...]
>>
>> MBR, Sergey
>>
> 
> 
> 


-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-16 11:44         ` Damien Le Moal
@ 2022-03-16 12:58           ` Ondrej Zary
  2022-10-19  7:34             ` Christoph Hellwig
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-03-16 12:58 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Sergey Shtylyov, Christoph Hellwig, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Wednesday 16 March 2022, Damien Le Moal wrote:
> On 3/16/22 20:28, Ondrej Zary wrote:
> > On Wednesday 16 March 2022, Sergey Shtylyov wrote:
> >> Hello!
> >>
> >> On 3/14/22 12:19 AM, Ondrej Zary wrote:
> >>
> >> [...]
> >>>>> The pata_parport is a libata-based replacement of the old PARIDE
> >>>>> subsystem - driver for parallel port IDE devices.
> >>>>> It uses the original paride low-level protocol drivers but does not
> >>>>> need the high-level drivers (pd, pcd, pf, pt, pg). The IDE devices
> >>>>> behind parallel port adapters are handled by the ATA layer.
> >>>>>
> >>>>> This will allow paride and its high-level drivers to be removed.
> >>>>>
> >>>>> paride and pata_parport are mutually exclusive because the compiled
> >>>>> protocol drivers are incompatible.
> >>>>>
> >>>>> Tested with Imation SuperDisk LS-120 and HP C4381A (both use EPAT
> >>>>> chip).
> >>>>>
> >>>>> Note: EPP-32 mode is buggy in EPAT - and also in all other protocol
> >>>>> drivers - they don't handle non-multiple-of-4 block transfers
> >>>>> correctly. This causes problems with LS-120 drive.
> >>>>> There is also another bug in EPAT: EPP modes don't work unless a 4-bit
> >>>>> or 8-bit mode is used first (probably some initialization missing?).
> >>>>> Once the device is initialized, EPP works until power cycle.
> >>>>>
> >>>>> So after device power on, you have to:
> >>>>> echo "parport0 epat 0" >/sys/bus/pata_parport/new_device
> >>>>> echo pata_parport.0 >/sys/bus/pata_parport/delete_device
> >>>>> echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
> >>>>> (autoprobe will initialize correctly as it tries the slowest modes
> >>>>> first but you'll get the broken EPP-32 mode)
> >>>>>
> >>>>> Signed-off-by: Ondrej Zary <linux@zary.sk>
> >>>> [...]
> >>>>> diff --git a/Documentation/admin-guide/blockdev/paride.rst b/Documentation/admin-guide/blockdev/paride.rst
> >>>>> index e1ce90af602a..e431a1ef41eb 100644
> >>>>> --- a/Documentation/admin-guide/blockdev/paride.rst
> >>>>> +++ b/Documentation/admin-guide/blockdev/paride.rst
> >>>> [...]
> >>>>> diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
> >>>>> new file mode 100644
> >>>>> index 000000000000..783764626a27
> >>>>> --- /dev/null
> >>>>> +++ b/drivers/ata/pata_parport.c
> >>>>> @@ -0,0 +1,819 @@
> >>>> [...]
> >>>>> +static void pata_parport_lost_interrupt(struct ata_port *ap)
> >>>>> +{
> >>>>> +	u8 status;
> >>>>> +	struct ata_queued_cmd *qc;
> >>>>> +
> >>>>> +	/* Only one outstanding command per SFF channel */
> >>>>> +	qc = ata_qc_from_tag(ap, ap->link.active_tag);
> >>>>> +	/* We cannot lose an interrupt on a non-existent or polled command */
> >>>>> +	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
> >>>>> +		return;
> >>>>> +	/*
> >>>>> +	 * See if the controller thinks it is still busy - if so the command
> >>>>> +	 * isn't a lost IRQ but is still in progress
> >>>>> +	 */
> >>>>> +	status = pata_parport_check_altstatus(ap);
> >>>>> +	if (status & ATA_BUSY)
> >>>>> +		return;
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * There was a command running, we are no longer busy and we have
> >>>>> +	 * no interrupt.
> >>>>> +	 */
> >>>>> +	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
> >>>>> +	/* Run the host interrupt logic as if the interrupt had not been lost */
> >>>>> +	ata_sff_port_intr(ap, qc);
> >>>>> +}
> >>>>
> >>>>    As I said, ata_sff_lost_interrupt() could be used instead...
> >>>
> >>> It couldn't be used because it calls ata_sff_altstatus().
> >>
> >>    And? That one used to call the sff_check_altstatus() method (which you define)
> >> even before my patch:
> >>
> >> https://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata.git/commit/?h=for-next&id=03c0e84f9c1e166d57d06b04497e11205f48e9a8
> > 
> > OK, I was probably confused by ata_sff_check_status which uses ioread directly.
> > 
> >> [...]
> >>>>> diff --git a/include/linux/pata_parport.h b/include/linux/pata_parport.h
> >>>>> new file mode 100644
> >>>>> index 000000000000..f1ba57bb319c
> >>>>> --- /dev/null
> >>>>> +++ b/include/linux/pata_parport.h
> >>>>> @@ -0,0 +1,108 @@
> >> [...]
> >>>>> +static inline u16 pi_swab16(char *b, int k)
> >>>>> +{
> >>>>> +	union { u16 u; char t[2]; } r;
> >>>>> +
> >>>>> +	r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
> >>>>> +	return r.u;
> >>>>> +}
> >>>>> +
> >>>>> +static inline u32 pi_swab32(char *b, int k)
> >>>>> +{
> >>>>> +	union { u32 u; char f[4]; } r;
> >>>>> +
> >>>>> +	r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
> >>>>> +	r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
> >>>>> +	return r.u;
> >>>>
> >>>>    Hey, I was serious about swab{16|32}p()! Please don't use home grown byte
> >>>> swapping...
> >>>
> >>> This crap comes from old paride.h and we can't get rid of it without touching the protocol drivers
> >>
> >>    I don't argue about the *inline*s themselves, just about the ineffective code inside them.
> >>
> >>> (comm.c and kbic.c). Maybe use something like:
> >>>
> >>> #define pi_swab16(char *b, int k) 	swab16p((u16 *)&b[2 * k])
> >>
> >>> but I'm not sure it's equivalent on a big-endian machine.
> >>
> >>    These functions are endian-agnostic -- they swap always.
> >>    If you only need to swab the bytes on big-endian machines, you should use cpu_to_le*() and/or
> >> le*_to_cpu()...
> > 
> > swab16 swaps always but pi_swab16 does not on big-endian. It's probably a bug but doing the correct thing by accident. Other protocol drivers completely ignore endianness, probably because PARIDE was meant for x86 only.
> 
> Fix that. ATA/IDE uses little endian. So all command & replies fields
> should be handled with put_unaligned_lexx()/get_unaligned_lexx(), or
> cpu_to_lexx() and lexx_to_cpu().

Fortunately, most of the code uses 8-bit only accesses (registers are 8-bit and also parport HW is mostly 8-bit). Only block reads/writes in EPP-16 and EPP-32 modes seem to be affected. I'll fix that later as I'm not touching protocol drivers now.

> 
> > 
> >> [...]
> >>
> >> MBR, Sergey
> >>
> > 
> > 
> > 
> 
> 



-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-03-16 12:58           ` Ondrej Zary
@ 2022-10-19  7:34             ` Christoph Hellwig
  2022-10-19 18:58               ` Ondrej Zary
  2022-11-12 11:17               ` Ondrej Zary
  0 siblings, 2 replies; 31+ messages in thread
From: Christoph Hellwig @ 2022-10-19  7:34 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Sergey Shtylyov, Christoph Hellwig, Jens Axboe,
	Tim Waugh, linux-block, linux-parport, linux-ide, linux-kernel

It's been a while - did you get a chance to make some progress on
this?  Do you need any help to unblock you?

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-10-19  7:34             ` Christoph Hellwig
@ 2022-10-19 18:58               ` Ondrej Zary
  2022-11-12 11:17               ` Ondrej Zary
  1 sibling, 0 replies; 31+ messages in thread
From: Ondrej Zary @ 2022-10-19 18:58 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

Sorry for the delay, I was still busy. IIRC, I was stuck with locking in libata.

On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> It's been a while - did you get a chance to make some progress on
> this?  Do you need any help to unblock you?
> 


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-10-19  7:34             ` Christoph Hellwig
  2022-10-19 18:58               ` Ondrej Zary
@ 2022-11-12 11:17               ` Ondrej Zary
  2022-11-13 23:23                 ` Damien Le Moal
  1 sibling, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-11-12 11:17 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> It's been a while - did you get a chance to make some progress on
> this?  Do you need any help to unblock you?
> 

Sorry again, I'm back now. Trying to fix locking problems.
Added this to each function for analysis how the functions are called wrt.
locking:

	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));

result (tail -n 4064 /var/log/messages | cut -d\] -f 2- | cut -d\  -f 2- | uniq -c)

      1 pata_parport: protocol epat registered
      1 (null): epat 1.02, Shuttle EPAT chip c6 at 0x378,
      1 mode 5 (EPP-32), delay 1
      1 pata_parport_check_status, locked=1
      1 scsi host4: pata_parport
      1 ata5: PATA max PIO0 port parport0 protocol epat
     85 pata_parport_check_status, locked=0
      1 ata5: link is slow to respond, please be patient (ready=0)
     83 pata_parport_check_status, locked=0
      1 ata5: device not ready (errno=-16), forcing hardreset
      1 pata_parport_check_status, locked=1
      1 pata_parport_softreset, locked=0
      1 pata_parport_devchk, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_devchk, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_bus_softreset, locked=0
      1 pata_parport_check_status, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_tf_read, locked=0
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
      1 pata_parport_tf_read, locked=0
   1001 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=0
      1 pata_parport_check_altstatus, locked=0
   1000 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 ata5.00: ATAPI: TOSHIBA CD-ROM XM-6202B, 1108, max MWDMA2
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      7 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 scsi 4:0:0:0: CD-ROM            TOSHIBA  CD-ROM XM-6202B  1108 PQ: 0 ANSI: 5
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_drain_fifo, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 scsi 4:0:0:0: Attached scsi generic sg1 type 5
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 sr 4:0:0:0: [sr0] scsi3-mmc drive: 32x/32x cd/rw xa/form2 cdda tray
      1 cdrom: Uniform CD-ROM driver Revision: 3.20
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_drain_fifo, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_drain_fifo, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_drain_fifo, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      7 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      8 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_drain_fifo, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      8 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      4 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      5 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      3 pata_parport_check_status, locked=1
      1 pata_parport_dev_select, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_tf_load, locked=1
      2 pata_parport_check_status, locked=1
      1 pata_parport_exec_command, locked=1
      1 pata_parport_check_altstatus, locked=1
      1 pata_parport_check_status, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      6 pata_parport_check_status, locked=1
      1 pata_parport_tf_read, locked=1
      1 pata_parport_data_xfer, locked=1
      1 pata_parport_check_altstatus, locked=1
      2 pata_parport_check_status, locked=1
      1 ata5.00: disable device
      1 pata_parport_check_status, locked=1


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-12 11:17               ` Ondrej Zary
@ 2022-11-13 23:23                 ` Damien Le Moal
  2022-11-14  7:53                   ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Damien Le Moal @ 2022-11-13 23:23 UTC (permalink / raw)
  To: Ondrej Zary, Christoph Hellwig
  Cc: Sergey Shtylyov, Jens Axboe, Tim Waugh, linux-block,
	linux-parport, linux-ide, linux-kernel

On 11/12/22 20:17, Ondrej Zary wrote:
> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>> It's been a while - did you get a chance to make some progress on
>> this?  Do you need any help to unblock you?
>>
> 
> Sorry again, I'm back now. Trying to fix locking problems.
> Added this to each function for analysis how the functions are called wrt.
> locking:
> 
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));

Do you have your code somewhere that we can look at ?

> 
> result (tail -n 4064 /var/log/messages | cut -d\] -f 2- | cut -d\  -f 2- | uniq -c)
> 
>       1 pata_parport: protocol epat registered
>       1 (null): epat 1.02, Shuttle EPAT chip c6 at 0x378,
>       1 mode 5 (EPP-32), delay 1
>       1 pata_parport_check_status, locked=1
>       1 scsi host4: pata_parport
>       1 ata5: PATA max PIO0 port parport0 protocol epat
>      85 pata_parport_check_status, locked=0
>       1 ata5: link is slow to respond, please be patient (ready=0)
>      83 pata_parport_check_status, locked=0
>       1 ata5: device not ready (errno=-16), forcing hardreset
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_softreset, locked=0
>       1 pata_parport_devchk, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_devchk, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_bus_softreset, locked=0
>       1 pata_parport_check_status, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_tf_read, locked=0
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>       1 pata_parport_tf_read, locked=0
>    1001 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=0
>       1 pata_parport_check_altstatus, locked=0
>    1000 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 ata5.00: ATAPI: TOSHIBA CD-ROM XM-6202B, 1108, max MWDMA2
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       7 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 scsi 4:0:0:0: CD-ROM            TOSHIBA  CD-ROM XM-6202B  1108 PQ: 0 ANSI: 5
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_drain_fifo, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 scsi 4:0:0:0: Attached scsi generic sg1 type 5
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 sr 4:0:0:0: [sr0] scsi3-mmc drive: 32x/32x cd/rw xa/form2 cdda tray
>       1 cdrom: Uniform CD-ROM driver Revision: 3.20
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_drain_fifo, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_drain_fifo, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_drain_fifo, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       7 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       8 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_drain_fifo, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       8 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       4 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       5 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       3 pata_parport_check_status, locked=1
>       1 pata_parport_dev_select, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_tf_load, locked=1
>       2 pata_parport_check_status, locked=1
>       1 pata_parport_exec_command, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       1 pata_parport_check_status, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       6 pata_parport_check_status, locked=1
>       1 pata_parport_tf_read, locked=1
>       1 pata_parport_data_xfer, locked=1
>       1 pata_parport_check_altstatus, locked=1
>       2 pata_parport_check_status, locked=1
>       1 ata5.00: disable device
>       1 pata_parport_check_status, locked=1
> 
> 

-- 
Damien Le Moal
Western Digital Research


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-13 23:23                 ` Damien Le Moal
@ 2022-11-14  7:53                   ` Ondrej Zary
  2022-11-14  8:03                     ` Damien Le Moal
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-11-14  7:53 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Monday 14 November 2022, Damien Le Moal wrote:
> On 11/12/22 20:17, Ondrej Zary wrote:
> > On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> >> It's been a while - did you get a chance to make some progress on
> >> this?  Do you need any help to unblock you?
> >>
> > 
> > Sorry again, I'm back now. Trying to fix locking problems.
> > Added this to each function for analysis how the functions are called wrt.
> > locking:
> > 
> > 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 
> Do you have your code somewhere that we can look at ?

This is the current version with debug printks. I've also added dump_stack()
to find out the code path but haven't analyzed the output yet.

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2022 Ondrej Zary
 * based on paride.c by Grant R. Guenther <grant@torque.net>
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/parport.h>
#include <linux/pata_parport.h>

#define DRV_NAME "pata_parport"

static DEFINE_IDR(parport_list);
static DEFINE_IDR(protocols);
static DEFINE_IDA(pata_parport_bus_dev_ids);
static DEFINE_MUTEX(pi_mutex);

static bool probe = true;
module_param(probe, bool, 0644);
MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");

static bool verbose;
module_param(verbose, bool, 0644);
MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");

#define DISCONNECT_TIMEOUT	(HZ / 10)

static void pi_connect(struct ata_port *ap)
{
	struct pi_adapter *pi = ap->host->private_data;
	if (spin_is_locked(ap->lock))
		dump_stack();

	del_timer_sync(&pi->timer);
	if (!pi->claimed) {
		pi->claimed = true;
		parport_claim_or_block(pi->pardev);
		pi->proto->connect(pi);
	}
}

static void pi_disconnect(struct pi_adapter *pi)
{
	if (pi->claimed) {
		pi->proto->disconnect(pi);
		parport_release(pi->pardev);
		pi->claimed = false;
	}
}

static void pi_disconnect_timer(struct timer_list *t)
{
	struct pi_adapter *pi = from_timer(pi, t, timer);

	pi_disconnect(pi);
}

/* functions taken from libata-sff.c and converted from direct port I/O */
static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
{
	struct pi_adapter *pi = ap->host->private_data;
	u8 tmp;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));

	if (device == 0)
		tmp = ATA_DEVICE_OBS;
	else
		tmp = ATA_DEVICE_OBS | ATA_DEV1;

	pi_connect(ap);
	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
	ata_sff_pause(ap);
}

static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
{
	struct pi_adapter *pi = ap->host->private_data;
	u8 nsect, lbal;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pata_parport_dev_select(ap, device);

	pi_connect(ap);
	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);

	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);

	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 055);
	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);

	nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
	lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);

	if ((nsect == 0x55) && (lbal == 0xaa))
		return true;	/* we found a device */

	return false;		/* nothing found */
}

static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
				      unsigned long deadline)
{
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	/* software reset.  causes dev0 to be selected */
	pi->proto->write_regr(pi, 1, 6, ap->ctl);
	udelay(20);
	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
	udelay(20);
	pi->proto->write_regr(pi, 1, 6, ap->ctl);
	ap->last_ctl = ap->ctl;
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);

	/* wait the port to become ready */
	return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
}

static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
				  unsigned long deadline)
{
	struct ata_port *ap = link->ap;
	unsigned int devmask = 0;
	int rc;
	u8 err;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	/* determine if device 0/1 are present */
	if (pata_parport_devchk(ap, 0))
		devmask |= (1 << 0);
	if (pata_parport_devchk(ap, 1))
		devmask |= (1 << 1);

	/* select device 0 again */
	pata_parport_dev_select(ap, 0);

	/* issue bus reset */
	rc = pata_parport_bus_softreset(ap, devmask, deadline);
	if (rc && rc != -ENODEV) {
		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
		return rc;
	}

	/* determine by signature whether we have ATA or ATAPI devices */
	classes[0] = ata_sff_dev_classify(&link->device[0],
					  devmask & (1 << 0), &err);
	if (err != 0x81)
		classes[1] = ata_sff_dev_classify(&link->device[1],
						  devmask & (1 << 1), &err);

	return 0;
}

static u8 pata_parport_check_status(struct ata_port *ap)
{
	u8 status;
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);

	return status;
}

static u8 pata_parport_check_altstatus(struct ata_port *ap)
{
	u8 altstatus;
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	altstatus = pi->proto->read_regr(pi, 1, 6);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);

	return altstatus;
}

static void pata_parport_tf_load(struct ata_port *ap,
				 const struct ata_taskfile *tf)
{
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	if (tf->ctl != ap->last_ctl) {
		pi->proto->write_regr(pi, 1, 6, tf->ctl);
		ap->last_ctl = tf->ctl;
		ata_wait_idle(ap);
	}

	if (tf->flags & ATA_TFLAG_ISADDR) {
		if (tf->flags & ATA_TFLAG_LBA48) {
			pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
					      tf->hob_feature);
			pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
					      tf->hob_nsect);
			pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
					      tf->hob_lbal);
			pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
					      tf->hob_lbam);
			pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
					      tf->hob_lbah);
		}
		pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
		pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
		pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
		pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
		pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
	}

	if (tf->flags & ATA_TFLAG_DEVICE)
		pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);

	ata_wait_idle(ap);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
}

static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
{
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
	tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
	tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
	tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
	tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
	tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
	tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);

	if (tf->flags & ATA_TFLAG_LBA48) {
		pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
		tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
		tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
		tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
		tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
		tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
		pi->proto->write_regr(pi, 1, 6, tf->ctl);
		ap->last_ctl = tf->ctl;
	}
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
}

static void pata_parport_exec_command(struct ata_port *ap,
				      const struct ata_taskfile *tf)
{
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
	ata_sff_pause(ap);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
}

static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
				unsigned char *buf, unsigned int buflen, int rw)
{
	struct ata_port *ap = qc->dev->link->ap;
	struct pi_adapter *pi = ap->host->private_data;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi_connect(ap);
	if (rw == READ)
		pi->proto->read_block(pi, buf, buflen);
	else
		pi->proto->write_block(pi, buf, buflen);
	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);

	return buflen;
}

static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
{
	int count;
	struct ata_port *ap;
	struct pi_adapter *pi;
	char junk[2];

	/* We only need to flush incoming data when a command was running */
	if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
		return;

	ap = qc->ap;
	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
	pi = ap->host->private_data;
	/* Drain up to 64K of data before we give up this recovery method */
	for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
						&& count < 65536; count += 2) {
		pi_connect(ap);
		pi->proto->read_block(pi, junk, 2);
		mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
	}

	if (count)
		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
}

static struct ata_port_operations pata_parport_port_ops = {
	.inherits		= &ata_sff_port_ops,

	.softreset		= pata_parport_softreset,
	.hardreset		= NULL,

	.sff_dev_select		= pata_parport_dev_select,
	.sff_check_status	= pata_parport_check_status,
	.sff_check_altstatus	= pata_parport_check_altstatus,
	.sff_tf_load		= pata_parport_tf_load,
	.sff_tf_read		= pata_parport_tf_read,
	.sff_exec_command	= pata_parport_exec_command,
	.sff_data_xfer		= pata_parport_data_xfer,
	.sff_drain_fifo		= pata_parport_drain_fifo,
};

static const struct ata_port_info pata_parport_port_info = {
	.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
	.pio_mask	= ATA_PIO0,
	/* No DMA */
	.port_ops	= &pata_parport_port_ops,
};

static void pi_release(struct pi_adapter *pi)
{
	parport_unregister_device(pi->pardev);
	if (pi->proto->release_proto)
		pi->proto->release_proto(pi);
	module_put(pi->proto->owner);
}

static int default_test_proto(struct pi_adapter *pi, char *scratch)
{
	int j, k;
	int e[2] = { 0, 0 };

	pi->proto->connect(pi);

	for (j = 0; j < 2; j++) {
		pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
		for (k = 0; k < 256; k++) {
			pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
			pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
			if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
				e[j]++;
		}
	}
	pi->proto->disconnect(pi);

	if (verbose)
		dev_info(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
		       pi->proto->name, pi->port,
		       pi->mode, e[0], e[1]);

	return e[0] && e[1];	/* not here if both > 0 */
}

static int pi_test_proto(struct pi_adapter *pi, char *scratch)
{
	int res;

	parport_claim_or_block(pi->pardev);
	if (pi->proto->test_proto)
		res = pi->proto->test_proto(pi, scratch, verbose);
	else
		res = default_test_proto(pi, scratch);
	parport_release(pi->pardev);

	return res;
}

static bool pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
{
	int best, range;

	if (pi->mode != -1) {
		if (pi->mode >= max)
			return false;
		range = 3;
		if (pi->mode >= pi->proto->epp_first)
			range = 8;
		if (range == 8 && pi->port % 8)
			return false;
		return !pi_test_proto(pi, scratch);
	}
	best = -1;
	for (pi->mode = 0; pi->mode < max; pi->mode++) {
		range = 3;
		if (pi->mode >= pi->proto->epp_first)
			range = 8;
		if (range == 8 && pi->port % 8)
			break;
		if (!pi_test_proto(pi, scratch))
			best = pi->mode;
	}
	pi->mode = best;
	return best > -1;
}

static bool pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
{
	int max, s, e;

	s = unit;
	e = s + 1;

	if (s == -1) {
		s = 0;
		e = pi->proto->max_units;
	}

	if (pi->proto->test_port) {
		parport_claim_or_block(pi->pardev);
		max = pi->proto->test_port(pi);
		parport_release(pi->pardev);
	} else {
		max = pi->proto->max_mode;
	}

	if (pi->proto->probe_unit) {
		parport_claim_or_block(pi->pardev);
		for (pi->unit = s; pi->unit < e; pi->unit++) {
			if (pi->proto->probe_unit(pi)) {
				parport_release(pi->pardev);
				return pi_probe_mode(pi, max, scratch);
			}
		}
		parport_release(pi->pardev);
		return false;
	}

	return pi_probe_mode(pi, max, scratch);
}

static void pata_parport_dev_release(struct device *dev)
{
	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);

	kfree(pi);
}

static void pata_parport_bus_release(struct device *dev)
{
	/* nothing to do here but required to avoid warning on device removal */
}

static struct bus_type pata_parport_bus_type = {
	.name = DRV_NAME,
};

static struct device pata_parport_bus = {
	.init_name = DRV_NAME,
	.release = pata_parport_bus_release,
};

/* temporary for old paride protocol modules */
static struct scsi_host_template pata_parport_sht = {
	PATA_PARPORT_SHT("pata_parport")
};

struct pi_device_match {
	struct parport *parport;
	struct pi_protocol *proto;
};

static int pi_find_dev(struct device *dev, void *data)
{
	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
	struct pi_device_match *match = data;

	return pi->pardev->port == match->parport && pi->proto == match->proto;
}

static struct pi_adapter *pi_init_one(struct parport *parport,
			struct pi_protocol *pr, int mode, int unit, int delay)
{
	struct pardev_cb par_cb = { };
	char scratch[512];
	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
	struct ata_host *host;
	struct pi_adapter *pi;
	struct pi_device_match match = { .parport = parport, .proto = pr };

	/*
	 * Abort if there's a device already registered on the same parport
	 * using the same protocol.
	 */
	if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
		return NULL;

	pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
	if (!pi)
		return NULL;

	/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
	pi->dev.parent = &pata_parport_bus;
	pi->dev.bus = &pata_parport_bus_type;
	pi->dev.driver = &pr->driver;
	pi->dev.release = pata_parport_dev_release;
	pi->dev.id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
	if (pi->dev.id < 0)
		return NULL; /* pata_parport_dev_release will do kfree(pi) */
	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
	if (device_register(&pi->dev)) {
		put_device(&pi->dev);
		goto out_ida_free;
	}

	pi->proto = pr;

	if (!try_module_get(pi->proto->owner))
		goto out_unreg_dev;
	if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
		goto out_module_put;

	pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
	pi->mode = mode;
	pi->port = parport->base;

	par_cb.private = pi;
	pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb,
						pi->dev.id);
	if (!pi->pardev)
		goto out_module_put;

	if (!pi_probe_unit(pi, unit, scratch)) {
		dev_info(&pi->dev, "Adapter not found\n");
		goto out_unreg_parport;
	}

	pi->proto->log_adapter(pi, scratch, verbose);

	host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
	if (!host)
		goto out_unreg_parport;
	dev_set_drvdata(&pi->dev, host);
	host->private_data = pi;

	ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
	ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);

	timer_setup(&pi->timer, pi_disconnect_timer, 0);

	if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
		goto out_unreg_parport;

	return pi;

out_unreg_parport:
	parport_unregister_device(pi->pardev);
	if (pi->proto->release_proto)
		pi->proto->release_proto(pi);
out_module_put:
	module_put(pi->proto->owner);
out_unreg_dev:
	device_unregister(&pi->dev);
out_ida_free:
	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
	return NULL;
}

int pata_parport_register_driver(struct pi_protocol *pr)
{
	int error;
	struct parport *parport;
	int port_num;

	pr->driver.bus = &pata_parport_bus_type;
	pr->driver.name = pr->name;
	error = driver_register(&pr->driver);
	if (error)
		return error;

	mutex_lock(&pi_mutex);
	error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
	if (error < 0) {
		driver_unregister(&pr->driver);
		mutex_unlock(&pi_mutex);
		return error;
	}

	pr_info("pata_parport: protocol %s registered\n", pr->name);

	if (probe) {
		/* probe all parports using this protocol */
		idr_for_each_entry(&parport_list, parport, port_num)
			pi_init_one(parport, pr, -1, 0, -1);
	}
	mutex_unlock(&pi_mutex);

	return 0;
}
EXPORT_SYMBOL(pata_parport_register_driver);

void pata_parport_unregister_driver(struct pi_protocol *pr)
{
	struct pi_protocol *pr_iter;
	int id = -1;

	mutex_lock(&pi_mutex);
	idr_for_each_entry(&protocols, pr_iter, id) {
		if (pr_iter == pr)
			break;
	}
	idr_remove(&protocols, id);
	mutex_unlock(&pi_mutex);
	driver_unregister(&pr->driver);
}
EXPORT_SYMBOL(pata_parport_unregister_driver);

static ssize_t new_device_store(struct bus_type *bus, const char *buf,
				size_t count)
{
	char port[12] = "auto";
	char protocol[8] = "auto";
	int mode = -1, unit = -1, delay = -1;
	struct pi_protocol *pr, *pr_wanted;
	struct device_driver *drv;
	struct parport *parport;
	int port_num, port_wanted, pr_num;
	bool ok = false;

	if (sscanf(buf, "%11s %7s %d %d %d",
			port, protocol, &mode, &unit, &delay) < 1)
		return -EINVAL;

	if (sscanf(port, "parport%u", &port_wanted) < 1) {
		if (!strcmp(port, "auto")) {
			port_wanted = -1;
		} else {
			pr_err("invalid port name %s\n", port);
			return -EINVAL;
		}
	}

	drv = driver_find(protocol, &pata_parport_bus_type);
	if (!drv) {
		if (!strcmp(protocol, "auto")) {
			pr_wanted = NULL;
		} else {
			pr_err("protocol %s not found\n", protocol);
			return -EINVAL;
		}
	} else {
		pr_wanted = container_of(drv, struct pi_protocol, driver);
	}

	mutex_lock(&pi_mutex);
	/* walk all parports */
	idr_for_each_entry(&parport_list, parport, port_num) {
		if (port_num == port_wanted || port_wanted == -1) {
			parport = parport_find_number(port_num);
			if (!parport) {
				pr_err("no such port %s\n", port);
				mutex_unlock(&pi_mutex);
				return -ENODEV;
			}
			/* walk all protocols */
			idr_for_each_entry(&protocols, pr, pr_num) {
				if (pr == pr_wanted || !pr_wanted)
					if (pi_init_one(parport, pr, mode, unit,
							delay))
						ok = true;
			}
			parport_put_port(parport);
		}
	}
	mutex_unlock(&pi_mutex);
	if (!ok)
		return -ENODEV;

	return count;
}
static BUS_ATTR_WO(new_device);

static void pi_remove_one(struct device *dev)
{
	struct ata_host *host = dev_get_drvdata(dev);
	struct pi_adapter *pi = host->private_data;

	ata_host_detach(host);
	del_timer_sync(&pi->timer);
	pi_disconnect(pi);
	pi_release(pi);
	device_unregister(dev);
	ida_free(&pata_parport_bus_dev_ids, dev->id);
	/* pata_parport_dev_release will do kfree(pi) */
}

static ssize_t delete_device_store(struct bus_type *bus, const char *buf,
				   size_t count)
{
	struct device *dev;
	char device_name[32];

	if (sscanf(buf, "%31s", device_name) < 1)
		return -EINVAL;

	mutex_lock(&pi_mutex);
	dev = bus_find_device_by_name(bus, NULL, device_name);
	if (!dev) {
		mutex_unlock(&pi_mutex);
		return -ENODEV;
	}

	pi_remove_one(dev);
	mutex_unlock(&pi_mutex);

	return count;
}
static BUS_ATTR_WO(delete_device);

static void pata_parport_attach(struct parport *port)
{
	struct pi_protocol *pr;
	int pr_num, id;

	mutex_lock(&pi_mutex);
	id = idr_alloc(&parport_list, port, port->number, port->number,
		       GFP_KERNEL);
	if (id < 0) {
		mutex_unlock(&pi_mutex);
		return;
	}

	if (probe) {
		/* probe this port using all protocols */
		idr_for_each_entry(&protocols, pr, pr_num)
			pi_init_one(port, pr, -1, 0, -1);
	}
	mutex_unlock(&pi_mutex);
}

static int pi_remove_port(struct device *dev, void *p)
{
	struct ata_host *host = dev_get_drvdata(dev);
	struct pi_adapter *pi = host->private_data;

	if (pi->pardev->port == p)
		pi_remove_one(dev);

	return 0;
}

static void pata_parport_detach(struct parport *port)
{
	mutex_lock(&pi_mutex);
	bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
	idr_remove(&parport_list, port->number);
	mutex_unlock(&pi_mutex);
}

static struct parport_driver pata_parport_driver = {
	.name = DRV_NAME,
	.match_port = pata_parport_attach,
	.detach = pata_parport_detach,
	.devmodel = true,
};

static __init int pata_parport_init(void)
{
	int error;

	error = bus_register(&pata_parport_bus_type);
	if (error) {
		pr_err("failed to register pata_parport bus, error: %d\n", error);
		return error;
	}

	error = device_register(&pata_parport_bus);
	if (error) {
		pr_err("failed to register pata_parport bus, error: %d\n", error);
		goto out_unregister_bus;
	}

	error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
	if (error) {
		pr_err("unable to create sysfs file, error: %d\n", error);
		goto out_unregister_dev;
	}

	error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
	if (error) {
		pr_err("unable to create sysfs file, error: %d\n", error);
		goto out_remove_new;
	}

	error = parport_register_driver(&pata_parport_driver);
	if (error) {
		pr_err("unable to register parport driver, error: %d\n", error);
		goto out_remove_del;
	}

	return 0;

out_remove_del:
	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
out_remove_new:
	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
out_unregister_dev:
	device_unregister(&pata_parport_bus);
out_unregister_bus:
	bus_unregister(&pata_parport_bus_type);
	return error;
}

static __exit void pata_parport_exit(void)
{
	parport_unregister_driver(&pata_parport_driver);
	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
	device_unregister(&pata_parport_bus);
	bus_unregister(&pata_parport_bus_type);
}

MODULE_AUTHOR("Ondrej Zary");
MODULE_DESCRIPTION("driver for parallel port ATA adapters");
MODULE_LICENSE("GPL");
MODULE_ALIAS("paride");

module_init(pata_parport_init);
module_exit(pata_parport_exit);



-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-14  7:53                   ` Ondrej Zary
@ 2022-11-14  8:03                     ` Damien Le Moal
  2022-11-14 19:25                       ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Damien Le Moal @ 2022-11-14  8:03 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On 11/14/22 16:53, Ondrej Zary wrote:
> On Monday 14 November 2022, Damien Le Moal wrote:
>> On 11/12/22 20:17, Ondrej Zary wrote:
>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>>>> It's been a while - did you get a chance to make some progress on
>>>> this?  Do you need any help to unblock you?
>>>>
>>>
>>> Sorry again, I'm back now. Trying to fix locking problems.
>>> Added this to each function for analysis how the functions are called wrt.
>>> locking:
>>>
>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
>>
>> Do you have your code somewhere that we can look at ?
> 
> This is the current version with debug printks. I've also added dump_stack()
> to find out the code path but haven't analyzed the output yet.

Can you send a proper patch ? Or a link to a git tree ? That is easier to
handle than pasted code in an email...

> 
> // SPDX-License-Identifier: GPL-2.0-only
> /*
>  * Copyright 2022 Ondrej Zary
>  * based on paride.c by Grant R. Guenther <grant@torque.net>
>  */
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/parport.h>
> #include <linux/pata_parport.h>
> 
> #define DRV_NAME "pata_parport"
> 
> static DEFINE_IDR(parport_list);
> static DEFINE_IDR(protocols);
> static DEFINE_IDA(pata_parport_bus_dev_ids);
> static DEFINE_MUTEX(pi_mutex);
> 
> static bool probe = true;
> module_param(probe, bool, 0644);
> MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
> 
> static bool verbose;
> module_param(verbose, bool, 0644);
> MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
> 
> #define DISCONNECT_TIMEOUT	(HZ / 10)
> 
> static void pi_connect(struct ata_port *ap)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	if (spin_is_locked(ap->lock))
> 		dump_stack();
> 
> 	del_timer_sync(&pi->timer);
> 	if (!pi->claimed) {
> 		pi->claimed = true;
> 		parport_claim_or_block(pi->pardev);
> 		pi->proto->connect(pi);
> 	}
> }
> 
> static void pi_disconnect(struct pi_adapter *pi)
> {
> 	if (pi->claimed) {
> 		pi->proto->disconnect(pi);
> 		parport_release(pi->pardev);
> 		pi->claimed = false;
> 	}
> }
> 
> static void pi_disconnect_timer(struct timer_list *t)
> {
> 	struct pi_adapter *pi = from_timer(pi, t, timer);
> 
> 	pi_disconnect(pi);
> }
> 
> /* functions taken from libata-sff.c and converted from direct port I/O */
> static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	u8 tmp;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 
> 	if (device == 0)
> 		tmp = ATA_DEVICE_OBS;
> 	else
> 		tmp = ATA_DEVICE_OBS | ATA_DEV1;
> 
> 	pi_connect(ap);
> 	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 	ata_sff_pause(ap);
> }
> 
> static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	u8 nsect, lbal;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pata_parport_dev_select(ap, device);
> 
> 	pi_connect(ap);
> 	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
> 	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
> 
> 	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
> 	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);
> 
> 	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 055);
> 	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
> 
> 	nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
> 	lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 
> 	if ((nsect == 0x55) && (lbal == 0xaa))
> 		return true;	/* we found a device */
> 
> 	return false;		/* nothing found */
> }
> 
> static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
> 				      unsigned long deadline)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	/* software reset.  causes dev0 to be selected */
> 	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> 	udelay(20);
> 	pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
> 	udelay(20);
> 	pi->proto->write_regr(pi, 1, 6, ap->ctl);
> 	ap->last_ctl = ap->ctl;
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 
> 	/* wait the port to become ready */
> 	return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
> }
> 
> static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
> 				  unsigned long deadline)
> {
> 	struct ata_port *ap = link->ap;
> 	unsigned int devmask = 0;
> 	int rc;
> 	u8 err;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	/* determine if device 0/1 are present */
> 	if (pata_parport_devchk(ap, 0))
> 		devmask |= (1 << 0);
> 	if (pata_parport_devchk(ap, 1))
> 		devmask |= (1 << 1);
> 
> 	/* select device 0 again */
> 	pata_parport_dev_select(ap, 0);
> 
> 	/* issue bus reset */
> 	rc = pata_parport_bus_softreset(ap, devmask, deadline);
> 	if (rc && rc != -ENODEV) {
> 		ata_link_err(link, "SRST failed (errno=%d)\n", rc);
> 		return rc;
> 	}
> 
> 	/* determine by signature whether we have ATA or ATAPI devices */
> 	classes[0] = ata_sff_dev_classify(&link->device[0],
> 					  devmask & (1 << 0), &err);
> 	if (err != 0x81)
> 		classes[1] = ata_sff_dev_classify(&link->device[1],
> 						  devmask & (1 << 1), &err);
> 
> 	return 0;
> }
> 
> static u8 pata_parport_check_status(struct ata_port *ap)
> {
> 	u8 status;
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 
> 	return status;
> }
> 
> static u8 pata_parport_check_altstatus(struct ata_port *ap)
> {
> 	u8 altstatus;
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	altstatus = pi->proto->read_regr(pi, 1, 6);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 
> 	return altstatus;
> }
> 
> static void pata_parport_tf_load(struct ata_port *ap,
> 				 const struct ata_taskfile *tf)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	if (tf->ctl != ap->last_ctl) {
> 		pi->proto->write_regr(pi, 1, 6, tf->ctl);
> 		ap->last_ctl = tf->ctl;
> 		ata_wait_idle(ap);
> 	}
> 
> 	if (tf->flags & ATA_TFLAG_ISADDR) {
> 		if (tf->flags & ATA_TFLAG_LBA48) {
> 			pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
> 					      tf->hob_feature);
> 			pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
> 					      tf->hob_nsect);
> 			pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
> 					      tf->hob_lbal);
> 			pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
> 					      tf->hob_lbam);
> 			pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
> 					      tf->hob_lbah);
> 		}
> 		pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
> 		pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
> 		pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
> 		pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
> 		pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
> 	}
> 
> 	if (tf->flags & ATA_TFLAG_DEVICE)
> 		pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);
> 
> 	ata_wait_idle(ap);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> }
> 
> static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
> 	tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
> 	tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
> 	tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
> 	tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
> 	tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
> 	tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);
> 
> 	if (tf->flags & ATA_TFLAG_LBA48) {
> 		pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
> 		tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
> 		tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
> 		tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
> 		tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
> 		tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
> 		pi->proto->write_regr(pi, 1, 6, tf->ctl);
> 		ap->last_ctl = tf->ctl;
> 	}
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> }
> 
> static void pata_parport_exec_command(struct ata_port *ap,
> 				      const struct ata_taskfile *tf)
> {
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
> 	ata_sff_pause(ap);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> }
> 
> static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
> 				unsigned char *buf, unsigned int buflen, int rw)
> {
> 	struct ata_port *ap = qc->dev->link->ap;
> 	struct pi_adapter *pi = ap->host->private_data;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi_connect(ap);
> 	if (rw == READ)
> 		pi->proto->read_block(pi, buf, buflen);
> 	else
> 		pi->proto->write_block(pi, buf, buflen);
> 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 
> 	return buflen;
> }
> 
> static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
> {
> 	int count;
> 	struct ata_port *ap;
> 	struct pi_adapter *pi;
> 	char junk[2];
> 
> 	/* We only need to flush incoming data when a command was running */
> 	if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
> 		return;
> 
> 	ap = qc->ap;
> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> 	pi = ap->host->private_data;
> 	/* Drain up to 64K of data before we give up this recovery method */
> 	for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
> 						&& count < 65536; count += 2) {
> 		pi_connect(ap);
> 		pi->proto->read_block(pi, junk, 2);
> 		mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
> 	}
> 
> 	if (count)
> 		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
> }
> 
> static struct ata_port_operations pata_parport_port_ops = {
> 	.inherits		= &ata_sff_port_ops,
> 
> 	.softreset		= pata_parport_softreset,
> 	.hardreset		= NULL,
> 
> 	.sff_dev_select		= pata_parport_dev_select,
> 	.sff_check_status	= pata_parport_check_status,
> 	.sff_check_altstatus	= pata_parport_check_altstatus,
> 	.sff_tf_load		= pata_parport_tf_load,
> 	.sff_tf_read		= pata_parport_tf_read,
> 	.sff_exec_command	= pata_parport_exec_command,
> 	.sff_data_xfer		= pata_parport_data_xfer,
> 	.sff_drain_fifo		= pata_parport_drain_fifo,
> };
> 
> static const struct ata_port_info pata_parport_port_info = {
> 	.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
> 	.pio_mask	= ATA_PIO0,
> 	/* No DMA */
> 	.port_ops	= &pata_parport_port_ops,
> };
> 
> static void pi_release(struct pi_adapter *pi)
> {
> 	parport_unregister_device(pi->pardev);
> 	if (pi->proto->release_proto)
> 		pi->proto->release_proto(pi);
> 	module_put(pi->proto->owner);
> }
> 
> static int default_test_proto(struct pi_adapter *pi, char *scratch)
> {
> 	int j, k;
> 	int e[2] = { 0, 0 };
> 
> 	pi->proto->connect(pi);
> 
> 	for (j = 0; j < 2; j++) {
> 		pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
> 		for (k = 0; k < 256; k++) {
> 			pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
> 			pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
> 			if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
> 				e[j]++;
> 		}
> 	}
> 	pi->proto->disconnect(pi);
> 
> 	if (verbose)
> 		dev_info(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
> 		       pi->proto->name, pi->port,
> 		       pi->mode, e[0], e[1]);
> 
> 	return e[0] && e[1];	/* not here if both > 0 */
> }
> 
> static int pi_test_proto(struct pi_adapter *pi, char *scratch)
> {
> 	int res;
> 
> 	parport_claim_or_block(pi->pardev);
> 	if (pi->proto->test_proto)
> 		res = pi->proto->test_proto(pi, scratch, verbose);
> 	else
> 		res = default_test_proto(pi, scratch);
> 	parport_release(pi->pardev);
> 
> 	return res;
> }
> 
> static bool pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
> {
> 	int best, range;
> 
> 	if (pi->mode != -1) {
> 		if (pi->mode >= max)
> 			return false;
> 		range = 3;
> 		if (pi->mode >= pi->proto->epp_first)
> 			range = 8;
> 		if (range == 8 && pi->port % 8)
> 			return false;
> 		return !pi_test_proto(pi, scratch);
> 	}
> 	best = -1;
> 	for (pi->mode = 0; pi->mode < max; pi->mode++) {
> 		range = 3;
> 		if (pi->mode >= pi->proto->epp_first)
> 			range = 8;
> 		if (range == 8 && pi->port % 8)
> 			break;
> 		if (!pi_test_proto(pi, scratch))
> 			best = pi->mode;
> 	}
> 	pi->mode = best;
> 	return best > -1;
> }
> 
> static bool pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
> {
> 	int max, s, e;
> 
> 	s = unit;
> 	e = s + 1;
> 
> 	if (s == -1) {
> 		s = 0;
> 		e = pi->proto->max_units;
> 	}
> 
> 	if (pi->proto->test_port) {
> 		parport_claim_or_block(pi->pardev);
> 		max = pi->proto->test_port(pi);
> 		parport_release(pi->pardev);
> 	} else {
> 		max = pi->proto->max_mode;
> 	}
> 
> 	if (pi->proto->probe_unit) {
> 		parport_claim_or_block(pi->pardev);
> 		for (pi->unit = s; pi->unit < e; pi->unit++) {
> 			if (pi->proto->probe_unit(pi)) {
> 				parport_release(pi->pardev);
> 				return pi_probe_mode(pi, max, scratch);
> 			}
> 		}
> 		parport_release(pi->pardev);
> 		return false;
> 	}
> 
> 	return pi_probe_mode(pi, max, scratch);
> }
> 
> static void pata_parport_dev_release(struct device *dev)
> {
> 	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
> 
> 	kfree(pi);
> }
> 
> static void pata_parport_bus_release(struct device *dev)
> {
> 	/* nothing to do here but required to avoid warning on device removal */
> }
> 
> static struct bus_type pata_parport_bus_type = {
> 	.name = DRV_NAME,
> };
> 
> static struct device pata_parport_bus = {
> 	.init_name = DRV_NAME,
> 	.release = pata_parport_bus_release,
> };
> 
> /* temporary for old paride protocol modules */
> static struct scsi_host_template pata_parport_sht = {
> 	PATA_PARPORT_SHT("pata_parport")
> };
> 
> struct pi_device_match {
> 	struct parport *parport;
> 	struct pi_protocol *proto;
> };
> 
> static int pi_find_dev(struct device *dev, void *data)
> {
> 	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
> 	struct pi_device_match *match = data;
> 
> 	return pi->pardev->port == match->parport && pi->proto == match->proto;
> }
> 
> static struct pi_adapter *pi_init_one(struct parport *parport,
> 			struct pi_protocol *pr, int mode, int unit, int delay)
> {
> 	struct pardev_cb par_cb = { };
> 	char scratch[512];
> 	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
> 	struct ata_host *host;
> 	struct pi_adapter *pi;
> 	struct pi_device_match match = { .parport = parport, .proto = pr };
> 
> 	/*
> 	 * Abort if there's a device already registered on the same parport
> 	 * using the same protocol.
> 	 */
> 	if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
> 		return NULL;
> 
> 	pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
> 	if (!pi)
> 		return NULL;
> 
> 	/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
> 	pi->dev.parent = &pata_parport_bus;
> 	pi->dev.bus = &pata_parport_bus_type;
> 	pi->dev.driver = &pr->driver;
> 	pi->dev.release = pata_parport_dev_release;
> 	pi->dev.id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
> 	if (pi->dev.id < 0)
> 		return NULL; /* pata_parport_dev_release will do kfree(pi) */
> 	dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
> 	if (device_register(&pi->dev)) {
> 		put_device(&pi->dev);
> 		goto out_ida_free;
> 	}
> 
> 	pi->proto = pr;
> 
> 	if (!try_module_get(pi->proto->owner))
> 		goto out_unreg_dev;
> 	if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
> 		goto out_module_put;
> 
> 	pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
> 	pi->mode = mode;
> 	pi->port = parport->base;
> 
> 	par_cb.private = pi;
> 	pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb,
> 						pi->dev.id);
> 	if (!pi->pardev)
> 		goto out_module_put;
> 
> 	if (!pi_probe_unit(pi, unit, scratch)) {
> 		dev_info(&pi->dev, "Adapter not found\n");
> 		goto out_unreg_parport;
> 	}
> 
> 	pi->proto->log_adapter(pi, scratch, verbose);
> 
> 	host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
> 	if (!host)
> 		goto out_unreg_parport;
> 	dev_set_drvdata(&pi->dev, host);
> 	host->private_data = pi;
> 
> 	ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
> 	ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
> 
> 	timer_setup(&pi->timer, pi_disconnect_timer, 0);
> 
> 	if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
> 		goto out_unreg_parport;
> 
> 	return pi;
> 
> out_unreg_parport:
> 	parport_unregister_device(pi->pardev);
> 	if (pi->proto->release_proto)
> 		pi->proto->release_proto(pi);
> out_module_put:
> 	module_put(pi->proto->owner);
> out_unreg_dev:
> 	device_unregister(&pi->dev);
> out_ida_free:
> 	ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
> 	return NULL;
> }
> 
> int pata_parport_register_driver(struct pi_protocol *pr)
> {
> 	int error;
> 	struct parport *parport;
> 	int port_num;
> 
> 	pr->driver.bus = &pata_parport_bus_type;
> 	pr->driver.name = pr->name;
> 	error = driver_register(&pr->driver);
> 	if (error)
> 		return error;
> 
> 	mutex_lock(&pi_mutex);
> 	error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
> 	if (error < 0) {
> 		driver_unregister(&pr->driver);
> 		mutex_unlock(&pi_mutex);
> 		return error;
> 	}
> 
> 	pr_info("pata_parport: protocol %s registered\n", pr->name);
> 
> 	if (probe) {
> 		/* probe all parports using this protocol */
> 		idr_for_each_entry(&parport_list, parport, port_num)
> 			pi_init_one(parport, pr, -1, 0, -1);
> 	}
> 	mutex_unlock(&pi_mutex);
> 
> 	return 0;
> }
> EXPORT_SYMBOL(pata_parport_register_driver);
> 
> void pata_parport_unregister_driver(struct pi_protocol *pr)
> {
> 	struct pi_protocol *pr_iter;
> 	int id = -1;
> 
> 	mutex_lock(&pi_mutex);
> 	idr_for_each_entry(&protocols, pr_iter, id) {
> 		if (pr_iter == pr)
> 			break;
> 	}
> 	idr_remove(&protocols, id);
> 	mutex_unlock(&pi_mutex);
> 	driver_unregister(&pr->driver);
> }
> EXPORT_SYMBOL(pata_parport_unregister_driver);
> 
> static ssize_t new_device_store(struct bus_type *bus, const char *buf,
> 				size_t count)
> {
> 	char port[12] = "auto";
> 	char protocol[8] = "auto";
> 	int mode = -1, unit = -1, delay = -1;
> 	struct pi_protocol *pr, *pr_wanted;
> 	struct device_driver *drv;
> 	struct parport *parport;
> 	int port_num, port_wanted, pr_num;
> 	bool ok = false;
> 
> 	if (sscanf(buf, "%11s %7s %d %d %d",
> 			port, protocol, &mode, &unit, &delay) < 1)
> 		return -EINVAL;
> 
> 	if (sscanf(port, "parport%u", &port_wanted) < 1) {
> 		if (!strcmp(port, "auto")) {
> 			port_wanted = -1;
> 		} else {
> 			pr_err("invalid port name %s\n", port);
> 			return -EINVAL;
> 		}
> 	}
> 
> 	drv = driver_find(protocol, &pata_parport_bus_type);
> 	if (!drv) {
> 		if (!strcmp(protocol, "auto")) {
> 			pr_wanted = NULL;
> 		} else {
> 			pr_err("protocol %s not found\n", protocol);
> 			return -EINVAL;
> 		}
> 	} else {
> 		pr_wanted = container_of(drv, struct pi_protocol, driver);
> 	}
> 
> 	mutex_lock(&pi_mutex);
> 	/* walk all parports */
> 	idr_for_each_entry(&parport_list, parport, port_num) {
> 		if (port_num == port_wanted || port_wanted == -1) {
> 			parport = parport_find_number(port_num);
> 			if (!parport) {
> 				pr_err("no such port %s\n", port);
> 				mutex_unlock(&pi_mutex);
> 				return -ENODEV;
> 			}
> 			/* walk all protocols */
> 			idr_for_each_entry(&protocols, pr, pr_num) {
> 				if (pr == pr_wanted || !pr_wanted)
> 					if (pi_init_one(parport, pr, mode, unit,
> 							delay))
> 						ok = true;
> 			}
> 			parport_put_port(parport);
> 		}
> 	}
> 	mutex_unlock(&pi_mutex);
> 	if (!ok)
> 		return -ENODEV;
> 
> 	return count;
> }
> static BUS_ATTR_WO(new_device);
> 
> static void pi_remove_one(struct device *dev)
> {
> 	struct ata_host *host = dev_get_drvdata(dev);
> 	struct pi_adapter *pi = host->private_data;
> 
> 	ata_host_detach(host);
> 	del_timer_sync(&pi->timer);
> 	pi_disconnect(pi);
> 	pi_release(pi);
> 	device_unregister(dev);
> 	ida_free(&pata_parport_bus_dev_ids, dev->id);
> 	/* pata_parport_dev_release will do kfree(pi) */
> }
> 
> static ssize_t delete_device_store(struct bus_type *bus, const char *buf,
> 				   size_t count)
> {
> 	struct device *dev;
> 	char device_name[32];
> 
> 	if (sscanf(buf, "%31s", device_name) < 1)
> 		return -EINVAL;
> 
> 	mutex_lock(&pi_mutex);
> 	dev = bus_find_device_by_name(bus, NULL, device_name);
> 	if (!dev) {
> 		mutex_unlock(&pi_mutex);
> 		return -ENODEV;
> 	}
> 
> 	pi_remove_one(dev);
> 	mutex_unlock(&pi_mutex);
> 
> 	return count;
> }
> static BUS_ATTR_WO(delete_device);
> 
> static void pata_parport_attach(struct parport *port)
> {
> 	struct pi_protocol *pr;
> 	int pr_num, id;
> 
> 	mutex_lock(&pi_mutex);
> 	id = idr_alloc(&parport_list, port, port->number, port->number,
> 		       GFP_KERNEL);
> 	if (id < 0) {
> 		mutex_unlock(&pi_mutex);
> 		return;
> 	}
> 
> 	if (probe) {
> 		/* probe this port using all protocols */
> 		idr_for_each_entry(&protocols, pr, pr_num)
> 			pi_init_one(port, pr, -1, 0, -1);
> 	}
> 	mutex_unlock(&pi_mutex);
> }
> 
> static int pi_remove_port(struct device *dev, void *p)
> {
> 	struct ata_host *host = dev_get_drvdata(dev);
> 	struct pi_adapter *pi = host->private_data;
> 
> 	if (pi->pardev->port == p)
> 		pi_remove_one(dev);
> 
> 	return 0;
> }
> 
> static void pata_parport_detach(struct parport *port)
> {
> 	mutex_lock(&pi_mutex);
> 	bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
> 	idr_remove(&parport_list, port->number);
> 	mutex_unlock(&pi_mutex);
> }
> 
> static struct parport_driver pata_parport_driver = {
> 	.name = DRV_NAME,
> 	.match_port = pata_parport_attach,
> 	.detach = pata_parport_detach,
> 	.devmodel = true,
> };
> 
> static __init int pata_parport_init(void)
> {
> 	int error;
> 
> 	error = bus_register(&pata_parport_bus_type);
> 	if (error) {
> 		pr_err("failed to register pata_parport bus, error: %d\n", error);
> 		return error;
> 	}
> 
> 	error = device_register(&pata_parport_bus);
> 	if (error) {
> 		pr_err("failed to register pata_parport bus, error: %d\n", error);
> 		goto out_unregister_bus;
> 	}
> 
> 	error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
> 	if (error) {
> 		pr_err("unable to create sysfs file, error: %d\n", error);
> 		goto out_unregister_dev;
> 	}
> 
> 	error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
> 	if (error) {
> 		pr_err("unable to create sysfs file, error: %d\n", error);
> 		goto out_remove_new;
> 	}
> 
> 	error = parport_register_driver(&pata_parport_driver);
> 	if (error) {
> 		pr_err("unable to register parport driver, error: %d\n", error);
> 		goto out_remove_del;
> 	}
> 
> 	return 0;
> 
> out_remove_del:
> 	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
> out_remove_new:
> 	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
> out_unregister_dev:
> 	device_unregister(&pata_parport_bus);
> out_unregister_bus:
> 	bus_unregister(&pata_parport_bus_type);
> 	return error;
> }
> 
> static __exit void pata_parport_exit(void)
> {
> 	parport_unregister_driver(&pata_parport_driver);
> 	bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
> 	bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
> 	device_unregister(&pata_parport_bus);
> 	bus_unregister(&pata_parport_bus_type);
> }
> 
> MODULE_AUTHOR("Ondrej Zary");
> MODULE_DESCRIPTION("driver for parallel port ATA adapters");
> MODULE_LICENSE("GPL");
> MODULE_ALIAS("paride");
> 
> module_init(pata_parport_init);
> module_exit(pata_parport_exit);
> 
> 
> 

-- 
Damien Le Moal
Western Digital Research


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-14  8:03                     ` Damien Le Moal
@ 2022-11-14 19:25                       ` Ondrej Zary
  2022-11-15  3:06                         ` Damien Le Moal
  0 siblings, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-11-14 19:25 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
> On 11/14/22 16:53, Ondrej Zary wrote:
> > On Monday 14 November 2022, Damien Le Moal wrote:
> >> On 11/12/22 20:17, Ondrej Zary wrote:
> >>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> >>>> It's been a while - did you get a chance to make some progress on
> >>>> this?  Do you need any help to unblock you?
> >>>>
> >>>
> >>> Sorry again, I'm back now. Trying to fix locking problems.
> >>> Added this to each function for analysis how the functions are called wrt.
> >>> locking:
> >>>
> >>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> >>
> >> Do you have your code somewhere that we can look at ?
> > 
> > This is the current version with debug printks. I've also added dump_stack()
> > to find out the code path but haven't analyzed the output yet.
> 
> Can you send a proper patch ? Or a link to a git tree ? That is easier to
> handle than pasted code in an email...

Patch against what? I don't have a git server.

I've done some call trace analysis. These code paths are calling
pata_parport functions with ap->lock locked during init.

Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus

Comm: modprobe
ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status

Comm: scsi_eh_4
ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-14 19:25                       ` Ondrej Zary
@ 2022-11-15  3:06                         ` Damien Le Moal
  2022-11-15  8:04                           ` Hannes Reinecke
  2022-11-15 14:56                           ` Ondrej Zary
  0 siblings, 2 replies; 31+ messages in thread
From: Damien Le Moal @ 2022-11-15  3:06 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On 11/15/22 04:25, Ondrej Zary wrote:
> On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
>> On 11/14/22 16:53, Ondrej Zary wrote:
>>> On Monday 14 November 2022, Damien Le Moal wrote:
>>>> On 11/12/22 20:17, Ondrej Zary wrote:
>>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>>>>>> It's been a while - did you get a chance to make some progress on
>>>>>> this?  Do you need any help to unblock you?
>>>>>>
>>>>>
>>>>> Sorry again, I'm back now. Trying to fix locking problems.
>>>>> Added this to each function for analysis how the functions are called wrt.
>>>>> locking:
>>>>>
>>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
>>>>
>>>> Do you have your code somewhere that we can look at ?
>>>
>>> This is the current version with debug printks. I've also added dump_stack()
>>> to find out the code path but haven't analyzed the output yet.
>>
>> Can you send a proper patch ? Or a link to a git tree ? That is easier to
>> handle than pasted code in an email...
> 
> Patch against what? I don't have a git server.

patch against current 6.1-rc, or against an older kernel should be OK too.
But please "git send-email" a patch, or push your dev tree to github ?

> I've done some call trace analysis. These code paths are calling
> pata_parport functions with ap->lock locked during init.
> 
> Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
> ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
> ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
> ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus
> 
> Comm: modprobe
> ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> 
> Comm: scsi_eh_4
> ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
> ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status

What exactly are the issues you are having with ap->lock ? It looks like
you have done a lot of analysis of the code, but without any context about
the problem, I do not understand what I am looking at.

-- 
Damien Le Moal
Western Digital Research


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-15  3:06                         ` Damien Le Moal
@ 2022-11-15  8:04                           ` Hannes Reinecke
  2022-11-15 14:56                           ` Ondrej Zary
  1 sibling, 0 replies; 31+ messages in thread
From: Hannes Reinecke @ 2022-11-15  8:04 UTC (permalink / raw)
  To: Damien Le Moal, Ondrej Zary
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1712 bytes --]

On 11/15/22 04:06, Damien Le Moal wrote:
> On 11/15/22 04:25, Ondrej Zary wrote:
>> On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
>>> On 11/14/22 16:53, Ondrej Zary wrote:
>>>> On Monday 14 November 2022, Damien Le Moal wrote:
>>>>> On 11/12/22 20:17, Ondrej Zary wrote:
>>>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>>>>>>> It's been a while - did you get a chance to make some progress on
>>>>>>> this?  Do you need any help to unblock you?
>>>>>>>
>>>>>>
>>>>>> Sorry again, I'm back now. Trying to fix locking problems.
>>>>>> Added this to each function for analysis how the functions are called wrt.
>>>>>> locking:
>>>>>>
>>>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
>>>>>
>>>>> Do you have your code somewhere that we can look at ?
>>>>
>>>> This is the current version with debug printks. I've also added dump_stack()
>>>> to find out the code path but haven't analyzed the output yet.
>>>
>>> Can you send a proper patch ? Or a link to a git tree ? That is easier to
>>> handle than pasted code in an email...
>>
>> Patch against what? I don't have a git server.
> 
> patch against current 6.1-rc, or against an older kernel should be OK too.
> But please "git send-email" a patch, or push your dev tree to github ?
> 
Please check the attachment; I've converted Ondrey mail to a patch 
relative to the original submission.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                Kernel Storage Architect
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Ivo Totev, Andrew
Myers, Andrew McDonald, Martje Boudien Moerman

[-- Attachment #2: 0001-pata_parport-debug-patch-for-locking-issues.patch --]
[-- Type: text/x-patch, Size: 8992 bytes --]

From 994330bafd68da875469718064ac622e42adcc97 Mon Sep 17 00:00:00 2001
From: Hannes Reinecke <hare@suse.de>
Date: Tue, 15 Nov 2022 08:57:51 +0100
Subject: [PATCH] pata_parport: debug patch for locking issues

Add logging messages to debug locking issues.

Signed-off-by: Ondrey Zary <linux@zary.sk>
---
 drivers/ata/pata_parport.c | 112 ++++++++++++++++++-------------------
 1 file changed, 56 insertions(+), 56 deletions(-)

diff --git a/drivers/ata/pata_parport.c b/drivers/ata/pata_parport.c
index 783764626a27..513567ce66a3 100644
--- a/drivers/ata/pata_parport.c
+++ b/drivers/ata/pata_parport.c
@@ -25,8 +25,12 @@ MODULE_PARM_DESC(verbose, "Enable verbose messages (0=off [default], 1=on)");
 
 #define DISCONNECT_TIMEOUT	(HZ / 10)
 
-static void pi_connect(struct pi_adapter *pi)
+static void pi_connect(struct ata_port *ap)
 {
+	struct pi_adapter *pi = ap->host->private_data;
+	if (spin_is_locked(ap->lock))
+		dump_stack();
+
 	del_timer_sync(&pi->timer);
 	if (!pi->claimed) {
 		pi->claimed = true;
@@ -35,10 +39,8 @@ static void pi_connect(struct pi_adapter *pi)
 	}
 }
 
-static void pi_disconnect_timer(struct timer_list *t)
+static void pi_disconnect(struct pi_adapter *pi)
 {
-	struct pi_adapter *pi = from_timer(pi, t, timer);
-
 	if (pi->claimed) {
 		pi->proto->disconnect(pi);
 		parport_release(pi->pardev);
@@ -46,18 +48,26 @@ static void pi_disconnect_timer(struct timer_list *t)
 	}
 }
 
+static void pi_disconnect_timer(struct timer_list *t)
+{
+	struct pi_adapter *pi = from_timer(pi, t, timer);
+
+	pi_disconnect(pi);
+}
+
 /* functions taken from libata-sff.c and converted from direct port I/O */
 static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
 {
 	struct pi_adapter *pi = ap->host->private_data;
 	u8 tmp;
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
 
 	if (device == 0)
 		tmp = ATA_DEVICE_OBS;
 	else
 		tmp = ATA_DEVICE_OBS | ATA_DEV1;
 
-	pi_connect(pi);
+	pi_connect(ap);
 	pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
 	ata_sff_pause(ap);
@@ -67,10 +77,10 @@ static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
 {
 	struct pi_adapter *pi = ap->host->private_data;
 	u8 nsect, lbal;
-
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
 	pata_parport_dev_select(ap, device);
 
-	pi_connect(pi);
+	pi_connect(ap);
 	pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
 	pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
 
@@ -94,8 +104,8 @@ static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
 				      unsigned long deadline)
 {
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	/* software reset.  causes dev0 to be selected */
 	pi->proto->write_regr(pi, 1, 6, ap->ctl);
 	udelay(20);
@@ -116,7 +126,7 @@ static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
 	unsigned int devmask = 0;
 	int rc;
 	u8 err;
-
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
 	/* determine if device 0/1 are present */
 	if (pata_parport_devchk(ap, 0))
 		devmask |= (1 << 0);
@@ -147,8 +157,8 @@ static u8 pata_parport_check_status(struct ata_port *ap)
 {
 	u8 status;
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
 
@@ -159,8 +169,8 @@ static u8 pata_parport_check_altstatus(struct ata_port *ap)
 {
 	u8 altstatus;
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	altstatus = pi->proto->read_regr(pi, 1, 6);
 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
 
@@ -171,8 +181,8 @@ static void pata_parport_tf_load(struct ata_port *ap,
 				 const struct ata_taskfile *tf)
 {
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	if (tf->ctl != ap->last_ctl) {
 		pi->proto->write_regr(pi, 1, 6, tf->ctl);
 		ap->last_ctl = tf->ctl;
@@ -209,8 +219,8 @@ static void pata_parport_tf_load(struct ata_port *ap,
 static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
 {
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
 	tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
 	tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
@@ -236,8 +246,8 @@ static void pata_parport_exec_command(struct ata_port *ap,
 				      const struct ata_taskfile *tf)
 {
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
 	ata_sff_pause(ap);
 	mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
@@ -248,8 +258,8 @@ static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
 {
 	struct ata_port *ap = qc->dev->link->ap;
 	struct pi_adapter *pi = ap->host->private_data;
-
-	pi_connect(pi);
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
+	pi_connect(ap);
 	if (rw == READ)
 		pi->proto->read_block(pi, buf, buflen);
 	else
@@ -271,11 +281,12 @@ static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
 		return;
 
 	ap = qc->ap;
+	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
 	pi = ap->host->private_data;
 	/* Drain up to 64K of data before we give up this recovery method */
 	for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
 						&& count < 65536; count += 2) {
-		pi_connect(pi);
+		pi_connect(ap);
 		pi->proto->read_block(pi, junk, 2);
 		mod_timer(&pi->timer, jiffies + DISCONNECT_TIMEOUT);
 	}
@@ -284,33 +295,6 @@ static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
 		ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
 }
 
-static void pata_parport_lost_interrupt(struct ata_port *ap)
-{
-	u8 status;
-	struct ata_queued_cmd *qc;
-
-	/* Only one outstanding command per SFF channel */
-	qc = ata_qc_from_tag(ap, ap->link.active_tag);
-	/* We cannot lose an interrupt on a non-existent or polled command */
-	if (!qc || qc->tf.flags & ATA_TFLAG_POLLING)
-		return;
-	/*
-	 * See if the controller thinks it is still busy - if so the command
-	 * isn't a lost IRQ but is still in progress
-	 */
-	status = pata_parport_check_altstatus(ap);
-	if (status & ATA_BUSY)
-		return;
-
-	/*
-	 * There was a command running, we are no longer busy and we have
-	 * no interrupt.
-	 */
-	ata_port_warn(ap, "lost interrupt (Status 0x%x)\n", status);
-	/* Run the host interrupt logic as if the interrupt had not been lost */
-	ata_sff_port_intr(ap, qc);
-}
-
 static struct ata_port_operations pata_parport_port_ops = {
 	.inherits		= &ata_sff_port_ops,
 
@@ -325,8 +309,6 @@ static struct ata_port_operations pata_parport_port_ops = {
 	.sff_exec_command	= pata_parport_exec_command,
 	.sff_data_xfer		= pata_parport_data_xfer,
 	.sff_drain_fifo		= pata_parport_drain_fifo,
-
-	.lost_interrupt		= pata_parport_lost_interrupt,
 };
 
 static const struct ata_port_info pata_parport_port_info = {
@@ -473,6 +455,19 @@ static struct scsi_host_template pata_parport_sht = {
 	PATA_PARPORT_SHT("pata_parport")
 };
 
+struct pi_device_match {
+	struct parport *parport;
+	struct pi_protocol *proto;
+};
+
+static int pi_find_dev(struct device *dev, void *data)
+{
+	struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
+	struct pi_device_match *match = data;
+
+	return pi->pardev->port == match->parport && pi->proto == match->proto;
+}
+
 static struct pi_adapter *pi_init_one(struct parport *parport,
 			struct pi_protocol *pr, int mode, int unit, int delay)
 {
@@ -481,6 +476,14 @@ static struct pi_adapter *pi_init_one(struct parport *parport,
 	const struct ata_port_info *ppi[] = { &pata_parport_port_info };
 	struct ata_host *host;
 	struct pi_adapter *pi;
+	struct pi_device_match match = { .parport = parport, .proto = pr };
+
+	/*
+	 * Abort if there's a device already registered on the same parport
+	 * using the same protocol.
+	 */
+	if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
+		return NULL;
 
 	pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
 	if (!pi)
@@ -674,10 +677,7 @@ static void pi_remove_one(struct device *dev)
 
 	ata_host_detach(host);
 	del_timer_sync(&pi->timer);
-	if (pi->claimed) {
-		pi->proto->disconnect(pi);
-		parport_release(pi->pardev);
-	}
+	pi_disconnect(pi);
 	pi_release(pi);
 	device_unregister(dev);
 	ida_free(&pata_parport_bus_dev_ids, dev->id);
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-15  3:06                         ` Damien Le Moal
  2022-11-15  8:04                           ` Hannes Reinecke
@ 2022-11-15 14:56                           ` Ondrej Zary
  2022-11-16  1:30                             ` Damien Le Moal
  1 sibling, 1 reply; 31+ messages in thread
From: Ondrej Zary @ 2022-11-15 14:56 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Tuesday 15 November 2022, Damien Le Moal wrote:
> On 11/15/22 04:25, Ondrej Zary wrote:
> > On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
> >> On 11/14/22 16:53, Ondrej Zary wrote:
> >>> On Monday 14 November 2022, Damien Le Moal wrote:
> >>>> On 11/12/22 20:17, Ondrej Zary wrote:
> >>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> >>>>>> It's been a while - did you get a chance to make some progress on
> >>>>>> this?  Do you need any help to unblock you?
> >>>>>>
> >>>>>
> >>>>> Sorry again, I'm back now. Trying to fix locking problems.
> >>>>> Added this to each function for analysis how the functions are called wrt.
> >>>>> locking:
> >>>>>
> >>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> >>>>
> >>>> Do you have your code somewhere that we can look at ?
> >>>
> >>> This is the current version with debug printks. I've also added dump_stack()
> >>> to find out the code path but haven't analyzed the output yet.
> >>
> >> Can you send a proper patch ? Or a link to a git tree ? That is easier to
> >> handle than pasted code in an email...
> > 
> > Patch against what? I don't have a git server.
> 
> patch against current 6.1-rc, or against an older kernel should be OK too.
> But please "git send-email" a patch, or push your dev tree to github ?
> 
> > I've done some call trace analysis. These code paths are calling
> > pata_parport functions with ap->lock locked during init.
> > 
> > Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
> > ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> > ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> > ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
> > ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> > ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> > ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
> > ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
> > ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
> > ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
> > ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
> > ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus
> > 
> > Comm: modprobe
> > ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> > 
> > Comm: scsi_eh_4
> > ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> > ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> > ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
> > ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status
> 
> What exactly are the issues you are having with ap->lock ? It looks like
> you have done a lot of analysis of the code, but without any context about
> the problem, I do not understand what I am looking at.
> 

The problem is that pi_connect() can sleep because it calls
parport_claim_or_block(). And any access (even reading ATA status register)
requires pi_connect.

-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-15 14:56                           ` Ondrej Zary
@ 2022-11-16  1:30                             ` Damien Le Moal
  2022-12-12 22:55                               ` Ondrej Zary
  0 siblings, 1 reply; 31+ messages in thread
From: Damien Le Moal @ 2022-11-16  1:30 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On 2022/11/15 23:56, Ondrej Zary wrote:
> On Tuesday 15 November 2022, Damien Le Moal wrote:
>> On 11/15/22 04:25, Ondrej Zary wrote:
>>> On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
>>>> On 11/14/22 16:53, Ondrej Zary wrote:
>>>>> On Monday 14 November 2022, Damien Le Moal wrote:
>>>>>> On 11/12/22 20:17, Ondrej Zary wrote:
>>>>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>>>>>>>> It's been a while - did you get a chance to make some progress on
>>>>>>>> this?  Do you need any help to unblock you?
>>>>>>>>
>>>>>>>
>>>>>>> Sorry again, I'm back now. Trying to fix locking problems.
>>>>>>> Added this to each function for analysis how the functions are called wrt.
>>>>>>> locking:
>>>>>>>
>>>>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
>>>>>>
>>>>>> Do you have your code somewhere that we can look at ?
>>>>>
>>>>> This is the current version with debug printks. I've also added dump_stack()
>>>>> to find out the code path but haven't analyzed the output yet.
>>>>
>>>> Can you send a proper patch ? Or a link to a git tree ? That is easier to
>>>> handle than pasted code in an email...
>>>
>>> Patch against what? I don't have a git server.
>>
>> patch against current 6.1-rc, or against an older kernel should be OK too.
>> But please "git send-email" a patch, or push your dev tree to github ?
>>
>>> I've done some call trace analysis. These code paths are calling
>>> pata_parport functions with ap->lock locked during init.
>>>
>>> Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
>>> ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
>>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
>>> ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
>>> ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>> ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
>>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
>>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
>>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
>>> ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
>>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus
>>>
>>> Comm: modprobe
>>> ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
>>>
>>> Comm: scsi_eh_4
>>> ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>> ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
>>> ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
>>> ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status
>>
>> What exactly are the issues you are having with ap->lock ? It looks like
>> you have done a lot of analysis of the code, but without any context about
>> the problem, I do not understand what I am looking at.
>>
> 
> The problem is that pi_connect() can sleep because it calls
> parport_claim_or_block(). And any access (even reading ATA status register)
> requires pi_connect.

OK. Let me have a look.

-- 
Damien Le Moal
Western Digital Research


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-11-16  1:30                             ` Damien Le Moal
@ 2022-12-12 22:55                               ` Ondrej Zary
  2022-12-12 23:07                                 ` Damien Le Moal
  2022-12-13  6:22                                 ` Christoph Hellwig
  0 siblings, 2 replies; 31+ messages in thread
From: Ondrej Zary @ 2022-12-12 22:55 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On Wednesday 16 November 2022 02:30:46 Damien Le Moal wrote:
> On 2022/11/15 23:56, Ondrej Zary wrote:
> > On Tuesday 15 November 2022, Damien Le Moal wrote:
> >> On 11/15/22 04:25, Ondrej Zary wrote:
> >>> On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
> >>>> On 11/14/22 16:53, Ondrej Zary wrote:
> >>>>> On Monday 14 November 2022, Damien Le Moal wrote:
> >>>>>> On 11/12/22 20:17, Ondrej Zary wrote:
> >>>>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
> >>>>>>>> It's been a while - did you get a chance to make some progress on
> >>>>>>>> this?  Do you need any help to unblock you?
> >>>>>>>>
> >>>>>>>
> >>>>>>> Sorry again, I'm back now. Trying to fix locking problems.
> >>>>>>> Added this to each function for analysis how the functions are called wrt.
> >>>>>>> locking:
> >>>>>>>
> >>>>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
> >>>>>>
> >>>>>> Do you have your code somewhere that we can look at ?
> >>>>>
> >>>>> This is the current version with debug printks. I've also added dump_stack()
> >>>>> to find out the code path but haven't analyzed the output yet.
> >>>>
> >>>> Can you send a proper patch ? Or a link to a git tree ? That is easier to
> >>>> handle than pasted code in an email...
> >>>
> >>> Patch against what? I don't have a git server.
> >>
> >> patch against current 6.1-rc, or against an older kernel should be OK too.
> >> But please "git send-email" a patch, or push your dev tree to github ?
> >>
> >>> I've done some call trace analysis. These code paths are calling
> >>> pata_parport functions with ap->lock locked during init.
> >>>
> >>> Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
> >>> ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> >>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
> >>> ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
> >>> ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> >>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> >>> ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
> >>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
> >>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
> >>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
> >>> ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
> >>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus
> >>>
> >>> Comm: modprobe
> >>> ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> >>>
> >>> Comm: scsi_eh_4
> >>> ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
> >>> ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
> >>> ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
> >>> ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status
> >>
> >> What exactly are the issues you are having with ap->lock ? It looks like
> >> you have done a lot of analysis of the code, but without any context about
> >> the problem, I do not understand what I am looking at.
> >>
> > 
> > The problem is that pi_connect() can sleep because it calls
> > parport_claim_or_block(). And any access (even reading ATA status register)
> > requires pi_connect.
> 
> OK. Let me have a look.
> 

The locking problems seem not to be easily solvable. Maybe a hack that grabs
the parport before registering ata interface (and keeps it until the
interface is disabled) will help? That will prevent multiple chained devices
on one parport from working but can get pata_parport moving.


-- 
Ondrej Zary

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-12-12 22:55                               ` Ondrej Zary
@ 2022-12-12 23:07                                 ` Damien Le Moal
  2022-12-13  6:22                                 ` Christoph Hellwig
  1 sibling, 0 replies; 31+ messages in thread
From: Damien Le Moal @ 2022-12-12 23:07 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Christoph Hellwig, Sergey Shtylyov, Jens Axboe, Tim Waugh,
	linux-block, linux-parport, linux-ide, linux-kernel

On 12/13/22 07:55, Ondrej Zary wrote:
> On Wednesday 16 November 2022 02:30:46 Damien Le Moal wrote:
>> On 2022/11/15 23:56, Ondrej Zary wrote:
>>> On Tuesday 15 November 2022, Damien Le Moal wrote:
>>>> On 11/15/22 04:25, Ondrej Zary wrote:
>>>>> On Monday 14 November 2022 09:03:28 Damien Le Moal wrote:
>>>>>> On 11/14/22 16:53, Ondrej Zary wrote:
>>>>>>> On Monday 14 November 2022, Damien Le Moal wrote:
>>>>>>>> On 11/12/22 20:17, Ondrej Zary wrote:
>>>>>>>>> On Wednesday 19 October 2022 09:34:31 Christoph Hellwig wrote:
>>>>>>>>>> It's been a while - did you get a chance to make some progress on
>>>>>>>>>> this?  Do you need any help to unblock you?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Sorry again, I'm back now. Trying to fix locking problems.
>>>>>>>>> Added this to each function for analysis how the functions are called wrt.
>>>>>>>>> locking:
>>>>>>>>>
>>>>>>>>> 	printk("%s, locked=%d\n", __FUNCTION__, spin_is_locked(ap->lock));
>>>>>>>>
>>>>>>>> Do you have your code somewhere that we can look at ?
>>>>>>>
>>>>>>> This is the current version with debug printks. I've also added dump_stack()
>>>>>>> to find out the code path but haven't analyzed the output yet.
>>>>>>
>>>>>> Can you send a proper patch ? Or a link to a git tree ? That is easier to
>>>>>> handle than pasted code in an email...
>>>>>
>>>>> Patch against what? I don't have a git server.
>>>>
>>>> patch against current 6.1-rc, or against an older kernel should be OK too.
>>>> But please "git send-email" a patch, or push your dev tree to github ?
>>>>
>>>>> I've done some call trace analysis. These code paths are calling
>>>>> pata_parport functions with ap->lock locked during init.
>>>>>
>>>>> Comm: kworker, Workqueue: ata_sff ata_sff_pio_task
>>>>> ata_sff_hsm_move -> ata_pio_sectors-> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
>>>>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_tf_read -> pata_parport_check_altstatus
>>>>> ata_sff_pio_task -> ata_sff_busy_wait -> pata_parport_check_status
>>>>> ata_sff_hsm_move -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>>>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>>>> ata_sff_pio_task -> ata_sff_hsm_move -> ata_pio_sectors -> ata_pio_sector -> ata_pio_xfer -> pata_parport_data_xfer
>>>>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_data_xfer
>>>>> ata_sff_pio_task -> ata_sff_hsm_move -> pata_parport_tf_read
>>>>> ata_sff_hsm_move -> ata_hsm_qc_complete -> ata_qc_complete -> fill_result_tf -> ata_sff_qc_fill_rtf -> pata_parport_tf_read
>>>>> ata_sff_hsm_move -> ata_pio_sectors -> ata_sff_altstatus -> pata_parport_check_altstatus
>>>>> ata_sff_hsm_move -> ata_sff_altstatus -> pata_parport_check_altstatus
>>>>>
>>>>> Comm: modprobe
>>>>> ata_host_start -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
>>>>>
>>>>> Comm: scsi_eh_4
>>>>> ata_eh_recover -> ata_eh_reset -> ata_eh_thaw_port -> ata_sff_thaw -> ata_sff_irq_on -> ata_wait_idle -> ata_sff_busy_wait -> pata_parport_check_status
>>>>> ata_eh_reset -> ata_eh_freeze_port -> ata_sff_freeze -> pata_parport_check_status
>>>>> ata_scsi_error -> ata_scsi_port_error_handler -> ata_port_freeze -> ata_sff_freeze -> pata_parport_check_status
>>>>> ata_sff_error_handler -> pata_parport_drain_fifo -> pata_parport_check_status
>>>>
>>>> What exactly are the issues you are having with ap->lock ? It looks like
>>>> you have done a lot of analysis of the code, but without any context about
>>>> the problem, I do not understand what I am looking at.
>>>>
>>>
>>> The problem is that pi_connect() can sleep because it calls
>>> parport_claim_or_block(). And any access (even reading ATA status register)
>>> requires pi_connect.
>>
>> OK. Let me have a look.
>>
> 
> The locking problems seem not to be easily solvable. Maybe a hack that grabs
> the parport before registering ata interface (and keeps it until the
> interface is disabled) will help? That will prevent multiple chained devices
> on one parport from working but can get pata_parport moving.

I have been very busy and had no time to look at this. Sorry about that. I
will have a look before year-end break.

-- 
Damien Le Moal
Western Digital Research


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] pata_parport: add driver (PARIDE replacement)
  2022-12-12 22:55                               ` Ondrej Zary
  2022-12-12 23:07                                 ` Damien Le Moal
@ 2022-12-13  6:22                                 ` Christoph Hellwig
  1 sibling, 0 replies; 31+ messages in thread
From: Christoph Hellwig @ 2022-12-13  6:22 UTC (permalink / raw)
  To: Ondrej Zary
  Cc: Damien Le Moal, Christoph Hellwig, Sergey Shtylyov, Jens Axboe,
	Tim Waugh, linux-block, linux-parport, linux-ide, linux-kernel

On Mon, Dec 12, 2022 at 11:55:30PM +0100, Ondrej Zary wrote:
> The locking problems seem not to be easily solvable. Maybe a hack that grabs
> the parport before registering ata interface (and keeps it until the
> interface is disabled) will help? That will prevent multiple chained devices
> on one parport from working but can get pata_parport moving.

That does seem like a good start.  Does anyone even still use chained
devices?

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2022-12-13  6:23 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-12 14:44 [PATCH] pata_parport: add driver (PARIDE replacement) Ondrej Zary
2022-03-13 19:15 ` Ondrej Zary
2022-03-13 23:19   ` Jens Axboe
2022-03-14 20:25     ` Ondrej Zary
2022-03-14 20:29       ` Jens Axboe
2022-03-15  4:22         ` Damien Le Moal
2022-03-15 18:44           ` Ondrej Zary
2022-03-15 18:47             ` Jens Axboe
2022-03-15 21:17               ` Ondrej Zary
2022-03-16  0:19                 ` Damien Le Moal
2022-03-15  8:24         ` Christoph Hellwig
2022-03-13 20:38 ` Sergey Shtylyov
2022-03-13 21:19   ` Ondrej Zary
2022-03-16  8:50     ` Sergey Shtylyov
2022-03-16 11:28       ` Ondrej Zary
2022-03-16 11:44         ` Damien Le Moal
2022-03-16 12:58           ` Ondrej Zary
2022-10-19  7:34             ` Christoph Hellwig
2022-10-19 18:58               ` Ondrej Zary
2022-11-12 11:17               ` Ondrej Zary
2022-11-13 23:23                 ` Damien Le Moal
2022-11-14  7:53                   ` Ondrej Zary
2022-11-14  8:03                     ` Damien Le Moal
2022-11-14 19:25                       ` Ondrej Zary
2022-11-15  3:06                         ` Damien Le Moal
2022-11-15  8:04                           ` Hannes Reinecke
2022-11-15 14:56                           ` Ondrej Zary
2022-11-16  1:30                             ` Damien Le Moal
2022-12-12 22:55                               ` Ondrej Zary
2022-12-12 23:07                                 ` Damien Le Moal
2022-12-13  6:22                                 ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).