md raid5 acceleration and async_tx
Subsystems (7)
cover md raid5 acceleration and async_tx
The following patch set implements the async_tx api and modifies md-raid5 to issue memory copies and xor calculations asynchronously. Async_tx is an extension of the existing dmaengine interface in the kernel. Async_tx allows kernel code to utilize application specific acceleration engines when present and fall back to software routines otherwise. Further details can be found in the individual patch headers.
The implementation has been in -mm since 2.6.20-mm1 and has been released in various forms to IOP users since 2.6.18
(http://sourceforge.net/projects/xscaleiop). This release includes cleanups to the iop-adma driver. The md-raid5 patches are just rebased versions of the 2.6.20-rc5 release.
Given the release of an acceleration engine driver for another platform (440spe http://marc.info/?l=linux-raid&m=117400143317440&w=2), I feel more comfortable broaching the subject of 2.6.22 inclusion.
Regards,
Dan
Dan Williams (15):
dmaengine: add base support for the async_tx api
ARM: Add drivers/dma to arch/arm/Kconfig
dmaengine: add the async_tx api
md: add raid5_run_ops and support routines
md: use raid5_run_ops for stripe cache operations
md: move write operations to raid5_run_ops
md: move raid5 compute block operations to raid5_run_ops
md: move raid5 parity checks to raid5_run_ops
md: satisfy raid5 read requests via raid5_run_ops
md: use async_tx and raid5_run_ops for raid5 expansion operations
md: move raid5 io requests to raid5_run_ops
md: remove raid5 compute_block and compute_parity5
dmaengine: driver for the iop32x, iop33x, and iop13xx raid engines
iop13xx: Surface the iop13xx adma units to the iop-adma driver
iop3xx: Surface the iop3xx DMA and AAU units to the iop-adma driver
PATCH 1/15 dmaengine: add base support for the async_tx api
The async_tx api provides methods for describing a chain of asynchronous bulk memory transfers/transforms with support for inter-transactional dependencies. It is implemented as a dmaengine client
that smooths over the details of different hardware offload engine implementations. Code that is written to the api can optimize for asynchrnous operation and the api will fit the chain of operations to the available offload resources.
Currently the raid5 implementation in the MD raid456 driver has been converted to the async_tx api. A driver for the offload engines on the Intel Xscale series of I/O processors, iop-adma, is provided. With the iop-adma driver and async_txi, raid456 is able to offload copy, xor, and xor-zero-sum operations to hardware engines.
On iop342 tiobench showed higher throughput for sequential writes (20 - 30% improvement) and sequential reads to a degraded array (40 - 55% improvement). For the other cases performance was roughly equal, +/- a few percentage points. On a x86-smp platform the performance of the async_tx implementation (in synchronous mode) was also +/- a few percentage points of the original implementation. According to 'top' CPU utilization was positively affected in the offload case, but exact measurements have yet to be taken.
The tiobench command line used for testing was:
tiobench --size 2048 --block 4096 --block 131072 --dir /mnt/raid --numruns 5
* iop342 had 1GB of memory available
This patch:
1/ introduces struct dma_async_tx_descriptor as a common field for all dmaengine software descriptors
2/ converts the device_memcpy_* methods into separate prep, set src/dest, and submit stages
3/ adds support for capabilities beyond memcpy (xor, memset, xor zero sum, completion interrupts)
4/ converts ioatdma to the new semantics
Changelog:
* drop dma mapping methods, suggested by Chris Leech
* fix ioat_dma_dependency_added, also caught by Andrew Morton
* fix dma_sync_wait, change from Andrew Morton
* uninline large functions, change from Andrew Morton
* add tx->callback = NULL to dmaengine calls to interoperate with async_tx calls
---
drivers/dma/dmaengine.c | 194 ++++++++++++++++++++++++++++++++++- drivers/dma/ioatdma.c | 248 ++++++++++++++++++++------------------------- drivers/dma/ioatdma.h | 8 + include/linux/dmaengine.h | 237 ++++++++++++++++++++++++------------------- 4 files changed, 439 insertions(+), 248 deletions(-)
drivers/dma/dmaengine.c +189 -5
@@ -59,6 +59,7 @@#include <linux/init.h> #include <linux/module.h> +#include <linux/mm.h> #include <linux/device.h> #include <linux/dmaengine.h> #include <linux/hardirq.h>
@@ -66,6 +67,7 @@#include <linux/percpu.h> #include <linux/rcupdate.h> #include <linux/mutex.h> +#include <linux/jiffies.h> static DEFINE_MUTEX (dma_list_mutex);static LIST_HEAD (dma_device_list);
@@ -165,6 +167,24 @@ static struct dma_chan *dma_client_chan_alloc(struct dma_client *client)return NULL ; } +enum dma_status dma_sync_wait (struct dma_chan *chan,dma_cookie_t cookie) +{ +enum dma_status status; +unsigned long dma_sync_wait_timeout = jiffies +msecs_to_jiffies (5000 ); + +dma_async_issue_pending (chan); +do { + status =dma_async_is_tx_complete (chan, cookie,NULL ,NULL ); +if (time_after_eq (jiffies, dma_sync_wait_timeout)) { +printk (KERN_ERR "dma_sync_wait_timeout!\n" ); +return DMA_ERROR ; + } + }while (status ==DMA_IN_PROGRESS ); + +return status; +} +EXPORT_SYMBOL (dma_sync_wait); +/** * dma_chan_cleanup - release a DMA channel's resources * @kref: kernel reference structure that contains the DMA channel device
@@ -211,7 +231,8 @@ static void dma_chans_rebalance(void)mutex_lock (&dma_list_mutex);list_for_each_entry (client, &dma_client_list, global_node) { -while (client->chans_desired > client->chan_count) { +while (client->chans_desired <0 || + client->chans_desired > client->chan_count) { chan =dma_client_chan_alloc (client);if (!chan)break ;
@@ -220,7 +241,8 @@ static void dma_chans_rebalance(void) chan, DMA_RESOURCE_ADDED); } -while (client->chans_desired < client->chan_count) { +while (client->chans_desired >=0 && + client->chans_desired < client->chan_count) {spin_lock_irqsave (&client->lock, flags); chan = list_entry(client->channels.next, struct dma_chan,
@@ -297,12 +319,12 @@ EXPORT_SYMBOL(dma_async_client_unregister);* @number: count of DMA channels requested * * Clients call dma_async_client_chan_request() to specify how many -* DMA channels they need, 0 to free all currently allocated. +* DMA channels they need, 0 to free all currently allocated. A request +* < 0 indicates the client wants to handle all engines in the system. * The resulting allocations/frees are indicated to the client via the * event callback. */ -void dma_async_client_chan_request (struct dma_client *client, -unsigned int number) +void dma_async_client_chan_request (struct dma_client *client,int number) { client->chans_desired = number;dma_chans_rebalance ();
@@ -322,6 +344,28 @@ int dma_async_device_register(struct dma_device *device)if (!device)return -ENODEV ; +/* validate device routines */ +BUG_ON (test_bit (DMA_MEMCPY , &device->capabilities) && + !device->device_prep_dma_memcpy); +BUG_ON (test_bit (DMA_XOR , &device->capabilities) && + !device->device_prep_dma_xor); +BUG_ON (test_bit (DMA_ZERO_SUM , &device->capabilities) && + !device->device_prep_dma_zero_sum); +BUG_ON (test_bit (DMA_MEMSET , &device->capabilities) && + !device->device_prep_dma_memset); +BUG_ON (test_bit (DMA_ZERO_SUM , &device->capabilities) && + !device->device_prep_dma_interrupt); + +BUG_ON (!device->device_alloc_chan_resources); +BUG_ON (!device->device_free_chan_resources); +BUG_ON (!device->device_tx_submit); +BUG_ON (!device->device_set_dest); +BUG_ON (!device->device_set_src); +BUG_ON (!device->device_dependency_added); +BUG_ON (!device->device_is_tx_complete); +BUG_ON (!device->device_issue_pending); +BUG_ON (!device->dev); +init_completion (&device->done);kref_init (&device->refcount); device->dev_id = id++;
@@ -397,6 +441,146 @@ void dma_async_device_unregister(struct dma_device *device) }EXPORT_SYMBOL (dma_async_device_unregister); +/** +* dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses +* @chan: DMA channel to offload copy to +* @dest: destination address (virtual) +* @src: source address (virtual) +* @len: length +* +* Both @dest and @src must be mappable to a bus address according to the +* DMA mapping API rules for streaming mappings. +* Both @dest and @src must stay memory resident (kernel memory or locked +* user space pages). +*/ +dma_cookie_t dma_async_memcpy_buf_to_buf (struct dma_chan *chan, +void *dest,void *src,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_single (dev->dev, src, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_single (dev->dev, dest, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_buf_to_buf); + +/** +* dma_async_memcpy_buf_to_pg - offloaded copy from address to page +* @chan: DMA channel to offload copy to +* @page: destination page +* @offset: offset in page to copy to +* @kdata: source address (virtual) +* @len: length +* +* Both @page/@offset and @kdata must be mappable to a bus address according +* to the DMA mapping API rules for streaming mappings. +* Both @page/@offset and @kdata must stay memory resident (kernel memory or +* locked user space pages) +*/ +dma_cookie_t dma_async_memcpy_buf_to_pg (struct dma_chan *chan, +struct page *page,unsigned int offset,void *kdata,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_single (dev->dev, kdata, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_page (dev->dev, page, offset, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_buf_to_pg); + +/** +* dma_async_memcpy_pg_to_pg - offloaded copy from page to page +* @chan: DMA channel to offload copy to +* @dest_pg: destination page +* @dest_off: offset in page to copy to +* @src_pg: source page +* @src_off: offset in page to copy from +* @len: length +* +* Both @dest_page/@dest_off and @src_page/@src_off must be mappable to a bus +* address according to the DMA mapping API rules for streaming mappings. +* Both @dest_page/@dest_off and @src_page/@src_off must stay memory resident +* (kernel memory or locked user space pages). +*/ +dma_cookie_t dma_async_memcpy_pg_to_pg (struct dma_chan *chan, +struct page *dest_pg,unsigned int dest_off,struct page *src_pg, +unsigned int src_off,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_page (dev->dev, src_pg, src_off, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_page (dev->dev, dest_pg, dest_off, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_pg_to_pg); + +void dma_async_tx_descriptor_init (struct dma_async_tx_descriptor *tx, +struct dma_chan *chan) +{ + tx->chan = chan; +spin_lock_init (&tx->lock); +INIT_LIST_HEAD (&tx->depend_node); +INIT_LIST_HEAD (&tx->depend_list); +} +EXPORT_SYMBOL (dma_async_tx_descriptor_init); +static int __init dma_bus_init (void ) {mutex_init (&dma_list_mutex);
drivers/dma/ioatdma.c +110 -138
370 lines not on this page · raw message
drivers/dma/ioatdma.h +6 -2
@@ -105,15 +105,20 @@ struct ioat_dma_chan {/** * struct ioat_desc_sw - wrapper around hardware descriptor * @hw: hardware DMA descriptor +* @async_tx: * @node: +* @group_list: +* @group_cnt: * @cookie: * @phys: */ struct ioat_desc_sw {struct ioat_dma_descriptor *hw; +struct dma_async_tx_descriptor async_tx;struct list_head node; -dma_cookie_t cookie; +struct list_head group_list; +int group_count;dma_addr_t phys;DECLARE_PCI_UNMAP_ADDR (src) DECLARE_PCI_UNMAP_LEN(src_len )
@@ -122,4 +127,3 @@ struct ioat_desc_sw { };#endif /* IOATDMA_H */ -
include/linux/dmaengine.h +134 -103
330 lines not on this page · raw message
Re: [PATCH 2.6.21-rc4 01/15] dmaengine: add base support for the async_tx api
Hi Dan,
Functions dma_async_memcpy_buf_to_buf(), dma_async_memcpy_buf_to_pg() anddma_async_memcpy_pg_to_pg() are practically identical. Maybe it makes sense to extract the common part into a separate inline function? Somehow like this:
static inline dma_cookie_t dma_async_memcpy (
struct dma_chan *chan,
dma_addr_t src,
dma_addr_t dst,
size_t len)
{
struct dma_device *dev = chan->device;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
int cpu;
tx = dev->device_prep_dma_memcpy(chan, len, 0);
if (!tx)
return -ENOMEM;
tx->ack = 1;
tx->callback = NULL;
dev->device_set_src(src, tx, 0);
dev->device_set_dest(dst, tx, 0);
cookie = dev->device_tx_submit(tx);
cpu = get_cpu();
per_cpu_ptr(chan->local, cpu)->bytes_transferred += len;
per_cpu_ptr(chan->local, cpu)->memcpy_count++;
put_cpu();
return cookie;
}dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan,
void *dest, void *src, size_t len)
{
dma_addr_t dsrc, ddst;
dsrc = dma_map_single(chan->device->dev, src, len, DMA_TO_DEVICE);
ddst = dma_map_single(chan->device->dev, dest, len, DMA_FROM_DEVICE);
return dma_async_memcpy(chan, dsrc, ddst, len);
}dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan,
struct page *page, unsigned int offset, void *kdata, size_t len)
{
dma_addr_t dsrc, ddst;
dsrc = dma_map_single(chan->device->dev, kdata, len, DMA_TO_DEVICE);
ddst = dma_map_page(chan->device->dev, page, offset, len, DMA_FROM_DEVICE);
return dma_async_memcpy(chan, dsrc, ddst, len);
}dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan,
struct page *dest_pg, unsigned int dest_off, struct page *src_pg, unsigned int src_off, size_t len)
{
dma_addr_t dsrc, ddst;
dsrc = dma_map_page(chan->device->dev, src_pg, src_off, len, DMA_TO_DEVICE);
ddst = dma_map_page(chan->device->dev,dest_pg,dest_off, len,DMA_FROM_DEVICE);
return dma_async_memcpy(chan, dsrc, ddst, len);
}Regards, Yuri.
Quoted 137 lines · you
The
async_txapi provides methods for describing a chain of asynchronous
...drivers/dma/dmaengine.c +51
-0+/** +* dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses +* @chan: DMA channel to offload copy to +* @dest: destination address (virtual) +* @src: source address (virtual) +* @len: length +* +* Both @dest and @src must be mappable to a bus address according to the +* DMA mapping API rules for streaming mappings. +* Both @dest and @src must stay memory resident (kernel memory or locked +* user space pages). +*/ +dma_cookie_t dma_async_memcpy_buf_to_buf (struct dma_chan *chan, +void *dest,void *src,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_single (dev->dev, src, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_single (dev->dev, dest, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_buf_to_buf); + +/** +* dma_async_memcpy_buf_to_pg - offloaded copy from address to page +* @chan: DMA channel to offload copy to +* @page: destination page +* @offset: offset in page to copy to +* @kdata: source address (virtual) +* @len: length +* +* Both @page/@offset and @kdata must be mappable to a bus address according + * to the DMA mapping API rules for streaming mappings.
+ * Both @page/@offset and @kdata must stay memory resident (kernel memory or + * locked user space pages)
+ */drivers/dma/dmaengine.c +40
-0+dma_cookie_t dma_async_memcpy_buf_to_pg (struct dma_chan *chan, +struct page *page,unsigned int offset,void *kdata,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_single (dev->dev, kdata, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_page (dev->dev, page, offset, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_buf_to_pg); + +/** +* dma_async_memcpy_pg_to_pg - offloaded copy from page to page +* @chan: DMA channel to offload copy to +* @dest_pg: destination page +* @dest_off: offset in page to copy to +* @src_pg: source page +* @src_off: offset in page to copy from +* @len: length +* +* Both @dest_page/@dest_off and @src_page/@src_off must be mappable to a bus + * address according to the DMA mapping API rules for streaming mappings. + * Both @dest_page/@dest_off and @src_page/@src_off must stay memory resident + * (kernel memory or locked user space pages).
+ */drivers/dma/dmaengine.c +30
-0+dma_cookie_t dma_async_memcpy_pg_to_pg (struct dma_chan *chan, +struct page *dest_pg,unsigned int dest_off,struct page *src_pg, +unsigned int src_off,size_t len) +{ +struct dma_device *dev = chan->device; +struct dma_async_tx_descriptor *tx; +dma_addr_t addr; +dma_cookie_t cookie; +int cpu; + + tx = dev->device_prep_dma_memcpy (chan, len,0 ); +if (!tx) +return -ENOMEM ; + + tx->ack =1 ; + tx->callback =NULL ; + addr =dma_map_page (dev->dev, src_pg, src_off, len,DMA_TO_DEVICE ); + dev->device_set_src (addr, tx,0 ); + addr =dma_map_page (dev->dev, dest_pg, dest_off, len,DMA_FROM_DEVICE ); + dev->device_set_dest (addr, tx,0 ); + cookie = dev->device_tx_submit (tx); + + cpu =get_cpu (); +per_cpu_ptr (chan->local, cpu)->bytes_transferred += len; +per_cpu_ptr (chan->local, cpu)->memcpy_count++; +put_cpu (); + +return cookie; +} +EXPORT_SYMBOL (dma_async_memcpy_pg_to_pg);
Re: [PATCH 2.6.21-rc4 01/15] dmaengine: add base support for the async_tx api
Quoted 7 lines · Yuri Tikhonov
Hi Dan,
Functions
dma_async_memcpy_buf_to_buf(),dma_async_memcpy_buf_to_pg()anddma_async_memcpy_pg_to_pg()are practically identical. Maybe it makes sense to extract the common part into a separate inline function? Somehow like this:
ok, I'll pull this in.
Thanks,
Dan
PATCH 2/15 ARM: Add drivers/dma to arch/arm/Kconfig
In reply to Dan Williams---
arch/arm/Kconfig | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
arch/arm/Kconfig +2 -0
@@ -997,6 +997,8 @@ source "drivers/mmc/Kconfig" source "drivers/rtc/Kconfig" +source "drivers/dma/Kconfig" + endmenu source "fs/Kconfig"
PATCH 3/15 dmaengine: add the async_tx api
In reply to Dan Williamsasync_tx is an api to describe a series of bulk memory transfers/transforms. When possible these transactions are carried out by asynchrounous dma engines. The api handles inter-transaction dependencies and hides dma channel management from the client. When a dma engine is not present the transaction is carried out via synchronous software routines.
Xor operations are handled by async_tx, to this end xor.c is moved into drivers/dma and is changed to take an explicit destination address and a series of sources to match the hardware engine implementation.
When CONFIG_DMA_ENGINE is not set the asynchrounous path is compiled away.
Changelog:
* fixed a leftover debug print
* don't allow callbacks in async_interrupt_cond
* fixed xor_block changes
* fixed usage of ASYNC_TX_XOR_DROP_DEST
* drop dma mapping methods, suggested by Chris Leech
* printk warning fixups from Andrew Morton
* don't use inline in C files, Adrian Bunk
* select the API when MD is enabled
---
drivers/Makefile | 1 drivers/dma/Kconfig | 15 + drivers/dma/Makefile | 1 drivers/dma/async_tx.c | 905 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/dma/xor.c | 153 ++++++++ drivers/md/Kconfig | 3 drivers/md/Makefile | 6 drivers/md/raid5.c | 52 +-- drivers/md/xor.c | 154 -------- include/linux/async_tx.h | 180 +++++++++ include/linux/raid/xor.h | 5 11 files changed, 1286 insertions(+), 189 deletions(-)
drivers/Makefile +1 -0
@@ -62,6 +62,7 @@ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_PHONE) += telephony/ +obj-$(CONFIG_ASYNC_TX_DMA) += dma/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_ISDN) += isdn/
drivers/dma/Kconfig +12 -3
@@ -7,8 +7,8 @@ menu "DMA Engine support" config DMA_ENGINE bool "Support for DMA engines" ---help--- - DMA engines offload copy operations from the CPU to dedicated - hardware, allowing the copies to happen asynchronously. + DMA engines offload bulk memory operations from the CPU to dedicated + hardware, allowing the operations to happen asynchronously. comment "DMA Clients"
@@ -22,6 +22,16 @@ config NET_DMA Since this is the main user of the DMA engine, it should be enabled; say Y here. +config ASYNC_TX_DMA + tristate "Asynchronous Bulk Memory Transfers/Transforms API" + ---help--- + This enables the async_tx management layer for dma engines. + Subsystems coded to this API will use offload engines for bulk + memory operations where present. Software implementations are + called when a dma engine is not present or fails to allocate + memory to carry out the transaction. + Current subsystems ported to async_tx: MD_RAID4,5 + comment "DMA Devices" config INTEL_IOATDMA
@@ -30,5 +40,4 @@ config INTEL_IOATDMA default m ---help--- Enable support for the Intel(R) I/OAT DMA engine. - endmenu
drivers/dma/Makefile +1 -0
@@ -1,3 +1,4 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o +obj-$(CONFIG_ASYNC_TX_DMA) += async_tx.o xor.o
drivers/dma/async_tx.c new file +905 -0
905 lines not on this page · raw message
drivers/dma/xor.c new file +153 -0
153 lines not on this page · raw message
drivers/md/Kconfig +2 -1
@@ -8,6 +8,7 @@ menu "Multi-device support (RAID and LVM)" config MD bool "Multiple devices driver support (RAID and LVM)" + select ASYNC_TX_DMA help Support multiple physical spindles through a single logical device. Required for RAID and logical volume management.
@@ -108,7 +109,7 @@ config MD_RAID10 config MD_RAID456 tristate "RAID-4/RAID-5/RAID-6 mode" - depends on BLK_DEV_MD + depends on BLK_DEV_MD && ASYNC_TX_DMA ---help--- A RAID-5 set of N drives with a capacity of C MB per drive provides the capacity of C * (N - 1) MB, and protects against a failure
drivers/md/Makefile +3 -3
@@ -17,15 +17,15 @@ raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \ hostprogs-y := mktables # Note: link order is important. All raid personalities -# and xor.o must come before md.o, as they each initialise -# themselves, and md.o may use the personalities when it +# must come before md.o, as they each initialise +# themselves, and md.o may use the personalities when it # auto-initialised. obj-$(CONFIG_MD_LINEAR) += linear.o obj-$(CONFIG_MD_RAID0) += raid0.o obj-$(CONFIG_MD_RAID1) += raid1.o obj-$(CONFIG_MD_RAID10) += raid10.o -obj-$(CONFIG_MD_RAID456) += raid456.o xor.o +obj-$(CONFIG_MD_RAID456) += raid456.o obj-$(CONFIG_MD_MULTIPATH) += multipath.o obj-$(CONFIG_MD_FAULTY) += faulty.o obj-$(CONFIG_BLK_DEV_MD) += md-mod.o
drivers/md/raid5.c +26 -26
121 lines not on this page · raw message
drivers/md/xor.c deleted +0 -154
154 lines not on this page · raw message
include/linux/async_tx.h new file +180 -0
180 lines not on this page · raw message
include/linux/raid/xor.h +3 -2
@@ -3,9 +3,10 @@#include <linux/raid/md.h> -#define MAX_XOR_BLOCKS 5 +#define MAX_XOR_BLOCKS 4 -extern void xor_block (unsigned int count,unsigned int bytes,void **ptr); +extern void xor_block (unsigned int count,unsigned int bytes, +void *dest,void **srcs);struct xor_block_template {struct xor_block_template *next;
PATCH 4/15 md: add raid5_run_ops and support routines
In reply to Dan WilliamsPrepare the raid5 implementation to use async_tx for running stripe operations:
* biofill (copy data into request buffers to satisfy a read request)
* compute block (generate a missing block in the cache from the other blocks)
* prexor (subtract existing data as part of the read-modify-write process)
* biodrain (copy data out of request buffers to satisfy a write request)
* postxor (recalculate parity for new data that has entered the cache)
* check (verify that the parity is correct)
* io (submit i/o to the member disks)
Changelog:
* removed ops_complete_biodrain in favor of ops_complete_postxor and ops_complete_write.
* removed the workqueue
* call bi_end_io for reads in ops_complete_biofill
---
drivers/md/raid5.c | 520 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/raid/raid5.h | 63 +++++ 2 files changed, 580 insertions(+), 3 deletions(-)
drivers/md/raid5.c +520 -0
@@ -52,6 +52,7 @@#include "raid6.h" #include <linux/raid/bitmap.h> +#include <linux/async_tx.h> /* * Stripe cache
@@ -324,6 +325,525 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sectorreturn sh; } +static int +raid5_end_read_request (struct bio * bi,unsigned int bytes_done,int error); +static int +raid5_end_write_request (struct bio *bi,unsigned int bytes_done,int error); + +static void ops_run_io (struct stripe_head *sh) +{ +raid5_conf_t *conf = sh->raid_conf; +int i, disks = sh->disks; + +might_sleep (); + +for (i=disks; i-- ;) { +int rw; +struct bio *bi; +mdk_rdev_t *rdev; +if (test_and_clear_bit (R5_Wantwrite, &sh->dev[i].flags)) + rw =WRITE ; +else if (test_and_clear_bit (R5_Wantread, &sh->dev[i].flags)) + rw =READ ; +else +continue ; + + bi = &sh->dev[i].req; + + bi->bi_rw = rw; +if (rw ==WRITE ) + bi->bi_end_io = raid5_end_write_request; +else + bi->bi_end_io = raid5_end_read_request; + +rcu_read_lock (); + rdev =rcu_dereference (conf->disks[i].rdev); +if (rdev &&test_bit (Faulty, &rdev->flags)) + rdev =NULL ; +if (rdev) +atomic_inc (&rdev->nr_pending); +rcu_read_unlock (); + +if (rdev) { +if (test_bit (STRIPE_SYNCING , &sh->state) || +test_bit (STRIPE_EXPAND_SOURCE , &sh->state) || +test_bit (STRIPE_EXPAND_READY , &sh->state)) +md_sync_acct (rdev->bdev,STRIPE_SECTORS ); + + bi->bi_bdev = rdev->bdev; +PRINTK ("%s: for %llu schedule op %ld on disc %d\n" , + __FUNCTION__, (unsigned long long )sh->sector, + bi->bi_rw, i); +atomic_inc (&sh->count); + bi->bi_sector = sh->sector + rdev->data_offset; + bi->bi_flags =1 <<BIO_UPTODATE ; + bi->bi_vcnt =1 ; + bi->bi_max_vecs =1 ; + bi->bi_idx =0 ; + bi->bi_io_vec = &sh->dev[i].vec; + bi->bi_io_vec[0 ].bv_len =STRIPE_SIZE ; + bi->bi_io_vec[0 ].bv_offset =0 ; + bi->bi_size =STRIPE_SIZE ; + bi->bi_next =NULL ; +if (rw ==WRITE && +test_bit (R5_ReWrite, &sh->dev[i].flags)) +atomic_add (STRIPE_SECTORS , &rdev->corrected_errors); +generic_make_request (bi); + }else { +if (rw ==WRITE ) +set_bit (STRIPE_DEGRADED , &sh->state); +PRINTK ("skip op %ld on disc %d for sector %llu\n" , + bi->bi_rw, i, (unsigned long long )sh->sector); +clear_bit (R5_LOCKED , &sh->dev[i].flags); +set_bit (STRIPE_HANDLE , &sh->state); + } + } +} + +static struct dma_async_tx_descriptor * +async_copy_data (int frombio,struct bio *bio,struct page *page,sector_t sector, +struct dma_async_tx_descriptor *tx) +{ +struct bio_vec *bvl; +struct page *bio_page; +int i; +int page_offset; + +if (bio->bi_sector >= sector) + page_offset = (signed )(bio->bi_sector - sector) *512 ; +else + page_offset = (signed )(sector - bio->bi_sector) *-512 ; +bio_for_each_segment (bvl, bio, i) { +int len =bio_iovec_idx (bio,i)->bv_len; +int clen; +int b_offset =0 ; + +if (page_offset <0 ) { + b_offset = -page_offset; + page_offset += b_offset; + len -= b_offset; + } + +if (len >0 && page_offset + len >STRIPE_SIZE ) + clen =STRIPE_SIZE - page_offset; +else clen = len; + +if (clen >0 ) { + b_offset +=bio_iovec_idx (bio,i)->bv_offset; + bio_page =bio_iovec_idx (bio,i)->bv_page; +if (frombio) + tx =async_memcpy (page, bio_page, page_offset, + b_offset, clen, +ASYNC_TX_DEP_ACK |ASYNC_TX_KMAP_SRC , + tx,NULL ,NULL ); +else + tx =async_memcpy (bio_page, page, b_offset, + page_offset, clen, +ASYNC_TX_DEP_ACK |ASYNC_TX_KMAP_DST , + tx,NULL ,NULL ); + } +if (clen < len)/* hit end of page */ +break ; + page_offset += len; + } + +return tx; +} + +static void ops_complete_biofill (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; +struct bio *return_bi =NULL , *bi; +int i; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +/* clear completed biofills */ +spin_lock (&sh->lock); +BUG_ON (!test_and_clear_bit (STRIPE_OP_BIOFILL , &sh->ops.ack)); +BUG_ON (!test_and_clear_bit (STRIPE_OP_BIOFILL , &sh->ops.pending)); +for (i=sh->disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +/* acknowledge completion of a biofill operation */ +if (test_bit (R5_Wantfill, &dev->flags) && !dev->toread) +clear_bit (R5_Wantfill, &dev->flags); + +/* reply to the read */ +if (dev->read && !test_bit (R5_Wantfill, &dev->flags) && + !test_bit (STRIPE_OP_BIOFILL , &sh->ops.pending)) { + return_bi = dev->read; + dev->read =NULL ; + } + } +spin_unlock (&sh->lock); + +while ((bi=return_bi)) { +int bytes = bi->bi_size; + + return_bi = bi->bi_next; + bi->bi_next =NULL ; + bi->bi_size =0 ; + bi->bi_end_io (bi, bytes, +test_bit (BIO_UPTODATE , &bi->bi_flags) + ?0 : -EIO ); + } + +release_stripe (sh); +} + +static void ops_run_biofill (struct stripe_head *sh) +{ +struct bio *return_bi =NULL ; +struct dma_async_tx_descriptor *tx =NULL ; +raid5_conf_t *conf = sh->raid_conf; +int i; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +for (i=sh->disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +if (test_bit (R5_Wantfill, &dev->flags)) { +struct bio *rbi, *rbi2; +spin_lock_irq (&conf->device_lock); + rbi = dev->toread; + dev->toread =NULL ; +spin_unlock_irq (&conf->device_lock); +while (rbi && rbi->bi_sector < dev->sector +STRIPE_SECTORS ) { + tx =async_copy_data (0 , rbi, dev->page, + dev->sector, tx); + rbi2 =r5_next_bio (rbi, dev->sector); +spin_lock_irq (&conf->device_lock); +if (--rbi->bi_phys_segments ==0 ) { + rbi->bi_next = return_bi; + return_bi = rbi; + } +spin_unlock_irq (&conf->device_lock); + rbi = rbi2; + } + dev->read = return_bi; + } + } + +atomic_inc (&sh->count); +async_interrupt (ASYNC_TX_DEP_ACK |ASYNC_TX_ACK , tx, + ops_complete_biofill, sh); +} + +static void ops_complete_compute5 (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; +int target = sh->ops.target; +struct r5dev *tgt = &sh->dev[target]; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +set_bit (R5_UPTODATE , &tgt->flags); +BUG_ON (!test_and_clear_bit (R5_Wantcompute, &tgt->flags)); +BUG_ON (test_and_set_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.complete)); +set_bit (STRIPE_HANDLE , &sh->state); +release_stripe (sh); +} + +static struct dma_async_tx_descriptor * +ops_run_compute5 (struct stripe_head *sh,unsigned long pending) +{ +/* kernel stack size limits the total number of disks */ +int disks = sh->disks; +struct page *xor_srcs[disks]; +int target = sh->ops.target; +struct r5dev *tgt = &sh->dev[target]; +struct page *xor_dest = tgt->page; +int count =0 ; +struct dma_async_tx_descriptor *tx; +int i; + +PRINTK ("%s: stripe %llu block: %d\n" , + __FUNCTION__, (unsigned long long )sh->sector, target); +BUG_ON (!test_bit (R5_Wantcompute, &tgt->flags)); + +for (i=disks ; i-- ; ) +if (i != target) + xor_srcs[count++] = sh->dev[i].page; + +atomic_inc (&sh->count); + + tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , +ASYNC_TX_XOR_ZERO_DST |ASYNC_TX_INT_EN ,NULL , + ops_complete_compute5, sh); + +/* ack now if postxor is not set to be run */ +if (tx && !test_bit (STRIPE_OP_POSTXOR , &pending)) +async_tx_ack (tx); + +return tx; +} + +static void ops_complete_prexor (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +set_bit (STRIPE_OP_PREXOR , &sh->ops.complete); +} + +static struct dma_async_tx_descriptor * +ops_run_prexor (struct stripe_head *sh,struct dma_async_tx_descriptor *tx) +{ +/* kernel stack size limits the total number of disks */ +int disks = sh->disks; +struct page *xor_srcs[disks]; +int count =0 , pd_idx = sh->pd_idx, i; + +/* existing parity data subtracted */ +struct page *xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +for (i=disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +/* Only process blocks that are known to be uptodate */ +if (dev->towrite &&test_bit (R5_Wantprexor, &dev->flags)) + xor_srcs[count++] = dev->page; + } + + tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , +ASYNC_TX_DEP_ACK |ASYNC_TX_XOR_DROP_DST , tx, + ops_complete_prexor, sh); + +/* trigger a channel switch if necesary */ + tx =async_interrupt_cond (DMA_MEMCPY ,ASYNC_TX_DEP_ACK , tx); + +return tx; +} + +static struct dma_async_tx_descriptor * +ops_run_biodrain (struct stripe_head *sh,struct dma_async_tx_descriptor *tx) +{ +int disks = sh->disks; +int pd_idx = sh->pd_idx, i; + +/* check if prexor is active which means only process blocks +* that are part of a read-modify-write (Wantprexor) +*/ +int prexor =test_bit (STRIPE_OP_PREXOR , &sh->ops.pending); + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +for (i=disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +struct bio *chosen; +int towrite; + + towrite =0 ; +if (prexor) {/* rmw */ +if (dev->towrite &&test_bit (R5_Wantprexor, &dev->flags)) + towrite =1 ; + }else {/* rcw */ +if (i!=pd_idx && dev->towrite && +test_bit (R5_LOCKED , &dev->flags)) + towrite =1 ; + } + +if (towrite) { +struct bio *wbi; + +spin_lock (&sh->lock); + chosen = dev->towrite; + dev->towrite =NULL ; +BUG_ON (dev->written); + wbi = dev->written = chosen; +spin_unlock (&sh->lock); + +while (wbi && wbi->bi_sector < dev->sector +STRIPE_SECTORS ) { + tx =async_copy_data (1 , wbi, dev->page, + dev->sector, tx); + wbi =r5_next_bio (wbi, dev->sector); + } + } + } + +return tx; +} + +static void ops_complete_postxor (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +BUG_ON (test_and_set_bit (STRIPE_OP_POSTXOR , &sh->ops.complete)); +set_bit (STRIPE_HANDLE , &sh->state); +release_stripe (sh); +} + +static void ops_complete_write (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; +int disks = sh->disks, i, pd_idx = sh->pd_idx; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +for (i=disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +if (dev->written || i == pd_idx) +set_bit (R5_UPTODATE , &dev->flags); + } + +BUG_ON (test_and_set_bit (STRIPE_OP_BIODRAIN , &sh->ops.complete)); +BUG_ON (test_and_set_bit (STRIPE_OP_POSTXOR , &sh->ops.complete)); + +set_bit (STRIPE_HANDLE , &sh->state); +release_stripe (sh); +} + +static void +ops_run_postxor (struct stripe_head *sh,struct dma_async_tx_descriptor *tx) +{ +/* kernel stack size limits the total number of disks */ +int disks = sh->disks; +struct page *xor_srcs[disks]; + +int count =0 , pd_idx = sh->pd_idx, i; +struct page *xor_dest; +int prexor =test_bit (STRIPE_OP_PREXOR , &sh->ops.pending); +unsigned long flags; +dma_async_tx_callback callback; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +/* check if prexor is active which means only process blocks +* that are part of a read-modify-write (written) +*/ +if (prexor) { + xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; +for (i=disks; i--;) { +struct r5dev *dev = &sh->dev[i]; +if (dev->written) + xor_srcs[count++] = dev->page; + } + }else { + xor_dest = sh->dev[pd_idx].page; +for (i=disks; i--;) { +struct r5dev *dev = &sh->dev[i]; +if (i!=pd_idx) + xor_srcs[count++] = dev->page; + } + } + +/* check whether this postxor is part of a write */ + callback =test_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending) ? + ops_complete_write : ops_complete_postxor; + +/* 1/ if we prexor'd then the dest is reused as a source +* 2/ if we did not prexor then we are redoing the parity +* set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST +* for the synchronous xor case +*/ + flags =ASYNC_TX_DEP_ACK |ASYNC_TX_ACK |ASYNC_TX_INT_EN | + (prexor ?ASYNC_TX_XOR_DROP_DST :ASYNC_TX_XOR_ZERO_DST ); + +atomic_inc (&sh->count); + tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , + flags, tx, callback, sh); +} + +static void ops_complete_check (void *stripe_head_ref) +{ +struct stripe_head *sh = stripe_head_ref; +int pd_idx = sh->pd_idx; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +if (test_and_clear_bit (STRIPE_OP_MOD_DMA_CHECK , &sh->ops.pending) && + sh->ops.zero_sum_result ==0 ) +set_bit (R5_UPTODATE , &sh->dev[pd_idx].flags); + +BUG_ON (test_and_set_bit (STRIPE_OP_CHECK , &sh->ops.complete)); +set_bit (STRIPE_HANDLE , &sh->state); +release_stripe (sh); +} + +static void ops_run_check (struct stripe_head *sh) +{ +/* kernel stack size limits the total number of disks */ +int disks = sh->disks; +struct page *xor_srcs[disks]; +struct dma_async_tx_descriptor *tx; + +int count =0 , pd_idx = sh->pd_idx, i; +struct page *xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +for (i=disks; i--;) { +struct r5dev *dev = &sh->dev[i]; +if (i != pd_idx) + xor_srcs[count++] = dev->page; + } + + tx =async_xor_zero_sum (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , + &sh->ops.zero_sum_result,0 ,NULL ,NULL ,NULL ); + +if (tx) +set_bit (STRIPE_OP_MOD_DMA_CHECK , &sh->ops.pending); +else +clear_bit (STRIPE_OP_MOD_DMA_CHECK , &sh->ops.pending); + +atomic_inc (&sh->count); + tx =async_interrupt (ASYNC_TX_DEP_ACK |ASYNC_TX_ACK , tx, + ops_complete_check, sh); +} + +static void raid5_run_ops (struct stripe_head *sh,unsigned long pending) +{ +int overlap_clear=0 , i, disks = sh->disks; +struct dma_async_tx_descriptor *tx =NULL ; + +if (test_bit (STRIPE_OP_BIOFILL , &pending)) { +ops_run_biofill (sh); + overlap_clear++; + } + +if (test_bit (STRIPE_OP_COMPUTE_BLK , &pending)) + tx =ops_run_compute5 (sh, pending); + +if (test_bit (STRIPE_OP_PREXOR , &pending)) + tx =ops_run_prexor (sh, tx); + +if (test_bit (STRIPE_OP_BIODRAIN , &pending)) { + tx =ops_run_biodrain (sh, tx); + overlap_clear++; + } + +if (test_bit (STRIPE_OP_POSTXOR , &pending)) +ops_run_postxor (sh, tx); + +if (test_bit (STRIPE_OP_CHECK , &pending)) +ops_run_check (sh); + +if (test_bit (STRIPE_OP_IO , &pending)) +ops_run_io (sh); + +if (overlap_clear) +for (i=disks; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +if (test_and_clear_bit (R5_Overlap, &dev->flags)) +wake_up (&sh->raid_conf->wait_for_overlap); + } +} +static int grow_one_stripe (raid5_conf_t *conf) {struct stripe_head *sh;
include/linux/raid/raid5.h +60 -3
96 lines not on this page · raw message
PATCH 5/15 md: use raid5_run_ops for stripe cache operations
In reply to Dan WilliamsEach stripe has three flag variables to reflect the state of operations (pending, ack, and complete).
-pending: set to request servicing in raid5_run_ops
-ack: set to reflect that raid5_runs_ops has seen this request -complete: set when the operation is complete and it is ok for handle_stripe5 to clear 'pending' and 'ack'.
---
drivers/md/raid5.c | 65 +++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 56 insertions(+), 9 deletions(-)
drivers/md/raid5.c +56 -9
@@ -126,6 +126,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) }md_wakeup_thread (conf->mddev->thread); }else { +BUG_ON (sh->ops.pending);if (test_and_clear_bit (STRIPE_PREREAD_ACTIVE , &sh->state)) {atomic_dec (&conf->preread_active_stripes);if (atomic_read (&conf->preread_active_stripes) <IO_THRESHOLD )
@@ -225,7 +226,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, intBUG_ON (atomic_read (&sh->count) !=0 );BUG_ON (test_bit (STRIPE_HANDLE , &sh->state)); - +BUG_ON (sh->ops.pending || sh->ops.ack || sh->ops.complete); +CHECK_DEVLOCK ();PRINTK ("init_stripe called, stripe %llu\n" , (unsigned long long )sh->sector);
@@ -241,11 +243,11 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, intfor (i = sh->disks; i--; ) {struct r5dev *dev = &sh->dev[i]; -if (dev->toread || dev->towrite || dev->written || +if (dev->toread || dev->read || dev->towrite || dev->written ||test_bit (R5_LOCKED , &dev->flags)) { -printk ("sector=%llx i=%d %p %p %p %d\n" , +printk ("sector=%llx i=%d %p %p %p %p %d\n" , (unsigned long long )sh->sector, i, dev->toread, - dev->towrite, dev->written, + dev->read, dev->towrite, dev->written,test_bit (R5_LOCKED , &dev->flags));BUG (); }
@@ -325,6 +327,43 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sectorreturn sh; } +/* check_op() ensures that we only dequeue an operation once */ +#define check_op (op) do {\ + if (test_bit(op, &sh->ops.pending) &&\ + !test_bit(op, &sh->ops.complete)) {\ + if (test_and_set_bit(op, &sh->ops.ack))\ + clear_bit(op, &pending);\ + else\ + ack++;\ + } else\ + clear_bit(op, &pending);\ +} while(0) + +/* find new work to run, do not resubmit work that is already +* in flight +*/ +static unsigned long get_stripe_work (struct stripe_head *sh) +{ +unsigned long pending; +int ack =0 ; + + pending = sh->ops.pending; + +check_op (STRIPE_OP_BIOFILL ); +check_op (STRIPE_OP_COMPUTE_BLK ); +check_op (STRIPE_OP_PREXOR ); +check_op (STRIPE_OP_BIODRAIN ); +check_op (STRIPE_OP_POSTXOR ); +check_op (STRIPE_OP_CHECK ); +if (test_and_clear_bit (STRIPE_OP_IO , &sh->ops.pending)) + ack++; + + sh->ops.count -= ack; +BUG_ON (sh->ops.count <0 ); + +return pending; +} +static int raid5_end_read_request (struct bio * bi,unsigned int bytes_done,int error); static int
@@ -1859,7 +1898,6 @@ static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks)* schedule a write of some buffers * return confirmation of parity correctness * -* Parity calculations are done inside the stripe lock * buffers are taken off read_list or write_list, and bh_cache buffers * get BH_Lock set before the stripe lock is released. *
@@ -1877,10 +1915,11 @@ static void handle_stripe5(struct stripe_head *sh)int non_overwrite =0 ;int failed_num=0 ;struct r5dev *dev; +unsigned long pending=0 ; -PRINTK ("handling stripe %llu, cnt=%d, pd_idx=%d\n" , - (unsigned long long )sh->sector,atomic_read (&sh->count), - sh->pd_idx); +PRINTK ("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d ops=%lx:%lx:%lx\n" , + (unsigned long long )sh->sector, sh->state,atomic_read (&sh->count), + sh->pd_idx, sh->ops.pending, sh->ops.ack, sh->ops.complete);spin_lock (&sh->lock);clear_bit (STRIPE_HANDLE , &sh->state);
@@ -2330,8 +2369,14 @@ static void handle_stripe5(struct stripe_head *sh) } } +if (sh->ops.count) + pending =get_stripe_work (sh); +spin_unlock (&sh->lock); +if (pending) +raid5_run_ops (sh, pending); +while ((bi=return_bi)) {int bytes = bi->bi_size;
@@ -3828,8 +3873,10 @@ static void raid5d (mddev_t *mddev) handled++; } -if (list_empty (&conf->handle_list)) +if (list_empty (&conf->handle_list)) { +async_tx_issue_pending_all ();break ; + } first = conf->handle_list.next; sh = list_entry(first, struct stripe_head, lru);
PATCH 6/15 md: move write operations to raid5_run_ops
In reply to Dan Williamshandle_stripe sets STRIPE_OP_PREXOR, STRIPE_OP_BIODRAIN, STRIPE_OP_POSTXOR to request a write to the stripe cache. raid5_run_ops is triggerred to run and executes the request outside the stripe lock.
---
drivers/md/raid5.c | 152 +++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 131 insertions(+), 21 deletions(-)
drivers/md/raid5.c +131 -21
@@ -1788,7 +1788,75 @@ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) } } +static int handle_write_operations5 (struct stripe_head *sh,int rcw,int expand) +{ +int i, pd_idx = sh->pd_idx, disks = sh->disks; +int locked=0 ; + +if (rcw ==0 ) { +/* skip the drain operation on an expand */ +if (!expand) { +BUG_ON (test_and_set_bit (STRIPE_OP_BIODRAIN , + &sh->ops.pending)); + sh->ops.count++; + } + +BUG_ON (test_and_set_bit (STRIPE_OP_POSTXOR , &sh->ops.pending)); + sh->ops.count++; + +for (i=disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; + +if (dev->towrite) { +set_bit (R5_LOCKED , &dev->flags); +if (!expand) +clear_bit (R5_UPTODATE , &dev->flags); + locked++; + } + } + }else { +BUG_ON (!(test_bit (R5_UPTODATE , &sh->dev[pd_idx].flags) || +test_bit (R5_Wantcompute, &sh->dev[pd_idx].flags))); + +BUG_ON (test_and_set_bit (STRIPE_OP_PREXOR , &sh->ops.pending) || +test_and_set_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending) || +test_and_set_bit (STRIPE_OP_POSTXOR , &sh->ops.pending)); + + sh->ops.count +=3 ; + +for (i=disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +if (i==pd_idx) +continue ; +/* For a read-modify write there may be blocks that are +* locked for reading while others are ready to be written +* so we distinguish these blocks by the R5_Wantprexor bit +*/ +if (dev->towrite && + (test_bit (R5_UPTODATE , &dev->flags) || +test_bit (R5_Wantcompute, &dev->flags))) { +set_bit (R5_Wantprexor, &dev->flags); +set_bit (R5_LOCKED , &dev->flags); +clear_bit (R5_UPTODATE , &dev->flags); + locked++; + } + } + } + +/* keep the parity disk locked while asynchronous operations +* are in flight +*/ +set_bit (R5_LOCKED , &sh->dev[pd_idx].flags); +clear_bit (R5_UPTODATE , &sh->dev[pd_idx].flags); + locked++; + +PRINTK ("%s: stripe %llu locked: %d pending: %lx\n" , + __FUNCTION__, (unsigned long long )sh->sector, + locked, sh->ops.pending); + +return locked; +}/* * Each stripe/dev can have one or more bion attached.
@@ -2151,8 +2219,67 @@ static void handle_stripe5(struct stripe_head *sh)set_bit (STRIPE_HANDLE , &sh->state); } -/* now to consider writing and what else, if anything should be read */ -if (to_write) { +/* Now we check to see if any write operations have recently +* completed +*/ + +/* leave prexor set until postxor is done, allows us to distinguish +* a rmw from a rcw during biodrain +*/ +if (test_bit (STRIPE_OP_PREXOR , &sh->ops.complete) && +test_bit (STRIPE_OP_POSTXOR , &sh->ops.complete)) { + +clear_bit (STRIPE_OP_PREXOR , &sh->ops.complete); +clear_bit (STRIPE_OP_PREXOR , &sh->ops.ack); +clear_bit (STRIPE_OP_PREXOR , &sh->ops.pending); + +for (i=disks; i--;) +clear_bit (R5_Wantprexor, &sh->dev[i].flags); + } + +/* if only POSTXOR is set then this is an 'expand' postxor */ +if (test_bit (STRIPE_OP_BIODRAIN , &sh->ops.complete) && +test_bit (STRIPE_OP_POSTXOR , &sh->ops.complete)) { + +clear_bit (STRIPE_OP_BIODRAIN , &sh->ops.complete); +clear_bit (STRIPE_OP_BIODRAIN , &sh->ops.ack); +clear_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending); + +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.complete); +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.ack); +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.pending); + +/* All the 'written' buffers and the parity block are ready to be +* written back to disk +*/ +BUG_ON (!test_bit (R5_UPTODATE , &sh->dev[sh->pd_idx].flags)); +for (i=disks; i--;) { + dev = &sh->dev[i]; +if (test_bit (R5_LOCKED , &dev->flags) && + (i == sh->pd_idx || dev->written)) { +PRINTK ("Writing block %d\n" , i); +set_bit (R5_Wantwrite, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++; +if (!test_bit (R5_Insync, &dev->flags) + || (i==sh->pd_idx && failed ==0 )) +set_bit (STRIPE_INSYNC , &sh->state); + } + } +if (test_and_clear_bit (STRIPE_PREREAD_ACTIVE , &sh->state)) { +atomic_dec (&conf->preread_active_stripes); +if (atomic_read (&conf->preread_active_stripes) <IO_THRESHOLD ) +md_wakeup_thread (conf->mddev->thread); + } + } + +/* 1/ Now to consider new write requests and what else, if anything should be read +* 2/ Check operations clobber the parity block so do not start new writes while +* a check is in flight +* 3/ Write operations do not stack +*/ +if (to_write && !test_bit (STRIPE_OP_POSTXOR , &sh->ops.pending) && + !test_bit (STRIPE_OP_CHECK , &sh->ops.pending)) {int rmw=0 , rcw=0 ;for (i=disks ; i--;) {/* would I have to read this buffer for read_modify_write */
@@ -2219,25 +2346,8 @@ static void handle_stripe5(struct stripe_head *sh) }/* now if nothing is locked, and if we have enough data, we can start a write request */ if (locked ==0 && (rcw ==0 ||rmw ==0 ) && - !test_bit (STRIPE_BIT_DELAY , &sh->state)) { -PRINTK ("Computing parity...\n" ); -compute_parity5 (sh, rcw==0 ?RECONSTRUCT_WRITE :READ_MODIFY_WRITE ); -/* now every locked buffer is ready to be written */ -for (i=disks; i--;) -if (test_bit (R5_LOCKED , &sh->dev[i].flags)) { -PRINTK ("Writing block %d\n" , i); - locked++; -set_bit (R5_Wantwrite, &sh->dev[i].flags); -if (!test_bit (R5_Insync, &sh->dev[i].flags) - || (i==sh->pd_idx && failed ==0 )) -set_bit (STRIPE_INSYNC , &sh->state); - } -if (test_and_clear_bit (STRIPE_PREREAD_ACTIVE , &sh->state)) { -atomic_dec (&conf->preread_active_stripes); -if (atomic_read (&conf->preread_active_stripes) <IO_THRESHOLD ) -md_wakeup_thread (conf->mddev->thread); - } - } + !test_bit (STRIPE_BIT_DELAY , &sh->state)) + locked +=handle_write_operations5 (sh, rcw,0 ); }/* maybe we need to check and possibly fix the parity for this stripe
PATCH 7/15 md: move raid5 compute block operations to raid5_run_ops
In reply to Dan Williamshandle_stripe sets STRIPE_OP_COMPUTE_BLK to request servicing from raid5_run_ops. It also sets a flag for the block being computed to let other parts of handle_stripe submit dependent operations. raid5_run_ops guarantees that the compute operation completes before any dependent operation starts.
---
drivers/md/raid5.c | 125 +++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 93 insertions(+), 32 deletions(-)
drivers/md/raid5.c +93 -32
@@ -1980,7 +1980,7 @@ static void handle_stripe5(struct stripe_head *sh)int i;int syncing, expanding, expanded;int locked=0 , uptodate=0 , to_read=0 , to_write=0 , failed=0 , written=0 ; -int non_overwrite =0 ; +int compute=0 , req_compute=0 , non_overwrite=0 ;int failed_num=0 ;struct r5dev *dev;unsigned long pending=0 ;
@@ -2032,8 +2032,8 @@ static void handle_stripe5(struct stripe_head *sh)/* now count some things */ if (test_bit (R5_LOCKED , &dev->flags)) locked++;if (test_bit (R5_UPTODATE , &dev->flags)) uptodate++; +if (test_bit (R5_Wantcompute, &dev->flags))BUG_ON (++compute >1 ); -if (dev->toread) to_read++;if (dev->towrite) { to_write++;
@@ -2188,31 +2188,82 @@ static void handle_stripe5(struct stripe_head *sh)* parity, or to satisfy requests * or to load a block that is being partially written. */ -if (to_read || non_overwrite || (syncing && (uptodate < disks)) || expanding) { -for (i=disks; i--;) { - dev = &sh->dev[i]; -if (!test_bit (R5_LOCKED , &dev->flags) && !test_bit (R5_UPTODATE , &dev->flags) && - (dev->toread || - (dev->towrite && !test_bit (R5_OVERWRITE , &dev->flags)) || - syncing || - expanding || - (failed && (sh->dev[failed_num].toread || - (sh->dev[failed_num].towrite && !test_bit (R5_OVERWRITE , &sh->dev[failed_num].flags)))) - ) - ) { -/* we would like to get this block, possibly -* by computing it, but we might not be able to +if (to_read || non_overwrite || (syncing && (uptodate + compute < disks)) || expanding || +test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending)) { + +/* Clear completed compute operations. Parity recovery +* (STRIPE_OP_MOD_REPAIR_PD) implies a write-back which is handled +* later on in this routine +*/ +if (test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.complete) && + !test_bit (STRIPE_OP_MOD_REPAIR_PD , &sh->ops.pending)) { +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.complete); +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.ack); +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending); + } + +/* look for blocks to read/compute, skip this if a compute +* is already in flight, or if the stripe contents are in the +* midst of changing due to a write +*/ +if (!test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending) && + !test_bit (STRIPE_OP_PREXOR , &sh->ops.pending) && + !test_bit (STRIPE_OP_POSTXOR , &sh->ops.pending)) { +for (i=disks; i--;) { + dev = &sh->dev[i]; + +/* don't schedule compute operations or reads on +* the parity block while a check is in flight */ -if (uptodate == disks-1 ) { -PRINTK ("Computing block %d\n" , i); -compute_block (sh, i); - uptodate++; - }else if (test_bit (R5_Insync, &dev->flags)) { -set_bit (R5_LOCKED , &dev->flags); -set_bit (R5_Wantread, &dev->flags); - locked++; -PRINTK ("Reading block %d (sync=%d)\n" , - i, syncing); +if ((i == sh->pd_idx) &&test_bit (STRIPE_OP_CHECK , &sh->ops.pending)) +continue ; + +if (!test_bit (R5_LOCKED , &dev->flags) && !test_bit (R5_UPTODATE , &dev->flags) && + (dev->toread || + (dev->towrite && !test_bit (R5_OVERWRITE , &dev->flags)) || + syncing || + expanding || + (failed && (sh->dev[failed_num].toread || + (sh->dev[failed_num].towrite && + !test_bit (R5_OVERWRITE , &sh->dev[failed_num].flags)))) + ) + ) { +/* 1/ We would like to get this block, possibly +* by computing it, but we might not be able to. +* +* 2/ Since parity check operations potentially +* make the parity block !uptodate it will need +* to be refreshed before any compute operations +* on data disks are scheduled. +* +* 3/ We hold off parity block re-reads until check +* operations have quiesced. +*/ +if ((uptodate == disks-1 ) && !test_bit (STRIPE_OP_CHECK , &sh->ops.pending)) { +set_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending); +set_bit (R5_Wantcompute, &dev->flags); + sh->ops.target = i; +BUG_ON (req_compute++); + sh->ops.count++; +/* Careful: from this point on 'uptodate' is in the eye of +* raid5_run_ops which services 'compute' operations before +* writes. R5_Wantcompute flags a block that will be R5_UPTODATE +* by the time it is needed for a subsequent operation. +*/ + uptodate++; + }else if ((uptodate < disks-1 ) &&test_bit (R5_Insync, &dev->flags)) { +/* Note: we hold off compute operations while checks are in flight, +* but we still prefer 'compute' over 'read' hence we only read if +* (uptodate < disks-1) +*/ +set_bit (R5_LOCKED , &dev->flags); +set_bit (R5_Wantread, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++; + locked++; +PRINTK ("Reading block %d (sync=%d)\n" , + i, syncing); + } } } }
@@ -2287,7 +2338,7 @@ static void handle_stripe5(struct stripe_head *sh)if ((dev->towrite || i == sh->pd_idx) && (!test_bit (R5_LOCKED , &dev->flags) ) && - !test_bit (R5_UPTODATE , &dev->flags)) { + !(test_bit (R5_UPTODATE , &dev->flags) ||test_bit (R5_Wantcompute, &dev->flags))) {if (test_bit (R5_Insync, &dev->flags)/* && !(!mddev->insync && i == sh->pd_idx) */ )
@@ -2298,7 +2349,7 @@ static void handle_stripe5(struct stripe_head *sh)if (!test_bit (R5_OVERWRITE , &dev->flags) && i != sh->pd_idx && (!test_bit (R5_LOCKED , &dev->flags) ) && - !test_bit (R5_UPTODATE , &dev->flags)) { + !(test_bit (R5_UPTODATE , &dev->flags) ||test_bit (R5_Wantcompute, &dev->flags))) {if (test_bit (R5_Insync, &dev->flags)) rcw++;else rcw +=2 *disks; }
@@ -2311,7 +2362,8 @@ static void handle_stripe5(struct stripe_head *sh)for (i=disks; i--;) { dev = &sh->dev[i];if ((dev->towrite || i == sh->pd_idx) && - !test_bit (R5_LOCKED , &dev->flags) && !test_bit (R5_UPTODATE , &dev->flags) && + !test_bit (R5_LOCKED , &dev->flags) && + !(test_bit (R5_UPTODATE , &dev->flags) ||test_bit (R5_Wantcompute, &dev->flags)) &&test_bit (R5_Insync, &dev->flags)) {if (test_bit (STRIPE_PREREAD_ACTIVE , &sh->state)) {
@@ -2330,7 +2382,8 @@ static void handle_stripe5(struct stripe_head *sh)for (i=disks; i--;) { dev = &sh->dev[i];if (!test_bit (R5_OVERWRITE , &dev->flags) && i != sh->pd_idx && - !test_bit (R5_LOCKED , &dev->flags) && !test_bit (R5_UPTODATE , &dev->flags) && + !test_bit (R5_LOCKED , &dev->flags) && + !(test_bit (R5_UPTODATE , &dev->flags) ||test_bit (R5_Wantcompute, &dev->flags)) &&test_bit (R5_Insync, &dev->flags)) {if (test_bit (STRIPE_PREREAD_ACTIVE , &sh->state)) {
@@ -2345,8 +2398,16 @@ static void handle_stripe5(struct stripe_head *sh) } }/* now if nothing is locked, and if we have enough data, we can start a write request */ -if (locked ==0 && (rcw ==0 ||rmw ==0 ) && - !test_bit (STRIPE_BIT_DELAY , &sh->state)) +/* since handle_stripe can be called at any time we need to handle the case +* where a compute block operation has been submitted and then a subsequent +* call wants to start a write request. raid5_run_ops only handles the case where +* compute block and postxor are requested simultaneously. If this +* is not the case then new writes need to be held off until the compute +* completes. +*/ +if ((req_compute || !test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending)) && + (locked ==0 && (rcw ==0 ||rmw ==0 ) && + !test_bit (STRIPE_BIT_DELAY , &sh->state))) locked +=handle_write_operations5 (sh, rcw,0 ); }
PATCH 8/15 md: move raid5 parity checks to raid5_run_ops
In reply to Dan Williamshandle_stripe sets STRIPE_OP_CHECK to request a check operation in raid5_run_ops. If raid5_run_ops is able to perform the check with a dma engine the parity will be preserved in memory removing the need to re-read it from disk, as is necessary in the synchronous case.
'Repair' operations re-use the same logic as compute block, with the caveat that the results of the compute block are immediately written back to the parity disk. To differentiate these operations the STRIPE_OP_MOD_REPAIR_PD flag is added.
---
drivers/md/raid5.c | 81 ++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 62 insertions(+), 19 deletions(-)
drivers/md/raid5.c +62 -19
@@ -2411,32 +2411,75 @@ static void handle_stripe5(struct stripe_head *sh) locked +=handle_write_operations5 (sh, rcw,0 ); } -/* maybe we need to check and possibly fix the parity for this stripe -* Any reads will already have been scheduled, so we just see if enough data -* is available +/* 1/ Maybe we need to check and possibly fix the parity for this stripe. +* Any reads will already have been scheduled, so we just see if enough data +* is available. +* 2/ Hold off parity checks while parity dependent operations are in flight +* (conflicting writes are protected by the 'locked' variable) */ -if (syncing && locked ==0 && - !test_bit (STRIPE_INSYNC , &sh->state)) { +if ((syncing && locked ==0 && !test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending) && + !test_bit (STRIPE_INSYNC , &sh->state)) || +test_bit (STRIPE_OP_CHECK , &sh->ops.pending) || +test_bit (STRIPE_OP_MOD_REPAIR_PD , &sh->ops.pending)) { +set_bit (STRIPE_HANDLE , &sh->state); -if (failed ==0 ) { -BUG_ON (uptodate != disks); -compute_parity5 (sh,CHECK_PARITY ); - uptodate--; -if (page_is_zero (sh->dev[sh->pd_idx].page)) { -/* parity is correct (on disc, not in buffer any more) */ -set_bit (STRIPE_INSYNC , &sh->state); - }else { - conf->mddev->resync_mismatches +=STRIPE_SECTORS ; -if (test_bit (MD_RECOVERY_CHECK , &conf->mddev->recovery)) -/* don't try to repair!! */ +/* Take one of the following actions: +* 1/ start a check parity operation if (uptodate == disks) +* 2/ finish a check parity operation and act on the result +* 3/ skip to the writeback section if we previously +* initiated a recovery operation +*/ +if (failed ==0 && !test_bit (STRIPE_OP_MOD_REPAIR_PD , &sh->ops.pending)) { +if (!test_and_set_bit (STRIPE_OP_CHECK , &sh->ops.pending)) { +BUG_ON (uptodate != disks); +clear_bit (R5_UPTODATE , &sh->dev[sh->pd_idx].flags); + sh->ops.count++; + uptodate--; + }else if (test_and_clear_bit (STRIPE_OP_CHECK , &sh->ops.complete)) { +clear_bit (STRIPE_OP_CHECK , &sh->ops.ack); +clear_bit (STRIPE_OP_CHECK , &sh->ops.pending); + +if (sh->ops.zero_sum_result ==0 ) +/* parity is correct (on disc, not in buffer any more) */ set_bit (STRIPE_INSYNC , &sh->state);else { -compute_block (sh, sh->pd_idx); - uptodate++; + conf->mddev->resync_mismatches +=STRIPE_SECTORS ; +if (test_bit (MD_RECOVERY_CHECK , &conf->mddev->recovery)) +/* don't try to repair!! */ +set_bit (STRIPE_INSYNC , &sh->state); +else { +BUG_ON (test_and_set_bit ( +STRIPE_OP_COMPUTE_BLK , + &sh->ops.pending)); +set_bit (STRIPE_OP_MOD_REPAIR_PD , + &sh->ops.pending); +BUG_ON (test_and_set_bit (R5_Wantcompute, + &sh->dev[sh->pd_idx].flags)); + sh->ops.target = sh->pd_idx; + sh->ops.count++; + uptodate++; + } } } } -if (!test_bit (STRIPE_INSYNC , &sh->state)) { + +/* check if we can clear a parity disk reconstruct */ +if (test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.complete) && +test_bit (STRIPE_OP_MOD_REPAIR_PD , &sh->ops.pending)) { + +clear_bit (STRIPE_OP_MOD_REPAIR_PD , &sh->ops.pending); +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.complete); +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.ack); +clear_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending); + } + +/* Wait for check parity and compute block operations to complete +* before write-back +*/ +if (!test_bit (STRIPE_INSYNC , &sh->state) && + !test_bit (STRIPE_OP_CHECK , &sh->ops.pending) && + !test_bit (STRIPE_OP_COMPUTE_BLK , &sh->ops.pending)) { +/* either failed parity check, or recovery is happening */ if (failed==0 ) failed_num = sh->pd_idx;
PATCH 9/15 md: satisfy raid5 read requests via raid5_run_ops
In reply to Dan WilliamsUse raid5_run_ops to carry out the memory copies for a raid5 read request.
---
drivers/md/raid5.c | 40 +++++++++++++++------------------------- 1 files changed, 15 insertions(+), 25 deletions(-)
drivers/md/raid5.c +15 -25
@@ -1980,7 +1980,7 @@ static void handle_stripe5(struct stripe_head *sh)int i;int syncing, expanding, expanded;int locked=0 , uptodate=0 , to_read=0 , to_write=0 , failed=0 , written=0 ; -int compute=0 , req_compute=0 , non_overwrite=0 ; +int to_fill=0 , compute=0 , req_compute=0 , non_overwrite=0 ;int failed_num=0 ;struct r5dev *dev;unsigned long pending=0 ;
@@ -2004,34 +2004,20 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[i];clear_bit (R5_Insync, &dev->flags); -PRINTK ("check %d: state 0x%lx read %p write %p written %p\n" , - i, dev->flags, dev->toread, dev->towrite, dev->written); -/* maybe we can reply to a read */ +PRINTK ("check %d: state 0x%lx toread %p read %p write %p written %p\n" , + i, dev->flags, dev->toread, dev->read, dev->towrite, dev->written); + +/* maybe we can start a biofill operation */ if (test_bit (R5_UPTODATE , &dev->flags) && dev->toread) { -struct bio *rbi, *rbi2; -PRINTK ("Return read for disc %d\n" , i); -spin_lock_irq (&conf->device_lock); - rbi = dev->toread; - dev->toread =NULL ; -if (test_and_clear_bit (R5_Overlap, &dev->flags)) -wake_up (&conf->wait_for_overlap); -spin_unlock_irq (&conf->device_lock); -while (rbi && rbi->bi_sector < dev->sector +STRIPE_SECTORS ) { -copy_data (0 , rbi, dev->page, dev->sector); - rbi2 =r5_next_bio (rbi, dev->sector); -spin_lock_irq (&conf->device_lock); -if (--rbi->bi_phys_segments ==0 ) { - rbi->bi_next = return_bi; - return_bi = rbi; - } -spin_unlock_irq (&conf->device_lock); - rbi = rbi2; - } + to_read--; +if (!test_bit (STRIPE_OP_BIOFILL , &sh->ops.pending)) +set_bit (R5_Wantfill, &dev->flags); }/* now count some things */ if (test_bit (R5_LOCKED , &dev->flags)) locked++;if (test_bit (R5_UPTODATE , &dev->flags)) uptodate++; +if (test_bit (R5_Wantfill, &dev->flags)) to_fill++;if (test_bit (R5_Wantcompute, &dev->flags))BUG_ON (++compute >1 );if (dev->toread) to_read++;
@@ -2055,9 +2041,13 @@ static void handle_stripe5(struct stripe_head *sh)set_bit (R5_Insync, &dev->flags); }rcu_read_unlock (); + +if (to_fill && !test_and_set_bit (STRIPE_OP_BIOFILL , &sh->ops.pending)) + sh->ops.count++; +PRINTK ("locked=%d uptodate=%d to_read=%d" -" to_write=%d failed=%d failed_num=%d\n" , - locked, uptodate, to_read, to_write, failed, failed_num); +" to_write=%d to_fill=%d failed=%d failed_num=%d\n" , + locked, uptodate, to_read, to_write, to_fill, failed, failed_num);/* check if the array has lost two devices and, if so, some requests might * need to be failed */
PATCH 10/15 md: use async_tx and raid5_run_ops for raid5 expansion operations
In reply to Dan WilliamsThe parity calculation for an expansion operation is the same as the calculation performed at the end of a write with the caveat that all blocks in the stripe are scheduled to be written. An expansion operation is identified as a stripe with the POSTXOR flag set and the BIODRAIN flag not set.
The bulk copy operation to the new stripe is handled inline by async_tx.
---
drivers/md/raid5.c | 48 ++++++++++++++++++++++++++++++++++++------------ 1 files changed, 36 insertions(+), 12 deletions(-)
drivers/md/raid5.c +36 -12
@@ -2511,18 +2511,32 @@ static void handle_stripe5(struct stripe_head *sh) } } -if (expanded &&test_bit (STRIPE_EXPANDING , &sh->state)) { -/* Need to write out all blocks after computing parity */ - sh->disks = conf->raid_disks; - sh->pd_idx =stripe_to_pdidx (sh->sector, conf, conf->raid_disks); -compute_parity5 (sh,RECONSTRUCT_WRITE ); +/* Finish postxor operations initiated by the expansion +* process +*/ +if (test_bit (STRIPE_OP_POSTXOR , &sh->ops.complete) && + !test_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending)) { + +clear_bit (STRIPE_EXPANDING , &sh->state); + +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.pending); +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.ack); +clear_bit (STRIPE_OP_POSTXOR , &sh->ops.complete); +for (i= conf->raid_disks; i--;) { -set_bit (R5_LOCKED , &sh->dev[i].flags); - locked++;set_bit (R5_Wantwrite, &sh->dev[i].flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++; } -clear_bit (STRIPE_EXPANDING , &sh->state); - }else if (expanded) { + } + +if (expanded &&test_bit (STRIPE_EXPANDING , &sh->state) && + !test_bit (STRIPE_OP_POSTXOR , &sh->ops.pending)) { +/* Need to write out all blocks after computing parity */ + sh->disks = conf->raid_disks; + sh->pd_idx =stripe_to_pdidx (sh->sector, conf, conf->raid_disks); + locked +=handle_write_operations5 (sh,0 ,1 ); + }else if (expanded && !test_bit (STRIPE_OP_POSTXOR , &sh->ops.pending)) {clear_bit (STRIPE_EXPAND_READY , &sh->state);atomic_dec (&conf->reshape_stripes);wake_up (&conf->wait_for_overlap);
@@ -2533,6 +2547,7 @@ static void handle_stripe5(struct stripe_head *sh)/* We have read all the blocks in this stripe and now we need to * copy some of them into a target stripe for expand. */ +struct dma_async_tx_descriptor *tx =NULL ;clear_bit (STRIPE_EXPAND_SOURCE , &sh->state);for (i=0 ; i< sh->disks; i++)if (i != sh->pd_idx) {
@@ -2556,9 +2571,12 @@ static void handle_stripe5(struct stripe_head *sh)release_stripe (sh2);continue ; } -memcpy (page_address (sh2->dev[dd_idx].page), -page_address (sh->dev[i].page), -STRIPE_SIZE ); + +/* place all the copies on one channel */ + tx =async_memcpy (sh2->dev[dd_idx].page, + sh->dev[i].page,0 ,0 ,STRIPE_SIZE , +ASYNC_TX_DEP_ACK , tx,NULL ,NULL ); +set_bit (R5_Expanded, &sh2->dev[dd_idx].flags);set_bit (R5_UPTODATE , &sh2->dev[dd_idx].flags);for (j=0 ; j<conf->raid_disks; j++)
@@ -2570,6 +2588,12 @@ static void handle_stripe5(struct stripe_head *sh)set_bit (STRIPE_HANDLE , &sh2->state); }release_stripe (sh2); + +/* done submitting copies, wait for them to complete */ +if (i +1 >= sh->disks) { +async_tx_ack (tx); +dma_wait_for_async_tx (tx); + } } }
PATCH 11/15 md: move raid5 io requests to raid5_run_ops
In reply to Dan Williamshandle_stripe now only updates the state of stripes. All execution of operations is moved to raid5_run_ops.
---
drivers/md/raid5.c | 68 ++++++++-------------------------------------------- 1 files changed, 10 insertions(+), 58 deletions(-)
drivers/md/raid5.c +10 -58
@@ -2360,6 +2360,8 @@ static void handle_stripe5(struct stripe_head *sh)PRINTK ("Read_old block %d for r-m-w\n" , i);set_bit (R5_LOCKED , &dev->flags);set_bit (R5_Wantread, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++; locked++; }else {set_bit (STRIPE_DELAYED , &sh->state);
@@ -2380,6 +2382,8 @@ static void handle_stripe5(struct stripe_head *sh)PRINTK ("Read_old block %d for Reconstruct\n" , i);set_bit (R5_LOCKED , &dev->flags);set_bit (R5_Wantread, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++; locked++; }else {set_bit (STRIPE_DELAYED , &sh->state);
@@ -2479,6 +2483,8 @@ static void handle_stripe5(struct stripe_head *sh)set_bit (R5_LOCKED , &dev->flags);set_bit (R5_Wantwrite, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++;clear_bit (STRIPE_DEGRADED , &sh->state); locked++;set_bit (STRIPE_INSYNC , &sh->state);
@@ -2500,12 +2506,16 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[failed_num];if (!test_bit (R5_ReWrite, &dev->flags)) {set_bit (R5_Wantwrite, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++;set_bit (R5_ReWrite, &dev->flags);set_bit (R5_LOCKED , &dev->flags); locked++; }else {/* let's read it back */ set_bit (R5_Wantread, &dev->flags); +if (!test_and_set_bit (STRIPE_OP_IO , &sh->ops.pending)) + sh->ops.count++;set_bit (R5_LOCKED , &dev->flags); locked++; }
@@ -2615,64 +2625,6 @@ static void handle_stripe5(struct stripe_head *sh)test_bit (BIO_UPTODATE , &bi->bi_flags) ? 0 : -EIO); } -for (i=disks; i-- ;) { -int rw; -struct bio *bi; -mdk_rdev_t *rdev; -if (test_and_clear_bit (R5_Wantwrite, &sh->dev[i].flags)) - rw =WRITE ; -else if (test_and_clear_bit (R5_Wantread, &sh->dev[i].flags)) - rw =READ ; -else -continue ; - - bi = &sh->dev[i].req; - - bi->bi_rw = rw; -if (rw ==WRITE ) - bi->bi_end_io = raid5_end_write_request; -else - bi->bi_end_io = raid5_end_read_request; - -rcu_read_lock (); - rdev =rcu_dereference (conf->disks[i].rdev); -if (rdev &&test_bit (Faulty, &rdev->flags)) - rdev =NULL ; -if (rdev) -atomic_inc (&rdev->nr_pending); -rcu_read_unlock (); - -if (rdev) { -if (syncing || expanding || expanded) -md_sync_acct (rdev->bdev,STRIPE_SECTORS ); - - bi->bi_bdev = rdev->bdev; -PRINTK ("for %llu schedule op %ld on disc %d\n" , - (unsigned long long )sh->sector, bi->bi_rw, i); -atomic_inc (&sh->count); - bi->bi_sector = sh->sector + rdev->data_offset; - bi->bi_flags =1 <<BIO_UPTODATE ; - bi->bi_vcnt =1 ; - bi->bi_max_vecs =1 ; - bi->bi_idx =0 ; - bi->bi_io_vec = &sh->dev[i].vec; - bi->bi_io_vec[0 ].bv_len =STRIPE_SIZE ; - bi->bi_io_vec[0 ].bv_offset =0 ; - bi->bi_size =STRIPE_SIZE ; - bi->bi_next =NULL ; -if (rw ==WRITE && -test_bit (R5_ReWrite, &sh->dev[i].flags)) -atomic_add (STRIPE_SECTORS , &rdev->corrected_errors); -generic_make_request (bi); - }else { -if (rw ==WRITE ) -set_bit (STRIPE_DEGRADED , &sh->state); -PRINTK ("skip op %ld on disc %d for sector %llu\n" , - bi->bi_rw, i, (unsigned long long )sh->sector); -clear_bit (R5_LOCKED , &sh->dev[i].flags); -set_bit (STRIPE_HANDLE , &sh->state); - } - } }static void handle_stripe6 (struct stripe_head *sh,struct page *tmp_page)
PATCH 12/15 md: remove raid5 compute_block and compute_parity5
In reply to Dan Williamsreplaced by raid5_run_ops
---
drivers/md/raid5.c | 124 ---------------------------------------------------- 1 files changed, 0 insertions(+), 124 deletions(-)
drivers/md/raid5.c +0 -124
@@ -1482,130 +1482,6 @@ static void copy_data(int frombio, struct bio *bio, } \ }while (0 ) - -static void compute_block(struct stripe_head *sh, int dd_idx) -{ -int i, count, disks = sh->disks; -void *ptr[MAX_XOR_BLOCKS ], *dest, *p; - -PRINTK ("compute_block, stripe %llu, idx %d\n" , - (unsigned long long )sh->sector, dd_idx); - - dest =page_address (sh->dev[dd_idx].page); -memset (dest,0 ,STRIPE_SIZE ); - count =0 ; -for (i = disks ; i--; ) { -if (i == dd_idx) -continue ; - p =page_address (sh->dev[i].page); -if (test_bit (R5_UPTODATE , &sh->dev[i].flags)) - ptr[count++] = p; -else -printk (KERN_ERR "compute_block() %d, stripe %llu, %d" -" not present\n" , dd_idx, - (unsigned long long )sh->sector, i); - -check_xor (); - } -if (count) -xor_block (count,STRIPE_SIZE , dest, ptr); -set_bit (R5_UPTODATE , &sh->dev[dd_idx].flags); -} - -static void compute_parity5 (struct stripe_head *sh,int method) -{ -raid5_conf_t *conf = sh->raid_conf; -int i, pd_idx = sh->pd_idx, disks = sh->disks, count; -void *ptr[MAX_XOR_BLOCKS ], *dest; -struct bio *chosen; - -PRINTK ("compute_parity5, stripe %llu, method %d\n" , - (unsigned long long )sh->sector, method); - - count =0 ; - dest =page_address (sh->dev[pd_idx].page); -switch (method) { -case READ_MODIFY_WRITE : -BUG_ON (!test_bit (R5_UPTODATE , &sh->dev[pd_idx].flags)); -for (i=disks ; i-- ;) { -if (i==pd_idx) -continue ; -if (sh->dev[i].towrite && -test_bit (R5_UPTODATE , &sh->dev[i].flags)) { - ptr[count++] =page_address (sh->dev[i].page); - chosen = sh->dev[i].towrite; - sh->dev[i].towrite =NULL ; - -if (test_and_clear_bit (R5_Overlap, &sh->dev[i].flags)) -wake_up (&conf->wait_for_overlap); - -BUG_ON (sh->dev[i].written); - sh->dev[i].written = chosen; -check_xor (); - } - } -break ; -case RECONSTRUCT_WRITE : -memset (dest,0 ,STRIPE_SIZE ); -for (i= disks; i-- ;) -if (i!=pd_idx && sh->dev[i].towrite) { - chosen = sh->dev[i].towrite; - sh->dev[i].towrite =NULL ; - -if (test_and_clear_bit (R5_Overlap, &sh->dev[i].flags)) -wake_up (&conf->wait_for_overlap); - -BUG_ON (sh->dev[i].written); - sh->dev[i].written = chosen; - } -break ; -case CHECK_PARITY : -break ; - } -if (count) { -xor_block (count,STRIPE_SIZE , dest, ptr); - count =0 ; - } - -for (i = disks; i--;) -if (sh->dev[i].written) { -sector_t sector = sh->dev[i].sector; -struct bio *wbi = sh->dev[i].written; -while (wbi && wbi->bi_sector < sector +STRIPE_SECTORS ) { -copy_data (1 , wbi, sh->dev[i].page, sector); - wbi =r5_next_bio (wbi, sector); - } - -set_bit (R5_LOCKED , &sh->dev[i].flags); -set_bit (R5_UPTODATE , &sh->dev[i].flags); - } - -switch (method) { -case RECONSTRUCT_WRITE : -case CHECK_PARITY : -for (i=disks; i--;) -if (i != pd_idx) { - ptr[count++] =page_address (sh->dev[i].page); -check_xor (); - } -break ; -case READ_MODIFY_WRITE : -for (i = disks; i--;) -if (sh->dev[i].written) { - ptr[count++] =page_address (sh->dev[i].page); -check_xor (); - } - } -if (count) -xor_block (count,STRIPE_SIZE , dest, ptr); - -if (method !=CHECK_PARITY ) { -set_bit (R5_UPTODATE , &sh->dev[pd_idx].flags); -set_bit (R5_LOCKED , &sh->dev[pd_idx].flags); - }else -clear_bit (R5_UPTODATE , &sh->dev[pd_idx].flags); -} - static void compute_parity6(struct stripe_head *sh, int method) {raid6_conf_t *conf = sh->raid_conf;
PATCH 13/15 dmaengine: driver for the iop32x, iop33x, and iop13xx raid engines
In reply to Dan WilliamsThis is a driver for the iop DMA/AAU/ADMA units which are capable of pq_xor, pq_update, pq_zero_sum, xor, dual_xor, xor_zero_sum, fill, copy+crc, and copy operations.
Changelog:
* fixed a slot allocation bug in do_iop13xx_adma_xor that caused too few slots to be requested eventually leading to data corruption
* enabled the slot allocation routine to attempt to free slots before returning -ENOMEM
* switched the cleanup routine to solely use the software chain and the status register to determine if a descriptor is complete. This is necessary to support other IOP engines that do not have status writeback capability
* make the driver iop generic
* modified the allocation routines to understand allocating a group of slots for a single operation
* added a null xor initialization operation for the xor only channel on iop3xx
* support xor operations on buffers larger than the hardware maximum
* split the do_* routines into separate prep, src/dest set, submit stages
* added async_tx support (dependent operations initiation at cleanup time)
* simplified group handling
* added interrupt support (callbacks via tasklets)
* brought the pending depth inline with ioat (i.e. 4 descriptors)
* drop dma mapping methods, suggested by Chris Leech
* don't use inline in C files, Adrian Bunk
* remove static tasklet declarations
* make iop_adma_alloc_slots easier to read and remove chances for a corrupted descriptor chain
* fix locking bug in iop_adma_alloc_chan_resources, Benjamin Herrenschmidt
---
drivers/dma/Kconfig | 8 drivers/dma/Makefile | 1 drivers/dma/iop-adma.c | 1469 +++++++++++++++++++++++++++++++++++ include/asm-arm/hardware/iop_adma.h | 121 +++ 4 files changed, 1599 insertions(+), 0 deletions(-)
drivers/dma/Kconfig +8 -0
@@ -40,4 +40,12 @@ config INTEL_IOATDMA default m ---help--- Enable support for the Intel(R) I/OAT DMA engine. + +config INTEL_IOP_ADMA + tristate "Intel IOP ADMA support" + depends on DMA_ENGINE && (ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX) + default m + ---help--- + Enable support for the Intel(R) IOP Series RAID engines. + endmenu
drivers/dma/Makefile +1 -0
@@ -1,4 +1,5 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o +obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_ASYNC_TX_DMA) += async_tx.o xor.o
drivers/dma/iop-adma.c new file +1469 -0
1,469 lines not on this page · raw message
include/asm-arm/hardware/iop_adma.h new file +121 -0
121 lines not on this page · raw message
PATCH 14/15 iop13xx: Surface the iop13xx adma units to the iop-adma driver
In reply to Dan WilliamsAdds the platform device definitions and the architecture specific support routines (i.e. register initialization and descriptor formats) for the iop-adma driver.
Changelog:
* added 'descriptor pool size' to the platform data
* add base support for buffer sizes larger than 16MB (hw max)
* build error fix from Kirill A. Shutemov
* rebase for async_tx changes
* add interrupt support
* do not call platform register macros in driver code
---
arch/arm/mach-iop13xx/setup.c | 194 +++++++++++ include/asm-arm/arch-iop13xx/adma.h | 545 ++++++++++++++++++++++++++++++++ include/asm-arm/arch-iop13xx/iop13xx.h | 34 +- 3 files changed, 752 insertions(+), 21 deletions(-)
arch/arm/mach-iop13xx/setup.c +193 -1
@@ -25,6 +25,7 @@#include <asm/hardware.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/hardware/iop_adma.h> #define IOP13XX_UART_XTAL 33334000#define IOP13XX_SETUP_DEBUG 0
@@ -236,6 +237,140 @@ static unsigned long iq8134x_probe_flash_size(void) }#endif +/* ADMA Channels */ +static struct resource iop13xx_adma_0_resources[] = { + [0 ] = { + .start =IOP13XX_ADMA_PHYS_BASE (0 ), + .end =IOP13XX_ADMA_UPPER_PA (0 ), + .flags =IORESOURCE_MEM , + }, + [1 ] = { + .start =IRQ_IOP13XX_ADMA0_EOT , + .end =IRQ_IOP13XX_ADMA0_EOT , + .flags =IORESOURCE_IRQ + }, + [2 ] = { + .start =IRQ_IOP13XX_ADMA0_EOC , + .end =IRQ_IOP13XX_ADMA0_EOC , + .flags =IORESOURCE_IRQ + }, + [3 ] = { + .start =IRQ_IOP13XX_ADMA0_ERR , + .end =IRQ_IOP13XX_ADMA0_ERR , + .flags =IORESOURCE_IRQ + } +}; + +static struct resource iop13xx_adma_1_resources[] = { + [0 ] = { + .start =IOP13XX_ADMA_PHYS_BASE (1 ), + .end =IOP13XX_ADMA_UPPER_PA (1 ), + .flags =IORESOURCE_MEM , + }, + [1 ] = { + .start =IRQ_IOP13XX_ADMA1_EOT , + .end =IRQ_IOP13XX_ADMA1_EOT , + .flags =IORESOURCE_IRQ + }, + [2 ] = { + .start =IRQ_IOP13XX_ADMA1_EOC , + .end =IRQ_IOP13XX_ADMA1_EOC , + .flags =IORESOURCE_IRQ + }, + [3 ] = { + .start =IRQ_IOP13XX_ADMA1_ERR , + .end =IRQ_IOP13XX_ADMA1_ERR , + .flags =IORESOURCE_IRQ + } +}; + +static struct resource iop13xx_adma_2_resources[] = { + [0 ] = { + .start =IOP13XX_ADMA_PHYS_BASE (2 ), + .end =IOP13XX_ADMA_UPPER_PA (2 ), + .flags =IORESOURCE_MEM , + }, + [1 ] = { + .start =IRQ_IOP13XX_ADMA2_EOT , + .end =IRQ_IOP13XX_ADMA2_EOT , + .flags =IORESOURCE_IRQ + }, + [2 ] = { + .start =IRQ_IOP13XX_ADMA2_EOC , + .end =IRQ_IOP13XX_ADMA2_EOC , + .flags =IORESOURCE_IRQ + }, + [3 ] = { + .start =IRQ_IOP13XX_ADMA2_ERR , + .end =IRQ_IOP13XX_ADMA2_ERR , + .flags =IORESOURCE_IRQ + } +}; + +static u64 iop13xx_adma_dmamask =DMA_64BIT_MASK ; +static struct iop_adma_platform_data iop13xx_adma_0_data = { + .hw_id =0 , + .capabilities =DMA_CAP_MEMCPY |DMA_CAP_XOR |DMA_CAP_DUAL_XOR | +DMA_CAP_ZERO_SUM |DMA_CAP_MEMSET | +DMA_CAP_MEMCPY_CRC32C |DMA_CAP_INTERRUPT , + .pool_size =PAGE_SIZE , +}; + +static struct iop_adma_platform_data iop13xx_adma_1_data = { + .hw_id =1 , + .capabilities =DMA_CAP_MEMCPY |DMA_CAP_XOR |DMA_CAP_DUAL_XOR | +DMA_CAP_ZERO_SUM |DMA_CAP_MEMSET | +DMA_CAP_MEMCPY_CRC32C |DMA_CAP_INTERRUPT , + .pool_size =PAGE_SIZE , +}; + +static struct iop_adma_platform_data iop13xx_adma_2_data = { + .hw_id =2 , + .capabilities =DMA_CAP_MEMCPY |DMA_CAP_XOR |DMA_CAP_DUAL_XOR | +DMA_CAP_ZERO_SUM |DMA_CAP_MEMSET | +DMA_CAP_MEMCPY_CRC32C |DMA_CAP_PQ_XOR | +DMA_CAP_PQ_UPDATE |DMA_CAP_PQ_ZERO_SUM | +DMA_CAP_INTERRUPT , + .pool_size =PAGE_SIZE , +}; + +/* The ids are fixed up later in iop13xx_platform_init */ +static struct platform_device iop13xx_adma_0_channel = { + .name ="IOP-ADMA" , + .id =0 , + .num_resources =4 , + .resource = iop13xx_adma_0_resources, + .dev = { + .dma_mask = &iop13xx_adma_dmamask, + .coherent_dma_mask =DMA_64BIT_MASK , + .platform_data = (void *) &iop13xx_adma_0_data, + }, +}; + +static struct platform_device iop13xx_adma_1_channel = { + .name ="IOP-ADMA" , + .id =0 , + .num_resources =4 , + .resource = iop13xx_adma_1_resources, + .dev = { + .dma_mask = &iop13xx_adma_dmamask, + .coherent_dma_mask =DMA_64BIT_MASK , + .platform_data = (void *) &iop13xx_adma_1_data, + }, +}; + +static struct platform_device iop13xx_adma_2_channel = { + .name ="IOP-ADMA" , + .id =0 , + .num_resources =4 , + .resource = iop13xx_adma_2_resources, + .dev = { + .dma_mask = &iop13xx_adma_dmamask, + .coherent_dma_mask =DMA_64BIT_MASK , + .platform_data = (void *) &iop13xx_adma_2_data, + }, +}; +void __init iop13xx_map_io (void ) {/* Initialize the Static Page Table maps */
@@ -244,11 +379,12 @@ void __init iop13xx_map_io(void)static int init_uart =0 ;static int init_i2c =0 ; +static int init_adma =0 ;void __init iop13xx_platform_init (void ) {int i; -u32 uart_idx, i2c_idx, plat_idx; +u32 uart_idx, i2c_idx, adma_idx, plat_idx;struct platform_device *iop13xx_devices[IQ81340_MAX_PLAT_DEVICES ];/* set the bases so we can read the device id */
@@ -298,6 +434,12 @@ void __init iop13xx_platform_init(void) } } +if (init_adma ==IOP13XX_INIT_ADMA_DEFAULT ) { + init_adma |=IOP13XX_INIT_ADMA_0 ; + init_adma |=IOP13XX_INIT_ADMA_1 ; + init_adma |=IOP13XX_INIT_ADMA_2 ; + } + plat_idx =0 ; uart_idx =0 ; i2c_idx =0 ;
@@ -336,6 +478,26 @@ void __init iop13xx_platform_init(void) } } + adma_idx =0 ; +for (i =0 ; i <IQ81340_NUM_ADMA ; i++) { +if ((init_adma & (1 << i)) &&IOP13XX_SETUP_DEBUG ) +printk ("Adding adma%d to platform device list\n" , i); +switch (init_adma & (1 << i)) { +case IOP13XX_INIT_ADMA_0 : + iop13xx_adma_0_channel.id = adma_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_adma_0_channel; +break ; +case IOP13XX_INIT_ADMA_1 : + iop13xx_adma_1_channel.id = adma_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_adma_1_channel; +break ; +case IOP13XX_INIT_ADMA_2 : + iop13xx_adma_2_channel.id = adma_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_adma_2_channel; +break ; + } + } +#ifdef CONFIG_MTD_PHYSMAP iq8134x_flash_resource.end = iq8134x_flash_resource.start +iq8134x_probe_flash_size () -1 ;
@@ -403,5 +565,35 @@ static int __init iop13xx_init_i2c_setup(char *str)return 1 ; } +static int __init iop13xx_init_adma_setup (char *str) +{ +if (str) { +while (*str !='\0' ) { +switch (*str) { +case '0' : + init_adma |=IOP13XX_INIT_ADMA_0 ; +break ; +case '1' : + init_adma |=IOP13XX_INIT_ADMA_1 ; +break ; +case '2' : + init_adma |=IOP13XX_INIT_ADMA_2 ; +break ; +case ',' : +case '=' : +break ; +default : +PRINTK ("\"iop13xx_init_adma\" malformed" +" at character: \'%c\'" , *str); + *(str +1 ) ='\0' ; + init_adma =IOP13XX_INIT_ADMA_DEFAULT ; + } + str++; + } + } +return 1 ; +} + +__setup ("iop13xx_init_adma" , iop13xx_init_adma_setup);__setup ("iop13xx_init_uart" , iop13xx_init_uart_setup);__setup ("iop13xx_init_i2c" , iop13xx_init_i2c_setup);
include/asm-arm/arch-iop13xx/adma.h new file +545 -0
545 lines not on this page · raw message
include/asm-arm/arch-iop13xx/iop13xx.h +14 -20
@@ -161,12 +161,22 @@ static inline int iop13xx_cpu_id(void)#define IOP13XX_INIT_I2C_1 (1 << 1)#define IOP13XX_INIT_I2C_2 (1 << 2) -#define IQ81340_NUM_UART 2 -#define IQ81340_NUM_I2C 3 -#define IQ81340_NUM_PHYS_MAP_FLASH 1 +/* ADMA selection flags */ +/* INIT_ADMA_DEFAULT = Rely on CONFIG_IOP13XX_ADMA* */ +#define IOP13XX_INIT_ADMA_DEFAULT (0) +#define IOP13XX_INIT_ADMA_0 (1 << 0) +#define IOP13XX_INIT_ADMA_1 (1 << 1) +#define IOP13XX_INIT_ADMA_2 (1 << 2) + +/* Platform devices */ +#define IQ81340_NUM_UART 2 +#define IQ81340_NUM_I2C 3 +#define IQ81340_NUM_PHYS_MAP_FLASH 1 +#define IQ81340_NUM_ADMA 3#define IQ81340_MAX_PLAT_DEVICES (IQ81340_NUM_UART +\ IQ81340_NUM_I2C +\ - IQ81340_NUM_PHYS_MAP_FLASH) + IQ81340_NUM_PHYS_MAP_FLASH +\ + IQ81340_NUM_ADMA)/*========================== PMMR offsets for key registers ============*/ #define IOP13XX_ATU0_PMMR_OFFSET 0x00048000
@@ -410,22 +420,6 @@ static inline int iop13xx_cpu_id(void)/*==============================ADMA UNITS===============================*/ #define IOP13XX_ADMA_PHYS_BASE (chan) IOP13XX_REG_ADDR32_PHYS((chan << 9))#define IOP13XX_ADMA_UPPER_PA (chan) (IOP13XX_ADMA_PHYS_BASE(chan) + 0xc0) -#define IOP13XX_ADMA_OFFSET (chan, ofs) IOP13XX_REG_ADDR32((chan << 9) + (ofs)) - -#define IOP13XX_ADMA_ACCR (chan) IOP13XX_ADMA_OFFSET(chan, 0x0) -#define IOP13XX_ADMA_ACSR (chan) IOP13XX_ADMA_OFFSET(chan, 0x4) -#define IOP13XX_ADMA_ADAR (chan) IOP13XX_ADMA_OFFSET(chan, 0x8) -#define IOP13XX_ADMA_IIPCR (chan) IOP13XX_ADMA_OFFSET(chan, 0x18) -#define IOP13XX_ADMA_IIPAR (chan) IOP13XX_ADMA_OFFSET(chan, 0x1c) -#define IOP13XX_ADMA_IIPUAR (chan) IOP13XX_ADMA_OFFSET(chan, 0x20) -#define IOP13XX_ADMA_ANDAR (chan) IOP13XX_ADMA_OFFSET(chan, 0x24) -#define IOP13XX_ADMA_ADCR (chan) IOP13XX_ADMA_OFFSET(chan, 0x28) -#define IOP13XX_ADMA_CARMD (chan) IOP13XX_ADMA_OFFSET(chan, 0x2c) -#define IOP13XX_ADMA_ABCR (chan) IOP13XX_ADMA_OFFSET(chan, 0x30) -#define IOP13XX_ADMA_DLADR (chan) IOP13XX_ADMA_OFFSET(chan, 0x34) -#define IOP13XX_ADMA_DUADR (chan) IOP13XX_ADMA_OFFSET(chan, 0x38) -#define IOP13XX_ADMA_SLAR (src, chan) IOP13XX_ADMA_OFFSET(chan, 0x3c + (src <<3)) -#define IOP13XX_ADMA_SUAR (src, chan) IOP13XX_ADMA_OFFSET(chan, 0x40 + (src <<3))/*==============================XSI BRIDGE===============================*/ #define IOP13XX_XBG_BECSR IOP13XX_REG_ADDR32(0x178c)
PATCH 15/15 iop3xx: Surface the iop3xx DMA and AAU units to the iop-adma driver
In reply to Dan WilliamsAdds the platform device definitions and the architecture specific support routines (i.e. register initialization and descriptor formats) for the iop-adma driver.
Changelog:
* add support for > 1k zero sum buffer sizes
* added dma/aau platform devices to iq80321 and iq80332 setup
* fixed the calculation in iop_desc_is_aligned
* support xor buffer sizes larger than 16MB
* fix places where software descriptors are assumed to be contiguous, only hardware descriptors are contiguous
for up to a PAGE_SIZE buffer size
* convert to async_tx
* add interrupt support
* add platform devices for 80219 boards
* do not call platform register macros in driver code
* remove switch() statements for compatible register offsets/layouts
---
arch/arm/mach-iop32x/glantank.c | 2 arch/arm/mach-iop32x/iq31244.c | 5 arch/arm/mach-iop32x/iq80321.c | 3 arch/arm/mach-iop32x/n2100.c | 2 arch/arm/mach-iop33x/iq80331.c | 3 arch/arm/mach-iop33x/iq80332.c | 3 arch/arm/plat-iop/Makefile | 2 arch/arm/plat-iop/adma.c | 198 +++++++ include/asm-arm/arch-iop32x/adma.h | 5 include/asm-arm/arch-iop33x/adma.h | 5 include/asm-arm/hardware/iop3xx-adma.h | 893 ++++++++++++++++++++++++++++++++ include/asm-arm/hardware/iop3xx.h | 68 -- 12 files changed, 1129 insertions(+), 60 deletions(-)
arch/arm/mach-iop32x/glantank.c +2 -0
@@ -180,6 +180,8 @@ static void __init glantank_init_machine(void)platform_device_register (&iop3xx_i2c1_device);platform_device_register (&glantank_flash_device);platform_device_register (&glantank_serial_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel); pm_power_off = glantank_power_off; }
arch/arm/mach-iop32x/iq31244.c +5 -0
@@ -276,9 +276,14 @@ static void __init iq31244_init_machine(void)platform_device_register (&iop3xx_i2c1_device);platform_device_register (&iq31244_flash_device);platform_device_register (&iq31244_serial_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel);if (is_80219 ()) pm_power_off = ep80219_power_off; + +if (!is_80219 ()) +platform_device_register (&iop3xx_aau_channel); }MACHINE_START (IQ31244 ,"Intel IQ31244" )
arch/arm/mach-iop32x/iq80321.c +3 -0
@@ -180,6 +180,9 @@ static void __init iq80321_init_machine(void)platform_device_register (&iop3xx_i2c1_device);platform_device_register (&iq80321_flash_device);platform_device_register (&iq80321_serial_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel); +platform_device_register (&iop3xx_aau_channel); }MACHINE_START (IQ80321 ,"Intel IQ80321" )
arch/arm/mach-iop32x/n2100.c +2 -0
@@ -245,6 +245,8 @@ static void __init n2100_init_machine(void)platform_device_register (&iop3xx_i2c0_device);platform_device_register (&n2100_flash_device);platform_device_register (&n2100_serial_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel); pm_power_off = n2100_power_off;
arch/arm/mach-iop33x/iq80331.c +3 -0
@@ -135,6 +135,9 @@ static void __init iq80331_init_machine(void)platform_device_register (&iop33x_uart0_device);platform_device_register (&iop33x_uart1_device);platform_device_register (&iq80331_flash_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel); +platform_device_register (&iop3xx_aau_channel); }MACHINE_START (IQ80331 ,"Intel IQ80331" )
arch/arm/mach-iop33x/iq80332.c +3 -0
@@ -135,6 +135,9 @@ static void __init iq80332_init_machine(void)platform_device_register (&iop33x_uart0_device);platform_device_register (&iop33x_uart1_device);platform_device_register (&iq80332_flash_device); +platform_device_register (&iop3xx_dma_0_channel); +platform_device_register (&iop3xx_dma_1_channel); +platform_device_register (&iop3xx_aau_channel); }MACHINE_START (IQ80332 ,"Intel IQ80332" )
arch/arm/plat-iop/Makefile +2 -0
@@ -12,6 +12,7 @@ obj-$(CONFIG_ARCH_IOP32X) += setup.o obj-$(CONFIG_ARCH_IOP32X) += time.o obj-$(CONFIG_ARCH_IOP32X) += io.o obj-$(CONFIG_ARCH_IOP32X) += cp6.o +obj-$(CONFIG_ARCH_IOP32X) += adma.o # IOP33X obj-$(CONFIG_ARCH_IOP33X) += gpio.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_ARCH_IOP33X) += setup.o obj-$(CONFIG_ARCH_IOP33X) += time.o obj-$(CONFIG_ARCH_IOP33X) += io.o obj-$(CONFIG_ARCH_IOP33X) += cp6.o +obj-$(CONFIG_ARCH_IOP33X) += adma.o # IOP13XX obj-$(CONFIG_ARCH_IOP13XX) += cp6.o
arch/arm/plat-iop/adma.c new file +198 -0
198 lines not on this page · raw message
include/asm-arm/arch-iop32x/adma.h new file +5 -0
@@ -0,0 +1,5 @@ +#ifndef IOP32X_ADMA_H +#define IOP32X_ADMA_H +#include <asm/hardware/iop3xx-adma.h> +#endif +
include/asm-arm/arch-iop33x/adma.h new file +5 -0
@@ -0,0 +1,5 @@ +#ifndef IOP33X_ADMA_H +#define IOP33X_ADMA_H +#include <asm/hardware/iop3xx-adma.h> +#endif +
include/asm-arm/hardware/iop3xx-adma.h new file +893 -0
893 lines not on this page · raw message
include/asm-arm/hardware/iop3xx.h +8 -60
86 lines not on this page · raw message