rtl8139: let go of the device when asked
The kernel's rtl8139 driver had no release hook, so /dev/pci refused
every request for the card ("the kernel would not release it") and a
hosted 8139too could never run on a machine where this driver probed
first.
release() follows e1000_release(): silence the chip before unhooking the
interrupt, reset it and clear bus mastering so it stops writing into its
buffers, take the interface out of the stack, then give the DMA buffers
back.
Tested in QEMU: ldlsd takes the card from the kernel, 8139too probes it,
DHCP binds ldls0, and ping 10.9.0.2 gets 3 of 3 replies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HH7xukt4aaiGj1nQEAoQf1
This commit is contained in:
parent
26bca020b6
commit
f1798e7443
1 changed files with 63 additions and 0 deletions
|
|
@ -370,6 +370,68 @@ rtl8139_probe(struct pci_dev *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Let go of the device, so another driver can have it.
|
||||
//
|
||||
// Without this the kernel's answer to "may I have the 8139" was always no, and
|
||||
// a hosted driver for it could never run on a machine where this one had
|
||||
// probed first -- which is every machine with one:
|
||||
//
|
||||
// found 10ec:8139 at 00:04.0, held by rtl8139
|
||||
// the kernel would not release it
|
||||
//
|
||||
// The order is the one e1000_release() follows and for the same reasons. The
|
||||
// chip is silenced *before* the handler goes, because a level-triggered line
|
||||
// left asserted with nobody to clear it is an interrupt storm. Then it is
|
||||
// reset, which stops the receiver writing into a buffer that is about to be
|
||||
// freed, and bus mastering is cleared so that nothing it still has queued can
|
||||
// reach memory. Only then are the buffers given back: the arena will hand them
|
||||
// to the next driver, and a chip still pointed at them would be writing frames
|
||||
// into somebody else's descriptors.
|
||||
static int
|
||||
rtl8139_release(struct pci_dev *d)
|
||||
{
|
||||
struct rtl8139 *r = 0;
|
||||
|
||||
for (int i = 0; i < RTL8139_MAX_NICS; i++)
|
||||
if (nics[i].pdev == d) { r = &nics[i]; break; }
|
||||
|
||||
if (!r || !r->ready)
|
||||
return 0; // never came up, or already let go
|
||||
|
||||
acquire(&r->lock);
|
||||
r->ready = 0; // transmit and receive refuse from here on
|
||||
wr16(r, R_IMR, 0); // no more interrupts from it
|
||||
wr16(r, R_ISR, 0xFFFF); // and none left pending
|
||||
wr8(r, R_CR, 0); // receiver and transmitter off
|
||||
release(&r->lock);
|
||||
|
||||
if (d->irq != 0xFF && d->irq < 24)
|
||||
irq_unregister(d->irq, rtl8139_intr, r);
|
||||
|
||||
// Reset, and wait for it, so the chip has truly stopped before its buffers
|
||||
// are reused. The reset bit clears itself when the chip is done.
|
||||
wr8(r, R_CR, CR_RST);
|
||||
for (int i = 0; i < 10000 && (rd8(r, R_CR) & CR_RST); i++)
|
||||
microdelay(10);
|
||||
pci_disable(d, PCI_CMD_BUS_MASTER);
|
||||
|
||||
// Out of the stack, for the reason e1000_release() spells out: an interface
|
||||
// left behind keeps its address and its route, and the stack goes on sending
|
||||
// into a driver that now refuses every frame.
|
||||
if (r->ndev)
|
||||
net_detach(r->ndev);
|
||||
|
||||
dma_free(r->rxbuf, RXBUF_TOTAL);
|
||||
dma_free(r->txbuf, NTX * TXBUF);
|
||||
r->rxbuf = r->txbuf = 0;
|
||||
r->tx_slot = 0;
|
||||
r->tx_packets = 0;
|
||||
r->rx_packets = 0;
|
||||
|
||||
cprintf("rtl8139: released; the interface is down\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rtl8139_attached(struct pci_dev *d, struct device *dev)
|
||||
{
|
||||
|
|
@ -400,4 +462,5 @@ const struct pci_driver rtl8139_driver = {
|
|||
.ops = &rtl8139_netdev_ops,
|
||||
.probe = rtl8139_probe,
|
||||
.attached = rtl8139_attached,
|
||||
.release = rtl8139_release,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue