raid acceleration and asynchronous offload api for 2.6.22
Subsystems (8)
cover raid acceleration and asynchronous offload api for 2.6.22
I am pleased to release this latest spin of the raid acceleration patches for merge consideration. This release aims to address all pending review items including MD bug fixes and async_tx api changes from Neil, and concerns on channel management from Chris and others.
Data integrity tests using home grown scripts and 'iozone -V' are passing. I am open to suggestions for additional testing criteria. I have also verified that git bisect is not broken by this set.
The short log below highlights the most recent changes. The patches will be sent as a reply to this message, and they are also available via git:
git pull git://lost.foo-projects.org/~dwillia2/git/iop md-accel-linus
Additional comments and feedback welcome.
Thanks,
Dan
--
01/16: dmaengine: add base support for the async_tx api
* convert channel capabilities to a 'cpumask_t like' bitmap
02/16: dmaengine: move channel management to the client
* this patch is new to this series
03/16: ARM: Add drivers/dma to arch/arm/Kconfig
04/16: dmaengine: add the async_tx api
* remove the per operation type list, and distribute operation capabilities evenly amongst the available channels
* simplify async_tx_find_channel to optimize the fast path
05/16: md: add raid5_run_ops and support routines
* explicitly handle the 2-disk raid5 case (xor becomes memcpy)
* fix race between async engines and bi_end_io call for reads, Neil Brown
* remove unnecessary spin_lock from ops_complete_biofill
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
* remove explicit interrupt handling, Neil Brown
06/16: md: use raid5_run_ops for stripe cache operations 07/16: md: move write operations to raid5_run_ops
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
08/16: md: move raid5 compute block operations to raid5_run_ops
* remove the req_compute BUG_ON
09/16: md: move raid5 parity checks to raid5_run_ops
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
10/16: md: satisfy raid5 read requests via raid5_run_ops
* cleanup to_read and to_fill accounting
* do not fail reads that have reached the cache
11/16: md: use async_tx and raid5_run_ops for raid5 expansion operations 12/16: md: move raid5 io requests to raid5_run_ops
13/16: md: remove raid5 compute_block and compute_parity5 14/16: dmaengine: driver for the iop32x, iop33x, and iop13xx raid engines
* fix locking bug in iop_adma_alloc_chan_resources, Benjamin Herrenschmidt
* convert capabilities over to dma_cap_mask_t
15/16: iop13xx: Surface the iop13xx adma units to the iop-adma driver 16/16: iop3xx: Surface the iop3xx DMA and AAU units to the iop-adma driver
(previous release: http://marc.info/?l=linux-raid&m=117463257423193&w=2)
PATCH 1/16 dmaengine: add base support for the async_tx api
In preparation for the async_tx (dmaengine client) API this patch:
1/ introduces struct dma_async_tx_descriptor as a common field for all
dmaengine software descriptors. The primary role of this structure is to enable callbacks at transaction completion time, and support transaction chains that span multiple channels
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). place holders for future capabilities are also included
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
* hookup ioat_tx_submit
* convert channel capabilities to a 'cpumask_t like' bitmap
---
drivers/dma/dmaengine.c | 182 +++++++++++++++++++++++++++++++++ drivers/dma/ioatdma.c | 248 ++++++++++++++++++++------------------------- drivers/dma/ioatdma.h | 8 + include/linux/dmaengine.h | 245 ++++++++++++++++++++++++++++---------------- 4 files changed, 454 insertions(+), 229 deletions(-)
drivers/dma/dmaengine.c +182 -0
@@ -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
@@ -322,6 +342,28 @@ int dma_async_device_register(struct dma_device *device)if (!device)return -ENODEV ; +/* validate device routines */ +BUG_ON (dma_has_cap (DMA_MEMCPY , device->cap_mask) && + !device->device_prep_dma_memcpy); +BUG_ON (dma_has_cap (DMA_XOR , device->cap_mask) && + !device->device_prep_dma_xor); +BUG_ON (dma_has_cap (DMA_ZERO_SUM , device->cap_mask) && + !device->device_prep_dma_zero_sum); +BUG_ON (dma_has_cap (DMA_MEMSET , device->cap_mask) && + !device->device_prep_dma_memset); +BUG_ON (dma_has_cap (DMA_ZERO_SUM , device->cap_mask) && + !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 +439,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
364 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 +156 -89
342 lines not on this page · raw message
PATCH 2/16 dmaengine: move channel management to the client
In reply to Dan WilliamsThis effectively makes channels a shared resource rather than tying them to a specific client. dmaengine now assumes that clients will internally track how many channels they need and dmaengine will learn if the client cares about a channel at dma_event_callback time. This also enables a client to ignore a channel if it does not meet extra client specific constraints beyond simple base capabilities.
This patch also fixes up the NET_DMA client to use the new mechanism.
---
drivers/dma/dmaengine.c | 206 ++++++++++++++++++++++----------------------- drivers/dma/ioatdma.c | 1 drivers/dma/ioatdma.h | 3 - include/linux/dmaengine.h | 46 +++++----- net/core/dev.c | 106 ++++++++++++++++------- 5 files changed, 198 insertions(+), 164 deletions(-)
drivers/dma/dmaengine.c +102 -104
@@ -37,8 +37,8 @@* Each device has a channels list, which runs unlocked but is never modified * once the device is registered, it's just setup by the driver. * -* Each client has a channels list, it's only modified under the client->lock -* and in an RCU callback, so it's safe to read under rcu_read_lock(). +* Each client is responsible for keeping track of the channels it uses. See +* the definition of dma_event_callback in dmaengine.h. * * Each device has a kref, which is initialized to 1 when the device is * registered. A kref_put is done for each class_device registered. When the
@@ -51,10 +51,12 @@* references to finish. * * Each channel has an open-coded implementation of Rusty Russell's "bigref," -* with a kref and a per_cpu local_t. A single reference is set when on an -* ADDED event, and removed with a REMOVE event. Net DMA client takes an -* extra reference per outstanding transaction. The relase function does a -* kref_put on the device. -ChrisL +* with a kref and a per_cpu local_t. A dma_chan_get is called when a client +* signals that it wants to use a channel, and dma_chan_put is called when +* a channel is removed or a client using it is unregesitered. A client can +* take extra references per outstanding transaction, as is the case with +* the NET DMA client. The release function does a kref_put on the device. +* -ChrisL, DanW */ #include <linux/init.h>
@@ -102,8 +104,18 @@ static ssize_t show_bytes_transferred(struct class_device *cd, char *buf)static ssize_t show_in_use (struct class_device *cd,char *buf) { struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); +int in_use =0 ; + +if (unlikely (chan->slow_ref) &&atomic_read (&chan->refcount.refcount) >1 ) + in_use =1 ; +else { +if (local_read (&(per_cpu_ptr (chan->local, +get_cpu ())->refcount)) >0 ) + in_use =1 ; +put_cpu (); + } -return sprintf (buf,"%d\n" , (chan->client ?1 :0 )); +return sprintf (buf,"%d\n" , in_use); }static struct class_device_attribute dma_class_attrs[] = {
@@ -129,42 +141,50 @@ static struct class dma_devclass = {/* --- client and device registration --- */ +#define dma_async_chan_satisfies_mask (chan, mask) __dma_async_chan_satisfies_mask((chan), &(mask)) +static int __dma_async_chan_satisfies_mask (struct dma_chan *chan,dma_cap_mask_t *want) +{ +dma_cap_mask_t has; + +bitmap_and (has.bits, want->bits, chan->device->cap_mask.bits,DMA_TX_TYPE_END ); +return bitmap_equal (want->bits, has.bits,DMA_TX_TYPE_END ); +} +/** -* dma_client_chan_alloc - try to allocate a channel to a client +* dma_client_chan_alloc - try to allocate channels to a client * @client: &dma_client * * Called with dma_list_mutex held. */ -static struct dma_chan *dma_client_chan_alloc (struct dma_client *client) +static void dma_client_chan_alloc (struct dma_client *client) {struct dma_device *device;struct dma_chan *chan; -unsigned long flags;int desc;/* allocated descriptor count */ +int ack;/* client has taken a reference to this channel */ -/* Find a channel, any DMA engine will do */ -list_for_each_entry (device, &dma_device_list, global_node) { +/* Find a channel */ +list_for_each_entry (device, &dma_device_list, global_node)list_for_each_entry (chan, &device->channels, device_node) { -if (chan->client) +if (!dma_async_chan_satisfies_mask (chan, client->cap_mask))continue ; desc = chan->device->device_alloc_chan_resources (chan);if (desc >=0 ) { -kref_get (&device->refcount); -kref_init (&chan->refcount); - chan->slow_ref =0 ; -INIT_RCU_HEAD (&chan->rcu); - chan->client = client; -spin_lock_irqsave (&client->lock, flags); -list_add_tail_rcu (&chan->client_node, - &client->channels); -spin_unlock_irqrestore (&client->lock, flags); -return chan; + ack = client->event_callback (client, + chan, +DMA_RESOURCE_AVAILABLE ); + +/* we are done once this client rejects +* an available resource +*/ +if (ack) { +dma_chan_get (chan); +kref_get (&device->refcount); + }else +return ; } } - } - -return NULL ; }enum dma_status dma_sync_wait (struct dma_chan *chan,dma_cookie_t cookie)
@@ -193,7 +213,6 @@ void dma_chan_cleanup(struct kref *kref) { struct dma_chan *chan = container_of(kref, struct dma_chan, refcount); chan->device->device_free_chan_resources (chan); - chan->client =NULL ;kref_put (&chan->device->refcount, dma_async_device_cleanup); }EXPORT_SYMBOL (dma_chan_cleanup);
@@ -217,70 +236,56 @@ static void dma_client_chan_free(struct dma_chan *chan) }/** -* dma_chans_rebalance - reallocate channels to clients -* -* When the number of DMA channel in the system changes, -* channels need to be rebalanced among clients. +* dma_chans_notify_available - broadcast available channels to the clients */ -static void dma_chans_rebalance (void ) +static void dma_clients_notify_available (void ) {struct dma_client *client; -struct dma_chan *chan; -unsigned long flags;mutex_lock (&dma_list_mutex); -list_for_each_entry (client, &dma_client_list, global_node) { -while (client->chans_desired > client->chan_count) { - chan =dma_client_chan_alloc (client); -if (!chan) -break ; - client->chan_count++; - client->event_callback (client, - chan, -DMA_RESOURCE_ADDED ); - } -while (client->chans_desired < client->chan_count) { -spin_lock_irqsave (&client->lock, flags); - chan =list_entry (client->channels.next, - struct dma_chan, - client_node); -list_del_rcu (&chan->client_node); -spin_unlock_irqrestore (&client->lock, flags); - client->chan_count--; - client->event_callback (client, - chan, -DMA_RESOURCE_REMOVED ); -dma_client_chan_free (chan); - } - } +list_for_each_entry (client, &dma_client_list, global_node) +dma_client_chan_alloc (client);mutex_unlock (&dma_list_mutex); }/** -* dma_async_client_register - allocate and register a &dma_client -* @event_callback: callback for notification of channel addition/removal +* dma_chans_notify_available - tell the clients that a channel is going away +* @chan: channel on its way out */ -struct dma_client *dma_async_client_register (dma_event_callback event_callback) +static void dma_clients_notify_removed (struct dma_chan *chan) {struct dma_client *client; +int ack; - client =kzalloc (sizeof (*client),GFP_KERNEL ); -if (!client) -return NULL ; +mutex_lock (&dma_list_mutex); -INIT_LIST_HEAD (&client->channels); -spin_lock_init (&client->lock); - client->chans_desired =0 ; - client->chan_count =0 ; - client->event_callback = event_callback; +list_for_each_entry (client, &dma_client_list, global_node) { + ack = client->event_callback (client, chan, +DMA_RESOURCE_REMOVED ); + +/* client was holding resources for this channel so +* free it +*/ +if (ack) { +dma_chan_put (chan); +dma_client_chan_free (chan); + } + } +mutex_unlock (&dma_list_mutex); +} + +/** +* dma_async_client_register - register a &dma_client +* @client: ptr to a client structure with valid 'event_callback' and 'cap_mask' +*/ +void dma_async_client_register (struct dma_client *client) +{mutex_lock (&dma_list_mutex);list_add_tail (&client->global_node, &dma_client_list);mutex_unlock (&dma_list_mutex); - -return client; }EXPORT_SYMBOL (dma_async_client_register);
@@ -292,40 +297,41 @@ EXPORT_SYMBOL(dma_async_client_register);*/ void dma_async_client_unregister (struct dma_client *client) { +struct dma_device *device;struct dma_chan *chan; +int ack;if (!client)return ; -rcu_read_lock (); -list_for_each_entry_rcu (chan, &client->channels, client_node) -dma_client_chan_free (chan); -rcu_read_unlock (); -mutex_lock (&dma_list_mutex); +/* free all channels the client is holding */ +list_for_each_entry (device, &dma_device_list, global_node) +list_for_each_entry (chan, &device->channels, device_node) { + ack = client->event_callback (client, chan, +DMA_RESOURCE_REMOVED ); + +if (ack) { +dma_chan_put (chan); +dma_client_chan_free (chan); + } + } +list_del (&client->global_node);mutex_unlock (&dma_list_mutex); - -kfree (client); -dma_chans_rebalance (); }EXPORT_SYMBOL (dma_async_client_unregister);/** -* dma_async_client_chan_request - request DMA channels -* @client: &dma_client -* @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. -* The resulting allocations/frees are indicated to the client via the -* event callback. +* dma_async_client_chan_request - send all available channels to the +* client that satisfy the capability mask +* @client - requester */ -void dma_async_client_chan_request (struct dma_client *client, -unsigned int number) +void dma_async_client_chan_request (struct dma_client *client) { - client->chans_desired = number; -dma_chans_rebalance (); +mutex_lock (&dma_list_mutex); +dma_client_chan_alloc (client); +mutex_unlock (&dma_list_mutex); }EXPORT_SYMBOL (dma_async_client_chan_request);
@@ -381,6 +387,9 @@ int dma_async_device_register(struct dma_device *device) device->dev_id, chan->chan_id);kref_get (&device->refcount); +kref_init (&chan->refcount); + chan->slow_ref =0 ; +INIT_RCU_HEAD (&chan->rcu);class_device_register (&chan->class_dev); }
@@ -388,7 +397,7 @@ int dma_async_device_register(struct dma_device *device)list_add_tail (&device->global_node, &dma_device_list);mutex_unlock (&dma_list_mutex); -dma_chans_rebalance (); +dma_clients_notify_available ();return 0 ; }
@@ -413,26 +422,15 @@ static void dma_async_device_cleanup(struct kref *kref)void dma_async_device_unregister (struct dma_device *device) {struct dma_chan *chan; -unsigned long flags;mutex_lock (&dma_list_mutex);list_del (&device->global_node);mutex_unlock (&dma_list_mutex);list_for_each_entry (chan, &device->channels, device_node) { -if (chan->client) { -spin_lock_irqsave (&chan->client->lock, flags); -list_del (&chan->client_node); - chan->client->chan_count--; -spin_unlock_irqrestore (&chan->client->lock, flags); - chan->client->event_callback (chan->client, - chan, -DMA_RESOURCE_REMOVED ); -dma_client_chan_free (chan); - } +dma_clients_notify_removed (chan);class_device_unregister (&chan->class_dev); } -dma_chans_rebalance ();kref_put (&device->refcount, dma_async_device_cleanup);wait_for_completion (&device->done);
drivers/dma/ioatdma.c +0 -1
@@ -72,7 +72,6 @@ static int enumerate_dma_channels(struct ioat_device *device)INIT_LIST_HEAD (&ioat_chan->used_desc);/* This should be made common somewhere in dmaengine.c */ ioat_chan->common.device = &device->common; - ioat_chan->common.client =NULL ;list_add_tail (&ioat_chan->common.device_node, &device->common.channels); }
drivers/dma/ioatdma.h +0 -3
@@ -30,9 +30,6 @@#define IOAT_LOW_COMPLETION_MASK 0xffffffc0 -extern struct list_head dma_device_list; -extern struct list_head dma_client_list; -/** * struct ioat_device - internal representation of a IOAT device * @pdev: PCI-Express device
include/linux/dmaengine.h +23 -23
112 lines not on this page · raw message
net/core/dev.c +73 -33
159 lines not on this page · raw message
PATCH 3/16 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 4/16 dmaengine: add the async_tx api
In reply to Dan WilliamsThe 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_tx, 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
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
* BUG_ON xor source counts <= 1
* implicitly handle hardware concerns like channel switching and interrupts, Neil Brown
* remove the per operation type list, and distribute operation capabilities evenly amongst the available channels
* simplify async_tx_find_channel to optimize the fast path
---
drivers/Makefile | 1 drivers/dma/Kconfig | 15 + drivers/dma/Makefile | 1 drivers/dma/async_tx.c | 889 ++++++++++++++++++++++++++++++++++++++++++++++ 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 | 173 +++++++++ include/linux/raid/xor.h | 5 11 files changed, 1263 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 +889 -0
889 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 +173 -0
173 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 5/16 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
* explicitly handle the 2-disk raid5 case (xor becomes memcpy)
* fix race between async engines and bi_end_io call for reads, Neil Brown
* remove unnecessary spin_lock from ops_complete_biofill
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
* remove explicit interrupt handling
---
drivers/md/raid5.c | 539 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/raid/raid5.h | 63 +++++ 2 files changed, 599 insertions(+), 3 deletions(-)
drivers/md/raid5.c +539 -0
@@ -52,6 +52,7 @@#include "raid6.h" #include <linux/raid/bitmap.h> +#include <linux/async_tx.h> /* * Stripe cache
@@ -324,6 +325,544 @@ 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; +raid5_conf_t *conf = sh->raid_conf; +int i, more_to_read=0 ; + +PRINTK ("%s: stripe %llu\n" , __FUNCTION__, + (unsigned long long )sh->sector); + +/* clear completed biofills */ +for (i=sh->disks ; i-- ;) { +struct r5dev *dev = &sh->dev[i]; +/* check if this stripe has new incoming reads */ +if (dev->toread) + more_to_read++; + +/* acknowledge completion of a biofill operation */ +/* and check if we need to reply to a read request +*/ +if (test_bit (R5_Wantfill, &dev->flags) && !dev->toread) { +struct bio *rbi, *rbi2; +clear_bit (R5_Wantfill, &dev->flags); + +/* dev->read is outside spin_lock_irq(&conf->device_lock) +* but is protected by the STRIPE_OP_BIOFILL pending bit +*/ +BUG_ON (!dev->read); + rbi = dev->read; + dev->read =NULL ; +while (rbi && rbi->bi_sector < dev->sector +STRIPE_SECTORS ) { + 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; + } + } + } +clear_bit (STRIPE_OP_BIOFILL , &sh->ops.ack); +clear_bit (STRIPE_OP_BIOFILL , &sh->ops.pending); + + +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 ); + } + +if (more_to_read) +set_bit (STRIPE_HANDLE , &sh->state); +release_stripe (sh); +} + +static void ops_run_biofill (struct stripe_head *sh) +{ +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; +spin_lock_irq (&conf->device_lock); + dev->read = 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); + rbi =r5_next_bio (rbi, dev->sector); + } + } + } + +atomic_inc (&sh->count); +async_trigger_callback (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_bit (R5_Wantcompute, &tgt->flags)); +clear_bit (R5_Wantcompute, &tgt->flags); +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); + +if (unlikely (count ==1 )) + tx =async_memcpy (xor_dest, xor_srcs[0 ],0 ,0 ,STRIPE_SIZE , +0 ,NULL , ops_complete_compute5, sh); +else + tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , +ASYNC_TX_XOR_ZERO_DST ,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); + +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); + +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); + } + +set_bit (STRIPE_OP_BIODRAIN , &sh->ops.complete); +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 | + (prexor ?ASYNC_TX_XOR_DROP_DST :ASYNC_TX_XOR_ZERO_DST ); + +atomic_inc (&sh->count); + +if (unlikely (count ==1 )) { + flags &= ~(ASYNC_TX_XOR_DROP_DST |ASYNC_TX_XOR_ZERO_DST ); + tx =async_memcpy (xor_dest, xor_srcs[0 ],0 ,0 ,STRIPE_SIZE , + flags, tx, callback, sh); + }else + 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); + +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_trigger_callback (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 6/16 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
@@ -1878,7 +1917,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. *
@@ -1896,10 +1934,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);
@@ -2349,8 +2388,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;
@@ -3847,8 +3892,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 7/16 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.
Changelog:
* make the 'rcw' parameter to handle_write_operations5 a simple flag, Neil Brown
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
---
drivers/md/raid5.c | 151 +++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 130 insertions(+), 21 deletions(-)
drivers/md/raid5.c +130 -21
@@ -1807,7 +1807,74 @@ 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) { +/* skip the drain operation on an expand */ +if (!expand) { +set_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending); + sh->ops.count++; + } + +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))); + +set_bit (STRIPE_OP_PREXOR , &sh->ops.pending); +set_bit (STRIPE_OP_BIODRAIN , &sh->ops.pending); +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.
@@ -2170,8 +2237,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 */
@@ -2238,25 +2364,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 ,0 ); }/* maybe we need to check and possibly fix the parity for this stripe
PATCH 8/16 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.
Changelog:
* remove the req_compute BUG_ON
---
drivers/md/raid5.c | 126 +++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 94 insertions(+), 32 deletions(-)
drivers/md/raid5.c +94 -32
@@ -1998,7 +1998,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 ;
@@ -2050,8 +2050,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++;
@@ -2206,31 +2206,83 @@ 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; + req_compute =1 ; + 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++; +break ;/* uptodate + compute == disks */ + }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); + } } } }
@@ -2305,7 +2357,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) */ )
@@ -2316,7 +2368,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; }
@@ -2329,7 +2381,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)) {
@@ -2348,7 +2401,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)) {
@@ -2363,8 +2417,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 ,0 ); }
PATCH 9/16 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.
Changelog:
* remove test_and_set/test_and_clear BUG_ONs, Neil Brown
---
drivers/md/raid5.c | 80 ++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 61 insertions(+), 19 deletions(-)
drivers/md/raid5.c +61 -19
@@ -2430,32 +2430,74 @@ static void handle_stripe5(struct stripe_head *sh) locked +=handle_write_operations5 (sh, rcw ==0 ,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 { +set_bit (STRIPE_OP_COMPUTE_BLK , + &sh->ops.pending); +set_bit (STRIPE_OP_MOD_REPAIR_PD , + &sh->ops.pending); +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 10/16 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.
Changelog:
* cleanup to_read and to_fill accounting
* do not fail reads that have reached the cache
---
drivers/md/raid5.c | 61 ++++++++++++++++++++++++++-------------------------- 1 files changed, 30 insertions(+), 31 deletions(-)
drivers/md/raid5.c +30 -31
@@ -1998,7 +1998,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 ;
@@ -2022,37 +2022,29 @@ 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 */ -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; - } - } +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 request a biofill operation +* +* new wantfill requests are only permitted while +* STRIPE_OP_BIOFILL is clear +*/ +if (test_bit (R5_UPTODATE , &dev->flags) && dev->toread && + !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++; +else if (dev->toread) + to_read++; +if (test_bit (R5_Wantcompute, &dev->flags))BUG_ON (++compute >1 ); -if (dev->toread) to_read++;if (dev->towrite) { to_write++;if (!test_bit (R5_OVERWRITE , &dev->flags))
@@ -2073,9 +2065,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 */
@@ -2127,9 +2123,12 @@ static void handle_stripe5(struct stripe_head *sh) bi = bi2; } -/* fail any reads if this device is non-operational */ -if (!test_bit (R5_Insync, &sh->dev[i].flags) || -test_bit (R5_ReadError, &sh->dev[i].flags)) { +/* fail any reads if this device is non-operational and +* the data has not reached the cache yet. +*/ +if (!test_bit (R5_Wantfill, &sh->dev[i].flags) && + (!test_bit (R5_Insync, &sh->dev[i].flags) || +test_bit (R5_ReadError, &sh->dev[i].flags))) { bi = sh->dev[i].toread; sh->dev[i].toread =NULL ;if (test_and_clear_bit (R5_Overlap, &sh->dev[i].flags))
PATCH 11/16 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
@@ -2538,18 +2538,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);
@@ -2560,6 +2574,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) {
@@ -2583,9 +2598,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++)
@@ -2597,6 +2615,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 12/16 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
@@ -2388,6 +2388,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);
@@ -2408,6 +2410,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);
@@ -2506,6 +2510,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);
@@ -2527,12 +2533,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++; }
@@ -2642,64 +2652,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 13/16 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
@@ -1501,130 +1501,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 14/16 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
* convert capabilities over to dma_cap_mask_t
---
drivers/dma/Kconfig | 8 drivers/dma/Makefile | 1 drivers/dma/iop-adma.c | 1464 +++++++++++++++++++++++++++++++++++ include/asm-arm/hardware/iop_adma.h | 121 +++ 4 files changed, 1594 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 +1464 -0
1,464 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 15/16 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 | 208 ++++++++++++ include/asm-arm/arch-iop13xx/adma.h | 545 ++++++++++++++++++++++++++++++++ include/asm-arm/arch-iop13xx/iop13xx.h | 34 +- 3 files changed, 766 insertions(+), 21 deletions(-)
arch/arm/mach-iop13xx/setup.c +207 -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,129 @@ 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 , + .pool_size =PAGE_SIZE , +}; + +static struct iop_adma_platform_data iop13xx_adma_1_data = { + .hw_id =1 , + .pool_size =PAGE_SIZE , +}; + +static struct iop_adma_platform_data iop13xx_adma_2_data = { + .hw_id =2 , + .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 +368,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 +423,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 +467,51 @@ void __init iop13xx_platform_init(void) } } +/* initialize adma channel ids and capabilities */ + 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; +dma_cap_set (DMA_MEMCPY , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_XOR , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_DUAL_XOR , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_ZERO_SUM , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_MEMSET , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_MEMCPY_CRC32C , iop13xx_adma_0_data.cap_mask); +dma_cap_set (DMA_INTERRUPT , iop13xx_adma_0_data.cap_mask); +break ; +case IOP13XX_INIT_ADMA_1 : + iop13xx_adma_1_channel.id = adma_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_adma_1_channel; +dma_cap_set (DMA_MEMCPY , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_XOR , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_DUAL_XOR , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_ZERO_SUM , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_MEMSET , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_MEMCPY_CRC32C , iop13xx_adma_1_data.cap_mask); +dma_cap_set (DMA_INTERRUPT , iop13xx_adma_1_data.cap_mask); +break ; +case IOP13XX_INIT_ADMA_2 : + iop13xx_adma_2_channel.id = adma_idx++; + iop13xx_devices[plat_idx++] = &iop13xx_adma_2_channel; +dma_cap_set (DMA_MEMCPY , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_XOR , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_DUAL_XOR , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_ZERO_SUM , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_MEMSET , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_MEMCPY_CRC32C , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_INTERRUPT , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_PQ_XOR , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_PQ_UPDATE , iop13xx_adma_2_data.cap_mask); +dma_cap_set (DMA_PQ_ZERO_SUM , iop13xx_adma_2_data.cap_mask); +break ; + } + } +#ifdef CONFIG_MTD_PHYSMAP iq8134x_flash_resource.end = iq8134x_flash_resource.start +iq8134x_probe_flash_size () -1 ;
@@ -403,5 +579,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 16/16 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
* change over to bitmap based capabilities
---
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 | 216 ++++++++ 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, 1147 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
@@ -295,9 +295,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_ep80219 ()) pm_power_off = ep80219_power_off; + +if (!is_80219 ()) +platform_device_register (&iop3xx_aau_channel); }static int __init force_ep80219_setup (char *str)
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 +216 -0
216 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
Quoted 7 lines · Dan Williams
I am pleased to release this latest spin of the raid acceleration patches for merge consideration. This release aims to address all pending review items including MD bug fixes and
async_txapi changes from Neil, and concerns on channel management from Chris and others.Data integrity tests using home grown scripts and 'iozone -V' are passing. I am open to suggestions for additional testing criteria.
Do you have performance numbers?
Signature · 1 line
SUSE Labs, Novell Inc.
Quoted 11 lines
From: Nick Piggin [mailto:[email protected]]
Quoted 7 lines
I am pleased to release this latest spin of the raid acceleration patches for merge consideration. This release aims to address all pending review items including MD bug fixes and
async_txapi changes from Neil, and concerns on channel management from Chris and others.Data integrity tests using home grown scripts and 'iozone -V' are passing. I am open to suggestions for additional testing criteria.
Do you have performance numbers?
Patch #4 outlines the throughput gains as reported by tiobench. 20-30% for sequential writes, and 40-55% for degraded reads.
Here are some recent screen captures of a resync operation with and without offload, this is on an iop13xx platform:
mdadm --create /dev/md0 /dev/sd[abc] missing -n 4 -l 5 mdadm --add /dev/md0 /dev/sdd
---CONFIG_DMA_ENGINE = n
top - 00:01:39 up 1 min, 1 user, load average: 0.77, 0.20, 0.06
Tasks: 50 total, 1 running, 49 sleeping, 0 stopped, 0 zombie Cpu(s): 0.3% us, 47.4% sy, 0.0% ni, 49.0% id, 0.0% wa, 0.7% hi, 2.6% si Mem: 2074836k total, 36276k used, 2038560k free, 0k buffers Swap: 0k total, 0k used, 0k free, 16560k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1307 root 11 -5 0 0 0 S 48.0 0.0 0:14.14 md0_raid5 1319 root 15 0 2260 1052 868 R 0.3 0.1 0:00.12 top
iq81340mc:~# cat /proc/mdstat
Personalities : [raid0] [raid1] [raid10] [raid6] [raid5] [raid4]
md0 : active raid5 sda[0] sdd[4] sdc[2] sdb[1]
468872448 blocks level 5, 64k chunk, algorithm 2 [4/3] [UUU_]
[>....................] recovery = 0.7% (1104000/156290816)
finish=108.1min speed=23919K/sec---CONFIG_DMA_ENGINE = y && CONFIG_INTEL_IOP_ADMA = y
top - 00:06:21 up 6 min, 1 user, load average: 1.10, 0.68, 0.29
Tasks: 51 total, 1 running, 50 sleeping, 0 stopped, 0 zombie Cpu(s): 0.5% us, 7.5% sy, 0.1% ni, 85.8% id, 1.9% wa, 0.5% hi, 3.5% si Mem: 2074776k total, 40520k used, 2034256k free, 0k buffers Swap: 0k total, 0k used, 0k free, 19448k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1354 root 10 -5 0 0 0 S 11.6 0.0 0:29.32 md0_raid5 1491 root 18 0 2256 964 780 R 1.9 0.0 0:00.03 top
iq81340mc:/mnt# cat /proc/mdstat
Personalities : [raid0] [raid1] [raid10] [raid6] [raid5] [raid4]
md0 : active raid5 sdd[4] sdc[2] sdb[1] sda[0]
468872448 blocks level 5, 64k chunk, algorithm 2 [4/3] [UUU_]
[>....................] recovery = 3.5% (5474916/156290816)
finish=52.2min speed=48061K/secSome older iozone data is available here:
https://sourceforge.net/project/showfiles.php?group_id=115074&package_id
=203776
Regards,
Dan
Quoted 66 lines · Dan J
Quoted 11 lines
From: Nick Piggin [mailto:[email protected]]
Quoted 7 lines
I am pleased to release this latest spin of the raid acceleration patches for merge consideration. This release aims to address all pending review items including MD bug fixes and
async_txapi changes from Neil, and concerns on channel management from Chris and others.Data integrity tests using home grown scripts and 'iozone -V' are passing. I am open to suggestions for additional testing criteria.
Do you have performance numbers?
Patch #4 outlines the throughput gains as reported by tiobench. 20-30% for sequential writes, and 40-55% for degraded reads.
Here are some recent screen captures of a resync operation with and without offload, this is on an iop13xx platform:
mdadm --create
/dev/md0/dev/sd[abc] missing -n 4 -l 5 mdadm --add/dev/md0/dev/sdd---
CONFIG_DMA_ENGINE= ntop - 00:01:39 up 1 min, 1 user, load average: 0.77, 0.20, 0.06
Tasks: 50 total, 1 running, 49 sleeping, 0 stopped, 0 zombie Cpu(s): 0.3% us, 47.4% sy, 0.0% ni, 49.0% id, 0.0% wa, 0.7% hi, 2.6% si Mem: 2074836k total, 36276k used, 2038560k free, 0k buffers Swap: 0k total, 0k used, 0k free, 16560k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1307 root 11 -5 0 0 0 S 48.0 0.0 0:14.14 md0_raid5 1319 root 15 0 2260 1052 868 R 0.3 0.1 0:00.12 topiq81340mc:~# cat /proc/mdstat Personalities : [raid0] [raid1] [raid10] [raid6] [raid5] [raid4] md0 : active raid5 sda[0] sdd[4] sdc[2] sdb[1] 468872448 blocks level 5, 64k chunk, algorithm 2 [4/3] [UUU_] [>....................] recovery = 0.7% (1104000/156290816) finish=108.1min speed=23919K/sec---
CONFIG_DMA_ENGINE= y &&CONFIG_INTEL_IOP_ADMA= ytop - 00:06:21 up 6 min, 1 user, load average: 1.10, 0.68, 0.29
Tasks: 51 total, 1 running, 50 sleeping, 0 stopped, 0 zombie Cpu(s): 0.5% us, 7.5% sy, 0.1% ni, 85.8% id, 1.9% wa, 0.5% hi, 3.5% si Mem: 2074776k total, 40520k used, 2034256k free, 0k buffers Swap: 0k total, 0k used, 0k free, 19448k cachedPID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1354 root 10 -5 0 0 0 S 11.6 0.0 0:29.32 md0_raid5 1491 root 18 0 2256 964 780 R 1.9 0.0 0:00.03 topiq81340mc:/mnt# cat /proc/mdstat Personalities : [raid0] [raid1] [raid10] [raid6] [raid5] [raid4] md0 : active raid5 sdd[4] sdc[2] sdb[1] sda[0] 468872448 blocks level 5, 64k chunk, algorithm 2 [4/3] [UUU_] [>....................] recovery = 3.5% (5474916/156290816) finish=52.2min speed=48061K/secSome older iozone data is available here:
https://sourceforge.net/project/showfiles.php?group_id=115074&package_id
=203776
Quoted 7 lines
Regards,
Dan
-
To unsubscribe from this list: send the line "unsubscribe linux-raid" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
I have not been following this closely, must you have an CONFIG_INTEL_IOP_ADMA piece of hardware and/or chipset to use this feature or can regular desktop users take hold of it as well?
Justin.
Quoted 5 lines
From: Justin Piszcz [mailto:[email protected]]
I have not been following this closely, must you have anCONFIG_INTEL_IOP_ADMApiece of hardware and/or chipset to use this feature or can regular desktop users take hold of it as well?
Currently this feature is available on the iop series of processors, e.g. http://www.intel.com/design/iio/iop348.htm
And there is a driver in the works to support the 440spe platform
(http://marc.info/?l=linux-raid&m=117400143317440&w=2)
Desktops currently take advantage of architecture specific SIMD instructions to accelerate xor calculations.
Quoted 1 line
Justin.
Dan
Quoted 11 lines · Dan J
Quoted 5 lines
From: Justin Piszcz [mailto:[email protected]]
I have not been following this closely, must you have anCONFIG_INTEL_IOP_ADMApiece of hardware and/or chipset to use this feature or can regular desktop users take hold of it as well?Currently this feature is available on the iop series of processors, e.g. http://www.intel.com/design/iio/iop348.htm
And there is a driver in the works to support the 440spe platform
(http://marc.info/?l=linux-raid&m=117400143317440&w=2)
Quoted 7 lines
Desktops currently take advantage of architecture specific SIMD instructions to accelerate xor calculations.
Quoted 1 line
Justin.
Dan
Ahh-- these are the family of chips in the Areca controllers (or close to them)-- so md/software raid will utilize the HW functions of the chip? If so, that is pretty slick :)
Justin.
Quoted 5 lines · Dan J
Quoted 5 lines
From: Justin Piszcz [mailto:[email protected]]
I have not been following this closely, must you have anCONFIG_INTEL_IOP_ADMApiece of hardware and/or chipset to use this feature or can regular desktop users take hold of it as well?
Quoted 8 lines
Currently this feature is available on the iop series of processors, e.g. http://www.intel.com/design/iio/iop348.htm
And there is a driver in the works to support the 440spe platform
(http://marc.info/?l=linux-raid&m=117400143317440&w=2)Desktops currently take advantage of architecture specific SIMD instructions to accelerate xor calculations.
How about hard drive controllers with RAID accelleration features (but not full hardware RAID capability) such as the Promise SX4
(http://linux-ata.org/faq-sata-raid.html#sx4)?
Signature · 3 lines
ilmari "A disappointingly low fraction of the human race is, at any given time, on fire." - Stig Sandbeck Mathisen
Quoted 5 lines
From: Dagfinn Ilmari Mannsåker [mailto:[email protected]] How about hard drive controllers with RAID accelleration features (but not full hardware RAID capability) such as the Promise SX4
(http://linux-ata.org/faq-sata-raid.html#sx4)?
See this thread:
http://marc.info/?l=linux-kernel&m=116793705601271&w=2
-
To unsubscribe from this list: send the line "unsubscribe linux-raid" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html