linux/drivers/hwtracing/intel_th/msu.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Intel(R) Trace Hub Memory Storage Unit
 *
 * Copyright (C) 2014-2015 Intel Corporation.
 */

#define pr_fmt(fmt)

#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/sizes.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>

#ifdef CONFIG_X86
#include <asm/set_memory.h>
#endif

#include <linux/intel_th.h>
#include "intel_th.h"
#include "msu.h"

#define msc_dev(x)

/*
 * Lockout state transitions:
 *   READY -> INUSE -+-> LOCKED -+-> READY -> etc.
 *                   \-----------/
 * WIN_READY:	window can be used by HW
 * WIN_INUSE:	window is in use
 * WIN_LOCKED:	window is filled up and is being processed by the buffer
 * handling code
 *
 * All state transitions happen automatically, except for the LOCKED->READY,
 * which needs to be signalled by the buffer code by calling
 * intel_th_msc_window_unlock().
 *
 * When the interrupt handler has to switch to the next window, it checks
 * whether it's READY, and if it is, it performs the switch and tracing
 * continues. If it's LOCKED, it stops the trace.
 */
enum lockout_state {};

/**
 * struct msc_window - multiblock mode window descriptor
 * @entry:	window list linkage (msc::win_list)
 * @pgoff:	page offset into the buffer that this window starts at
 * @lockout:	lockout state, see comment below
 * @lo_lock:	lockout state serialization
 * @nr_blocks:	number of blocks (pages) in this window
 * @nr_segs:	number of segments in this window (<= @nr_blocks)
 * @msc:	pointer to the MSC device
 * @_sgt:	array of block descriptors
 * @sgt:	array of block descriptors
 */
struct msc_window {};

/**
 * struct msc_iter - iterator for msc buffer
 * @entry:		msc::iter_list linkage
 * @msc:		pointer to the MSC device
 * @start_win:		oldest window
 * @win:		current window
 * @offset:		current logical offset into the buffer
 * @start_block:	oldest block in the window
 * @block:		block number in the window
 * @block_off:		offset into current block
 * @wrap_count:		block wrapping handling
 * @eof:		end of buffer reached
 */
struct msc_iter {};

/**
 * struct msc - MSC device representation
 * @reg_base:		register window base address
 * @thdev:		intel_th_device pointer
 * @mbuf:		MSU buffer, if assigned
 * @mbuf_priv		MSU buffer's private data, if @mbuf
 * @win_list:		list of windows in multiblock mode
 * @single_sgt:		single mode buffer
 * @cur_win:		current window
 * @nr_pages:		total number of pages allocated for this buffer
 * @single_sz:		amount of data in single mode
 * @single_wrap:	single mode wrap occurred
 * @base:		buffer's base pointer
 * @base_addr:		buffer's base address
 * @user_count:		number of users of the buffer
 * @mmap_count:		number of mappings
 * @buf_mutex:		mutex to serialize access to buffer-related bits
 * @enabled:		MSC is enabled
 * @wrap:		wrapping is enabled
 * @mode:		MSC operating mode
 * @burst_len:		write burst length
 * @index:		number of this MSC in the MSU
 */
struct msc {};

static LIST_HEAD(msu_buffer_list);
static DEFINE_MUTEX(msu_buffer_mutex);

/**
 * struct msu_buffer_entry - internal MSU buffer bookkeeping
 * @entry:	link to msu_buffer_list
 * @mbuf:	MSU buffer object
 * @owner:	module that provides this MSU buffer
 */
struct msu_buffer_entry {};

static struct msu_buffer_entry *__msu_buffer_entry_find(const char *name)
{}

static const struct msu_buffer *
msu_buffer_get(const char *name)
{}

static void msu_buffer_put(const struct msu_buffer *mbuf)
{}

int intel_th_msu_buffer_register(const struct msu_buffer *mbuf,
				 struct module *owner)
{}
EXPORT_SYMBOL_GPL();

void intel_th_msu_buffer_unregister(const struct msu_buffer *mbuf)
{}
EXPORT_SYMBOL_GPL();

static inline bool msc_block_is_empty(struct msc_block_desc *bdesc)
{}

static inline struct scatterlist *msc_win_base_sg(struct msc_window *win)
{}

static inline struct msc_block_desc *msc_win_base(struct msc_window *win)
{}

static inline dma_addr_t msc_win_base_dma(struct msc_window *win)
{}

static inline unsigned long
msc_win_base_pfn(struct msc_window *win)
{}

/**
 * msc_is_last_win() - check if a window is the last one for a given MSC
 * @win:	window
 * Return:	true if @win is the last window in MSC's multiblock buffer
 */
static inline bool msc_is_last_win(struct msc_window *win)
{}

/**
 * msc_next_window() - return next window in the multiblock buffer
 * @win:	current window
 *
 * Return:	window following the current one
 */
static struct msc_window *msc_next_window(struct msc_window *win)
{}

static size_t msc_win_total_sz(struct msc_window *win)
{}

/**
 * msc_find_window() - find a window matching a given sg_table
 * @msc:	MSC device
 * @sgt:	SG table of the window
 * @nonempty:	skip over empty windows
 *
 * Return:	MSC window structure pointer or NULL if the window
 *		could not be found.
 */
static struct msc_window *
msc_find_window(struct msc *msc, struct sg_table *sgt, bool nonempty)
{}

/**
 * msc_oldest_window() - locate the window with oldest data
 * @msc:	MSC device
 *
 * This should only be used in multiblock mode. Caller should hold the
 * msc::user_count reference.
 *
 * Return:	the oldest window with valid data
 */
static struct msc_window *msc_oldest_window(struct msc *msc)
{}

/**
 * msc_win_oldest_sg() - locate the oldest block in a given window
 * @win:	window to look at
 *
 * Return:	index of the block with the oldest data
 */
static struct scatterlist *msc_win_oldest_sg(struct msc_window *win)
{}

static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter)
{}

static struct msc_iter *msc_iter_install(struct msc *msc)
{}

static void msc_iter_remove(struct msc_iter *iter, struct msc *msc)
{}

static void msc_iter_block_start(struct msc_iter *iter)
{}

static int msc_iter_win_start(struct msc_iter *iter, struct msc *msc)
{}

static int msc_iter_win_advance(struct msc_iter *iter)
{}

static int msc_iter_block_advance(struct msc_iter *iter)
{}

/**
 * msc_buffer_iterate() - go through multiblock buffer's data
 * @iter:	iterator structure
 * @size:	amount of data to scan
 * @data:	callback's private data
 * @fn:		iterator callback
 *
 * This will start at the window which will be written to next (containing
 * the oldest data) and work its way to the current window, calling @fn
 * for each chunk of data as it goes.
 *
 * Caller should have msc::user_count reference to make sure the buffer
 * doesn't disappear from under us.
 *
 * Return:	amount of data actually scanned.
 */
static ssize_t
msc_buffer_iterate(struct msc_iter *iter, size_t size, void *data,
		   unsigned long (*fn)(void *, void *, size_t))
{}

/**
 * msc_buffer_clear_hw_header() - clear hw header for multiblock
 * @msc:	MSC device
 */
static void msc_buffer_clear_hw_header(struct msc *msc)
{}

static int intel_th_msu_init(struct msc *msc)
{}

static void intel_th_msu_deinit(struct msc *msc)
{}

static int msc_win_set_lockout(struct msc_window *win,
			       enum lockout_state expect,
			       enum lockout_state new)
{}
/**
 * msc_configure() - set up MSC hardware
 * @msc:	the MSC device to configure
 *
 * Program storage mode, wrapping, burst length and trace buffer address
 * into a given MSC. Then, enable tracing and set msc::enabled.
 * The latter is serialized on msc::buf_mutex, so make sure to hold it.
 *
 * Return:	%0 for success or a negative error code otherwise.
 */
static int msc_configure(struct msc *msc)
{}

/**
 * msc_disable() - disable MSC hardware
 * @msc:	MSC device to disable
 *
 * If @msc is enabled, disable tracing on the switch and then disable MSC
 * storage. Caller must hold msc::buf_mutex.
 */
static void msc_disable(struct msc *msc)
{}

static int intel_th_msc_activate(struct intel_th_device *thdev)
{}

static void intel_th_msc_deactivate(struct intel_th_device *thdev)
{}

/**
 * msc_buffer_contig_alloc() - allocate a contiguous buffer for SINGLE mode
 * @msc:	MSC device
 * @size:	allocation size in bytes
 *
 * This modifies msc::base, which requires msc::buf_mutex to serialize, so the
 * caller is expected to hold it.
 *
 * Return:	0 on success, -errno otherwise.
 */
static int msc_buffer_contig_alloc(struct msc *msc, unsigned long size)
{}

/**
 * msc_buffer_contig_free() - free a contiguous buffer
 * @msc:	MSC configured in SINGLE mode
 */
static void msc_buffer_contig_free(struct msc *msc)
{}

/**
 * msc_buffer_contig_get_page() - find a page at a given offset
 * @msc:	MSC configured in SINGLE mode
 * @pgoff:	page offset
 *
 * Return:	page, if @pgoff is within the range, NULL otherwise.
 */
static struct page *msc_buffer_contig_get_page(struct msc *msc,
					       unsigned long pgoff)
{}

static int __msc_buffer_win_alloc(struct msc_window *win,
				  unsigned int nr_segs)
{}

#ifdef CONFIG_X86
static void msc_buffer_set_uc(struct msc *msc)
{}

static void msc_buffer_set_wb(struct msc *msc)
{}
#else /* !X86 */
static inline void
msc_buffer_set_uc(struct msc *msc) {}
static inline void msc_buffer_set_wb(struct msc *msc) {}
#endif /* CONFIG_X86 */

static struct page *msc_sg_page(struct scatterlist *sg)
{}

/**
 * msc_buffer_win_alloc() - alloc a window for a multiblock mode
 * @msc:	MSC device
 * @nr_blocks:	number of pages in this window
 *
 * This modifies msc::win_list and msc::base, which requires msc::buf_mutex
 * to serialize, so the caller is expected to hold it.
 *
 * Return:	0 on success, -errno otherwise.
 */
static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
{}

static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win)
{}

/**
 * msc_buffer_win_free() - free a window from MSC's window list
 * @msc:	MSC device
 * @win:	window to free
 *
 * This modifies msc::win_list and msc::base, which requires msc::buf_mutex
 * to serialize, so the caller is expected to hold it.
 */
static void msc_buffer_win_free(struct msc *msc, struct msc_window *win)
{}

/**
 * msc_buffer_relink() - set up block descriptors for multiblock mode
 * @msc:	MSC device
 *
 * This traverses msc::win_list, which requires msc::buf_mutex to serialize,
 * so the caller is expected to hold it.
 */
static void msc_buffer_relink(struct msc *msc)
{}

static void msc_buffer_multi_free(struct msc *msc)
{}

static int msc_buffer_multi_alloc(struct msc *msc, unsigned long *nr_pages,
				  unsigned int nr_wins)
{}

/**
 * msc_buffer_free() - free buffers for MSC
 * @msc:	MSC device
 *
 * Free MSC's storage buffers.
 *
 * This modifies msc::win_list and msc::base, which requires msc::buf_mutex to
 * serialize, so the caller is expected to hold it.
 */
static void msc_buffer_free(struct msc *msc)
{}

/**
 * msc_buffer_alloc() - allocate a buffer for MSC
 * @msc:	MSC device
 * @nr_pages:	number of pages for each window
 * @nr_wins:	number of windows
 *
 * Allocate a storage buffer for MSC, depending on the msc::mode, it will be
 * either done via msc_buffer_contig_alloc() for SINGLE operation mode or
 * msc_buffer_win_alloc() for multiblock operation. The latter allocates one
 * window per invocation, so in multiblock mode this can be called multiple
 * times for the same MSC to allocate multiple windows.
 *
 * This modifies msc::win_list and msc::base, which requires msc::buf_mutex
 * to serialize, so the caller is expected to hold it.
 *
 * Return:	0 on success, -errno otherwise.
 */
static int msc_buffer_alloc(struct msc *msc, unsigned long *nr_pages,
			    unsigned int nr_wins)
{}

/**
 * msc_buffer_unlocked_free_unless_used() - free a buffer unless it's in use
 * @msc:	MSC device
 *
 * This will free MSC buffer unless it is in use or there is no allocated
 * buffer.
 * Caller needs to hold msc::buf_mutex.
 *
 * Return:	0 on successful deallocation or if there was no buffer to
 *		deallocate, -EBUSY if there are active users.
 */
static int msc_buffer_unlocked_free_unless_used(struct msc *msc)
{}

/**
 * msc_buffer_free_unless_used() - free a buffer unless it's in use
 * @msc:	MSC device
 *
 * This is a locked version of msc_buffer_unlocked_free_unless_used().
 *
 * Return:	0 on successful deallocation or if there was no buffer to
 *		deallocate, -EBUSY if there are active users.
 */
static int msc_buffer_free_unless_used(struct msc *msc)
{}

/**
 * msc_buffer_get_page() - get MSC buffer page at a given offset
 * @msc:	MSC device
 * @pgoff:	page offset into the storage buffer
 *
 * This traverses msc::win_list, so holding msc::buf_mutex is expected from
 * the caller.
 *
 * Return:	page if @pgoff corresponds to a valid buffer page or NULL.
 */
static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff)
{}

/**
 * struct msc_win_to_user_struct - data for copy_to_user() callback
 * @buf:	userspace buffer to copy data to
 * @offset:	running offset
 */
struct msc_win_to_user_struct {};

/**
 * msc_win_to_user() - iterator for msc_buffer_iterate() to copy data to user
 * @data:	callback's private data
 * @src:	source buffer
 * @len:	amount of data to copy from the source buffer
 *
 * Return:	>= %0 for success or -errno for error.
 */
static unsigned long msc_win_to_user(void *data, void *src, size_t len)
{}


/*
 * file operations' callbacks
 */

static int intel_th_msc_open(struct inode *inode, struct file *file)
{}

static int intel_th_msc_release(struct inode *inode, struct file *file)
{}

static ssize_t
msc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len)
{}

static ssize_t intel_th_msc_read(struct file *file, char __user *buf,
				 size_t len, loff_t *ppos)
{}

/*
 * vm operations callbacks (vm_ops)
 */

static void msc_mmap_open(struct vm_area_struct *vma)
{}

static void msc_mmap_close(struct vm_area_struct *vma)
{}

static vm_fault_t msc_mmap_fault(struct vm_fault *vmf)
{}

static const struct vm_operations_struct msc_mmap_ops =;

static int intel_th_msc_mmap(struct file *file, struct vm_area_struct *vma)
{}

static const struct file_operations intel_th_msc_fops =;

static void intel_th_msc_wait_empty(struct intel_th_device *thdev)
{}

static int intel_th_msc_init(struct msc *msc)
{}

static int msc_win_switch(struct msc *msc)
{}

/**
 * intel_th_msc_window_unlock - put the window back in rotation
 * @dev:	MSC device to which this relates
 * @sgt:	buffer's sg_table for the window, does nothing if NULL
 */
void intel_th_msc_window_unlock(struct device *dev, struct sg_table *sgt)
{}
EXPORT_SYMBOL_GPL();

static void msc_work(struct work_struct *work)
{}

static irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev)
{}

static const char * const msc_mode[] =;

static ssize_t
wrap_show(struct device *dev, struct device_attribute *attr, char *buf)
{}

static ssize_t
wrap_store(struct device *dev, struct device_attribute *attr, const char *buf,
	   size_t size)
{}

static DEVICE_ATTR_RW(wrap);

static void msc_buffer_unassign(struct msc *msc)
{}

static ssize_t
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{}

static ssize_t
mode_store(struct device *dev, struct device_attribute *attr, const char *buf,
	   size_t size)
{}

static DEVICE_ATTR_RW(mode);

static ssize_t
nr_pages_show(struct device *dev, struct device_attribute *attr, char *buf)
{}

static ssize_t
nr_pages_store(struct device *dev, struct device_attribute *attr,
	       const char *buf, size_t size)
{}

static DEVICE_ATTR_RW(nr_pages);

static ssize_t
win_switch_store(struct device *dev, struct device_attribute *attr,
		 const char *buf, size_t size)
{}

static DEVICE_ATTR_WO(win_switch);

static ssize_t stop_on_full_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{}

static ssize_t stop_on_full_store(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t size)
{}

static DEVICE_ATTR_RW(stop_on_full);

static struct attribute *msc_output_attrs[] =;

static const struct attribute_group msc_output_group =;

static int intel_th_msc_probe(struct intel_th_device *thdev)
{}

static void intel_th_msc_remove(struct intel_th_device *thdev)
{}

static struct intel_th_driver intel_th_msc_driver =;

module_driver();

MODULE_LICENSE();
MODULE_DESCRIPTION();
MODULE_AUTHOR();