Jeff,
This looked prime to cut since ahci_remove_one() was a functionally
identical to ata_pci_remove_one() except for the interrupt disable
(have_msi) bits, which fit nicely into ahci_host_stop(). However,
1) Will it work?
2) Isn't it wrong for the IRQ disable at the chip to occur *after*
free_irq() is called to disconnect the handler (independent of
question 1...since this is the case currently)? Granted, all of the
ports have gone through scsi_remove_host() but theoretically there
still is a possibility the chip could interrupt.
If I'm wrong on both counts I'll blame it on need for sleep... :-)
BR
This patch depends on the PCI INTx patch (but will probably work w/o):
http://lkml.org/lkml/2005/8/15/165
Signed-off-by: Brett Russ <[email protected]>
Index: linux-2.6.13/drivers/scsi/ahci.c
===================================================================
--- linux-2.6.13.orig/drivers/scsi/ahci.c
+++ linux-2.6.13/drivers/scsi/ahci.c
@@ -187,7 +187,6 @@ static void ahci_qc_prep(struct ata_queu
static u8 ahci_check_status(struct ata_port *ap);
static u8 ahci_check_err(struct ata_port *ap);
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
-static void ahci_remove_one (struct pci_dev *pdev);
static Scsi_Host_Template ahci_sht = {
.module = THIS_MODULE,
@@ -277,7 +276,7 @@ static struct pci_driver ahci_pci_driver
.name = DRV_NAME,
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
- .remove = ahci_remove_one,
+ .remove = ata_pci_remove_one,
};
@@ -294,6 +293,13 @@ static inline void *ahci_port_base (void
static void ahci_host_stop(struct ata_host_set *host_set)
{
struct ahci_host_priv *hpriv = host_set->private_data;
+ struct pci_dev *pdev = to_pci_dev(host_set->dev);
+
+ if (hpriv->flags & AHCI_FLAG_MSI)
+ pci_disable_msi(pdev);
+ else
+ pci_intx(pdev, 0);
+
kfree(hpriv);
ata_host_stop(host_set);
@@ -1036,43 +1042,6 @@ err_out:
return rc;
}
-static void ahci_remove_one (struct pci_dev *pdev)
-{
- struct device *dev = pci_dev_to_dev(pdev);
- struct ata_host_set *host_set = dev_get_drvdata(dev);
- struct ahci_host_priv *hpriv = host_set->private_data;
- struct ata_port *ap;
- unsigned int i;
- int have_msi;
-
- for (i = 0; i < host_set->n_ports; i++) {
- ap = host_set->ports[i];
-
- scsi_remove_host(ap->host);
- }
-
- have_msi = hpriv->flags & AHCI_FLAG_MSI;
- free_irq(host_set->irq, host_set);
-
- for (i = 0; i < host_set->n_ports; i++) {
- ap = host_set->ports[i];
-
- ata_scsi_release(ap->host);
- scsi_host_put(ap->host);
- }
-
- host_set->ops->host_stop(host_set);
- kfree(host_set);
-
- if (have_msi)
- pci_disable_msi(pdev);
- else
- pci_intx(pdev, 0);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- dev_set_drvdata(dev, NULL);
-}
-
static int __init ahci_init(void)
{
return pci_module_init(&ahci_pci_driver);
Brett Russ wrote:
> Jeff,
>
> This looked prime to cut since ahci_remove_one() was a functionally
> identical to ata_pci_remove_one() except for the interrupt disable
> (have_msi) bits, which fit nicely into ahci_host_stop(). However,
>
> 1) Will it work?
>
> 2) Isn't it wrong for the IRQ disable at the chip to occur *after*
> free_irq() is called to disconnect the handler (independent of
> question 1...since this is the case currently)? Granted, all of the
> ports have gone through scsi_remove_host() but theoretically there
> still is a possibility the chip could interrupt.
>
> If I'm wrong on both counts I'll blame it on need for sleep... :-)
Moving AHCI away from ata_pci_remove_one() was actually intentional.
This gives the driver a bit more freedom: legacy region handling and
->host_stop() became unnecessary. Also, I was concerned that
ata_pci_remove_one() might grow into a one-size-fits-all unmaintainable
behemoth.
Short term, if one were being obsessive, a potential cleanup could be to
make common the two loops in ahci_remove_one()/ata_pci_remove_one().
Long term, libata driver API should become more like the
register_foo()/unregister_foo() interfaces you see elsewhere in the
kernel. That direction has the potential to shake up the current code
path through ata_pci_remove_one().
So... your patch, while technically correct, is going in the opposite
direction to where I want to go :)
Jeff
Jeff Garzik wrote:
> Brett Russ wrote:
>> 2) Isn't it wrong for the IRQ disable at the chip to occur *after*
>> free_irq() is called to disconnect the handler (independent of
>> question 1...since this is the case currently)? Granted, all of the
>> ports have gone through scsi_remove_host() but theoretically there
>> still is a possibility the chip could interrupt.
>>
>> If I'm wrong on both counts I'll blame it on need for sleep... :-)
>
>
>
> Moving AHCI away from ata_pci_remove_one() was actually intentional.
> This gives the driver a bit more freedom: legacy region handling and
> ->host_stop() became unnecessary. Also, I was concerned that
> ata_pci_remove_one() might grow into a one-size-fits-all unmaintainable
> behemoth.
>
> Short term, if one were being obsessive, a potential cleanup could be to
> make common the two loops in ahci_remove_one()/ata_pci_remove_one().
>
> Long term, libata driver API should become more like the
> register_foo()/unregister_foo() interfaces you see elsewhere in the
> kernel. That direction has the potential to shake up the current code
> path through ata_pci_remove_one().
>
> So... your patch, while technically correct, is going in the opposite
> direction to where I want to go :)
Good to know, thanks for explaining the goals. How about question #2 above?
Thanks,
BR
Brett Russ wrote:
> Jeff Garzik wrote:
>> Brett Russ wrote:
>>> 2) Isn't it wrong for the IRQ disable at the chip to occur *after*
>>> free_irq() is called to disconnect the handler (independent of
>>> question 1...since this is the case currently)? Granted, all of the
>>> ports have gone through scsi_remove_host() but theoretically there
>>> still is a possibility the chip could interrupt.
Answer: depends on hardware.
For all hardware, the conditions that generate interrupts should be shut
down, and then free_irq() is called after that.
Some hardware only needs per-port disable, or nothing besides clearing
any commands, to ensure that interrupts from that hardware are disabled
(this excludes shared interrupts, of course).
This logic is another reason why a driver author may choose to implement
their own PCI ->remove() hook, rather than using the generic
ata_pci_remove_one(). Eliminates the need for a ->host_stop() hook
implementation, and allows one to perform tasks before calling
free_irq(), as well as tasks after the call (normal ->host_stop stuff).
Jeff