Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx: (22 commits)
  ioat: fix self test for multi-channel case
  dmaengine: bump initcall level to arch_initcall
  dmaengine: advertise all channels on a device to dma_filter_fn
  dmaengine: use idr for registering dma device numbers
  dmaengine: add a release for dma class devices and dependent infrastructure
  ioat: do not perform removal actions at shutdown
  iop-adma: enable module removal
  iop-adma: kill debug BUG_ON
  iop-adma: let devm do its job, don't duplicate free
  dmaengine: kill enum dma_state_client
  dmaengine: remove 'bigref' infrastructure
  dmaengine: kill struct dma_client and supporting infrastructure
  dmaengine: replace dma_async_client_register with dmaengine_get
  atmel-mci: convert to dma_request_channel and down-level dma_slave
  dmatest: convert to dma_request_channel
  dmaengine: introduce dma_request_channel and private channels
  net_dma: convert to dma_find_channel
  dmaengine: provide a common 'issue_pending_all' implementation
  dmaengine: centralize channel allocation, introduce dma_find_channel
  dmaengine: up-level reference counting to the module level
  ...
This commit is contained in:
Linus Torvalds 2009-01-09 11:52:14 -08:00
Родитель 2150edc6c5 b9bdcbba01
Коммит d9e8a3a5b8
26 изменённых файлов: 918 добавлений и 1278 удалений

Просмотреть файл

@ -13,9 +13,9 @@
3.6 Constraints
3.7 Example
4 DRIVER DEVELOPER NOTES
4 DMAENGINE DRIVER DEVELOPER NOTES
4.1 Conformance points
4.2 "My application needs finer control of hardware channels"
4.2 "My application needs exclusive control of hardware channels"
5 SOURCE
@ -150,6 +150,7 @@ ops_run_* and ops_complete_* routines in drivers/md/raid5.c for more
implementation examples.
4 DRIVER DEVELOPMENT NOTES
4.1 Conformance points:
There are a few conformance points required in dmaengine drivers to
accommodate assumptions made by applications using the async_tx API:
@ -158,58 +159,49 @@ accommodate assumptions made by applications using the async_tx API:
3/ Use async_tx_run_dependencies() in the descriptor clean up path to
handle submission of dependent operations
4.2 "My application needs finer control of hardware channels"
This requirement seems to arise from cases where a DMA engine driver is
trying to support device-to-memory DMA. The dmaengine and async_tx
implementations were designed for offloading memory-to-memory
operations; however, there are some capabilities of the dmaengine layer
that can be used for platform-specific channel management.
Platform-specific constraints can be handled by registering the
application as a 'dma_client' and implementing a 'dma_event_callback' to
apply a filter to the available channels in the system. Before showing
how to implement a custom dma_event callback some background of
dmaengine's client support is required.
4.2 "My application needs exclusive control of hardware channels"
Primarily this requirement arises from cases where a DMA engine driver
is being used to support device-to-memory operations. A channel that is
performing these operations cannot, for many platform specific reasons,
be shared. For these cases the dma_request_channel() interface is
provided.
The following routines in dmaengine support multiple clients requesting
use of a channel:
- dma_async_client_register(struct dma_client *client)
- dma_async_client_chan_request(struct dma_client *client)
The interface is:
struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
dma_filter_fn filter_fn,
void *filter_param);
dma_async_client_register takes a pointer to an initialized dma_client
structure. It expects that the 'event_callback' and 'cap_mask' fields
are already initialized.
Where dma_filter_fn is defined as:
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
dma_async_client_chan_request triggers dmaengine to notify the client of
all channels that satisfy the capability mask. It is up to the client's
event_callback routine to track how many channels the client needs and
how many it is currently using. The dma_event_callback routine returns a
dma_state_client code to let dmaengine know the status of the
allocation.
When the optional 'filter_fn' parameter is set to NULL
dma_request_channel simply returns the first channel that satisfies the
capability mask. Otherwise, when the mask parameter is insufficient for
specifying the necessary channel, the filter_fn routine can be used to
disposition the available channels in the system. The filter_fn routine
is called once for each free channel in the system. Upon seeing a
suitable channel filter_fn returns DMA_ACK which flags that channel to
be the return value from dma_request_channel. A channel allocated via
this interface is exclusive to the caller, until dma_release_channel()
is called.
Below is the example of how to extend this functionality for
platform-specific filtering of the available channels beyond the
standard capability mask:
The DMA_PRIVATE capability flag is used to tag dma devices that should
not be used by the general-purpose allocator. It can be set at
initialization time if it is known that a channel will always be
private. Alternatively, it is set when dma_request_channel() finds an
unused "public" channel.
static enum dma_state_client
my_dma_client_callback(struct dma_client *client,
struct dma_chan *chan, enum dma_state state)
{
struct dma_device *dma_dev;
struct my_platform_specific_dma *plat_dma_dev;
dma_dev = chan->device;
plat_dma_dev = container_of(dma_dev,
struct my_platform_specific_dma,
dma_dev);
if (!plat_dma_dev->platform_specific_capability)
return DMA_DUP;
. . .
}
A couple caveats to note when implementing a driver and consumer:
1/ Once a channel has been privately allocated it will no longer be
considered by the general-purpose allocator even after a call to
dma_release_channel().
2/ Since capabilities are specified at the device level a dma_device
with multiple channels will either have all channels public, or all
channels private.
5 SOURCE
include/linux/dmaengine.h: core header file for DMA drivers and clients
include/linux/dmaengine.h: core header file for DMA drivers and api users
drivers/dma/dmaengine.c: offload engine channel management routines
drivers/dma/: location for offload engine drivers
include/linux/async_tx.h: core header file for the async_tx api

Просмотреть файл

@ -0,0 +1 @@
See Documentation/crypto/async-tx-api.txt

Просмотреть файл

@ -1305,7 +1305,7 @@ struct platform_device *__init
at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
{
struct platform_device *pdev;
struct dw_dma_slave *dws;
struct dw_dma_slave *dws = &data->dma_slave;
u32 pioa_mask;
u32 piob_mask;
@ -1324,22 +1324,13 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
ARRAY_SIZE(atmel_mci0_resource)))
goto fail;
if (data->dma_slave)
dws = kmemdup(to_dw_dma_slave(data->dma_slave),
sizeof(struct dw_dma_slave), GFP_KERNEL);
else
dws = kzalloc(sizeof(struct dw_dma_slave), GFP_KERNEL);
dws->slave.dev = &pdev->dev;
dws->slave.dma_dev = &dw_dmac0_device.dev;
dws->slave.reg_width = DMA_SLAVE_WIDTH_32BIT;
dws->dma_dev = &dw_dmac0_device.dev;
dws->reg_width = DW_DMA_SLAVE_WIDTH_32BIT;
dws->cfg_hi = (DWC_CFGH_SRC_PER(0)
| DWC_CFGH_DST_PER(1));
dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL
| DWC_CFGL_HS_SRC_POL);
data->dma_slave = &dws->slave;
if (platform_device_add_data(pdev, data,
sizeof(struct mci_platform_data)))
goto fail;

Просмотреть файл

@ -28,351 +28,18 @@
#include <linux/async_tx.h>
#ifdef CONFIG_DMA_ENGINE
static enum dma_state_client
dma_channel_add_remove(struct dma_client *client,
struct dma_chan *chan, enum dma_state state);
static struct dma_client async_tx_dma = {
.event_callback = dma_channel_add_remove,
/* .cap_mask == 0 defaults to all channels */
};
/**
* dma_cap_mask_all - enable iteration over all operation types
*/
static dma_cap_mask_t dma_cap_mask_all;
/**
* chan_ref_percpu - tracks channel allocations per core/opertion
*/
struct chan_ref_percpu {
struct dma_chan_ref *ref;
};
static int channel_table_initialized;
static struct chan_ref_percpu *channel_table[DMA_TX_TYPE_END];
/**
* async_tx_lock - protect modification of async_tx_master_list and serialize
* rebalance operations
*/
static spinlock_t async_tx_lock;
static LIST_HEAD(async_tx_master_list);
/* async_tx_issue_pending_all - start all transactions on all channels */
void async_tx_issue_pending_all(void)
static int __init async_tx_init(void)
{
struct dma_chan_ref *ref;
rcu_read_lock();
list_for_each_entry_rcu(ref, &async_tx_master_list, node)
ref->chan->device->device_issue_pending(ref->chan);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(async_tx_issue_pending_all);
/* dma_wait_for_async_tx - spin wait for a transcation to complete
* @tx: transaction to wait on
*/
enum dma_status
dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
{
enum dma_status status;
struct dma_async_tx_descriptor *iter;
struct dma_async_tx_descriptor *parent;
if (!tx)
return DMA_SUCCESS;
/* poll through the dependency chain, return when tx is complete */
do {
iter = tx;
/* find the root of the unsubmitted dependency chain */
do {
parent = iter->parent;
if (!parent)
break;
else
iter = parent;
} while (parent);
/* there is a small window for ->parent == NULL and
* ->cookie == -EBUSY
*/
while (iter->cookie == -EBUSY)
cpu_relax();
status = dma_sync_wait(iter->chan, iter->cookie);
} while (status == DMA_IN_PROGRESS || (iter != tx));
return status;
}
EXPORT_SYMBOL_GPL(dma_wait_for_async_tx);
/* async_tx_run_dependencies - helper routine for dma drivers to process
* (start) dependent operations on their target channel
* @tx: transaction with dependencies
*/
void async_tx_run_dependencies(struct dma_async_tx_descriptor *tx)
{
struct dma_async_tx_descriptor *dep = tx->next;
struct dma_async_tx_descriptor *dep_next;
struct dma_chan *chan;
if (!dep)
return;
chan = dep->chan;
/* keep submitting up until a channel switch is detected
* in that case we will be called again as a result of
* processing the interrupt from async_tx_channel_switch
*/
for (; dep; dep = dep_next) {
spin_lock_bh(&dep->lock);
dep->parent = NULL;
dep_next = dep->next;
if (dep_next && dep_next->chan == chan)
dep->next = NULL; /* ->next will be submitted */
else
dep_next = NULL; /* submit current dep and terminate */
spin_unlock_bh(&dep->lock);
dep->tx_submit(dep);
}
chan->device->device_issue_pending(chan);
}
EXPORT_SYMBOL_GPL(async_tx_run_dependencies);
static void
free_dma_chan_ref(struct rcu_head *rcu)
{
struct dma_chan_ref *ref;
ref = container_of(rcu, struct dma_chan_ref, rcu);
kfree(ref);
}
static void
init_dma_chan_ref(struct dma_chan_ref *ref, struct dma_chan *chan)
{
INIT_LIST_HEAD(&ref->node);
INIT_RCU_HEAD(&ref->rcu);
ref->chan = chan;
atomic_set(&ref->count, 0);
}
/**
* get_chan_ref_by_cap - returns the nth channel of the given capability
* defaults to returning the channel with the desired capability and the
* lowest reference count if the index can not be satisfied
* @cap: capability to match
* @index: nth channel desired, passing -1 has the effect of forcing the
* default return value
*/
static struct dma_chan_ref *
get_chan_ref_by_cap(enum dma_transaction_type cap, int index)
{
struct dma_chan_ref *ret_ref = NULL, *min_ref = NULL, *ref;
rcu_read_lock();
list_for_each_entry_rcu(ref, &async_tx_master_list, node)
if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
if (!min_ref)
min_ref = ref;
else if (atomic_read(&ref->count) <
atomic_read(&min_ref->count))
min_ref = ref;
if (index-- == 0) {
ret_ref = ref;
break;
}
}
rcu_read_unlock();
if (!ret_ref)
ret_ref = min_ref;
if (ret_ref)
atomic_inc(&ret_ref->count);
return ret_ref;
}
/**
* async_tx_rebalance - redistribute the available channels, optimize
* for cpu isolation in the SMP case, and opertaion isolation in the
* uniprocessor case
*/
static void async_tx_rebalance(void)
{
int cpu, cap, cpu_idx = 0;
unsigned long flags;
if (!channel_table_initialized)
return;
spin_lock_irqsave(&async_tx_lock, flags);
/* undo the last distribution */
for_each_dma_cap_mask(cap, dma_cap_mask_all)
for_each_possible_cpu(cpu) {
struct dma_chan_ref *ref =
per_cpu_ptr(channel_table[cap], cpu)->ref;
if (ref) {
atomic_set(&ref->count, 0);
per_cpu_ptr(channel_table[cap], cpu)->ref =
NULL;
}
}
for_each_dma_cap_mask(cap, dma_cap_mask_all)
for_each_online_cpu(cpu) {
struct dma_chan_ref *new;
if (NR_CPUS > 1)
new = get_chan_ref_by_cap(cap, cpu_idx++);
else
new = get_chan_ref_by_cap(cap, -1);
per_cpu_ptr(channel_table[cap], cpu)->ref = new;
}
spin_unlock_irqrestore(&async_tx_lock, flags);
}
static enum dma_state_client
dma_channel_add_remove(struct dma_client *client,
struct dma_chan *chan, enum dma_state state)
{
unsigned long found, flags;
struct dma_chan_ref *master_ref, *ref;
enum dma_state_client ack = DMA_DUP; /* default: take no action */
switch (state) {
case DMA_RESOURCE_AVAILABLE:
found = 0;
rcu_read_lock();
list_for_each_entry_rcu(ref, &async_tx_master_list, node)
if (ref->chan == chan) {
found = 1;
break;
}
rcu_read_unlock();
pr_debug("async_tx: dma resource available [%s]\n",
found ? "old" : "new");
if (!found)
ack = DMA_ACK;
else
break;
/* add the channel to the generic management list */
master_ref = kmalloc(sizeof(*master_ref), GFP_KERNEL);
if (master_ref) {
/* keep a reference until async_tx is unloaded */
dma_chan_get(chan);
init_dma_chan_ref(master_ref, chan);
spin_lock_irqsave(&async_tx_lock, flags);
list_add_tail_rcu(&master_ref->node,
&async_tx_master_list);
spin_unlock_irqrestore(&async_tx_lock,
flags);
} else {
printk(KERN_WARNING "async_tx: unable to create"
" new master entry in response to"
" a DMA_RESOURCE_ADDED event"
" (-ENOMEM)\n");
return 0;
}
async_tx_rebalance();
break;
case DMA_RESOURCE_REMOVED:
found = 0;
spin_lock_irqsave(&async_tx_lock, flags);
list_for_each_entry(ref, &async_tx_master_list, node)
if (ref->chan == chan) {
/* permit backing devices to go away */
dma_chan_put(ref->chan);
list_del_rcu(&ref->node);
call_rcu(&ref->rcu, free_dma_chan_ref);
found = 1;
break;
}
spin_unlock_irqrestore(&async_tx_lock, flags);
pr_debug("async_tx: dma resource removed [%s]\n",
found ? "ours" : "not ours");
if (found)
ack = DMA_ACK;
else
break;
async_tx_rebalance();
break;
case DMA_RESOURCE_SUSPEND:
case DMA_RESOURCE_RESUME:
printk(KERN_WARNING "async_tx: does not support dma channel"
" suspend/resume\n");
break;
default:
BUG();
}
return ack;
}
static int __init
async_tx_init(void)
{
enum dma_transaction_type cap;
spin_lock_init(&async_tx_lock);
bitmap_fill(dma_cap_mask_all.bits, DMA_TX_TYPE_END);
/* an interrupt will never be an explicit operation type.
* clearing this bit prevents allocation to a slot in 'channel_table'
*/
clear_bit(DMA_INTERRUPT, dma_cap_mask_all.bits);
for_each_dma_cap_mask(cap, dma_cap_mask_all) {
channel_table[cap] = alloc_percpu(struct chan_ref_percpu);
if (!channel_table[cap])
goto err;
}
channel_table_initialized = 1;
dma_async_client_register(&async_tx_dma);
dma_async_client_chan_request(&async_tx_dma);
dmaengine_get();
printk(KERN_INFO "async_tx: api initialized (async)\n");
return 0;
err:
printk(KERN_ERR "async_tx: initialization failure\n");
while (--cap >= 0)
free_percpu(channel_table[cap]);
return 1;
}
static void __exit async_tx_exit(void)
{
enum dma_transaction_type cap;
channel_table_initialized = 0;
for_each_dma_cap_mask(cap, dma_cap_mask_all)
if (channel_table[cap])
free_percpu(channel_table[cap]);
dma_async_client_unregister(&async_tx_dma);
dmaengine_put();
}
/**
@ -389,14 +56,7 @@ __async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
if (depend_tx &&
dma_has_cap(tx_type, depend_tx->chan->device->cap_mask))
return depend_tx->chan;
else if (likely(channel_table_initialized)) {
struct dma_chan_ref *ref;
int cpu = get_cpu();
ref = per_cpu_ptr(channel_table[tx_type], cpu)->ref;
put_cpu();
return ref ? ref->chan : NULL;
} else
return NULL;
return dma_find_channel(tx_type);
}
EXPORT_SYMBOL_GPL(__async_tx_find_channel);
#else

Просмотреть файл

@ -270,6 +270,6 @@ static void __exit dca_exit(void)
dca_sysfs_exit();
}
subsys_initcall(dca_init);
arch_initcall(dca_init);
module_exit(dca_exit);

Просмотреть файл

@ -33,7 +33,6 @@ config INTEL_IOATDMA
config INTEL_IOP_ADMA
tristate "Intel IOP ADMA support"
depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX
select ASYNC_CORE
select DMA_ENGINE
help
Enable support for the Intel(R) IOP Series RAID engines.
@ -59,7 +58,6 @@ config FSL_DMA
config MV_XOR
bool "Marvell XOR engine support"
depends on PLAT_ORION
select ASYNC_CORE
select DMA_ENGINE
---help---
Enable support for the Marvell XOR engine.

Просмотреть файл

@ -31,32 +31,18 @@
*
* LOCKING:
*
* The subsystem keeps two global lists, dma_device_list and dma_client_list.
* Both of these are protected by a mutex, dma_list_mutex.
* The subsystem keeps a global list of dma_device structs it is protected by a
* mutex, dma_list_mutex.
*
* A subsystem can get access to a channel by calling dmaengine_get() followed
* by dma_find_channel(), or if it has need for an exclusive channel it can call
* dma_request_channel(). Once a channel is allocated a reference is taken
* against its corresponding driver to disable removal.
*
* 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 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_get is done for each device registered. When the
* device is released, the corresponding kref_put is done in the release
* method. Every time one of the device's channels is allocated to a client,
* a kref_get occurs. When the channel is freed, the corresponding kref_put
* happens. The device's release function does a completion, so
* unregister_device does a remove event, device_unregister, a kref_put
* for the first reference, then waits on the completion for all other
* 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 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 unregistered. 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
* See Documentation/dmaengine.txt for more details
*/
#include <linux/init.h>
@ -70,54 +56,85 @@
#include <linux/rcupdate.h>
#include <linux/mutex.h>
#include <linux/jiffies.h>
#include <linux/rculist.h>
#include <linux/idr.h>
static DEFINE_MUTEX(dma_list_mutex);
static LIST_HEAD(dma_device_list);
static LIST_HEAD(dma_client_list);
static long dmaengine_ref_count;
static struct idr dma_idr;
/* --- sysfs implementation --- */
/**
* dev_to_dma_chan - convert a device pointer to the its sysfs container object
* @dev - device node
*
* Must be called under dma_list_mutex
*/
static struct dma_chan *dev_to_dma_chan(struct device *dev)
{
struct dma_chan_dev *chan_dev;
chan_dev = container_of(dev, typeof(*chan_dev), device);
return chan_dev->chan;
}
static ssize_t show_memcpy_count(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dma_chan *chan = to_dma_chan(dev);
struct dma_chan *chan;
unsigned long count = 0;
int i;
int err;
mutex_lock(&dma_list_mutex);
chan = dev_to_dma_chan(dev);
if (chan) {
for_each_possible_cpu(i)
count += per_cpu_ptr(chan->local, i)->memcpy_count;
err = sprintf(buf, "%lu\n", count);
} else
err = -ENODEV;
mutex_unlock(&dma_list_mutex);
return sprintf(buf, "%lu\n", count);
return err;
}
static ssize_t show_bytes_transferred(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dma_chan *chan = to_dma_chan(dev);
struct dma_chan *chan;
unsigned long count = 0;
int i;
int err;
mutex_lock(&dma_list_mutex);
chan = dev_to_dma_chan(dev);
if (chan) {
for_each_possible_cpu(i)
count += per_cpu_ptr(chan->local, i)->bytes_transferred;
err = sprintf(buf, "%lu\n", count);
} else
err = -ENODEV;
mutex_unlock(&dma_list_mutex);
return sprintf(buf, "%lu\n", count);
return err;
}
static ssize_t show_in_use(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dma_chan *chan = to_dma_chan(dev);
int in_use = 0;
struct dma_chan *chan;
int err;
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();
}
mutex_lock(&dma_list_mutex);
chan = dev_to_dma_chan(dev);
if (chan)
err = sprintf(buf, "%d\n", chan->client_count);
else
err = -ENODEV;
mutex_unlock(&dma_list_mutex);
return sprintf(buf, "%d\n", in_use);
return err;
}
static struct device_attribute dma_attrs[] = {
@ -127,76 +144,110 @@ static struct device_attribute dma_attrs[] = {
__ATTR_NULL
};
static void dma_async_device_cleanup(struct kref *kref);
static void dma_dev_release(struct device *dev)
static void chan_dev_release(struct device *dev)
{
struct dma_chan *chan = to_dma_chan(dev);
kref_put(&chan->device->refcount, dma_async_device_cleanup);
struct dma_chan_dev *chan_dev;
chan_dev = container_of(dev, typeof(*chan_dev), device);
if (atomic_dec_and_test(chan_dev->idr_ref)) {
mutex_lock(&dma_list_mutex);
idr_remove(&dma_idr, chan_dev->dev_id);
mutex_unlock(&dma_list_mutex);
kfree(chan_dev->idr_ref);
}
kfree(chan_dev);
}
static struct class dma_devclass = {
.name = "dma",
.dev_attrs = dma_attrs,
.dev_release = dma_dev_release,
.dev_release = chan_dev_release,
};
/* --- client and device registration --- */
#define dma_chan_satisfies_mask(chan, mask) \
__dma_chan_satisfies_mask((chan), &(mask))
#define dma_device_satisfies_mask(device, mask) \
__dma_device_satisfies_mask((device), &(mask))
static int
__dma_chan_satisfies_mask(struct dma_chan *chan, dma_cap_mask_t *want)
__dma_device_satisfies_mask(struct dma_device *device, dma_cap_mask_t *want)
{
dma_cap_mask_t has;
bitmap_and(has.bits, want->bits, chan->device->cap_mask.bits,
bitmap_and(has.bits, want->bits, 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 channels to a client
* @client: &dma_client
*
* Called with dma_list_mutex held.
*/
static void dma_client_chan_alloc(struct dma_client *client)
static struct module *dma_chan_to_owner(struct dma_chan *chan)
{
struct dma_device *device;
struct dma_chan *chan;
int desc; /* allocated descriptor count */
enum dma_state_client ack;
return chan->device->dev->driver->owner;
}
/* Find a channel */
list_for_each_entry(device, &dma_device_list, global_node) {
/* Does the client require a specific DMA controller? */
if (client->slave && client->slave->dma_dev
&& client->slave->dma_dev != device->dev)
continue;
list_for_each_entry(chan, &device->channels, device_node) {
if (!dma_chan_satisfies_mask(chan, client->cap_mask))
continue;
desc = chan->device->device_alloc_chan_resources(
chan, client);
if (desc >= 0) {
ack = client->event_callback(client,
chan,
DMA_RESOURCE_AVAILABLE);
/* we are done once this client rejects
* an available resource
/**
* balance_ref_count - catch up the channel reference count
* @chan - channel to balance ->client_count versus dmaengine_ref_count
*
* balance_ref_count must be called under dma_list_mutex
*/
if (ack == DMA_ACK) {
dma_chan_get(chan);
static void balance_ref_count(struct dma_chan *chan)
{
struct module *owner = dma_chan_to_owner(chan);
while (chan->client_count < dmaengine_ref_count) {
__module_get(owner);
chan->client_count++;
} else if (ack == DMA_NAK)
return;
}
}
/**
* dma_chan_get - try to grab a dma channel's parent driver module
* @chan - channel to grab
*
* Must be called under dma_list_mutex
*/
static int dma_chan_get(struct dma_chan *chan)
{
int err = -ENODEV;
struct module *owner = dma_chan_to_owner(chan);
if (chan->client_count) {
__module_get(owner);
err = 0;
} else if (try_module_get(owner))
err = 0;
if (err == 0)
chan->client_count++;
/* allocate upon first client reference */
if (chan->client_count == 1 && err == 0) {
int desc_cnt = chan->device->device_alloc_chan_resources(chan);
if (desc_cnt < 0) {
err = desc_cnt;
chan->client_count = 0;
module_put(owner);
} else if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask))
balance_ref_count(chan);
}
return err;
}
/**
* dma_chan_put - drop a reference to a dma channel's parent driver module
* @chan - channel to release
*
* Must be called under dma_list_mutex
*/
static void dma_chan_put(struct dma_chan *chan)
{
if (!chan->client_count)
return; /* this channel failed alloc_chan_resources */
chan->client_count--;
module_put(dma_chan_to_owner(chan));
if (chan->client_count == 0)
chan->device->device_free_chan_resources(chan);
}
enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie)
@ -218,138 +269,342 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie)
EXPORT_SYMBOL(dma_sync_wait);
/**
* dma_chan_cleanup - release a DMA channel's resources
* @kref: kernel reference structure that contains the DMA channel device
* dma_cap_mask_all - enable iteration over all operation types
*/
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);
kref_put(&chan->device->refcount, dma_async_device_cleanup);
}
EXPORT_SYMBOL(dma_chan_cleanup);
static void dma_chan_free_rcu(struct rcu_head *rcu)
{
struct dma_chan *chan = container_of(rcu, struct dma_chan, rcu);
int bias = 0x7FFFFFFF;
int i;
for_each_possible_cpu(i)
bias -= local_read(&per_cpu_ptr(chan->local, i)->refcount);
atomic_sub(bias, &chan->refcount.refcount);
kref_put(&chan->refcount, dma_chan_cleanup);
}
static void dma_chan_release(struct dma_chan *chan)
{
atomic_add(0x7FFFFFFF, &chan->refcount.refcount);
chan->slow_ref = 1;
call_rcu(&chan->rcu, dma_chan_free_rcu);
}
static dma_cap_mask_t dma_cap_mask_all;
/**
* dma_chans_notify_available - broadcast available channels to the clients
* dma_chan_tbl_ent - tracks channel allocations per core/operation
* @chan - associated channel for this entry
*/
static void dma_clients_notify_available(void)
{
struct dma_client *client;
mutex_lock(&dma_list_mutex);
list_for_each_entry(client, &dma_client_list, global_node)
dma_client_chan_alloc(client);
mutex_unlock(&dma_list_mutex);
}
struct dma_chan_tbl_ent {
struct dma_chan *chan;
};
/**
* dma_chans_notify_available - tell the clients that a channel is going away
* @chan: channel on its way out
* channel_table - percpu lookup table for memory-to-memory offload providers
*/
static void dma_clients_notify_removed(struct dma_chan *chan)
static struct dma_chan_tbl_ent *channel_table[DMA_TX_TYPE_END];
static int __init dma_channel_table_init(void)
{
struct dma_client *client;
enum dma_state_client ack;
enum dma_transaction_type cap;
int err = 0;
mutex_lock(&dma_list_mutex);
bitmap_fill(dma_cap_mask_all.bits, DMA_TX_TYPE_END);
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
/* 'interrupt', 'private', and 'slave' are channel capabilities,
* but are not associated with an operation so they do not need
* an entry in the channel_table
*/
if (ack == DMA_ACK) {
dma_chan_put(chan);
chan->client_count--;
clear_bit(DMA_INTERRUPT, dma_cap_mask_all.bits);
clear_bit(DMA_PRIVATE, dma_cap_mask_all.bits);
clear_bit(DMA_SLAVE, dma_cap_mask_all.bits);
for_each_dma_cap_mask(cap, dma_cap_mask_all) {
channel_table[cap] = alloc_percpu(struct dma_chan_tbl_ent);
if (!channel_table[cap]) {
err = -ENOMEM;
break;
}
}
mutex_unlock(&dma_list_mutex);
if (err) {
pr_err("dmaengine: initialization failure\n");
for_each_dma_cap_mask(cap, dma_cap_mask_all)
if (channel_table[cap])
free_percpu(channel_table[cap]);
}
return err;
}
arch_initcall(dma_channel_table_init);
/**
* dma_async_client_register - register a &dma_client
* @client: ptr to a client structure with valid 'event_callback' and 'cap_mask'
* dma_find_channel - find a channel to carry out the operation
* @tx_type: transaction type
*/
void dma_async_client_register(struct dma_client *client)
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
{
/* validate client data */
BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) &&
!client->slave);
struct dma_chan *chan;
int cpu;
mutex_lock(&dma_list_mutex);
list_add_tail(&client->global_node, &dma_client_list);
mutex_unlock(&dma_list_mutex);
WARN_ONCE(dmaengine_ref_count == 0,
"client called %s without a reference", __func__);
cpu = get_cpu();
chan = per_cpu_ptr(channel_table[tx_type], cpu)->chan;
put_cpu();
return chan;
}
EXPORT_SYMBOL(dma_async_client_register);
EXPORT_SYMBOL(dma_find_channel);
/**
* dma_async_client_unregister - unregister a client and free the &dma_client
* @client: &dma_client to free
*
* Force frees any allocated DMA channels, frees the &dma_client memory
* dma_issue_pending_all - flush all pending operations across all channels
*/
void dma_async_client_unregister(struct dma_client *client)
void dma_issue_pending_all(void)
{
struct dma_device *device;
struct dma_chan *chan;
enum dma_state_client ack;
if (!client)
return;
WARN_ONCE(dmaengine_ref_count == 0,
"client called %s without a reference", __func__);
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_ACK) {
dma_chan_put(chan);
chan->client_count--;
rcu_read_lock();
list_for_each_entry_rcu(device, &dma_device_list, global_node) {
if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
continue;
list_for_each_entry(chan, &device->channels, device_node)
if (chan->client_count)
device->device_issue_pending(chan);
}
rcu_read_unlock();
}
list_del(&client->global_node);
mutex_unlock(&dma_list_mutex);
}
EXPORT_SYMBOL(dma_async_client_unregister);
EXPORT_SYMBOL(dma_issue_pending_all);
/**
* dma_async_client_chan_request - send all available channels to the
* client that satisfy the capability mask
* @client - requester
* nth_chan - returns the nth channel of the given capability
* @cap: capability to match
* @n: nth channel desired
*
* Defaults to returning the channel with the desired capability and the
* lowest reference count when 'n' cannot be satisfied. Must be called
* under dma_list_mutex.
*/
void dma_async_client_chan_request(struct dma_client *client)
static struct dma_chan *nth_chan(enum dma_transaction_type cap, int n)
{
struct dma_device *device;
struct dma_chan *chan;
struct dma_chan *ret = NULL;
struct dma_chan *min = NULL;
list_for_each_entry(device, &dma_device_list, global_node) {
if (!dma_has_cap(cap, device->cap_mask) ||
dma_has_cap(DMA_PRIVATE, device->cap_mask))
continue;
list_for_each_entry(chan, &device->channels, device_node) {
if (!chan->client_count)
continue;
if (!min)
min = chan;
else if (chan->table_count < min->table_count)
min = chan;
if (n-- == 0) {
ret = chan;
break; /* done */
}
}
if (ret)
break; /* done */
}
if (!ret)
ret = min;
if (ret)
ret->table_count++;
return ret;
}
/**
* dma_channel_rebalance - redistribute the available channels
*
* Optimize for cpu isolation (each cpu gets a dedicated channel for an
* operation type) in the SMP case, and operation isolation (avoid
* multi-tasking channels) in the non-SMP case. Must be called under
* dma_list_mutex.
*/
static void dma_channel_rebalance(void)
{
struct dma_chan *chan;
struct dma_device *device;
int cpu;
int cap;
int n;
/* undo the last distribution */
for_each_dma_cap_mask(cap, dma_cap_mask_all)
for_each_possible_cpu(cpu)
per_cpu_ptr(channel_table[cap], cpu)->chan = NULL;
list_for_each_entry(device, &dma_device_list, global_node) {
if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
continue;
list_for_each_entry(chan, &device->channels, device_node)
chan->table_count = 0;
}
/* don't populate the channel_table if no clients are available */
if (!dmaengine_ref_count)
return;
/* redistribute available channels */
n = 0;
for_each_dma_cap_mask(cap, dma_cap_mask_all)
for_each_online_cpu(cpu) {
if (num_possible_cpus() > 1)
chan = nth_chan(cap, n++);
else
chan = nth_chan(cap, -1);
per_cpu_ptr(channel_table[cap], cpu)->chan = chan;
}
}
static struct dma_chan *private_candidate(dma_cap_mask_t *mask, struct dma_device *dev,
dma_filter_fn fn, void *fn_param)
{
struct dma_chan *chan;
if (!__dma_device_satisfies_mask(dev, mask)) {
pr_debug("%s: wrong capabilities\n", __func__);
return NULL;
}
/* devices with multiple channels need special handling as we need to
* ensure that all channels are either private or public.
*/
if (dev->chancnt > 1 && !dma_has_cap(DMA_PRIVATE, dev->cap_mask))
list_for_each_entry(chan, &dev->channels, device_node) {
/* some channels are already publicly allocated */
if (chan->client_count)
return NULL;
}
list_for_each_entry(chan, &dev->channels, device_node) {
if (chan->client_count) {
pr_debug("%s: %s busy\n",
__func__, dma_chan_name(chan));
continue;
}
if (fn && !fn(chan, fn_param)) {
pr_debug("%s: %s filter said false\n",
__func__, dma_chan_name(chan));
continue;
}
return chan;
}
return NULL;
}
/**
* dma_request_channel - try to allocate an exclusive channel
* @mask: capabilities that the channel must satisfy
* @fn: optional callback to disposition available channels
* @fn_param: opaque parameter to pass to dma_filter_fn
*/
struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param)
{
struct dma_device *device, *_d;
struct dma_chan *chan = NULL;
int err;
/* Find a channel */
mutex_lock(&dma_list_mutex);
list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
chan = private_candidate(mask, device, fn, fn_param);
if (chan) {
/* Found a suitable channel, try to grab, prep, and
* return it. We first set DMA_PRIVATE to disable
* balance_ref_count as this channel will not be
* published in the general-purpose allocator
*/
dma_cap_set(DMA_PRIVATE, device->cap_mask);
err = dma_chan_get(chan);
if (err == -ENODEV) {
pr_debug("%s: %s module removed\n", __func__,
dma_chan_name(chan));
list_del_rcu(&device->global_node);
} else if (err)
pr_err("dmaengine: failed to get %s: (%d)\n",
dma_chan_name(chan), err);
else
break;
chan = NULL;
}
}
mutex_unlock(&dma_list_mutex);
pr_debug("%s: %s (%s)\n", __func__, chan ? "success" : "fail",
chan ? dma_chan_name(chan) : NULL);
return chan;
}
EXPORT_SYMBOL_GPL(__dma_request_channel);
void dma_release_channel(struct dma_chan *chan)
{
mutex_lock(&dma_list_mutex);
dma_client_chan_alloc(client);
WARN_ONCE(chan->client_count != 1,
"chan reference count %d != 1\n", chan->client_count);
dma_chan_put(chan);
mutex_unlock(&dma_list_mutex);
}
EXPORT_SYMBOL(dma_async_client_chan_request);
EXPORT_SYMBOL_GPL(dma_release_channel);
/**
* dmaengine_get - register interest in dma_channels
*/
void dmaengine_get(void)
{
struct dma_device *device, *_d;
struct dma_chan *chan;
int err;
mutex_lock(&dma_list_mutex);
dmaengine_ref_count++;
/* try to grab channels */
list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
continue;
list_for_each_entry(chan, &device->channels, device_node) {
err = dma_chan_get(chan);
if (err == -ENODEV) {
/* module removed before we could use it */
list_del_rcu(&device->global_node);
break;
} else if (err)
pr_err("dmaengine: failed to get %s: (%d)\n",
dma_chan_name(chan), err);
}
}
/* if this is the first reference and there were channels
* waiting we need to rebalance to get those channels
* incorporated into the channel table
*/
if (dmaengine_ref_count == 1)
dma_channel_rebalance();
mutex_unlock(&dma_list_mutex);
}
EXPORT_SYMBOL(dmaengine_get);
/**
* dmaengine_put - let dma drivers be removed when ref_count == 0
*/
void dmaengine_put(void)
{
struct dma_device *device;
struct dma_chan *chan;
mutex_lock(&dma_list_mutex);
dmaengine_ref_count--;
BUG_ON(dmaengine_ref_count < 0);
/* drop channel references */
list_for_each_entry(device, &dma_device_list, global_node) {
if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
continue;
list_for_each_entry(chan, &device->channels, device_node)
dma_chan_put(chan);
}
mutex_unlock(&dma_list_mutex);
}
EXPORT_SYMBOL(dmaengine_put);
/**
* dma_async_device_register - registers DMA devices found
@ -357,9 +612,9 @@ EXPORT_SYMBOL(dma_async_client_chan_request);
*/
int dma_async_device_register(struct dma_device *device)
{
static int id;
int chancnt = 0, rc;
struct dma_chan* chan;
atomic_t *idr_ref;
if (!device)
return -ENODEV;
@ -386,47 +641,72 @@ int dma_async_device_register(struct dma_device *device)
BUG_ON(!device->device_issue_pending);
BUG_ON(!device->dev);
init_completion(&device->done);
kref_init(&device->refcount);
idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL);
if (!idr_ref)
return -ENOMEM;
atomic_set(idr_ref, 0);
idr_retry:
if (!idr_pre_get(&dma_idr, GFP_KERNEL))
return -ENOMEM;
mutex_lock(&dma_list_mutex);
device->dev_id = id++;
rc = idr_get_new(&dma_idr, NULL, &device->dev_id);
mutex_unlock(&dma_list_mutex);
if (rc == -EAGAIN)
goto idr_retry;
else if (rc != 0)
return rc;
/* represent channels in sysfs. Probably want devs too */
list_for_each_entry(chan, &device->channels, device_node) {
chan->local = alloc_percpu(typeof(*chan->local));
if (chan->local == NULL)
continue;
chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL);
if (chan->dev == NULL) {
free_percpu(chan->local);
continue;
}
chan->chan_id = chancnt++;
chan->dev.class = &dma_devclass;
chan->dev.parent = device->dev;
dev_set_name(&chan->dev, "dma%dchan%d",
chan->dev->device.class = &dma_devclass;
chan->dev->device.parent = device->dev;
chan->dev->chan = chan;
chan->dev->idr_ref = idr_ref;
chan->dev->dev_id = device->dev_id;
atomic_inc(idr_ref);
dev_set_name(&chan->dev->device, "dma%dchan%d",
device->dev_id, chan->chan_id);
rc = device_register(&chan->dev);
rc = device_register(&chan->dev->device);
if (rc) {
chancnt--;
free_percpu(chan->local);
chan->local = NULL;
goto err_out;
}
/* One for the channel, one of the class device */
kref_get(&device->refcount);
kref_get(&device->refcount);
kref_init(&chan->refcount);
chan->client_count = 0;
chan->slow_ref = 0;
INIT_RCU_HEAD(&chan->rcu);
}
device->chancnt = chancnt;
mutex_lock(&dma_list_mutex);
list_add_tail(&device->global_node, &dma_device_list);
/* take references on public channels */
if (dmaengine_ref_count && !dma_has_cap(DMA_PRIVATE, device->cap_mask))
list_for_each_entry(chan, &device->channels, device_node) {
/* if clients are already waiting for channels we need
* to take references on their behalf
*/
if (dma_chan_get(chan) == -ENODEV) {
/* note we can only get here for the first
* channel as the remaining channels are
* guaranteed to get a reference
*/
rc = -ENODEV;
mutex_unlock(&dma_list_mutex);
goto err_out;
}
}
list_add_tail_rcu(&device->global_node, &dma_device_list);
dma_channel_rebalance();
mutex_unlock(&dma_list_mutex);
dma_clients_notify_available();
return 0;
@ -434,9 +714,10 @@ err_out:
list_for_each_entry(chan, &device->channels, device_node) {
if (chan->local == NULL)
continue;
kref_put(&device->refcount, dma_async_device_cleanup);
device_unregister(&chan->dev);
chancnt--;
mutex_lock(&dma_list_mutex);
chan->dev->chan = NULL;
mutex_unlock(&dma_list_mutex);
device_unregister(&chan->dev->device);
free_percpu(chan->local);
}
return rc;
@ -444,37 +725,30 @@ err_out:
EXPORT_SYMBOL(dma_async_device_register);
/**
* dma_async_device_cleanup - function called when all references are released
* @kref: kernel reference object
*/
static void dma_async_device_cleanup(struct kref *kref)
{
struct dma_device *device;
device = container_of(kref, struct dma_device, refcount);
complete(&device->done);
}
/**
* dma_async_device_unregister - unregisters DMA devices
* dma_async_device_unregister - unregister a DMA device
* @device: &dma_device
*
* This routine is called by dma driver exit routines, dmaengine holds module
* references to prevent it being called while channels are in use.
*/
void dma_async_device_unregister(struct dma_device *device)
{
struct dma_chan *chan;
mutex_lock(&dma_list_mutex);
list_del(&device->global_node);
list_del_rcu(&device->global_node);
dma_channel_rebalance();
mutex_unlock(&dma_list_mutex);
list_for_each_entry(chan, &device->channels, device_node) {
dma_clients_notify_removed(chan);
device_unregister(&chan->dev);
dma_chan_release(chan);
WARN_ONCE(chan->client_count,
"%s called while %d clients hold a reference\n",
__func__, chan->client_count);
mutex_lock(&dma_list_mutex);
chan->dev->chan = NULL;
mutex_unlock(&dma_list_mutex);
device_unregister(&chan->dev->device);
}
kref_put(&device->refcount, dma_async_device_cleanup);
wait_for_completion(&device->done);
}
EXPORT_SYMBOL(dma_async_device_unregister);
@ -626,10 +900,96 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx,
}
EXPORT_SYMBOL(dma_async_tx_descriptor_init);
/* dma_wait_for_async_tx - spin wait for a transaction to complete
* @tx: in-flight transaction to wait on
*
* This routine assumes that tx was obtained from a call to async_memcpy,
* async_xor, async_memset, etc which ensures that tx is "in-flight" (prepped
* and submitted). Walking the parent chain is only meant to cover for DMA
* drivers that do not implement the DMA_INTERRUPT capability and may race with
* the driver's descriptor cleanup routine.
*/
enum dma_status
dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
{
enum dma_status status;
struct dma_async_tx_descriptor *iter;
struct dma_async_tx_descriptor *parent;
if (!tx)
return DMA_SUCCESS;
WARN_ONCE(tx->parent, "%s: speculatively walking dependency chain for"
" %s\n", __func__, dma_chan_name(tx->chan));
/* poll through the dependency chain, return when tx is complete */
do {
iter = tx;
/* find the root of the unsubmitted dependency chain */
do {
parent = iter->parent;
if (!parent)
break;
else
iter = parent;
} while (parent);
/* there is a small window for ->parent == NULL and
* ->cookie == -EBUSY
*/
while (iter->cookie == -EBUSY)
cpu_relax();
status = dma_sync_wait(iter->chan, iter->cookie);
} while (status == DMA_IN_PROGRESS || (iter != tx));
return status;
}
EXPORT_SYMBOL_GPL(dma_wait_for_async_tx);
/* dma_run_dependencies - helper routine for dma drivers to process
* (start) dependent operations on their target channel
* @tx: transaction with dependencies
*/
void dma_run_dependencies(struct dma_async_tx_descriptor *tx)
{
struct dma_async_tx_descriptor *dep = tx->next;
struct dma_async_tx_descriptor *dep_next;
struct dma_chan *chan;
if (!dep)
return;
chan = dep->chan;
/* keep submitting up until a channel switch is detected
* in that case we will be called again as a result of
* processing the interrupt from async_tx_channel_switch
*/
for (; dep; dep = dep_next) {
spin_lock_bh(&dep->lock);
dep->parent = NULL;
dep_next = dep->next;
if (dep_next && dep_next->chan == chan)
dep->next = NULL; /* ->next will be submitted */
else
dep_next = NULL; /* submit current dep and terminate */
spin_unlock_bh(&dep->lock);
dep->tx_submit(dep);
}
chan->device->device_issue_pending(chan);
}
EXPORT_SYMBOL_GPL(dma_run_dependencies);
static int __init dma_bus_init(void)
{
idr_init(&dma_idr);
mutex_init(&dma_list_mutex);
return class_register(&dma_devclass);
}
subsys_initcall(dma_bus_init);
arch_initcall(dma_bus_init);

Просмотреть файл

@ -35,7 +35,7 @@ MODULE_PARM_DESC(threads_per_chan,
static unsigned int max_channels;
module_param(max_channels, uint, S_IRUGO);
MODULE_PARM_DESC(nr_channels,
MODULE_PARM_DESC(max_channels,
"Maximum number of channels to use (default: all)");
/*
@ -71,7 +71,7 @@ struct dmatest_chan {
/*
* These are protected by dma_list_mutex since they're only used by
* the DMA client event callback
* the DMA filter function callback
*/
static LIST_HEAD(dmatest_channels);
static unsigned int nr_channels;
@ -80,7 +80,7 @@ static bool dmatest_match_channel(struct dma_chan *chan)
{
if (test_channel[0] == '\0')
return true;
return strcmp(dev_name(&chan->dev), test_channel) == 0;
return strcmp(dma_chan_name(chan), test_channel) == 0;
}
static bool dmatest_match_device(struct dma_device *device)
@ -215,7 +215,6 @@ static int dmatest_func(void *data)
smp_rmb();
chan = thread->chan;
dma_chan_get(chan);
while (!kthread_should_stop()) {
total_tests++;
@ -293,7 +292,6 @@ static int dmatest_func(void *data)
}
ret = 0;
dma_chan_put(chan);
kfree(thread->dstbuf);
err_dstbuf:
kfree(thread->srcbuf);
@ -319,21 +317,16 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
kfree(dtc);
}
static enum dma_state_client dmatest_add_channel(struct dma_chan *chan)
static int dmatest_add_channel(struct dma_chan *chan)
{
struct dmatest_chan *dtc;
struct dmatest_thread *thread;
unsigned int i;
/* Have we already been told about this channel? */
list_for_each_entry(dtc, &dmatest_channels, node)
if (dtc->chan == chan)
return DMA_DUP;
dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL);
if (!dtc) {
pr_warning("dmatest: No memory for %s\n", dev_name(&chan->dev));
return DMA_NAK;
pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan));
return -ENOMEM;
}
dtc->chan = chan;
@ -343,16 +336,16 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan)
thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL);
if (!thread) {
pr_warning("dmatest: No memory for %s-test%u\n",
dev_name(&chan->dev), i);
dma_chan_name(chan), i);
break;
}
thread->chan = dtc->chan;
smp_wmb();
thread->task = kthread_run(dmatest_func, thread, "%s-test%u",
dev_name(&chan->dev), i);
dma_chan_name(chan), i);
if (IS_ERR(thread->task)) {
pr_warning("dmatest: Failed to run thread %s-test%u\n",
dev_name(&chan->dev), i);
dma_chan_name(chan), i);
kfree(thread);
break;
}
@ -362,87 +355,63 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan)
list_add_tail(&thread->node, &dtc->threads);
}
pr_info("dmatest: Started %u threads using %s\n", i, dev_name(&chan->dev));
pr_info("dmatest: Started %u threads using %s\n", i, dma_chan_name(chan));
list_add_tail(&dtc->node, &dmatest_channels);
nr_channels++;
return DMA_ACK;
return 0;
}
static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan)
static bool filter(struct dma_chan *chan, void *param)
{
if (!dmatest_match_channel(chan) || !dmatest_match_device(chan->device))
return false;
else
return true;
}
static int __init dmatest_init(void)
{
dma_cap_mask_t mask;
struct dma_chan *chan;
int err = 0;
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
for (;;) {
chan = dma_request_channel(mask, filter, NULL);
if (chan) {
err = dmatest_add_channel(chan);
if (err == 0)
continue;
else {
dma_release_channel(chan);
break; /* add_channel failed, punt */
}
} else
break; /* no more channels available */
if (max_channels && nr_channels >= max_channels)
break; /* we have all we need */
}
return err;
}
/* when compiled-in wait for drivers to load first */
late_initcall(dmatest_init);
static void __exit dmatest_exit(void)
{
struct dmatest_chan *dtc, *_dtc;
list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) {
if (dtc->chan == chan) {
list_del(&dtc->node);
dmatest_cleanup_channel(dtc);
pr_debug("dmatest: lost channel %s\n",
dev_name(&chan->dev));
return DMA_ACK;
pr_debug("dmatest: dropped channel %s\n",
dma_chan_name(dtc->chan));
dma_release_channel(dtc->chan);
}
}
return DMA_DUP;
}
/*
* Start testing threads as new channels are assigned to us, and kill
* them when the channels go away.
*
* When we unregister the client, all channels are removed so this
* will also take care of cleaning things up when the module is
* unloaded.
*/
static enum dma_state_client
dmatest_event(struct dma_client *client, struct dma_chan *chan,
enum dma_state state)
{
enum dma_state_client ack = DMA_NAK;
switch (state) {
case DMA_RESOURCE_AVAILABLE:
if (!dmatest_match_channel(chan)
|| !dmatest_match_device(chan->device))
ack = DMA_DUP;
else if (max_channels && nr_channels >= max_channels)
ack = DMA_NAK;
else
ack = dmatest_add_channel(chan);
break;
case DMA_RESOURCE_REMOVED:
ack = dmatest_remove_channel(chan);
break;
default:
pr_info("dmatest: Unhandled event %u (%s)\n",
state, dev_name(&chan->dev));
break;
}
return ack;
}
static struct dma_client dmatest_client = {
.event_callback = dmatest_event,
};
static int __init dmatest_init(void)
{
dma_cap_set(DMA_MEMCPY, dmatest_client.cap_mask);
dma_async_client_register(&dmatest_client);
dma_async_client_chan_request(&dmatest_client);
return 0;
}
module_init(dmatest_init);
static void __exit dmatest_exit(void)
{
dma_async_client_unregister(&dmatest_client);
}
module_exit(dmatest_exit);
MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");

Просмотреть файл

@ -70,6 +70,15 @@
* the controller, though.
*/
static struct device *chan2dev(struct dma_chan *chan)
{
return &chan->dev->device;
}
static struct device *chan2parent(struct dma_chan *chan)
{
return chan->dev->device.parent;
}
static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc)
{
return list_entry(dwc->active_list.next, struct dw_desc, desc_node);
@ -93,12 +102,12 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc)
ret = desc;
break;
}
dev_dbg(&dwc->chan.dev, "desc %p not ACKed\n", desc);
dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc);
i++;
}
spin_unlock_bh(&dwc->lock);
dev_vdbg(&dwc->chan.dev, "scanned %u descriptors on freelist\n", i);
dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i);
return ret;
}
@ -108,10 +117,10 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc)
struct dw_desc *child;
list_for_each_entry(child, &desc->txd.tx_list, desc_node)
dma_sync_single_for_cpu(dwc->chan.dev.parent,
dma_sync_single_for_cpu(chan2parent(&dwc->chan),
child->txd.phys, sizeof(child->lli),
DMA_TO_DEVICE);
dma_sync_single_for_cpu(dwc->chan.dev.parent,
dma_sync_single_for_cpu(chan2parent(&dwc->chan),
desc->txd.phys, sizeof(desc->lli),
DMA_TO_DEVICE);
}
@ -129,11 +138,11 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
spin_lock_bh(&dwc->lock);
list_for_each_entry(child, &desc->txd.tx_list, desc_node)
dev_vdbg(&dwc->chan.dev,
dev_vdbg(chan2dev(&dwc->chan),
"moving child desc %p to freelist\n",
child);
list_splice_init(&desc->txd.tx_list, &dwc->free_list);
dev_vdbg(&dwc->chan.dev, "moving desc %p to freelist\n", desc);
dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc);
list_add(&desc->desc_node, &dwc->free_list);
spin_unlock_bh(&dwc->lock);
}
@ -163,9 +172,9 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
/* ASSERT: channel is idle */
if (dma_readl(dw, CH_EN) & dwc->mask) {
dev_err(&dwc->chan.dev,
dev_err(chan2dev(&dwc->chan),
"BUG: Attempted to start non-idle channel\n");
dev_err(&dwc->chan.dev,
dev_err(chan2dev(&dwc->chan),
" SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n",
channel_readl(dwc, SAR),
channel_readl(dwc, DAR),
@ -193,7 +202,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
void *param;
struct dma_async_tx_descriptor *txd = &desc->txd;
dev_vdbg(&dwc->chan.dev, "descriptor %u complete\n", txd->cookie);
dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie);
dwc->completed = txd->cookie;
callback = txd->callback;
@ -208,11 +217,11 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc)
* mapped before they were submitted...
*/
if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP))
dma_unmap_page(dwc->chan.dev.parent, desc->lli.dar, desc->len,
DMA_FROM_DEVICE);
dma_unmap_page(chan2parent(&dwc->chan), desc->lli.dar,
desc->len, DMA_FROM_DEVICE);
if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP))
dma_unmap_page(dwc->chan.dev.parent, desc->lli.sar, desc->len,
DMA_TO_DEVICE);
dma_unmap_page(chan2parent(&dwc->chan), desc->lli.sar,
desc->len, DMA_TO_DEVICE);
/*
* The API requires that no submissions are done from a
@ -228,7 +237,7 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc)
LIST_HEAD(list);
if (dma_readl(dw, CH_EN) & dwc->mask) {
dev_err(&dwc->chan.dev,
dev_err(chan2dev(&dwc->chan),
"BUG: XFER bit set, but channel not idle!\n");
/* Try to continue after resetting the channel... */
@ -273,7 +282,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
return;
}
dev_vdbg(&dwc->chan.dev, "scan_descriptors: llp=0x%x\n", llp);
dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp);
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
if (desc->lli.llp == llp)
@ -292,7 +301,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
dwc_descriptor_complete(dwc, desc);
}
dev_err(&dwc->chan.dev,
dev_err(chan2dev(&dwc->chan),
"BUG: All descriptors done, but channel not idle!\n");
/* Try to continue after resetting the channel... */
@ -308,7 +317,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli)
{
dev_printk(KERN_CRIT, &dwc->chan.dev,
dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
" desc: s0x%x d0x%x l0x%x c0x%x:%x\n",
lli->sar, lli->dar, lli->llp,
lli->ctlhi, lli->ctllo);
@ -342,9 +351,9 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
* controller flagged an error instead of scribbling over
* random memory locations.
*/
dev_printk(KERN_CRIT, &dwc->chan.dev,
dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
"Bad descriptor submitted for DMA!\n");
dev_printk(KERN_CRIT, &dwc->chan.dev,
dev_printk(KERN_CRIT, chan2dev(&dwc->chan),
" cookie: %d\n", bad_desc->txd.cookie);
dwc_dump_lli(dwc, &bad_desc->lli);
list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node)
@ -442,12 +451,12 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
* for DMA. But this is hard to do in a race-free manner.
*/
if (list_empty(&dwc->active_list)) {
dev_vdbg(&tx->chan->dev, "tx_submit: started %u\n",
dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n",
desc->txd.cookie);
dwc_dostart(dwc, desc);
list_add_tail(&desc->desc_node, &dwc->active_list);
} else {
dev_vdbg(&tx->chan->dev, "tx_submit: queued %u\n",
dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u\n",
desc->txd.cookie);
list_add_tail(&desc->desc_node, &dwc->queue);
@ -472,11 +481,11 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
unsigned int dst_width;
u32 ctllo;
dev_vdbg(&chan->dev, "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n",
dev_vdbg(chan2dev(chan), "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n",
dest, src, len, flags);
if (unlikely(!len)) {
dev_dbg(&chan->dev, "prep_dma_memcpy: length is zero!\n");
dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
return NULL;
}
@ -516,7 +525,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
first = desc;
} else {
prev->lli.llp = desc->txd.phys;
dma_sync_single_for_device(chan->dev.parent,
dma_sync_single_for_device(chan2parent(chan),
prev->txd.phys, sizeof(prev->lli),
DMA_TO_DEVICE);
list_add_tail(&desc->desc_node,
@ -531,7 +540,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
prev->lli.ctllo |= DWC_CTLL_INT_EN;
prev->lli.llp = 0;
dma_sync_single_for_device(chan->dev.parent,
dma_sync_single_for_device(chan2parent(chan),
prev->txd.phys, sizeof(prev->lli),
DMA_TO_DEVICE);
@ -562,15 +571,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct scatterlist *sg;
size_t total_len = 0;
dev_vdbg(&chan->dev, "prep_dma_slave\n");
dev_vdbg(chan2dev(chan), "prep_dma_slave\n");
if (unlikely(!dws || !sg_len))
return NULL;
reg_width = dws->slave.reg_width;
reg_width = dws->reg_width;
prev = first = NULL;
sg_len = dma_map_sg(chan->dev.parent, sgl, sg_len, direction);
sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction);
switch (direction) {
case DMA_TO_DEVICE:
@ -579,7 +588,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
| DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC
| DWC_CTLL_FC_M2P);
reg = dws->slave.tx_reg;
reg = dws->tx_reg;
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u32 len;
@ -587,7 +596,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
desc = dwc_desc_get(dwc);
if (!desc) {
dev_err(&chan->dev,
dev_err(chan2dev(chan),
"not enough descriptors available\n");
goto err_desc_get;
}
@ -607,7 +616,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
first = desc;
} else {
prev->lli.llp = desc->txd.phys;
dma_sync_single_for_device(chan->dev.parent,
dma_sync_single_for_device(chan2parent(chan),
prev->txd.phys,
sizeof(prev->lli),
DMA_TO_DEVICE);
@ -625,7 +634,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
| DWC_CTLL_SRC_FIX
| DWC_CTLL_FC_P2M);
reg = dws->slave.rx_reg;
reg = dws->rx_reg;
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u32 len;
@ -633,7 +642,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
desc = dwc_desc_get(dwc);
if (!desc) {
dev_err(&chan->dev,
dev_err(chan2dev(chan),
"not enough descriptors available\n");
goto err_desc_get;
}
@ -653,7 +662,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
first = desc;
} else {
prev->lli.llp = desc->txd.phys;
dma_sync_single_for_device(chan->dev.parent,
dma_sync_single_for_device(chan2parent(chan),
prev->txd.phys,
sizeof(prev->lli),
DMA_TO_DEVICE);
@ -673,7 +682,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
prev->lli.ctllo |= DWC_CTLL_INT_EN;
prev->lli.llp = 0;
dma_sync_single_for_device(chan->dev.parent,
dma_sync_single_for_device(chan2parent(chan),
prev->txd.phys, sizeof(prev->lli),
DMA_TO_DEVICE);
@ -758,29 +767,21 @@ static void dwc_issue_pending(struct dma_chan *chan)
spin_unlock_bh(&dwc->lock);
}
static int dwc_alloc_chan_resources(struct dma_chan *chan,
struct dma_client *client)
static int dwc_alloc_chan_resources(struct dma_chan *chan)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma *dw = to_dw_dma(chan->device);
struct dw_desc *desc;
struct dma_slave *slave;
struct dw_dma_slave *dws;
int i;
u32 cfghi;
u32 cfglo;
dev_vdbg(&chan->dev, "alloc_chan_resources\n");
/* Channels doing slave DMA can only handle one client. */
if (dwc->dws || client->slave) {
if (chan->client_count)
return -EBUSY;
}
dev_vdbg(chan2dev(chan), "alloc_chan_resources\n");
/* ASSERT: channel is idle */
if (dma_readl(dw, CH_EN) & dwc->mask) {
dev_dbg(&chan->dev, "DMA channel not idle?\n");
dev_dbg(chan2dev(chan), "DMA channel not idle?\n");
return -EIO;
}
@ -789,23 +790,17 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan,
cfghi = DWC_CFGH_FIFO_MODE;
cfglo = 0;
slave = client->slave;
if (slave) {
dws = dwc->dws;
if (dws) {
/*
* We need controller-specific data to set up slave
* transfers.
*/
BUG_ON(!slave->dma_dev || slave->dma_dev != dw->dma.dev);
BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev);
dws = container_of(slave, struct dw_dma_slave, slave);
dwc->dws = dws;
cfghi = dws->cfg_hi;
cfglo = dws->cfg_lo;
} else {
dwc->dws = NULL;
}
channel_writel(dwc, CFG_LO, cfglo);
channel_writel(dwc, CFG_HI, cfghi);
@ -822,7 +817,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan,
desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL);
if (!desc) {
dev_info(&chan->dev,
dev_info(chan2dev(chan),
"only allocated %d descriptors\n", i);
spin_lock_bh(&dwc->lock);
break;
@ -832,7 +827,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan,
desc->txd.tx_submit = dwc_tx_submit;
desc->txd.flags = DMA_CTRL_ACK;
INIT_LIST_HEAD(&desc->txd.tx_list);
desc->txd.phys = dma_map_single(chan->dev.parent, &desc->lli,
desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli,
sizeof(desc->lli), DMA_TO_DEVICE);
dwc_desc_put(dwc, desc);
@ -847,7 +842,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan,
spin_unlock_bh(&dwc->lock);
dev_dbg(&chan->dev,
dev_dbg(chan2dev(chan),
"alloc_chan_resources allocated %d descriptors\n", i);
return i;
@ -860,7 +855,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
struct dw_desc *desc, *_desc;
LIST_HEAD(list);
dev_dbg(&chan->dev, "free_chan_resources (descs allocated=%u)\n",
dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n",
dwc->descs_allocated);
/* ASSERT: channel is idle */
@ -881,13 +876,13 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
spin_unlock_bh(&dwc->lock);
list_for_each_entry_safe(desc, _desc, &list, desc_node) {
dev_vdbg(&chan->dev, " freeing descriptor %p\n", desc);
dma_unmap_single(chan->dev.parent, desc->txd.phys,
dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc);
dma_unmap_single(chan2parent(chan), desc->txd.phys,
sizeof(desc->lli), DMA_TO_DEVICE);
kfree(desc);
}
dev_vdbg(&chan->dev, "free_chan_resources done\n");
dev_vdbg(chan2dev(chan), "free_chan_resources done\n");
}
/*----------------------------------------------------------------------*/

Просмотреть файл

@ -366,8 +366,7 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
*
* Return - The number of descriptors allocated.
*/
static int fsl_dma_alloc_chan_resources(struct dma_chan *chan,
struct dma_client *client)
static int fsl_dma_alloc_chan_resources(struct dma_chan *chan)
{
struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
@ -823,7 +822,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
*/
WARN_ON(fdev->feature != new_fsl_chan->feature);
new_fsl_chan->dev = &new_fsl_chan->common.dev;
new_fsl_chan->dev = &new_fsl_chan->common.dev->device;
new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start,
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);

Просмотреть файл

@ -75,60 +75,10 @@ static int ioat_dca_enabled = 1;
module_param(ioat_dca_enabled, int, 0644);
MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)");
static int ioat_setup_functionality(struct pci_dev *pdev, void __iomem *iobase)
{
struct ioat_device *device = pci_get_drvdata(pdev);
u8 version;
int err = 0;
version = readb(iobase + IOAT_VER_OFFSET);
switch (version) {
case IOAT_VER_1_2:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat_dca_init(pdev, iobase);
break;
case IOAT_VER_2_0:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat2_dca_init(pdev, iobase);
break;
case IOAT_VER_3_0:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat3_dca_init(pdev, iobase);
break;
default:
err = -ENODEV;
break;
}
if (!device->dma)
err = -ENODEV;
return err;
}
static void ioat_shutdown_functionality(struct pci_dev *pdev)
{
struct ioat_device *device = pci_get_drvdata(pdev);
dev_err(&pdev->dev, "Removing dma and dca services\n");
if (device->dca) {
unregister_dca_provider(device->dca);
free_dca_provider(device->dca);
device->dca = NULL;
}
if (device->dma) {
ioat_dma_remove(device->dma);
device->dma = NULL;
}
}
static struct pci_driver ioat_pci_driver = {
.name = "ioatdma",
.id_table = ioat_pci_tbl,
.probe = ioat_probe,
.shutdown = ioat_shutdown_functionality,
.remove = __devexit_p(ioat_remove),
};
@ -179,7 +129,29 @@ static int __devinit ioat_probe(struct pci_dev *pdev,
pci_set_master(pdev);
err = ioat_setup_functionality(pdev, iobase);
switch (readb(iobase + IOAT_VER_OFFSET)) {
case IOAT_VER_1_2:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat_dca_init(pdev, iobase);
break;
case IOAT_VER_2_0:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat2_dca_init(pdev, iobase);
break;
case IOAT_VER_3_0:
device->dma = ioat_dma_probe(pdev, iobase);
if (device->dma && ioat_dca_enabled)
device->dca = ioat3_dca_init(pdev, iobase);
break;
default:
err = -ENODEV;
break;
}
if (!device->dma)
err = -ENODEV;
if (err)
goto err_version;
@ -198,17 +170,21 @@ err_enable_device:
return err;
}
/*
* It is unsafe to remove this module: if removed while a requested
* dma is outstanding, esp. from tcp, it is possible to hang while
* waiting for something that will never finish. However, if you're
* feeling lucky, this usually works just fine.
*/
static void __devexit ioat_remove(struct pci_dev *pdev)
{
struct ioat_device *device = pci_get_drvdata(pdev);
ioat_shutdown_functionality(pdev);
dev_err(&pdev->dev, "Removing dma and dca services\n");
if (device->dca) {
unregister_dca_provider(device->dca);
free_dca_provider(device->dca);
device->dca = NULL;
}
if (device->dma) {
ioat_dma_remove(device->dma);
device->dma = NULL;
}
kfree(device);
}

Просмотреть файл

@ -734,8 +734,7 @@ static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan)
* ioat_dma_alloc_chan_resources - returns the number of allocated descriptors
* @chan: the channel to be filled out
*/
static int ioat_dma_alloc_chan_resources(struct dma_chan *chan,
struct dma_client *client)
static int ioat_dma_alloc_chan_resources(struct dma_chan *chan)
{
struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan);
struct ioat_desc_sw *desc;
@ -1341,12 +1340,11 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan)
*/
#define IOAT_TEST_SIZE 2000
DECLARE_COMPLETION(test_completion);
static void ioat_dma_test_callback(void *dma_async_param)
{
printk(KERN_ERR "ioatdma: ioat_dma_test_callback(%p)\n",
dma_async_param);
complete(&test_completion);
struct completion *cmp = dma_async_param;
complete(cmp);
}
/**
@ -1363,6 +1361,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
dma_addr_t dma_dest, dma_src;
dma_cookie_t cookie;
int err = 0;
struct completion cmp;
src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL);
if (!src)
@ -1381,7 +1380,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
if (device->common.device_alloc_chan_resources(dma_chan, NULL) < 1) {
if (device->common.device_alloc_chan_resources(dma_chan) < 1) {
dev_err(&device->pdev->dev,
"selftest cannot allocate chan resource\n");
err = -ENODEV;
@ -1402,8 +1401,9 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
}
async_tx_ack(tx);
init_completion(&cmp);
tx->callback = ioat_dma_test_callback;
tx->callback_param = (void *)0x8086;
tx->callback_param = &cmp;
cookie = tx->tx_submit(tx);
if (cookie < 0) {
dev_err(&device->pdev->dev,
@ -1413,7 +1413,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
}
device->common.device_issue_pending(dma_chan);
wait_for_completion_timeout(&test_completion, msecs_to_jiffies(3000));
wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000));
if (device->common.device_is_tx_complete(dma_chan, cookie, NULL, NULL)
!= DMA_SUCCESS) {

Просмотреть файл

@ -24,7 +24,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/async_tx.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
@ -116,7 +115,7 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc,
}
/* run dependent operations */
async_tx_run_dependencies(&desc->async_tx);
dma_run_dependencies(&desc->async_tx);
return cookie;
}
@ -270,8 +269,6 @@ static void __iop_adma_slot_cleanup(struct iop_adma_chan *iop_chan)
break;
}
BUG_ON(!seen_current);
if (cookie > 0) {
iop_chan->completed_cookie = cookie;
pr_debug("\tcompleted cookie %d\n", cookie);
@ -471,8 +468,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan);
* greater than 2x the number slots needed to satisfy a device->max_xor
* request.
* */
static int iop_adma_alloc_chan_resources(struct dma_chan *chan,
struct dma_client *client)
static int iop_adma_alloc_chan_resources(struct dma_chan *chan)
{
char *hw_desc;
int idx;
@ -866,7 +862,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device)
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) {
if (iop_adma_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
}
@ -964,7 +960,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device)
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) {
if (iop_adma_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
}
@ -1115,26 +1111,13 @@ static int __devexit iop_adma_remove(struct platform_device *dev)
struct iop_adma_device *device = platform_get_drvdata(dev);
struct dma_chan *chan, *_chan;
struct iop_adma_chan *iop_chan;
int i;
struct iop_adma_platform_data *plat_data = dev->dev.platform_data;
dma_async_device_unregister(&device->common);
for (i = 0; i < 3; i++) {
unsigned int irq;
irq = platform_get_irq(dev, i);
free_irq(irq, device);
}
dma_free_coherent(&dev->dev, plat_data->pool_size,
device->dma_desc_pool_virt, device->dma_desc_pool);
do {
struct resource *res;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start);
} while (0);
list_for_each_entry_safe(chan, _chan, &device->common.channels,
device_node) {
iop_chan = to_iop_adma_chan(chan);
@ -1255,7 +1238,6 @@ static int __devinit iop_adma_probe(struct platform_device *pdev)
spin_lock_init(&iop_chan->lock);
INIT_LIST_HEAD(&iop_chan->chain);
INIT_LIST_HEAD(&iop_chan->all_slots);
INIT_RCU_HEAD(&iop_chan->common.rcu);
iop_chan->common.device = dma_dev;
list_add_tail(&iop_chan->common.device_node, &dma_dev->channels);
@ -1431,16 +1413,12 @@ static int __init iop_adma_init (void)
return platform_driver_register(&iop_adma_driver);
}
/* it's currently unsafe to unload this module */
#if 0
static void __exit iop_adma_exit (void)
{
platform_driver_unregister(&iop_adma_driver);
return;
}
module_exit(iop_adma_exit);
#endif
module_init(iop_adma_init);
MODULE_AUTHOR("Intel Corporation");

Просмотреть файл

@ -18,7 +18,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/async_tx.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
@ -340,7 +339,7 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
}
/* run dependent operations */
async_tx_run_dependencies(&desc->async_tx);
dma_run_dependencies(&desc->async_tx);
return cookie;
}
@ -607,8 +606,7 @@ submit_done:
}
/* returns the number of allocated descriptors */
static int mv_xor_alloc_chan_resources(struct dma_chan *chan,
struct dma_client *client)
static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
{
char *hw_desc;
int idx;
@ -958,7 +956,7 @@ static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device)
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) {
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
}
@ -1053,7 +1051,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) {
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
}
@ -1221,7 +1219,6 @@ static int __devinit mv_xor_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mv_chan->chain);
INIT_LIST_HEAD(&mv_chan->completed_slots);
INIT_LIST_HEAD(&mv_chan->all_slots);
INIT_RCU_HEAD(&mv_chan->common.rcu);
mv_chan->common.device = dma_dev;
list_add_tail(&mv_chan->common.device_node, &dma_dev->channels);

Просмотреть файл

@ -55,7 +55,6 @@ enum atmel_mci_state {
struct atmel_mci_dma {
#ifdef CONFIG_MMC_ATMELMCI_DMA
struct dma_client client;
struct dma_chan *chan;
struct dma_async_tx_descriptor *data_desc;
#endif
@ -593,10 +592,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
/* If we don't have a channel, we can't do DMA */
chan = host->dma.chan;
if (chan) {
dma_chan_get(chan);
if (chan)
host->data_chan = chan;
}
if (!chan)
return -ENODEV;
@ -1443,60 +1440,6 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
#ifdef CONFIG_MMC_ATMELMCI_DMA
static inline struct atmel_mci *
dma_client_to_atmel_mci(struct dma_client *client)
{
return container_of(client, struct atmel_mci, dma.client);
}
static enum dma_state_client atmci_dma_event(struct dma_client *client,
struct dma_chan *chan, enum dma_state state)
{
struct atmel_mci *host;
enum dma_state_client ret = DMA_NAK;
host = dma_client_to_atmel_mci(client);
switch (state) {
case DMA_RESOURCE_AVAILABLE:
spin_lock_bh(&host->lock);
if (!host->dma.chan) {
host->dma.chan = chan;
ret = DMA_ACK;
}
spin_unlock_bh(&host->lock);
if (ret == DMA_ACK)
dev_info(&host->pdev->dev,
"Using %s for DMA transfers\n",
chan->dev.bus_id);
break;
case DMA_RESOURCE_REMOVED:
spin_lock_bh(&host->lock);
if (host->dma.chan == chan) {
host->dma.chan = NULL;
ret = DMA_ACK;
}
spin_unlock_bh(&host->lock);
if (ret == DMA_ACK)
dev_info(&host->pdev->dev,
"Lost %s, falling back to PIO\n",
chan->dev.bus_id);
break;
default:
break;
}
return ret;
}
#endif /* CONFIG_MMC_ATMELMCI_DMA */
static int __init atmci_init_slot(struct atmel_mci *host,
struct mci_slot_pdata *slot_data, unsigned int id,
u32 sdc_reg)
@ -1600,6 +1543,18 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
mmc_free_host(slot->mmc);
}
#ifdef CONFIG_MMC_ATMELMCI_DMA
static bool filter(struct dma_chan *chan, void *slave)
{
struct dw_dma_slave *dws = slave;
if (dws->dma_dev == chan->device->dev)
return true;
else
return false;
}
#endif
static int __init atmci_probe(struct platform_device *pdev)
{
struct mci_platform_data *pdata;
@ -1652,22 +1607,20 @@ static int __init atmci_probe(struct platform_device *pdev)
goto err_request_irq;
#ifdef CONFIG_MMC_ATMELMCI_DMA
if (pdata->dma_slave) {
struct dma_slave *slave = pdata->dma_slave;
if (pdata->dma_slave.dma_dev) {
struct dw_dma_slave *dws = &pdata->dma_slave;
dma_cap_mask_t mask;
slave->tx_reg = regs->start + MCI_TDR;
slave->rx_reg = regs->start + MCI_RDR;
dws->tx_reg = regs->start + MCI_TDR;
dws->rx_reg = regs->start + MCI_RDR;
/* Try to grab a DMA channel */
host->dma.client.event_callback = atmci_dma_event;
dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask);
host->dma.client.slave = slave;
dma_async_client_register(&host->dma.client);
dma_async_client_chan_request(&host->dma.client);
} else {
dev_notice(&pdev->dev, "DMA not available, using PIO\n");
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma.chan = dma_request_channel(mask, filter, dws);
}
if (!host->dma.chan)
dev_notice(&pdev->dev, "DMA not available, using PIO\n");
#endif /* CONFIG_MMC_ATMELMCI_DMA */
platform_set_drvdata(pdev, host);
@ -1699,8 +1652,8 @@ static int __init atmci_probe(struct platform_device *pdev)
err_init_slot:
#ifdef CONFIG_MMC_ATMELMCI_DMA
if (pdata->dma_slave)
dma_async_client_unregister(&host->dma.client);
if (host->dma.chan)
dma_release_channel(host->dma.chan);
#endif
free_irq(irq, host);
err_request_irq:
@ -1731,8 +1684,8 @@ static int __exit atmci_remove(struct platform_device *pdev)
clk_disable(host->mck);
#ifdef CONFIG_MMC_ATMELMCI_DMA
if (host->dma.client.slave)
dma_async_client_unregister(&host->dma.client);
if (host->dma.chan)
dma_release_channel(host->dma.chan);
#endif
free_irq(platform_get_irq(pdev, 0), host);
@ -1761,7 +1714,7 @@ static void __exit atmci_exit(void)
platform_driver_unregister(&atmci_driver);
}
module_init(atmci_init);
late_initcall(atmci_init); /* try to load after dma driver when built-in */
module_exit(atmci_exit);
MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");

Просмотреть файл

@ -59,9 +59,7 @@ enum async_tx_flags {
};
#ifdef CONFIG_DMA_ENGINE
void async_tx_issue_pending_all(void);
enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
void async_tx_run_dependencies(struct dma_async_tx_descriptor *tx);
#define async_tx_issue_pending_all dma_issue_pending_all
#ifdef CONFIG_ARCH_HAS_ASYNC_TX_FIND_CHANNEL
#include <asm/async_tx.h>
#else
@ -77,19 +75,6 @@ static inline void async_tx_issue_pending_all(void)
do { } while (0);
}
static inline enum dma_status
dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
{
return DMA_SUCCESS;
}
static inline void
async_tx_run_dependencies(struct dma_async_tx_descriptor *tx,
struct dma_chan *host_chan)
{
do { } while (0);
}
static inline struct dma_chan *
async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
enum dma_transaction_type tx_type, struct page **dst, int dst_count,

Просмотреть файл

@ -3,7 +3,7 @@
#define ATMEL_MCI_MAX_NR_SLOTS 2
struct dma_slave;
#include <linux/dw_dmac.h>
/**
* struct mci_slot_pdata - board-specific per-slot configuration
@ -28,11 +28,11 @@ struct mci_slot_pdata {
/**
* struct mci_platform_data - board-specific MMC/SDcard configuration
* @dma_slave: DMA slave interface to use in data transfers, or NULL.
* @dma_slave: DMA slave interface to use in data transfers.
* @slot: Per-slot configuration data.
*/
struct mci_platform_data {
struct dma_slave *dma_slave;
struct dw_dma_slave dma_slave;
struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS];
};

Просмотреть файл

@ -28,32 +28,6 @@
#include <linux/rcupdate.h>
#include <linux/dma-mapping.h>
/**
* enum dma_state - resource PNP/power management state
* @DMA_RESOURCE_SUSPEND: DMA device going into low power state
* @DMA_RESOURCE_RESUME: DMA device returning to full power
* @DMA_RESOURCE_AVAILABLE: DMA device available to the system
* @DMA_RESOURCE_REMOVED: DMA device removed from the system
*/
enum dma_state {
DMA_RESOURCE_SUSPEND,
DMA_RESOURCE_RESUME,
DMA_RESOURCE_AVAILABLE,
DMA_RESOURCE_REMOVED,
};
/**
* enum dma_state_client - state of the channel in the client
* @DMA_ACK: client would like to use, or was using this channel
* @DMA_DUP: client has already seen this channel, or is not using this channel
* @DMA_NAK: client does not want to see any more channels
*/
enum dma_state_client {
DMA_ACK,
DMA_DUP,
DMA_NAK,
};
/**
* typedef dma_cookie_t - an opaque DMA cookie
*
@ -89,23 +63,13 @@ enum dma_transaction_type {
DMA_MEMSET,
DMA_MEMCPY_CRC32C,
DMA_INTERRUPT,
DMA_PRIVATE,
DMA_SLAVE,
};
/* last transaction type for creation of the capabilities mask */
#define DMA_TX_TYPE_END (DMA_SLAVE + 1)
/**
* enum dma_slave_width - DMA slave register access width.
* @DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses
* @DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses
* @DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses
*/
enum dma_slave_width {
DMA_SLAVE_WIDTH_8BIT,
DMA_SLAVE_WIDTH_16BIT,
DMA_SLAVE_WIDTH_32BIT,
};
/**
* enum dma_ctrl_flags - DMA flags to augment operation preparation,
@ -131,32 +95,6 @@ enum dma_ctrl_flags {
*/
typedef struct { DECLARE_BITMAP(bits, DMA_TX_TYPE_END); } dma_cap_mask_t;
/**
* struct dma_slave - Information about a DMA slave
* @dev: device acting as DMA slave
* @dma_dev: required DMA master device. If non-NULL, the client can not be
* bound to other masters than this.
* @tx_reg: physical address of data register used for
* memory-to-peripheral transfers
* @rx_reg: physical address of data register used for
* peripheral-to-memory transfers
* @reg_width: peripheral register width
*
* If dma_dev is non-NULL, the client can not be bound to other DMA
* masters than the one corresponding to this device. The DMA master
* driver may use this to determine if there is controller-specific
* data wrapped around this struct. Drivers of platform code that sets
* the dma_dev field must therefore make sure to use an appropriate
* controller-specific dma slave structure wrapping this struct.
*/
struct dma_slave {
struct device *dev;
struct device *dma_dev;
dma_addr_t tx_reg;
dma_addr_t rx_reg;
enum dma_slave_width reg_width;
};
/**
* struct dma_chan_percpu - the per-CPU part of struct dma_chan
* @refcount: local_t used for open-coded "bigref" counting
@ -165,7 +103,6 @@ struct dma_slave {
*/
struct dma_chan_percpu {
local_t refcount;
/* stats */
unsigned long memcpy_count;
unsigned long bytes_transferred;
@ -176,13 +113,14 @@ struct dma_chan_percpu {
* @device: ptr to the dma device who supplies this channel, always !%NULL
* @cookie: last cookie value returned to client
* @chan_id: channel ID for sysfs
* @class_dev: class device for sysfs
* @dev: class device for sysfs
* @refcount: kref, used in "bigref" slow-mode
* @slow_ref: indicates that the DMA channel is free
* @rcu: the DMA channel's RCU head
* @device_node: used to add this to the device chan list
* @local: per-cpu pointer to a struct dma_chan_percpu
* @client-count: how many clients are using this channel
* @table_count: number of appearances in the mem-to-mem allocation table
*/
struct dma_chan {
struct dma_device *device;
@ -190,73 +128,47 @@ struct dma_chan {
/* sysfs */
int chan_id;
struct device dev;
struct kref refcount;
int slow_ref;
struct rcu_head rcu;
struct dma_chan_dev *dev;
struct list_head device_node;
struct dma_chan_percpu *local;
int client_count;
int table_count;
};
#define to_dma_chan(p) container_of(p, struct dma_chan, dev)
/**
* struct dma_chan_dev - relate sysfs device node to backing channel device
* @chan - driver channel device
* @device - sysfs device
* @dev_id - parent dma_device dev_id
* @idr_ref - reference count to gate release of dma_device dev_id
*/
struct dma_chan_dev {
struct dma_chan *chan;
struct device device;
int dev_id;
atomic_t *idr_ref;
};
static inline const char *dma_chan_name(struct dma_chan *chan)
{
return dev_name(&chan->dev->device);
}
void dma_chan_cleanup(struct kref *kref);
static inline void dma_chan_get(struct dma_chan *chan)
{
if (unlikely(chan->slow_ref))
kref_get(&chan->refcount);
else {
local_inc(&(per_cpu_ptr(chan->local, get_cpu())->refcount));
put_cpu();
}
}
static inline void dma_chan_put(struct dma_chan *chan)
{
if (unlikely(chan->slow_ref))
kref_put(&chan->refcount, dma_chan_cleanup);
else {
local_dec(&(per_cpu_ptr(chan->local, get_cpu())->refcount));
put_cpu();
}
}
/*
* typedef dma_event_callback - function pointer to a DMA event callback
* For each channel added to the system this routine is called for each client.
* If the client would like to use the channel it returns '1' to signal (ack)
* the dmaengine core to take out a reference on the channel and its
* corresponding device. A client must not 'ack' an available channel more
* than once. When a channel is removed all clients are notified. If a client
* is using the channel it must 'ack' the removal. A client must not 'ack' a
* removed channel more than once.
* @client - 'this' pointer for the client context
* @chan - channel to be acted upon
* @state - available or removed
*/
struct dma_client;
typedef enum dma_state_client (*dma_event_callback) (struct dma_client *client,
struct dma_chan *chan, enum dma_state state);
/**
* struct dma_client - info on the entity making use of DMA services
* @event_callback: func ptr to call when something happens
* @cap_mask: only return channels that satisfy the requested capabilities
* a value of zero corresponds to any capability
* @slave: data for preparing slave transfer. Must be non-NULL iff the
* DMA_SLAVE capability is requested.
* @global_node: list_head for global dma_client_list
* typedef dma_filter_fn - callback filter for dma_request_channel
* @chan: channel to be reviewed
* @filter_param: opaque parameter passed through dma_request_channel
*
* When this optional parameter is specified in a call to dma_request_channel a
* suitable channel is passed to this routine for further dispositioning before
* being returned. Where 'suitable' indicates a non-busy channel that
* satisfies the given capability mask. It returns 'true' to indicate that the
* channel is suitable.
*/
struct dma_client {
dma_event_callback event_callback;
dma_cap_mask_t cap_mask;
struct dma_slave *slave;
struct list_head global_node;
};
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
typedef void (*dma_async_tx_callback)(void *dma_async_param);
/**
@ -323,14 +235,10 @@ struct dma_device {
dma_cap_mask_t cap_mask;
int max_xor;
struct kref refcount;
struct completion done;
int dev_id;
struct device *dev;
int (*device_alloc_chan_resources)(struct dma_chan *chan,
struct dma_client *client);
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
@ -362,9 +270,8 @@ struct dma_device {
/* --- public DMA engine API --- */
void dma_async_client_register(struct dma_client *client);
void dma_async_client_unregister(struct dma_client *client);
void dma_async_client_chan_request(struct dma_client *client);
void dmaengine_get(void);
void dmaengine_put(void);
dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan,
void *dest, void *src, size_t len);
dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan,
@ -406,6 +313,12 @@ __dma_cap_set(enum dma_transaction_type tx_type, dma_cap_mask_t *dstp)
set_bit(tx_type, dstp->bits);
}
#define dma_cap_zero(mask) __dma_cap_zero(&(mask))
static inline void __dma_cap_zero(dma_cap_mask_t *dstp)
{
bitmap_zero(dstp->bits, DMA_TX_TYPE_END);
}
#define dma_has_cap(tx, mask) __dma_has_cap((tx), &(mask))
static inline int
__dma_has_cap(enum dma_transaction_type tx_type, dma_cap_mask_t *srcp)
@ -475,11 +388,25 @@ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie,
}
enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie);
#ifdef CONFIG_DMA_ENGINE
enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
#else
static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
{
return DMA_SUCCESS;
}
#endif
/* --- DMA device --- */
int dma_async_device_register(struct dma_device *device);
void dma_async_device_unregister(struct dma_device *device);
void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type);
void dma_issue_pending_all(void);
#define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param);
void dma_release_channel(struct dma_chan *chan);
/* --- Helper iov-locking functions --- */

Просмотреть файл

@ -21,15 +21,35 @@ struct dw_dma_platform_data {
unsigned int nr_channels;
};
/**
* enum dw_dma_slave_width - DMA slave register access width.
* @DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses
* @DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses
* @DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses
*/
enum dw_dma_slave_width {
DW_DMA_SLAVE_WIDTH_8BIT,
DW_DMA_SLAVE_WIDTH_16BIT,
DW_DMA_SLAVE_WIDTH_32BIT,
};
/**
* struct dw_dma_slave - Controller-specific information about a slave
* @slave: Generic information about the slave
* @ctl_lo: Platform-specific initializer for the CTL_LO register
*
* @dma_dev: required DMA master device
* @tx_reg: physical address of data register used for
* memory-to-peripheral transfers
* @rx_reg: physical address of data register used for
* peripheral-to-memory transfers
* @reg_width: peripheral register width
* @cfg_hi: Platform-specific initializer for the CFG_HI register
* @cfg_lo: Platform-specific initializer for the CFG_LO register
*/
struct dw_dma_slave {
struct dma_slave slave;
struct device *dma_dev;
dma_addr_t tx_reg;
dma_addr_t rx_reg;
enum dw_dma_slave_width reg_width;
u32 cfg_hi;
u32 cfg_lo;
};
@ -54,9 +74,4 @@ struct dw_dma_slave {
#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */
#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */
static inline struct dw_dma_slave *to_dw_dma_slave(struct dma_slave *slave)
{
return container_of(slave, struct dw_dma_slave, slave);
}
#endif /* DW_DMAC_H */

Просмотреть файл

@ -1125,9 +1125,6 @@ struct softnet_data
struct sk_buff *completion_queue;
struct napi_struct backlog;
#ifdef CONFIG_NET_DMA
struct dma_chan *net_dma;
#endif
};
DECLARE_PER_CPU(struct softnet_data,softnet_data);

Просмотреть файл

@ -24,17 +24,6 @@
#include <linux/dmaengine.h>
#include <linux/skbuff.h>
static inline struct dma_chan *get_softnet_dma(void)
{
struct dma_chan *chan;
rcu_read_lock();
chan = rcu_dereference(__get_cpu_var(softnet_data).net_dma);
if (chan)
dma_chan_get(chan);
rcu_read_unlock();
return chan;
}
int dma_skb_copy_datagram_iovec(struct dma_chan* chan,
struct sk_buff *skb, int offset, struct iovec *to,
size_t len, struct dma_pinned_list *pinned_list);

Просмотреть файл

@ -170,25 +170,6 @@ static DEFINE_SPINLOCK(ptype_lock);
static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
static struct list_head ptype_all __read_mostly; /* Taps */
#ifdef CONFIG_NET_DMA
struct net_dma {
struct dma_client client;
spinlock_t lock;
cpumask_t channel_mask;
struct dma_chan **channels;
};
static enum dma_state_client
netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
enum dma_state state);
static struct net_dma net_dma = {
.client = {
.event_callback = netdev_dma_event,
},
};
#endif
/*
* The @dev_base_head list is protected by @dev_base_lock and the rtnl
* semaphore.
@ -2754,14 +2735,7 @@ out:
* There may not be any more sk_buffs coming right now, so push
* any pending DMA copies to hardware
*/
if (!cpus_empty(net_dma.channel_mask)) {
int chan_idx;
for_each_cpu_mask_nr(chan_idx, net_dma.channel_mask) {
struct dma_chan *chan = net_dma.channels[chan_idx];
if (chan)
dma_async_memcpy_issue_pending(chan);
}
}
dma_issue_pending_all();
#endif
return;
@ -4952,122 +4926,6 @@ static int dev_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
#ifdef CONFIG_NET_DMA
/**
* net_dma_rebalance - try to maintain one DMA channel per CPU
* @net_dma: DMA client and associated data (lock, channels, channel_mask)
*
* This is called when the number of channels allocated to the net_dma client
* changes. The net_dma client tries to have one DMA channel per CPU.
*/
static void net_dma_rebalance(struct net_dma *net_dma)
{
unsigned int cpu, i, n, chan_idx;
struct dma_chan *chan;
if (cpus_empty(net_dma->channel_mask)) {
for_each_online_cpu(cpu)
rcu_assign_pointer(per_cpu(softnet_data, cpu).net_dma, NULL);
return;
}
i = 0;
cpu = first_cpu(cpu_online_map);
for_each_cpu_mask_nr(chan_idx, net_dma->channel_mask) {
chan = net_dma->channels[chan_idx];
n = ((num_online_cpus() / cpus_weight(net_dma->channel_mask))
+ (i < (num_online_cpus() %
cpus_weight(net_dma->channel_mask)) ? 1 : 0));
while(n) {
per_cpu(softnet_data, cpu).net_dma = chan;
cpu = next_cpu(cpu, cpu_online_map);
n--;
}
i++;
}
}
/**
* netdev_dma_event - event callback for the net_dma_client
* @client: should always be net_dma_client
* @chan: DMA channel for the event
* @state: DMA state to be handled
*/
static enum dma_state_client
netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
enum dma_state state)
{
int i, found = 0, pos = -1;
struct net_dma *net_dma =
container_of(client, struct net_dma, client);
enum dma_state_client ack = DMA_DUP; /* default: take no action */
spin_lock(&net_dma->lock);
switch (state) {
case DMA_RESOURCE_AVAILABLE:
for (i = 0; i < nr_cpu_ids; i++)
if (net_dma->channels[i] == chan) {
found = 1;
break;
} else if (net_dma->channels[i] == NULL && pos < 0)
pos = i;
if (!found && pos >= 0) {
ack = DMA_ACK;
net_dma->channels[pos] = chan;
cpu_set(pos, net_dma->channel_mask);
net_dma_rebalance(net_dma);
}
break;
case DMA_RESOURCE_REMOVED:
for (i = 0; i < nr_cpu_ids; i++)
if (net_dma->channels[i] == chan) {
found = 1;
pos = i;
break;
}
if (found) {
ack = DMA_ACK;
cpu_clear(pos, net_dma->channel_mask);
net_dma->channels[i] = NULL;
net_dma_rebalance(net_dma);
}
break;
default:
break;
}
spin_unlock(&net_dma->lock);
return ack;
}
/**
* netdev_dma_register - register the networking subsystem as a DMA client
*/
static int __init netdev_dma_register(void)
{
net_dma.channels = kzalloc(nr_cpu_ids * sizeof(struct net_dma),
GFP_KERNEL);
if (unlikely(!net_dma.channels)) {
printk(KERN_NOTICE
"netdev_dma: no memory for net_dma.channels\n");
return -ENOMEM;
}
spin_lock_init(&net_dma.lock);
dma_cap_set(DMA_MEMCPY, net_dma.client.cap_mask);
dma_async_client_register(&net_dma.client);
dma_async_client_chan_request(&net_dma.client);
return 0;
}
#else
static int __init netdev_dma_register(void) { return -ENODEV; }
#endif /* CONFIG_NET_DMA */
/**
* netdev_increment_features - increment feature set by one
@ -5287,14 +5145,15 @@ static int __init net_dev_init(void)
if (register_pernet_device(&default_device_ops))
goto out;
netdev_dma_register();
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
hotcpu_notifier(dev_cpu_callback, 0);
dst_init();
dev_mcast_init();
#ifdef CONFIG_NET_DMA
dmaengine_get();
#endif
rc = 0;
out:
return rc;

Просмотреть файл

@ -1313,7 +1313,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if ((available < target) &&
(len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) &&
!sysctl_tcp_low_latency &&
__get_cpu_var(softnet_data).net_dma) {
dma_find_channel(DMA_MEMCPY)) {
preempt_enable_no_resched();
tp->ucopy.pinned_list =
dma_pin_iovec_pages(msg->msg_iov, len);
@ -1523,7 +1523,7 @@ do_prequeue:
if (!(flags & MSG_TRUNC)) {
#ifdef CONFIG_NET_DMA
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
if (tp->ucopy.dma_chan) {
tp->ucopy.dma_cookie = dma_skb_copy_datagram_iovec(
@ -1628,7 +1628,6 @@ skip_copy:
/* Safe to free early-copied skbs now */
__skb_queue_purge(&sk->sk_async_wait_queue);
dma_chan_put(tp->ucopy.dma_chan);
tp->ucopy.dma_chan = NULL;
}
if (tp->ucopy.pinned_list) {

Просмотреть файл

@ -5005,7 +5005,7 @@ static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb,
return 0;
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
if (tp->ucopy.dma_chan && skb_csum_unnecessary(skb)) {

Просмотреть файл

@ -1594,7 +1594,7 @@ process:
#ifdef CONFIG_NET_DMA
struct tcp_sock *tp = tcp_sk(sk);
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
if (tp->ucopy.dma_chan)
ret = tcp_v4_do_rcv(sk, skb);
else

Просмотреть файл

@ -1675,7 +1675,7 @@ process:
#ifdef CONFIG_NET_DMA
struct tcp_sock *tp = tcp_sk(sk);
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
if (tp->ucopy.dma_chan)
ret = tcp_v6_do_rcv(sk, skb);
else