I/OAT fixes
Subsystems (6)
cover I/OAT fixes
Andrew Morton (1):
I/OAT: warning fix
Chris Leech (6):
ioatdma: Push pending transactions to hardware more frequently
ioatdma: Remove the wrappers around read(bwl)/write(bwl) in ioatdma
ioatdma: Remove the use of writeq from the ioatdma driver I/OAT: Add documentation for the tcp_dma_copybreak sysctl I/OAT: Add entries to MAINTAINERS for the DMA memcpy subsystem and ioatdma I/OAT: Only offload copies for TCP when there will be a context switch
Dan Aloni (1):
I/OAT: fix I/OAT for kexec
Jeff Garzik (1):
drivers/dma: handle sysfs errors
Documentation/networking/ip-sysctl.txt | 6 + MAINTAINERS | 12 +++ drivers/dma/dmaengine.c | 22 +++++- drivers/dma/ioatdma.c | 81 ++++++++++++---------- drivers/dma/ioatdma_io.h | 118 --------------------------------- net/ipv4/tcp.c | 26 +++++-- 6 files changed, 100 insertions(+), 165 deletions(-)
--
Chris Leech <[email protected]>
I/O Acceleration Technology Software Development
LAN Access Division / Digital Enterprise Group
PATCH 1/9 ioatdma: Push pending transactions to hardware more frequently
Every 20 descriptors turns out to be to few append commands with newer/faster CPUs. Pushing every 4 still cuts down on MMIO writes to an acceptable level without letting the DMA engine run out of work.
---
drivers/dma/ioatdma.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/dma/ioatdma.c +2 -2
@@ -310,7 +310,7 @@ static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan,list_splice_init (&new_chain, ioat_chan->used_desc.prev); ioat_chan->pending += desc_count; -if (ioat_chan->pending >=20 ) { +if (ioat_chan->pending >=4 ) { append =1 ; ioat_chan->pending =0 ; }
@@ -818,7 +818,7 @@ static void __devexit ioat_remove(struct pci_dev *pdev) }/* MODULE API */ -MODULE_VERSION ("1.7" ); +MODULE_VERSION ("1.9" );MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Intel Corporation" );
Re: [PATCH 1/9] ioatdma: Push pending transactions to hardware more frequently
Quoted 15 lines · Chris Leech
Every 20 descriptors turns out to be to few append commands with newer/faster CPUs. Pushing every 4 still cuts down on MMIO writes to an acceptable level without letting the DMA engine run out of work.
Signed-off-by Chris Leech <[email protected]>---
drivers/dma/ioatdma.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)drivers/dma/ioatdma.c +0
-0
drivers/dma/ioatdma.c static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan, · line 313
Quoted 5 lines
drivers/dma/ioatdma.c +1
-1@@ -310,7 +310,7 @@ static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan,list_splice_init (&new_chain, ioat_chan->used_desc.prev); ioat_chan->pending += desc_count; -if (ioat_chan->pending >=20 ) { +if (ioat_chan->pending >=4 ) {
This sounds like something that will always be wrong -- or in other words, always be right for only the latest CPUs. Can this be made dynamic, based on some timing factor?
Jeff
David Miller
maintainerRe: [PATCH 1/9] ioatdma: Push pending transactions to hardware more frequently
From: Jeff Garzik <[email protected]>
Date: Fri, 02 Mar 2007 22:14:39 -0500
Quoted 18 lines
Quoted 17 lines · Chris Leech
Every 20 descriptors turns out to be to few append commands with newer/faster CPUs. Pushing every 4 still cuts down on MMIO writes to an acceptable level without letting the DMA engine run out of work.
Signed-off-by Chris Leech <[email protected]>---
drivers/dma/ioatdma.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)drivers/dma/ioatdma.c +0
-0@@ -310,7 +310,7 @@ static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan,list_splice_init (&new_chain, ioat_chan->used_desc.prev);
Quoted 7 lines
drivers/dma/ioatdma.c static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan, · line 313
Quoted 3 lines
drivers/dma/ioatdma.c +1
-1ioat_chan->pending += desc_count; -if (ioat_chan->pending >=20 ) { +if (ioat_chan->pending >=4 ) {This sounds like something that will always be wrong -- or in other words, always be right for only the latest CPUs. Can this be made dynamic, based on some timing factor?
In fact I think this has been tweaked twice in the vanilla tree already.
Re: [PATCH 1/9] ioatdma: Push pending transactions to hardware more frequently
Quoted 6 lines
Quoted 3 lines
This sounds like something that will always be wrong -- or in other words, always be right for only the latest CPUs. Can this be made dynamic, based on some timing factor?
In fact I think this has been tweaked twice in the vanilla tree already.
This is actually just the same tweak you remember me posting before and I never pushed to get it in mainline, but Jeff's right. The problem isn't so much in the driver itself, as in how it's used by I/OAT in the TCP receive code, there are inherent assumptions about how long a context switch takes compared to how long an offloaded memcpy takes.
I'm working on using completion interrupts for the device so as not to end up polling when the CPUs are faster than the code was tuned for, and doing it in a way that doesn't introduce extra context switches. I'm hoping to have something ready for 2.6.22, or at least ready for MM in that time frame.
As for this change in the short term, we did go back and make sure that it didn't performance worse with the older CPUs supported on these platforms. We should have tested more intermediate values instead of just jumping from 1 t o 20 for that threshold.
- Chris
PATCH 2/9 drivers/dma: handle sysfs errors
In reply to Chris LeechFrom: Jeff Garzik <[email protected]>
---
drivers/dma/dmaengine.c | 22 ++++++++++++++++++++-- 1 files changed, 20 insertions(+), 2 deletions(-)
drivers/dma/dmaengine.c +20 -2
@@ -312,7 +312,7 @@ void dma_async_client_chan_request(struct dma_client *client,int dma_async_device_register (struct dma_device *device) {static int id; -int chancnt =0 ; +int chancnt =0 , rc;struct dma_chan * chan;if (!device)
@@ -334,8 +334,15 @@ int dma_async_device_register(struct dma_device *device)snprintf (chan->class_dev.class_id,BUS_ID_SIZE ,"dma%dchan%d" , device->dev_id, chan->chan_id); + rc =class_device_register (&chan->class_dev); +if (rc) { + chancnt--; +free_percpu (chan->local); + chan->local =NULL ; + goto err_out; + } +kref_get (&device->refcount); -class_device_register (&chan->class_dev); }mutex_lock (&dma_list_mutex);
@@ -345,6 +352,17 @@ int dma_async_device_register(struct dma_device *device)dma_chans_rebalance ();return 0 ; + +err_out: +list_for_each_entry (chan, &device->channels, device_node) { +if (chan->local ==NULL ) +continue ; +kref_put (&device->refcount, dma_async_device_cleanup); +class_device_unregister (&chan->class_dev); + chancnt--; +free_percpu (chan->local); + } +return rc; }/**
PATCH 3/9 ioatdma: Remove the wrappers around read(bwl)/write(bwl) in ioatdma
In reply to Chris Leech---
drivers/dma/ioatdma.c | 60 +++++++++++------------ drivers/dma/ioatdma_io.h | 118 ---------------------------------------------- 2 files changed, 28 insertions(+), 150 deletions(-)
drivers/dma/ioatdma.c +28 -32
@@ -32,7 +32,6 @@#include <linux/delay.h> #include <linux/dma-mapping.h> #include "ioatdma.h" -#include "ioatdma_io.h" #include "ioatdma_registers.h" #include "ioatdma_hw.h"
@@ -51,8 +50,8 @@ static int enumerate_dma_channels(struct ioat_device *device)int i;struct ioat_dma_chan *ioat_chan; - device->common.chancnt =ioatdma_read8 (device,IOAT_CHANCNT_OFFSET ); - xfercap_scale =ioatdma_read8 (device,IOAT_XFERCAP_OFFSET ); + device->common.chancnt =readb (device->reg_base +IOAT_CHANCNT_OFFSET ); + xfercap_scale =readb (device->reg_base +IOAT_XFERCAP_OFFSET ); xfercap = (xfercap_scale ==0 ?-1 : (1UL << xfercap_scale));for (i =0 ; i < device->common.chancnt; i++) {
@@ -123,7 +122,7 @@ static int ioat_dma_alloc_chan_resources(struct dma_chan *chan)* In-use bit automatically set by reading chanctrl * If 0, we got it, if 1, someone else did */ - chanctrl =ioatdma_chan_read16 (ioat_chan,IOAT_CHANCTRL_OFFSET ); + chanctrl =readw (ioat_chan->reg_base +IOAT_CHANCTRL_OFFSET );if (chanctrl &IOAT_CHANCTRL_CHANNEL_IN_USE )return -EBUSY ;
@@ -132,12 +131,12 @@ static int ioat_dma_alloc_chan_resources(struct dma_chan *chan)IOAT_CHANCTRL_ERR_INT_EN |IOAT_CHANCTRL_ANY_ERR_ABORT_EN |IOAT_CHANCTRL_ERR_COMPLETION_EN ; -ioatdma_chan_write16 (ioat_chan,IOAT_CHANCTRL_OFFSET , chanctrl); +writew (chanctrl, ioat_chan->reg_base +IOAT_CHANCTRL_OFFSET ); - chanerr =ioatdma_chan_read32 (ioat_chan,IOAT_CHANERR_OFFSET ); + chanerr =readl (ioat_chan->reg_base +IOAT_CHANERR_OFFSET );if (chanerr) {printk ("IOAT: CHANERR = %x, clearing\n" , chanerr); -ioatdma_chan_write32 (ioat_chan,IOAT_CHANERR_OFFSET , chanerr); +writel (chanerr, ioat_chan->reg_base +IOAT_CHANERR_OFFSET ); }/* Allocate descriptors */
@@ -161,10 +160,10 @@ static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) &ioat_chan->completion_addr);memset (ioat_chan->completion_virt,0 ,sizeof (*ioat_chan->completion_virt)); -ioatdma_chan_write32 (ioat_chan,IOAT_CHANCMP_OFFSET_LOW , - ((u64 ) ioat_chan->completion_addr) &0x00000000FFFFFFFF ); -ioatdma_chan_write32 (ioat_chan,IOAT_CHANCMP_OFFSET_HIGH , - ((u64 ) ioat_chan->completion_addr) >>32 ); +writel (((u64 ) ioat_chan->completion_addr) &0x00000000FFFFFFFF , + ioat_chan->reg_base +IOAT_CHANCMP_OFFSET_LOW ); +writel (((u64 ) ioat_chan->completion_addr) >>32 , + ioat_chan->reg_base +IOAT_CHANCMP_OFFSET_HIGH );ioat_start_null_desc (ioat_chan);return i;
@@ -182,7 +181,7 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan)ioat_dma_memcpy_cleanup (ioat_chan); -ioatdma_chan_write8 (ioat_chan,IOAT_CHANCMD_OFFSET ,IOAT_CHANCMD_RESET ); +writeb (IOAT_CHANCMD_RESET , ioat_chan->reg_base +IOAT_CHANCMD_OFFSET );spin_lock_bh (&ioat_chan->desc_lock);list_for_each_entry_safe (desc, _desc, &ioat_chan->used_desc, node) {
@@ -210,9 +209,9 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) ioat_chan->last_completion = ioat_chan->completion_addr =0 ;/* Tell hw the chan is free */ - chanctrl =ioatdma_chan_read16 (ioat_chan,IOAT_CHANCTRL_OFFSET ); + chanctrl =readw (ioat_chan->reg_base +IOAT_CHANCTRL_OFFSET ); chanctrl &= ~IOAT_CHANCTRL_CHANNEL_IN_USE ; -ioatdma_chan_write16 (ioat_chan,IOAT_CHANCTRL_OFFSET , chanctrl); +writew (chanctrl, ioat_chan->reg_base +IOAT_CHANCTRL_OFFSET ); }/**
@@ -318,9 +317,8 @@ static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan,spin_unlock_bh (&ioat_chan->desc_lock);if (append) -ioatdma_chan_write8 (ioat_chan, -IOAT_CHANCMD_OFFSET , -IOAT_CHANCMD_APPEND ); +writeb (IOAT_CHANCMD_APPEND , + ioat_chan->reg_base +IOAT_CHANCMD_OFFSET );return cookie; }
@@ -417,9 +415,8 @@ static void ioat_dma_memcpy_issue_pending(struct dma_chan *chan)if (ioat_chan->pending !=0 ) { ioat_chan->pending =0 ; -ioatdma_chan_write8 (ioat_chan, -IOAT_CHANCMD_OFFSET , -IOAT_CHANCMD_APPEND ); +writeb (IOAT_CHANCMD_APPEND , + ioat_chan->reg_base +IOAT_CHANCMD_OFFSET ); } }
@@ -449,7 +446,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *chan)if ((chan->completion_virt->full &IOAT_CHANSTS_DMA_TRANSFER_STATUS ) ==IOAT_CHANSTS_DMA_TRANSFER_STATUS_HALTED ) {printk ("IOAT: Channel halted, chanerr = %x\n" , -ioatdma_chan_read32 (chan,IOAT_CHANERR_OFFSET )); +readl (chan->reg_base +IOAT_CHANERR_OFFSET ));/* TODO do something to salvage the situation */ }
@@ -569,21 +566,21 @@ static irqreturn_t ioat_do_interrupt(int irq, void *data)unsigned long attnstatus;u8 intrctrl; - intrctrl =ioatdma_read8 (instance,IOAT_INTRCTRL_OFFSET ); + intrctrl =readb (instance->reg_base +IOAT_INTRCTRL_OFFSET );if (!(intrctrl &IOAT_INTRCTRL_MASTER_INT_EN ))return IRQ_NONE ;if (!(intrctrl &IOAT_INTRCTRL_INT_STATUS )) { -ioatdma_write8 (instance,IOAT_INTRCTRL_OFFSET , intrctrl); +writeb (intrctrl, instance->reg_base +IOAT_INTRCTRL_OFFSET );return IRQ_NONE ; } - attnstatus =ioatdma_read32 (instance,IOAT_ATTNSTATUS_OFFSET ); + attnstatus =readl (instance->reg_base +IOAT_ATTNSTATUS_OFFSET );printk (KERN_ERR "ioatdma error: interrupt! status %lx\n" , attnstatus); -ioatdma_write8 (instance,IOAT_INTRCTRL_OFFSET , intrctrl); +writeb (intrctrl, instance->reg_base +IOAT_INTRCTRL_OFFSET );return IRQ_HANDLED ; }
@@ -612,14 +609,13 @@ static void ioat_start_null_desc(struct ioat_dma_chan *ioat_chan)spin_unlock_bh (&ioat_chan->desc_lock);#if (BITS_PER_LONG ==64 ) -ioatdma_chan_write64 (ioat_chan,IOAT_CHAINADDR_OFFSET , desc->phys); +writeq (desc->phys, ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET );#else -ioatdma_chan_write32 (ioat_chan, -IOAT_CHAINADDR_OFFSET_LOW , - (u32 ) desc->phys); -ioatdma_chan_write32 (ioat_chan,IOAT_CHAINADDR_OFFSET_HIGH ,0 ); +writel ((u32 ) desc->phys, + ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET_LOW ); +writel (0 , ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET_HIGH );#endif -ioatdma_chan_write8 (ioat_chan,IOAT_CHANCMD_OFFSET ,IOAT_CHANCMD_START ); +writeb (IOAT_CHANCMD_START , ioat_chan->reg_base +IOAT_CHANCMD_OFFSET ); }/*
@@ -748,7 +744,7 @@ static int __devinit ioat_probe(struct pci_dev *pdev, device->reg_base = reg_base; -ioatdma_write8 (device,IOAT_INTRCTRL_OFFSET ,IOAT_INTRCTRL_MASTER_INT_EN ); +writeb (IOAT_INTRCTRL_MASTER_INT_EN , device->reg_base +IOAT_INTRCTRL_OFFSET );pci_set_master (pdev);INIT_LIST_HEAD (&device->common.channels);
drivers/dma/ioatdma_io.h deleted +0 -118
118 lines not on this page · raw message
PATCH 4/9 ioatdma: Remove the use of writeq from the ioatdma driver
In reply to Chris LeechThere's only one now anyway, and it's not in a performance path, so make it behave the same on 32-bit and 64-bit CPUs.
---
drivers/dma/ioatdma.c | 10 ++++------ 1 files changed, 4 insertions(+), 6 deletions(-)
drivers/dma/ioatdma.c +4 -6
@@ -608,13 +608,11 @@ static void ioat_start_null_desc(struct ioat_dma_chan *ioat_chan)list_add_tail (&desc->node, &ioat_chan->used_desc);spin_unlock_bh (&ioat_chan->desc_lock); -#if (BITS_PER_LONG ==64 ) -writeq (desc->phys, ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET ); -#else -writel ((u32 ) desc->phys, +writel (((u64 ) desc->phys) &0x00000000FFFFFFFF , ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET_LOW ); -writel (0 , ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET_HIGH ); -#endif +writel (((u64 ) desc->phys) >>32 , + ioat_chan->reg_base +IOAT_CHAINADDR_OFFSET_HIGH ); +writeb (IOAT_CHANCMD_START , ioat_chan->reg_base +IOAT_CHANCMD_OFFSET ); }
PATCH 5/9 I/OAT: Add documentation for the tcp_dma_copybreak sysctl
In reply to Chris Leech---
Documentation/networking/ip-sysctl.txt | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
Documentation/networking/ip-sysctl.txt +6 -0
@@ -413,6 +413,12 @@ tcp_workaround_signed_windows - BOOLEAN not receive a window scaling option from them. Default: 0 +tcp_dma_copybreak - INTEGER + Lower limit, in bytes, of the size of socket reads that will be + offloaded to a DMA copy engine, if one is present in the system + and CONFIG_NET_DMA is enabled. + Default: 4096 + CIPSOv4 Variables: cipso_cache_enable - BOOLEAN
PATCH 6/9 I/OAT: Add entries to MAINTAINERS for the DMA memcpy subsystem and ioatdma
In reply to Chris Leech---
MAINTAINERS | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
MAINTAINERS +12 -0
@@ -1156,6 +1156,12 @@ M: [email protected] L: [email protected] S: Maintained +DMA GENERIC MEMCPY SUBSYSTEM +P: Chris Leech +M: [email protected] +L: [email protected] +S: Maintained + DOCBOOK FOR DOCUMENTATION P: Randy Dunlap M: [email protected]
@@ -1777,6 +1783,12 @@ P: Tigran Aivazian M: [email protected] S: Maintained +INTEL I/OAT DMA DRIVER +P: Chris Leech +M: [email protected] +L: [email protected] +S: Supported + INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT P: Deepak Saxena M: [email protected]
PATCH 7/9 I/OAT: Only offload copies for TCP when there will be a context switch
In reply to Chris LeechThe performance wins come with having the DMA copy engine doing the copies in parallel with the context switch. If there is enough data ready on the socket at recv time just use a regular copy.
---
net/ipv4/tcp.c | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +7 -3
@@ -1110,6 +1110,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,long timeo;struct task_struct *user_recv =NULL ;int copied_early =0 ; +int available =0 ; +struct sk_buff *skb;lock_sock (sk);
@@ -1136,7 +1138,11 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,#ifdef CONFIG_NET_DMA tp->ucopy.dma_chan =NULL ;preempt_disable (); -if ((len > sysctl_tcp_dma_copybreak) && !(flags &MSG_PEEK ) && + skb =skb_peek_tail (&sk->sk_receive_queue); +if (skb) + available =TCP_SKB_CB (skb)->seq + skb->len - (*seq); +if ((available < target) && + (len > sysctl_tcp_dma_copybreak) && !(flags &MSG_PEEK ) && !sysctl_tcp_low_latency &&__get_cpu_var (softnet_data).net_dma) {preempt_enable_no_resched (); tp->ucopy.pinned_list =dma_pin_iovec_pages (msg->msg_iov, len);
@@ -1145,7 +1151,6 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,#endif do { - struct sk_buff *skb; u32 offset; /* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */
@@ -1433,7 +1438,6 @@ skip_copy:#ifdef CONFIG_NET_DMA if (tp->ucopy.dma_chan) { -struct sk_buff *skb;dma_cookie_t done, used;dma_async_memcpy_issue_pending (tp->ucopy.dma_chan);
PATCH 8/9 I/OAT: warning fix
In reply to Chris Leechnet/ipv4/tcp.c: In function 'tcp_recvmsg':net/ipv4/tcp.c:1111: warning: unused variable 'available'
---
net/ipv4/tcp.c | 26 ++++++++++++++++---------- 1 files changed, 16 insertions(+), 10 deletions(-)
net/ipv4/tcp.c +16 -10
@@ -1110,7 +1110,6 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,long timeo;struct task_struct *user_recv =NULL ;int copied_early =0 ; -int available =0 ;struct sk_buff *skb;lock_sock (sk);
@@ -1139,15 +1138,22 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, tp->ucopy.dma_chan =NULL ;preempt_disable (); skb =skb_peek_tail (&sk->sk_receive_queue); -if (skb) - available =TCP_SKB_CB (skb)->seq + skb->len - (*seq); -if ((available < target) && - (len > sysctl_tcp_dma_copybreak) && !(flags &MSG_PEEK ) && - !sysctl_tcp_low_latency &&__get_cpu_var (softnet_data).net_dma) { -preempt_enable_no_resched (); - tp->ucopy.pinned_list =dma_pin_iovec_pages (msg->msg_iov, len); - }else -preempt_enable_no_resched (); + { +int available =0 ; + +if (skb) + available =TCP_SKB_CB (skb)->seq + skb->len - (*seq); +if ((available < target) && + (len > sysctl_tcp_dma_copybreak) && !(flags &MSG_PEEK ) && + !sysctl_tcp_low_latency && +__get_cpu_var (softnet_data).net_dma) { +preempt_enable_no_resched (); + tp->ucopy.pinned_list = +dma_pin_iovec_pages (msg->msg_iov, len); + }else { +preempt_enable_no_resched (); + } + }#endif do {
PATCH 9/9 I/OAT: fix I/OAT for kexec
In reply to Chris LeechUnder kexec, I/OAT initialization breaks over busy resources because the previous kernel did not release them.
I'm not sure this fix can be considered a complete one but it works for me.
I guess something similar to the *_remove method should occur there..
---
drivers/dma/ioatdma.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
drivers/dma/ioatdma.c +13 -0
@@ -41,6 +41,7 @@/* internal functions */ static int __devinit ioat_probe (struct pci_dev *pdev,const struct pci_device_id *ent); +static void ioat_shutdown (struct pci_dev *pdev); static void __devexit ioat_remove(struct pci_dev *pdev);static int enumerate_dma_channels (struct ioat_device *device)
@@ -557,6 +558,7 @@ static struct pci_driver ioat_pci_drv = { .name = "ioatdma", .id_table = ioat_pci_tbl, .probe = ioat_probe, + .shutdown = ioat_shutdown, .remove = __devexit_p(ioat_remove), };
@@ -781,9 +783,20 @@ err_request_regions: err_set_dma_mask:pci_disable_device (pdev); err_enable_device: + +printk (KERN_ERR "Intel(R) I/OAT DMA Engine initialization failed\n" ); +return err; } +static void ioat_shutdown (struct pci_dev *pdev) +{ +struct ioat_device *device; + device =pci_get_drvdata (pdev); + +dma_async_device_unregister (&device->common); +} + static void __devexit ioat_remove(struct pci_dev *pdev) {struct ioat_device *device;