// 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(…) …;