cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dma_fifo.c (7660B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * DMA-able FIFO implementation
      4 *
      5 * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com>
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/slab.h>
     10#include <linux/list.h>
     11#include <linux/bug.h>
     12
     13#include "dma_fifo.h"
     14
     15#ifdef DEBUG_TRACING
     16#define df_trace(s, args...) pr_debug(s, ##args)
     17#else
     18#define df_trace(s, args...)
     19#endif
     20
     21#define FAIL(fifo, condition, format...) ({				\
     22	fifo->corrupt = !!(condition);					\
     23	WARN(fifo->corrupt, format);					\
     24})
     25
     26/*
     27 * private helper fn to determine if check is in open interval (lo,hi)
     28 */
     29static bool addr_check(unsigned int check, unsigned int lo, unsigned int hi)
     30{
     31	return check - (lo + 1) < (hi - 1) - lo;
     32}
     33
     34/**
     35 * dma_fifo_init: initialize the fifo to a valid but inoperative state
     36 * @fifo: address of in-place "struct dma_fifo" object
     37 */
     38void dma_fifo_init(struct dma_fifo *fifo)
     39{
     40	memset(fifo, 0, sizeof(*fifo));
     41	INIT_LIST_HEAD(&fifo->pending);
     42}
     43
     44/**
     45 * dma_fifo_alloc - initialize and allocate dma_fifo
     46 * @fifo: address of in-place "struct dma_fifo" object
     47 * @size: 'apparent' size, in bytes, of fifo
     48 * @align: dma alignment to maintain (should be at least cpu cache alignment),
     49 *         must be power of 2
     50 * @tx_limit: maximum # of bytes transmissible per dma (rounded down to
     51 *            multiple of alignment, but at least align size)
     52 * @open_limit: maximum # of outstanding dma transactions allowed
     53 * @gfp_mask: get_free_pages mask, passed to kmalloc()
     54 *
     55 * The 'apparent' size will be rounded up to next greater aligned size.
     56 * Returns 0 if no error, otherwise an error code
     57 */
     58int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned int align,
     59		   int tx_limit, int open_limit, gfp_t gfp_mask)
     60{
     61	int capacity;
     62
     63	if (!is_power_of_2(align) || size < 0)
     64		return -EINVAL;
     65
     66	size = round_up(size, align);
     67	capacity = size + align * open_limit + align * DMA_FIFO_GUARD;
     68	fifo->data = kmalloc(capacity, gfp_mask);
     69	if (!fifo->data)
     70		return -ENOMEM;
     71
     72	fifo->in = 0;
     73	fifo->out = 0;
     74	fifo->done = 0;
     75	fifo->size = size;
     76	fifo->avail = size;
     77	fifo->align = align;
     78	fifo->tx_limit = max_t(int, round_down(tx_limit, align), align);
     79	fifo->open = 0;
     80	fifo->open_limit = open_limit;
     81	fifo->guard = size + align * open_limit;
     82	fifo->capacity = capacity;
     83	fifo->corrupt = 0;
     84
     85	return 0;
     86}
     87
     88/**
     89 * dma_fifo_free - frees the fifo
     90 * @fifo: address of in-place "struct dma_fifo" to free
     91 *
     92 * Also reinits the fifo to a valid but inoperative state. This
     93 * allows the fifo to be reused with a different target requiring
     94 * different fifo parameters.
     95 */
     96void dma_fifo_free(struct dma_fifo *fifo)
     97{
     98	struct dma_pending *pending, *next;
     99
    100	if (!fifo->data)
    101		return;
    102
    103	list_for_each_entry_safe(pending, next, &fifo->pending, link)
    104		list_del_init(&pending->link);
    105	kfree(fifo->data);
    106	fifo->data = NULL;
    107}
    108
    109/**
    110 * dma_fifo_reset - dumps the fifo contents and reinits for reuse
    111 * @fifo: address of in-place "struct dma_fifo" to reset
    112 */
    113void dma_fifo_reset(struct dma_fifo *fifo)
    114{
    115	struct dma_pending *pending, *next;
    116
    117	if (!fifo->data)
    118		return;
    119
    120	list_for_each_entry_safe(pending, next, &fifo->pending, link)
    121		list_del_init(&pending->link);
    122	fifo->in = 0;
    123	fifo->out = 0;
    124	fifo->done = 0;
    125	fifo->avail = fifo->size;
    126	fifo->open = 0;
    127	fifo->corrupt = 0;
    128}
    129
    130/**
    131 * dma_fifo_in - copies data into the fifo
    132 * @fifo: address of in-place "struct dma_fifo" to write to
    133 * @src: buffer to copy from
    134 * @n: # of bytes to copy
    135 *
    136 * Returns the # of bytes actually copied, which can be less than requested if
    137 * the fifo becomes full. If < 0, return is error code.
    138 */
    139int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n)
    140{
    141	int ofs, l;
    142
    143	if (!fifo->data)
    144		return -ENOENT;
    145	if (fifo->corrupt)
    146		return -ENXIO;
    147
    148	if (n > fifo->avail)
    149		n = fifo->avail;
    150	if (n <= 0)
    151		return 0;
    152
    153	ofs = fifo->in % fifo->capacity;
    154	l = min(n, fifo->capacity - ofs);
    155	memcpy(fifo->data + ofs, src, l);
    156	memcpy(fifo->data, src + l, n - l);
    157
    158	if (FAIL(fifo, addr_check(fifo->done, fifo->in, fifo->in + n) ||
    159		 fifo->avail < n,
    160		 "fifo corrupt: in:%u out:%u done:%u n:%d avail:%d",
    161		 fifo->in, fifo->out, fifo->done, n, fifo->avail))
    162		return -ENXIO;
    163
    164	fifo->in += n;
    165	fifo->avail -= n;
    166
    167	df_trace("in:%u out:%u done:%u n:%d avail:%d", fifo->in, fifo->out,
    168		 fifo->done, n, fifo->avail);
    169
    170	return n;
    171}
    172
    173/**
    174 * dma_fifo_out_pend - gets address/len of next avail read and marks as pended
    175 * @fifo: address of in-place "struct dma_fifo" to read from
    176 * @pended: address of structure to fill with read address/len
    177 *          The data/len fields will be NULL/0 if no dma is pended.
    178 *
    179 * Returns the # of used bytes remaining in fifo (ie, if > 0, more data
    180 * remains in the fifo that was not pended). If < 0, return is error code.
    181 */
    182int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended)
    183{
    184	unsigned int len, n, ofs, l, limit;
    185
    186	if (!fifo->data)
    187		return -ENOENT;
    188	if (fifo->corrupt)
    189		return -ENXIO;
    190
    191	pended->len = 0;
    192	pended->data = NULL;
    193	pended->out = fifo->out;
    194
    195	len = fifo->in - fifo->out;
    196	if (!len)
    197		return -ENODATA;
    198	if (fifo->open == fifo->open_limit)
    199		return -EAGAIN;
    200
    201	n = len;
    202	ofs = fifo->out % fifo->capacity;
    203	l = fifo->capacity - ofs;
    204	limit = min_t(unsigned int, l, fifo->tx_limit);
    205	if (n > limit) {
    206		n = limit;
    207		fifo->out += limit;
    208	} else if (ofs + n > fifo->guard) {
    209		fifo->out += l;
    210		fifo->in = fifo->out;
    211	} else {
    212		fifo->out += round_up(n, fifo->align);
    213		fifo->in = fifo->out;
    214	}
    215
    216	df_trace("in: %u out: %u done: %u n: %d len: %u avail: %d", fifo->in,
    217		 fifo->out, fifo->done, n, len, fifo->avail);
    218
    219	pended->len = n;
    220	pended->data = fifo->data + ofs;
    221	pended->next = fifo->out;
    222	list_add_tail(&pended->link, &fifo->pending);
    223	++fifo->open;
    224
    225	if (FAIL(fifo, fifo->open > fifo->open_limit,
    226		 "past open limit:%d (limit:%d)",
    227		 fifo->open, fifo->open_limit))
    228		return -ENXIO;
    229	if (FAIL(fifo, fifo->out & (fifo->align - 1),
    230		 "fifo out unaligned:%u (align:%u)",
    231		 fifo->out, fifo->align))
    232		return -ENXIO;
    233
    234	return len - n;
    235}
    236
    237/**
    238 * dma_fifo_out_complete - marks pended dma as completed
    239 * @fifo: address of in-place "struct dma_fifo" which was read from
    240 * @complete: address of structure for previously pended dma to mark completed
    241 */
    242int dma_fifo_out_complete(struct dma_fifo *fifo, struct dma_pending *complete)
    243{
    244	struct dma_pending *pending, *next, *tmp;
    245
    246	if (!fifo->data)
    247		return -ENOENT;
    248	if (fifo->corrupt)
    249		return -ENXIO;
    250	if (list_empty(&fifo->pending) && fifo->open == 0)
    251		return -EINVAL;
    252
    253	if (FAIL(fifo, list_empty(&fifo->pending) != (fifo->open == 0),
    254		 "pending list disagrees with open count:%d",
    255		 fifo->open))
    256		return -ENXIO;
    257
    258	tmp = complete->data;
    259	*tmp = *complete;
    260	list_replace(&complete->link, &tmp->link);
    261	dp_mark_completed(tmp);
    262
    263	/* Only update the fifo in the original pended order */
    264	list_for_each_entry_safe(pending, next, &fifo->pending, link) {
    265		if (!dp_is_completed(pending)) {
    266			df_trace("still pending: saved out: %u len: %d",
    267				 pending->out, pending->len);
    268			break;
    269		}
    270
    271		if (FAIL(fifo, pending->out != fifo->done ||
    272			 addr_check(fifo->in, fifo->done, pending->next),
    273			 "in:%u out:%u done:%u saved:%u next:%u",
    274			 fifo->in, fifo->out, fifo->done, pending->out,
    275			 pending->next))
    276			return -ENXIO;
    277
    278		list_del_init(&pending->link);
    279		fifo->done = pending->next;
    280		fifo->avail += pending->len;
    281		--fifo->open;
    282
    283		df_trace("in: %u out: %u done: %u len: %u avail: %d", fifo->in,
    284			 fifo->out, fifo->done, pending->len, fifo->avail);
    285	}
    286
    287	if (FAIL(fifo, fifo->open < 0, "open dma:%d < 0", fifo->open))
    288		return -ENXIO;
    289	if (FAIL(fifo, fifo->avail > fifo->size, "fifo avail:%d > size:%d",
    290		 fifo->avail, fifo->size))
    291		return -ENXIO;
    292
    293	return 0;
    294}