linux/crypto/async_tx/async_tx.c
<<
>>
Prefs
   1/*
   2 * core routines for the asynchronous memory transfer/transform api
   3 *
   4 * Copyright \xC2\xA9 2006, Intel Corporation.
   5 *
   6 *      Dan Williams <dan.j.williams@intel.com>
   7 *
   8 *      with architecture considerations by:
   9 *      Neil Brown <neilb@suse.de>
  10 *      Jeff Garzik <jeff@garzik.org>
  11 *
  12 * This program is free software; you can redistribute it and/or modify it
  13 * under the terms and conditions of the GNU General Public License,
  14 * version 2, as published by the Free Software Foundation.
  15 *
  16 * This program is distributed in the hope it will be useful, but WITHOUT
  17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  18 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  19 * more details.
  20 *
  21 * You should have received a copy of the GNU General Public License along with
  22 * this program; if not, write to the Free Software Foundation, Inc.,
  23 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  24 *
  25 */
  26#include <linux/kernel.h>
  27#include <linux/async_tx.h>
  28
  29#ifdef CONFIG_DMA_ENGINE
  30static enum dma_state_client
  31dma_channel_add_remove(struct dma_client *client,
  32        struct dma_chan *chan, enum dma_state state);
  33
  34static struct dma_client async_tx_dma = {
  35        .event_callback = dma_channel_add_remove,
  36        /* .cap_mask == 0 defaults to all channels */
  37};
  38
  39/**
  40 * dma_cap_mask_all - enable iteration over all operation types
  41 */
  42static dma_cap_mask_t dma_cap_mask_all;
  43
  44/**
  45 * chan_ref_percpu - tracks channel allocations per core/opertion
  46 */
  47struct chan_ref_percpu {
  48        struct dma_chan_ref *ref;
  49};
  50
  51static int channel_table_initialized;
  52static struct chan_ref_percpu *channel_table[DMA_TX_TYPE_END];
  53
  54/**
  55 * async_tx_lock - protect modification of async_tx_master_list and serialize
  56 *      rebalance operations
  57 */
  58static spinlock_t async_tx_lock;
  59
  60static struct list_head
  61async_tx_master_list = LIST_HEAD_INIT(async_tx_master_list);
  62
  63/* async_tx_issue_pending_all - start all transactions on all channels */
  64void async_tx_issue_pending_all(void)
  65{
  66        struct dma_chan_ref *ref;
  67
  68        rcu_read_lock();
  69        list_for_each_entry_rcu(ref, &async_tx_master_list, node)
  70                ref->chan->device->device_issue_pending(ref->chan);
  71        rcu_read_unlock();
  72}
  73EXPORT_SYMBOL_GPL(async_tx_issue_pending_all);
  74
  75/* dma_wait_for_async_tx - spin wait for a transcation to complete
  76 * @tx: transaction to wait on
  77 */
  78enum dma_status
  79dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
  80{
  81        enum dma_status status;
  82        struct dma_async_tx_descriptor *iter;
  83        struct dma_async_tx_descriptor *parent;
  84
  85        if (!tx)
  86                return DMA_SUCCESS;
  87
  88        /* poll through the dependency chain, return when tx is complete */
  89        do {
  90                iter = tx;
  91
  92                /* find the root of the unsubmitted dependency chain */
  93                while (iter->cookie == -EBUSY) {
  94                        parent = iter->parent;
  95                        if (parent && parent->cookie == -EBUSY)
  96                                iter = iter->parent;
  97                        else
  98                                break;
  99                }
 100
 101                status = dma_sync_wait(iter->chan, iter->cookie);
 102        } while (status == DMA_IN_PROGRESS || (iter != tx));
 103
 104        return status;
 105}
 106EXPORT_SYMBOL_GPL(dma_wait_for_async_tx);
 107
 108/* async_tx_run_dependencies - helper routine for dma drivers to process
 109 *      (start) dependent operations on their target channel
 110 * @tx: transaction with dependencies
 111 */
 112void
 113async_tx_run_dependencies(struct dma_async_tx_descriptor *tx)
 114{
 115        struct dma_async_tx_descriptor *dep_tx, *_dep_tx;
 116        struct dma_device *dev;
 117        struct dma_chan *chan;
 118
 119        list_for_each_entry_safe(dep_tx, _dep_tx, &tx->depend_list,
 120                depend_node) {
 121                chan = dep_tx->chan;
 122                dev = chan->device;
 123                /* we can't depend on ourselves */
 124                BUG_ON(chan == tx->chan);
 125                list_del(&dep_tx->depend_node);
 126                tx->tx_submit(dep_tx);
 127
 128                /* we need to poke the engine as client code does not
 129                 * know about dependency submission events
 130                 */
 131                dev->device_issue_pending(chan);
 132        }
 133}
 134EXPORT_SYMBOL_GPL(async_tx_run_dependencies);
 135
 136static void
 137free_dma_chan_ref(struct rcu_head *rcu)
 138{
 139        struct dma_chan_ref *ref;
 140        ref = container_of(rcu, struct dma_chan_ref, rcu);
 141        kfree(ref);
 142}
 143
 144static void
 145init_dma_chan_ref(struct dma_chan_ref *ref, struct dma_chan *chan)
 146{
 147        INIT_LIST_HEAD(&ref->node);
 148        INIT_RCU_HEAD(&ref->rcu);
 149        ref->chan = chan;
 150        atomic_set(&ref->count, 0);
 151}
 152
 153/**
 154 * get_chan_ref_by_cap - returns the nth channel of the given capability
 155 *      defaults to returning the channel with the desired capability and the
 156 *      lowest reference count if the index can not be satisfied
 157 * @cap: capability to match
 158 * @index: nth channel desired, passing -1 has the effect of forcing the
 159 *  default return value
 160 */
 161static struct dma_chan_ref *
 162get_chan_ref_by_cap(enum dma_transaction_type cap, int index)
 163{
 164        struct dma_chan_ref *ret_ref = NULL, *min_ref = NULL, *ref;
 165
 166        rcu_read_lock();
 167        list_for_each_entry_rcu(ref, &async_tx_master_list, node)
 168                if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
 169                        if (!min_ref)
 170                                min_ref = ref;
 171                        else if (atomic_read(&ref->count) <
 172                                atomic_read(&min_ref->count))
 173                                min_ref = ref;
 174
 175                        if (index-- == 0) {
 176                                ret_ref = ref;
 177                                break;
 178                        }
 179                }
 180        rcu_read_unlock();
 181
 182        if (!ret_ref)
 183                ret_ref = min_ref;
 184
 185        if (ret_ref)
 186                atomic_inc(&ret_ref->count);
 187
 188        return ret_ref;
 189}
 190
 191/**
 192 * async_tx_rebalance - redistribute the available channels, optimize
 193 * for cpu isolation in the SMP case, and opertaion isolation in the
 194 * uniprocessor case
 195 */
 196static void async_tx_rebalance(void)
 197{
 198        int cpu, cap, cpu_idx = 0;
 199        unsigned long flags;
 200
 201        if (!channel_table_initialized)
 202                return;
 203
 204        spin_lock_irqsave(&async_tx_lock, flags);
 205
 206        /* undo the last distribution */
 207        for_each_dma_cap_mask(cap, dma_cap_mask_all)
 208                for_each_possible_cpu(cpu) {
 209                        struct dma_chan_ref *ref =
 210                                per_cpu_ptr(channel_table[cap], cpu)->ref;
 211                        if (ref) {
 212                                atomic_set(&ref->count, 0);
 213                                per_cpu_ptr(channel_table[cap], cpu)->ref =
 214                                                                        NULL;
 215                        }
 216                }
 217
 218        for_each_dma_cap_mask(cap, dma_cap_mask_all)
 219                for_each_online_cpu(cpu) {
 220                        struct dma_chan_ref *new;
 221                        if (NR_CPUS > 1)
 222                                new = get_chan_ref_by_cap(cap, cpu_idx++);
 223                        else
 224                                new = get_chan_ref_by_cap(cap, -1);
 225
 226                        per_cpu_ptr(channel_table[cap], cpu)->ref = new;
 227                }
 228
 229        spin_unlock_irqrestore(&async_tx_lock, flags);
 230}
 231
 232static enum dma_state_client
 233dma_channel_add_remove(struct dma_client *client,
 234        struct dma_chan *chan, enum dma_state state)
 235{
 236        unsigned long found, flags;
 237        struct dma_chan_ref *master_ref, *ref;
 238        enum dma_state_client ack = DMA_DUP; /* default: take no action */
 239
 240        switch (state) {
 241        case DMA_RESOURCE_AVAILABLE:
 242                found = 0;
 243                rcu_read_lock();
 244                list_for_each_entry_rcu(ref, &async_tx_master_list, node)
 245                        if (ref->chan == chan) {
 246                                found = 1;
 247                                break;
 248                        }
 249                rcu_read_unlock();
 250
 251                pr_debug("async_tx: dma resource available [%s]\n",
 252                        found ? "old" : "new");
 253
 254                if (!found)
 255                        ack = DMA_ACK;
 256                else
 257                        break;
 258
 259                /* add the channel to the generic management list */
 260                master_ref = kmalloc(sizeof(*master_ref), GFP_KERNEL);
 261                if (master_ref) {
 262                        /* keep a reference until async_tx is unloaded */
 263                        dma_chan_get(chan);
 264                        init_dma_chan_ref(master_ref, chan);
 265                        spin_lock_irqsave(&async_tx_lock, flags);
 266                        list_add_tail_rcu(&master_ref->node,
 267                                &async_tx_master_list);
 268                        spin_unlock_irqrestore(&async_tx_lock,
 269                                flags);
 270                } else {
 271                        printk(KERN_WARNING "async_tx: unable to create"
 272                                " new master entry in response to"
 273                                " a DMA_RESOURCE_ADDED event"
 274                                " (-ENOMEM)\n");
 275                        return 0;
 276                }
 277
 278                async_tx_rebalance();
 279                break;
 280        case DMA_RESOURCE_REMOVED:
 281                found = 0;
 282                spin_lock_irqsave(&async_tx_lock, flags);
 283                list_for_each_entry_rcu(ref, &async_tx_master_list, node)
 284                        if (ref->chan == chan) {
 285                                /* permit backing devices to go away */
 286                                dma_chan_put(ref->chan);
 287                                list_del_rcu(&ref->node);
 288                                call_rcu(&ref->rcu, free_dma_chan_ref);
 289                                found = 1;
 290                                break;
 291                        }
 292                spin_unlock_irqrestore(&async_tx_lock, flags);
 293
 294                pr_debug("async_tx: dma resource removed [%s]\n",
 295                        found ? "ours" : "not ours");
 296
 297                if (found)
 298                        ack = DMA_ACK;
 299                else
 300                        break;
 301
 302                async_tx_rebalance();
 303                break;
 304        case DMA_RESOURCE_SUSPEND:
 305        case DMA_RESOURCE_RESUME:
 306                printk(KERN_WARNING "async_tx: does not support dma channel"
 307                        " suspend/resume\n");
 308                break;
 309        default:
 310                BUG();
 311        }
 312
 313        return ack;
 314}
 315
 316static int __init
 317async_tx_init(void)
 318{
 319        enum dma_transaction_type cap;
 320
 321        spin_lock_init(&async_tx_lock);
 322        bitmap_fill(dma_cap_mask_all.bits, DMA_TX_TYPE_END);
 323
 324        /* an interrupt will never be an explicit operation type.
 325         * clearing this bit prevents allocation to a slot in 'channel_table'
 326         */
 327        clear_bit(DMA_INTERRUPT, dma_cap_mask_all.bits);
 328
 329        for_each_dma_cap_mask(cap, dma_cap_mask_all) {
 330                channel_table[cap] = alloc_percpu(struct chan_ref_percpu);
 331                if (!channel_table[cap])
 332                        goto err;
 333        }
 334
 335        channel_table_initialized = 1;
 336        dma_async_client_register(&async_tx_dma);
 337        dma_async_client_chan_request(&async_tx_dma);
 338
 339        printk(KERN_INFO "async_tx: api initialized (async)\n");
 340
 341        return 0;
 342err:
 343        printk(KERN_ERR "async_tx: initialization failure\n");
 344
 345        while (--cap >= 0)
 346                free_percpu(channel_table[cap]);
 347
 348        return 1;
 349}
 350
 351static void __exit async_tx_exit(void)
 352{
 353        enum dma_transaction_type cap;
 354
 355        channel_table_initialized = 0;
 356
 357        for_each_dma_cap_mask(cap, dma_cap_mask_all)
 358                if (channel_table[cap])
 359                        free_percpu(channel_table[cap]);
 360
 361        dma_async_client_unregister(&async_tx_dma);
 362}
 363
 364/**
 365 * async_tx_find_channel - find a channel to carry out the operation or let
 366 *      the transaction execute synchronously
 367 * @depend_tx: transaction dependency
 368 * @tx_type: transaction type
 369 */
 370struct dma_chan *
 371async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx,
 372        enum dma_transaction_type tx_type)
 373{
 374        /* see if we can keep the chain on one channel */
 375        if (depend_tx &&
 376                dma_has_cap(tx_type, depend_tx->chan->device->cap_mask))
 377                return depend_tx->chan;
 378        else if (likely(channel_table_initialized)) {
 379                struct dma_chan_ref *ref;
 380                int cpu = get_cpu();
 381                ref = per_cpu_ptr(channel_table[tx_type], cpu)->ref;
 382                put_cpu();
 383                return ref ? ref->chan : NULL;
 384        } else
 385                return NULL;
 386}
 387EXPORT_SYMBOL_GPL(async_tx_find_channel);
 388#else
 389static int __init async_tx_init(void)
 390{
 391        printk(KERN_INFO "async_tx: api initialized (sync-only)\n");
 392        return 0;
 393}
 394
 395static void __exit async_tx_exit(void)
 396{
 397        do { } while (0);
 398}
 399#endif
 400
 401void
 402async_tx_submit(struct dma_chan *chan, struct dma_async_tx_descriptor *tx,
 403        enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx,
 404        dma_async_tx_callback cb_fn, void *cb_param)
 405{
 406        tx->callback = cb_fn;
 407        tx->callback_param = cb_param;
 408
 409        /* set this new tx to run after depend_tx if:
 410         * 1/ a dependency exists (depend_tx is !NULL)
 411         * 2/ the tx can not be submitted to the current channel
 412         */
 413        if (depend_tx && depend_tx->chan != chan) {
 414                /* if ack is already set then we cannot be sure
 415                 * we are referring to the correct operation
 416                 */
 417                BUG_ON(depend_tx->ack);
 418
 419                tx->parent = depend_tx;
 420                spin_lock_bh(&depend_tx->lock);
 421                list_add_tail(&tx->depend_node, &depend_tx->depend_list);
 422                if (depend_tx->cookie == 0) {
 423                        struct dma_chan *dep_chan = depend_tx->chan;
 424                        struct dma_device *dep_dev = dep_chan->device;
 425                        dep_dev->device_dependency_added(dep_chan);
 426                }
 427                spin_unlock_bh(&depend_tx->lock);
 428
 429                /* schedule an interrupt to trigger the channel switch */
 430                async_trigger_callback(ASYNC_TX_ACK, depend_tx, NULL, NULL);
 431        } else {
 432                tx->parent = NULL;
 433                tx->tx_submit(tx);
 434        }
 435
 436        if (flags & ASYNC_TX_ACK)
 437                async_tx_ack(tx);
 438
 439        if (depend_tx && (flags & ASYNC_TX_DEP_ACK))
 440                async_tx_ack(depend_tx);
 441}
 442EXPORT_SYMBOL_GPL(async_tx_submit);
 443
 444/**
 445 * async_trigger_callback - schedules the callback function to be run after
 446 * any dependent operations have been completed.
 447 * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
 448 * @depend_tx: 'callback' requires the completion of this transaction
 449 * @cb_fn: function to call after depend_tx completes
 450 * @cb_param: parameter to pass to the callback routine
 451 */
 452struct dma_async_tx_descriptor *
 453async_trigger_callback(enum async_tx_flags flags,
 454        struct dma_async_tx_descriptor *depend_tx,
 455        dma_async_tx_callback cb_fn, void *cb_param)
 456{
 457        struct dma_chan *chan;
 458        struct dma_device *device;
 459        struct dma_async_tx_descriptor *tx;
 460
 461        if (depend_tx) {
 462                chan = depend_tx->chan;
 463                device = chan->device;
 464
 465                /* see if we can schedule an interrupt
 466                 * otherwise poll for completion
 467                 */
 468                if (device && !dma_has_cap(DMA_INTERRUPT, device->cap_mask))
 469                        device = NULL;
 470
 471                tx = device ? device->device_prep_dma_interrupt(chan) : NULL;
 472        } else
 473                tx = NULL;
 474
 475        if (tx) {
 476                pr_debug("%s: (async)\n", __FUNCTION__);
 477
 478                async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param);
 479        } else {
 480                pr_debug("%s: (sync)\n", __FUNCTION__);
 481
 482                /* wait for any prerequisite operations */
 483                if (depend_tx) {
 484                        /* if ack is already set then we cannot be sure
 485                         * we are referring to the correct operation
 486                         */
 487                        BUG_ON(depend_tx->ack);
 488                        if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR)
 489                                panic("%s: DMA_ERROR waiting for depend_tx\n",
 490                                        __FUNCTION__);
 491                }
 492
 493                async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param);
 494        }
 495
 496        return tx;
 497}
 498EXPORT_SYMBOL_GPL(async_trigger_callback);
 499
 500module_init(async_tx_init);
 501module_exit(async_tx_exit);
 502
 503MODULE_AUTHOR("Intel Corporation");
 504MODULE_DESCRIPTION("Asynchronous Bulk Memory Transactions API");
 505MODULE_LICENSE("GPL");
 506