staging: xillybus: Use devm_ API for memory allocation and DMA mapping

Managed device resource API replaces code that reinvents it for memory
allocation, page allocation and DMA mapping.

devm_add_action() is used for unwinding DMA mappings, since there is no
devm_* API for dma_map_single(). A recent patch that introduces such API
was rejected, mainly on the grounds that it may cause an unnecessary waste of
resources.

Suggested-by: Baruch Siach <baruch@tkos.co.il>
Signed-off-by: Eli Billauer <eli.billauer@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Eli Billauer 2014-06-21 14:07:12 +03:00 committed by Greg Kroah-Hartman
parent 31cd921dea
commit 525be905dc
4 changed files with 132 additions and 238 deletions

View File

@ -25,33 +25,12 @@
struct xilly_endpoint_hardware; struct xilly_endpoint_hardware;
struct xilly_page {
struct list_head node;
unsigned long addr;
unsigned int order;
};
struct xilly_dma {
struct list_head node;
struct pci_dev *pdev;
struct device *dev;
dma_addr_t dma_addr;
size_t size;
int direction;
};
struct xilly_buffer { struct xilly_buffer {
void *addr; void *addr;
dma_addr_t dma_addr; dma_addr_t dma_addr;
int end_offset; /* Counting elements, not bytes */ int end_offset; /* Counting elements, not bytes */
}; };
struct xilly_cleanup {
struct list_head to_kfree;
struct list_head to_pagefree;
struct list_head to_unmap;
};
struct xilly_idt_handle { struct xilly_idt_handle {
unsigned char *chandesc; unsigned char *chandesc;
unsigned char *idt; unsigned char *idt;
@ -126,9 +105,6 @@ struct xilly_endpoint {
struct mutex register_mutex; struct mutex register_mutex;
wait_queue_head_t ep_wait; wait_queue_head_t ep_wait;
/* List of memory allocations, to make release easy */
struct xilly_cleanup cleanup;
/* Channels and message handling */ /* Channels and message handling */
struct cdev cdev; struct cdev cdev;
@ -156,18 +132,22 @@ struct xilly_endpoint_hardware {
dma_addr_t, dma_addr_t,
size_t, size_t,
int); int);
dma_addr_t (*map_single)(struct xilly_cleanup *, int (*map_single)(struct xilly_endpoint *,
struct xilly_endpoint *,
void *, void *,
size_t, size_t,
int); int,
void (*unmap_single)(struct xilly_dma *entry); dma_addr_t *);
}; };
irqreturn_t xillybus_isr(int irq, void *data); struct xilly_mapping {
void *device;
dma_addr_t dma_addr;
size_t size;
int direction;
};
void xillybus_do_cleanup(struct xilly_cleanup *mem,
struct xilly_endpoint *endpoint); irqreturn_t xillybus_isr(int irq, void *data);
struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
struct device *dev, struct device *dev,

View File

@ -311,85 +311,14 @@ EXPORT_SYMBOL(xillybus_isr);
* no locks are applied! * no locks are applied!
*/ */
void xillybus_do_cleanup(struct xilly_cleanup *mem,
struct xilly_endpoint *endpoint)
{
struct list_head *this, *next;
list_for_each_safe(this, next, &mem->to_unmap) {
struct xilly_dma *entry =
list_entry(this, struct xilly_dma, node);
endpoint->ephw->unmap_single(entry);
kfree(entry);
}
INIT_LIST_HEAD(&mem->to_unmap);
list_for_each_safe(this, next, &mem->to_kfree)
kfree(this);
INIT_LIST_HEAD(&mem->to_kfree);
list_for_each_safe(this, next, &mem->to_pagefree) {
struct xilly_page *entry =
list_entry(this, struct xilly_page, node);
free_pages(entry->addr, entry->order);
kfree(entry);
}
INIT_LIST_HEAD(&mem->to_pagefree);
}
EXPORT_SYMBOL(xillybus_do_cleanup);
static void *xilly_malloc(struct xilly_cleanup *mem, size_t size)
{
void *ptr;
ptr = kzalloc(sizeof(struct list_head) + size, GFP_KERNEL);
if (!ptr)
return ptr;
list_add_tail((struct list_head *) ptr, &mem->to_kfree);
return ptr + sizeof(struct list_head);
}
static unsigned long xilly_pagealloc(struct xilly_cleanup *mem,
unsigned long order)
{
unsigned long addr;
struct xilly_page *this;
this = kmalloc(sizeof(struct xilly_page), GFP_KERNEL);
if (!this)
return 0;
addr = __get_free_pages(GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO, order);
if (!addr) {
kfree(this);
return 0;
}
this->addr = addr;
this->order = order;
list_add_tail(&this->node, &mem->to_pagefree);
return addr;
}
static void xillybus_autoflush(struct work_struct *work); static void xillybus_autoflush(struct work_struct *work);
static int xilly_setupchannels(struct xilly_endpoint *ep, static int xilly_setupchannels(struct xilly_endpoint *ep,
struct xilly_cleanup *mem,
unsigned char *chandesc, unsigned char *chandesc,
int entries int entries
) )
{ {
struct device *dev = ep->dev;
int i, entry, wr_nbuffer, rd_nbuffer; int i, entry, wr_nbuffer, rd_nbuffer;
struct xilly_channel *channel; struct xilly_channel *channel;
int channelnum, bufnum, bufsize, format, is_writebuf; int channelnum, bufnum, bufsize, format, is_writebuf;
@ -402,17 +331,20 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
int left_of_rd_salami = 0; int left_of_rd_salami = 0;
dma_addr_t dma_addr; dma_addr_t dma_addr;
int msg_buf_done = 0; int msg_buf_done = 0;
const gfp_t gfp_mask = GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO;
struct xilly_buffer *this_buffer = NULL; /* Init to silence warning */ struct xilly_buffer *this_buffer = NULL; /* Init to silence warning */
int rc = 0;
channel = xilly_malloc(mem, ep->num_channels * channel = devm_kzalloc(dev, ep->num_channels *
sizeof(struct xilly_channel)); sizeof(struct xilly_channel), GFP_KERNEL);
if (!channel) if (!channel)
goto memfail; goto memfail;
ep->channels = xilly_malloc(mem, (ep->num_channels + 1) * ep->channels = devm_kzalloc(dev, (ep->num_channels + 1) *
sizeof(struct xilly_channel *)); sizeof(struct xilly_channel *),
GFP_KERNEL);
if (!ep->channels) if (!ep->channels)
goto memfail; goto memfail;
@ -501,16 +433,16 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
channel->rd_exclusive_open = exclusive_open; channel->rd_exclusive_open = exclusive_open;
channel->seekable = seekable; channel->seekable = seekable;
channel->rd_buffers = xilly_malloc( channel->rd_buffers = devm_kzalloc(dev,
mem, bufnum * sizeof(struct xilly_buffer *),
bufnum * sizeof(struct xilly_buffer *)); GFP_KERNEL);
if (!channel->rd_buffers) if (!channel->rd_buffers)
goto memfail; goto memfail;
this_buffer = xilly_malloc( this_buffer = devm_kzalloc(dev,
mem, bufnum * sizeof(struct xilly_buffer),
bufnum * sizeof(struct xilly_buffer)); GFP_KERNEL);
if (!this_buffer) if (!this_buffer)
goto memfail; goto memfail;
@ -530,16 +462,16 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
channel->wr_synchronous = synchronous; channel->wr_synchronous = synchronous;
channel->wr_exclusive_open = exclusive_open; channel->wr_exclusive_open = exclusive_open;
channel->wr_buffers = xilly_malloc( channel->wr_buffers = devm_kzalloc(dev,
mem, bufnum * sizeof(struct xilly_buffer *),
bufnum * sizeof(struct xilly_buffer *)); GFP_KERNEL);
if (!channel->wr_buffers) if (!channel->wr_buffers)
goto memfail; goto memfail;
this_buffer = xilly_malloc( this_buffer = devm_kzalloc(dev,
mem, bufnum * sizeof(struct xilly_buffer),
bufnum * sizeof(struct xilly_buffer)); GFP_KERNEL);
if (!this_buffer) if (!this_buffer)
goto memfail; goto memfail;
@ -576,21 +508,21 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
} }
wr_salami = (void *) wr_salami = (void *)
xilly_pagealloc(mem, devm_get_free_pages(
dev, gfp_mask,
allocorder); allocorder);
if (!wr_salami) if (!wr_salami)
goto memfail; goto memfail;
left_of_wr_salami = allocsize; left_of_wr_salami = allocsize;
} }
dma_addr = ep->ephw->map_single( rc = ep->ephw->map_single(ep, wr_salami,
mem,
ep,
wr_salami,
bytebufsize, bytebufsize,
DMA_FROM_DEVICE); DMA_FROM_DEVICE,
&dma_addr);
if (!dma_addr) if (rc)
goto dmafail; goto dmafail;
iowrite32( iowrite32(
@ -654,8 +586,8 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
} }
rd_salami = (void *) rd_salami = (void *)
xilly_pagealloc( devm_get_free_pages(
mem, dev, gfp_mask,
allocorder); allocorder);
if (!rd_salami) if (!rd_salami)
@ -663,14 +595,13 @@ static int xilly_setupchannels(struct xilly_endpoint *ep,
left_of_rd_salami = allocsize; left_of_rd_salami = allocsize;
} }
dma_addr = ep->ephw->map_single(
mem,
ep,
rd_salami,
bytebufsize,
DMA_TO_DEVICE);
if (!dma_addr) rc = ep->ephw->map_single(ep, rd_salami,
bytebufsize,
DMA_TO_DEVICE,
&dma_addr);
if (rc)
goto dmafail; goto dmafail;
iowrite32( iowrite32(
@ -712,7 +643,7 @@ memfail:
return -ENOMEM; return -ENOMEM;
dmafail: dmafail:
dev_err(ep->dev, "Failed to map DMA memory!. Aborting.\n"); dev_err(ep->dev, "Failed to map DMA memory!. Aborting.\n");
return -ENOMEM; return rc;
} }
static void xilly_scan_idt(struct xilly_endpoint *endpoint, static void xilly_scan_idt(struct xilly_endpoint *endpoint,
@ -2103,9 +2034,6 @@ struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
endpoint->pdev = pdev; endpoint->pdev = pdev;
endpoint->dev = dev; endpoint->dev = dev;
endpoint->ephw = ephw; endpoint->ephw = ephw;
INIT_LIST_HEAD(&endpoint->cleanup.to_kfree);
INIT_LIST_HEAD(&endpoint->cleanup.to_pagefree);
INIT_LIST_HEAD(&endpoint->cleanup.to_unmap);
endpoint->msg_counter = 0x0b; endpoint->msg_counter = 0x0b;
endpoint->failed_messages = 0; endpoint->failed_messages = 0;
endpoint->fatal_error = 0; endpoint->fatal_error = 0;
@ -2131,7 +2059,7 @@ static int xilly_quiesce(struct xilly_endpoint *endpoint)
if (endpoint->idtlen < 0) { if (endpoint->idtlen < 0) {
dev_err(endpoint->dev, dev_err(endpoint->dev,
"Failed to quiesce the device on exit. Quitting while leaving a mess.\n"); "Failed to quiesce the device on exit.\n");
return -ENODEV; return -ENODEV;
} }
return 0; /* Success */ return 0; /* Success */
@ -2141,8 +2069,9 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
{ {
int rc = 0; int rc = 0;
struct xilly_cleanup tmpmem; void *bootstrap_resources;
int idtbuffersize = (1 << PAGE_SHIFT); int idtbuffersize = (1 << PAGE_SHIFT);
struct device *dev = endpoint->dev;
/* /*
* The bogus IDT is used during bootstrap for allocating the initial * The bogus IDT is used during bootstrap for allocating the initial
@ -2155,10 +2084,6 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
3, 192, PAGE_SHIFT, 0 }; 3, 192, PAGE_SHIFT, 0 };
struct xilly_idt_handle idt_handle; struct xilly_idt_handle idt_handle;
INIT_LIST_HEAD(&tmpmem.to_kfree);
INIT_LIST_HEAD(&tmpmem.to_pagefree);
INIT_LIST_HEAD(&tmpmem.to_unmap);
/* /*
* Writing the value 0x00000001 to Endianness register signals which * Writing the value 0x00000001 to Endianness register signals which
* endianness this processor is using, so the FPGA can swap words as * endianness this processor is using, so the FPGA can swap words as
@ -2170,12 +2095,16 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
/* Bootstrap phase I: Allocate temporary message buffer */ /* Bootstrap phase I: Allocate temporary message buffer */
bootstrap_resources = devres_open_group(dev, NULL, GFP_KERNEL);
if (!bootstrap_resources)
return -ENOMEM;
endpoint->num_channels = 0; endpoint->num_channels = 0;
rc = xilly_setupchannels(endpoint, &tmpmem, bogus_idt, 1); rc = xilly_setupchannels(endpoint, bogus_idt, 1);
if (rc) if (rc)
goto failed_buffers; return rc;
/* Clear the message subsystem (and counter in particular) */ /* Clear the message subsystem (and counter in particular) */
iowrite32(0x04, &endpoint->registers[fpga_msg_ctrl_reg]); iowrite32(0x04, &endpoint->registers[fpga_msg_ctrl_reg]);
@ -2199,8 +2128,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
if (endpoint->idtlen < 0) { if (endpoint->idtlen < 0) {
dev_err(endpoint->dev, "No response from FPGA. Aborting.\n"); dev_err(endpoint->dev, "No response from FPGA. Aborting.\n");
rc = -ENODEV; return -ENODEV;
goto failed_quiesce;
} }
/* Enable DMA */ /* Enable DMA */
@ -2216,7 +2144,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
endpoint->num_channels = 1; endpoint->num_channels = 1;
rc = xilly_setupchannels(endpoint, &tmpmem, bogus_idt, 2); rc = xilly_setupchannels(endpoint, bogus_idt, 2);
if (rc) if (rc)
goto failed_idt; goto failed_idt;
@ -2234,10 +2162,12 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
rc = -ENODEV; rc = -ENODEV;
goto failed_idt; goto failed_idt;
} }
devres_close_group(dev, bootstrap_resources);
/* Bootstrap phase III: Allocate buffers according to IDT */ /* Bootstrap phase III: Allocate buffers according to IDT */
rc = xilly_setupchannels(endpoint, rc = xilly_setupchannels(endpoint,
&endpoint->cleanup,
idt_handle.chandesc, idt_handle.chandesc,
idt_handle.entries); idt_handle.entries);
@ -2260,7 +2190,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
if (rc) if (rc)
goto failed_chrdevs; goto failed_chrdevs;
xillybus_do_cleanup(&tmpmem, endpoint); devres_release_group(dev, bootstrap_resources);
return 0; return 0;
@ -2270,16 +2200,8 @@ failed_chrdevs:
mutex_unlock(&ep_list_lock); mutex_unlock(&ep_list_lock);
failed_idt: failed_idt:
/* Quiesce the device. Now it's serious to do it */ xilly_quiesce(endpoint);
rc = xilly_quiesce(endpoint);
if (rc)
return rc; /* FPGA may still DMA, so no release */
flush_workqueue(xillybus_wq); flush_workqueue(xillybus_wq);
failed_quiesce:
failed_buffers:
xillybus_do_cleanup(&tmpmem, endpoint);
return rc; return rc;
} }

View File

@ -62,44 +62,53 @@ static void xilly_dma_sync_single_nop(struct xilly_endpoint *ep,
{ {
} }
static dma_addr_t xilly_map_single_of(struct xilly_cleanup *mem, static void xilly_of_unmap(void *ptr)
struct xilly_endpoint *ep, {
struct xilly_mapping *data = ptr;
dma_unmap_single(data->device, data->dma_addr,
data->size, data->direction);
kfree(ptr);
}
static int xilly_map_single_of(struct xilly_endpoint *ep,
void *ptr, void *ptr,
size_t size, size_t size,
int direction int direction,
dma_addr_t *ret_dma_handle
) )
{ {
dma_addr_t addr;
struct xilly_mapping *this;
int rc;
dma_addr_t addr = 0; this = kzalloc(sizeof(*this), GFP_KERNEL);
struct xilly_dma *this;
this = kmalloc(sizeof(struct xilly_dma), GFP_KERNEL);
if (!this) if (!this)
return 0; return -ENOMEM;
addr = dma_map_single(ep->dev, ptr, size, direction); addr = dma_map_single(ep->dev, ptr, size, direction);
this->direction = direction;
if (dma_mapping_error(ep->dev, addr)) { if (dma_mapping_error(ep->dev, addr)) {
kfree(this); kfree(this);
return 0; return -ENODEV;
} }
this->device = ep->dev;
this->dma_addr = addr; this->dma_addr = addr;
this->dev = ep->dev;
this->size = size; this->size = size;
this->direction = direction;
list_add_tail(&this->node, &mem->to_unmap); *ret_dma_handle = addr;
return addr; rc = devm_add_action(ep->dev, xilly_of_unmap, this);
}
static void xilly_unmap_single_of(struct xilly_dma *entry) if (rc) {
{ dma_unmap_single(ep->dev, addr, size, direction);
dma_unmap_single(entry->dev, kfree(this);
entry->dma_addr, }
entry->size,
entry->direction); return rc;
} }
static struct xilly_endpoint_hardware of_hw = { static struct xilly_endpoint_hardware of_hw = {
@ -107,7 +116,6 @@ static struct xilly_endpoint_hardware of_hw = {
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of, .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of,
.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of, .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of,
.map_single = xilly_map_single_of, .map_single = xilly_map_single_of,
.unmap_single = xilly_unmap_single_of
}; };
static struct xilly_endpoint_hardware of_hw_coherent = { static struct xilly_endpoint_hardware of_hw_coherent = {
@ -115,7 +123,6 @@ static struct xilly_endpoint_hardware of_hw_coherent = {
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop, .hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop,
.hw_sync_sgl_for_device = xilly_dma_sync_single_nop, .hw_sync_sgl_for_device = xilly_dma_sync_single_nop,
.map_single = xilly_map_single_of, .map_single = xilly_map_single_of,
.unmap_single = xilly_unmap_single_of
}; };
static int xilly_drv_probe(struct platform_device *op) static int xilly_drv_probe(struct platform_device *op)
@ -138,12 +145,6 @@ static int xilly_drv_probe(struct platform_device *op)
dev_set_drvdata(dev, endpoint); dev_set_drvdata(dev, endpoint);
rc = of_address_to_resource(dev->of_node, 0, &res); rc = of_address_to_resource(dev->of_node, 0, &res);
if (rc) {
dev_warn(endpoint->dev,
"Failed to obtain device tree resource\n");
return rc;
}
endpoint->registers = devm_ioremap_resource(dev, &res); endpoint->registers = devm_ioremap_resource(dev, &res);
if (IS_ERR(endpoint->registers)) if (IS_ERR(endpoint->registers))
@ -159,14 +160,7 @@ static int xilly_drv_probe(struct platform_device *op)
return -ENODEV; return -ENODEV;
} }
rc = xillybus_endpoint_discovery(endpoint); return xillybus_endpoint_discovery(endpoint);
if (!rc)
return 0;
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
return rc;
} }
static int xilly_drv_remove(struct platform_device *op) static int xilly_drv_remove(struct platform_device *op)
@ -176,8 +170,6 @@ static int xilly_drv_remove(struct platform_device *op)
xillybus_endpoint_remove(endpoint); xillybus_endpoint_remove(endpoint);
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
return 0; return 0;
} }

View File

@ -72,52 +72,62 @@ static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
xilly_pci_direction(direction)); xilly_pci_direction(direction));
} }
static void xilly_pci_unmap(void *ptr)
{
struct xilly_mapping *data = ptr;
pci_unmap_single(data->device, data->dma_addr,
data->size, data->direction);
kfree(ptr);
}
/* /*
* Map either through the PCI DMA mapper or the non_PCI one. Behind the * Map either through the PCI DMA mapper or the non_PCI one. Behind the
* scenes exactly the same functions are called with the same parameters, * scenes exactly the same functions are called with the same parameters,
* but that can change. * but that can change.
*/ */
static dma_addr_t xilly_map_single_pci(struct xilly_cleanup *mem, static int xilly_map_single_pci(struct xilly_endpoint *ep,
struct xilly_endpoint *ep,
void *ptr, void *ptr,
size_t size, size_t size,
int direction int direction,
dma_addr_t *ret_dma_handle
) )
{ {
dma_addr_t addr = 0;
struct xilly_dma *this;
int pci_direction; int pci_direction;
dma_addr_t addr;
struct xilly_mapping *this;
int rc = 0;
this = kmalloc(sizeof(struct xilly_dma), GFP_KERNEL); this = kzalloc(sizeof(*this), GFP_KERNEL);
if (!this) if (!this)
return 0; return -ENOMEM;
pci_direction = xilly_pci_direction(direction); pci_direction = xilly_pci_direction(direction);
addr = pci_map_single(ep->pdev, ptr, size, pci_direction); addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
this->direction = pci_direction;
if (pci_dma_mapping_error(ep->pdev, addr)) { if (pci_dma_mapping_error(ep->pdev, addr)) {
kfree(this); kfree(this);
return 0; return -ENODEV;
} }
this->device = ep->pdev;
this->dma_addr = addr; this->dma_addr = addr;
this->pdev = ep->pdev;
this->size = size; this->size = size;
this->direction = pci_direction;
list_add_tail(&this->node, &mem->to_unmap); *ret_dma_handle = addr;
return addr; rc = devm_add_action(ep->dev, xilly_pci_unmap, this);
}
static void xilly_unmap_single_pci(struct xilly_dma *entry) if (rc) {
{ pci_unmap_single(ep->pdev, addr, size, pci_direction);
pci_unmap_single(entry->pdev, kfree(this);
entry->dma_addr, }
entry->size,
entry->direction); return rc;
} }
static struct xilly_endpoint_hardware pci_hw = { static struct xilly_endpoint_hardware pci_hw = {
@ -125,7 +135,6 @@ static struct xilly_endpoint_hardware pci_hw = {
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
.map_single = xilly_map_single_pci, .map_single = xilly_map_single_pci,
.unmap_single = xilly_unmap_single_pci
}; };
static int xilly_probe(struct pci_dev *pdev, static int xilly_probe(struct pci_dev *pdev,
@ -199,14 +208,7 @@ static int xilly_probe(struct pci_dev *pdev,
return -ENODEV; return -ENODEV;
} }
rc = xillybus_endpoint_discovery(endpoint); return xillybus_endpoint_discovery(endpoint);
if (!rc)
return 0;
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
return rc;
} }
static void xilly_remove(struct pci_dev *pdev) static void xilly_remove(struct pci_dev *pdev)
@ -214,8 +216,6 @@ static void xilly_remove(struct pci_dev *pdev)
struct xilly_endpoint *endpoint = pci_get_drvdata(pdev); struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
xillybus_endpoint_remove(endpoint); xillybus_endpoint_remove(endpoint);
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
} }
MODULE_DEVICE_TABLE(pci, xillyids); MODULE_DEVICE_TABLE(pci, xillyids);