| /* |
| * |
| * (C) COPYRIGHT 2015-2016 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the |
| * GNU General Public License version 2 as published by the Free Software |
| * Foundation, and any use by you of this program is subject to the terms |
| * of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained |
| * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| |
| |
| #include <linux/anon_inodes.h> |
| #include <linux/atomic.h> |
| #include <linux/file.h> |
| #include <linux/mutex.h> |
| #include <linux/poll.h> |
| #include <linux/spinlock.h> |
| #include <linux/string.h> |
| #include <linux/stringify.h> |
| #include <linux/timer.h> |
| #include <linux/wait.h> |
| |
| #include <mali_kbase.h> |
| #include <mali_kbase_jm.h> |
| #include <mali_kbase_tlstream.h> |
| |
| /*****************************************************************************/ |
| |
| /* The version of swtrace protocol used in timeline stream. */ |
| #define SWTRACE_VERSION 3 |
| |
| /* The maximum expected length of string in tracepoint descriptor. */ |
| #define STRLEN_MAX 64 /* bytes */ |
| |
| /* The number of nanoseconds in a second. */ |
| #define NSECS_IN_SEC 1000000000ull /* ns */ |
| |
| /* The period of autoflush checker execution in milliseconds. */ |
| #define AUTOFLUSH_INTERVAL 1000 /* ms */ |
| |
| /* The maximum size of a single packet used by timeline. */ |
| #define PACKET_SIZE 4096 /* bytes */ |
| |
| /* The number of packets used by one timeline stream. */ |
| #define PACKET_COUNT 16 |
| |
| /* The number of bytes reserved for packet header. |
| * These value must be defined according to MIPE documentation. */ |
| #define PACKET_HEADER_SIZE 8 /* bytes */ |
| |
| /* The number of bytes reserved for packet sequence number. |
| * These value must be defined according to MIPE documentation. */ |
| #define PACKET_NUMBER_SIZE 4 /* bytes */ |
| |
| /* Packet header - first word. |
| * These values must be defined according to MIPE documentation. */ |
| #define PACKET_STREAMID_POS 0 |
| #define PACKET_STREAMID_LEN 8 |
| #define PACKET_RSVD1_POS (PACKET_STREAMID_POS + PACKET_STREAMID_LEN) |
| #define PACKET_RSVD1_LEN 8 |
| #define PACKET_TYPE_POS (PACKET_RSVD1_POS + PACKET_RSVD1_LEN) |
| #define PACKET_TYPE_LEN 3 |
| #define PACKET_CLASS_POS (PACKET_TYPE_POS + PACKET_TYPE_LEN) |
| #define PACKET_CLASS_LEN 7 |
| #define PACKET_FAMILY_POS (PACKET_CLASS_POS + PACKET_CLASS_LEN) |
| #define PACKET_FAMILY_LEN 6 |
| |
| /* Packet header - second word |
| * These values must be defined according to MIPE documentation. */ |
| #define PACKET_LENGTH_POS 0 |
| #define PACKET_LENGTH_LEN 24 |
| #define PACKET_SEQBIT_POS (PACKET_LENGTH_POS + PACKET_LENGTH_LEN) |
| #define PACKET_SEQBIT_LEN 1 |
| #define PACKET_RSVD2_POS (PACKET_SEQBIT_POS + PACKET_SEQBIT_LEN) |
| #define PACKET_RSVD2_LEN 7 |
| |
| /* Types of streams generated by timeline. |
| * Order is significant! Header streams must precede respective body streams. */ |
| enum tl_stream_type { |
| TL_STREAM_TYPE_OBJ_HEADER, |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| TL_STREAM_TYPE_OBJ, |
| TL_STREAM_TYPE_AUX_HEADER, |
| TL_STREAM_TYPE_AUX, |
| |
| TL_STREAM_TYPE_COUNT |
| }; |
| |
| /* Timeline packet family ids. |
| * Values are significant! Check MIPE documentation. */ |
| enum tl_packet_family { |
| TL_PACKET_FAMILY_CTRL = 0, /* control packets */ |
| TL_PACKET_FAMILY_TL = 1, /* timeline packets */ |
| |
| TL_PACKET_FAMILY_COUNT |
| }; |
| |
| /* Packet classes used in timeline streams. |
| * Values are significant! Check MIPE documentation. */ |
| enum tl_packet_class { |
| TL_PACKET_CLASS_OBJ = 0, /* timeline objects packet */ |
| TL_PACKET_CLASS_AUX = 1, /* auxiliary events packet */ |
| }; |
| |
| /* Packet types used in timeline streams. |
| * Values are significant! Check MIPE documentation. */ |
| enum tl_packet_type { |
| TL_PACKET_TYPE_HEADER = 0, /* stream's header/directory */ |
| TL_PACKET_TYPE_BODY = 1, /* stream's body */ |
| TL_PACKET_TYPE_SUMMARY = 2, /* stream's summary */ |
| }; |
| |
| /* Message ids of trace events that are recorded in the timeline stream. */ |
| enum tl_msg_id_obj { |
| /* Timeline object events. */ |
| KBASE_TL_NEW_CTX, |
| KBASE_TL_NEW_GPU, |
| KBASE_TL_NEW_LPU, |
| KBASE_TL_NEW_ATOM, |
| KBASE_TL_NEW_AS, |
| KBASE_TL_DEL_CTX, |
| KBASE_TL_DEL_ATOM, |
| KBASE_TL_LIFELINK_LPU_GPU, |
| KBASE_TL_LIFELINK_AS_GPU, |
| KBASE_TL_RET_CTX_LPU, |
| KBASE_TL_RET_ATOM_CTX, |
| KBASE_TL_RET_ATOM_LPU, |
| KBASE_TL_NRET_CTX_LPU, |
| KBASE_TL_NRET_ATOM_CTX, |
| KBASE_TL_NRET_ATOM_LPU, |
| KBASE_TL_RET_AS_CTX, |
| KBASE_TL_NRET_AS_CTX, |
| KBASE_TL_RET_ATOM_AS, |
| KBASE_TL_NRET_ATOM_AS, |
| KBASE_TL_DEP_ATOM_ATOM, |
| KBASE_TL_NDEP_ATOM_ATOM, |
| KBASE_TL_RDEP_ATOM_ATOM, |
| KBASE_TL_ATTRIB_ATOM_CONFIG, |
| KBASE_TL_ATTRIB_ATOM_PRIORITY, |
| KBASE_TL_ATTRIB_ATOM_STATE, |
| KBASE_TL_ATTRIB_ATOM_PRIORITY_CHANGE, |
| KBASE_TL_ATTRIB_AS_CONFIG, |
| KBASE_TL_EVENT_LPU_SOFTSTOP, |
| KBASE_TL_EVENT_ATOM_SOFTSTOP_EX, |
| KBASE_TL_EVENT_ATOM_SOFTSTOP_ISSUE, |
| |
| /* Job dump specific events. */ |
| KBASE_JD_GPU_SOFT_RESET |
| }; |
| |
| /* Message ids of trace events that are recorded in the auxiliary stream. */ |
| enum tl_msg_id_aux { |
| KBASE_AUX_PM_STATE, |
| KBASE_AUX_PAGEFAULT, |
| KBASE_AUX_PAGESALLOC, |
| KBASE_AUX_DEVFREQ_TARGET |
| }; |
| |
| /*****************************************************************************/ |
| |
| /** |
| * struct tl_stream - timeline stream structure |
| * @lock: message order lock |
| * @buffer: array of buffers |
| * @wbi: write buffer index |
| * @rbi: read buffer index |
| * @numbered: if non-zero stream's packets are sequentially numbered |
| * @autoflush_counter: counter tracking stream's autoflush state |
| * |
| * This structure holds information needed to construct proper packets in the |
| * timeline stream. Each message in sequence must bear timestamp that is greater |
| * to one in previous message in the same stream. For this reason lock is held |
| * throughout the process of message creation. Each stream contains set of |
| * buffers. Each buffer will hold one MIPE packet. In case there is no free |
| * space required to store incoming message the oldest buffer is discarded. |
| * Each packet in timeline body stream has sequence number embedded (this value |
| * must increment monotonically and is used by packets receiver to discover |
| * buffer overflows. |
| * Autoflush counter is set to negative number when there is no data pending |
| * for flush and it is set to zero on every update of the buffer. Autoflush |
| * timer will increment the counter by one on every expiry. In case there will |
| * be no activity on the buffer during two consecutive timer expiries, stream |
| * buffer will be flushed. |
| */ |
| struct tl_stream { |
| spinlock_t lock; |
| |
| struct { |
| atomic_t size; /* number of bytes in buffer */ |
| char data[PACKET_SIZE]; /* buffer's data */ |
| } buffer[PACKET_COUNT]; |
| |
| atomic_t wbi; |
| atomic_t rbi; |
| |
| int numbered; |
| atomic_t autoflush_counter; |
| }; |
| |
| /** |
| * struct tp_desc - tracepoint message descriptor structure |
| * @id: tracepoint ID identifying message in stream |
| * @id_str: human readable version of tracepoint ID |
| * @name: tracepoint description |
| * @arg_types: tracepoint's arguments types declaration |
| * @arg_names: comma separated list of tracepoint's arguments names |
| */ |
| struct tp_desc { |
| u32 id; |
| const char *id_str; |
| const char *name; |
| const char *arg_types; |
| const char *arg_names; |
| }; |
| |
| /*****************************************************************************/ |
| |
| /* Configuration of timeline streams generated by kernel. |
| * Kernel emit only streams containing either timeline object events or |
| * auxiliary events. All streams have stream id value of 1 (as opposed to user |
| * space streams that have value of 0). */ |
| static const struct { |
| enum tl_packet_family pkt_family; |
| enum tl_packet_class pkt_class; |
| enum tl_packet_type pkt_type; |
| unsigned int stream_id; |
| } tl_stream_cfg[TL_STREAM_TYPE_COUNT] = { |
| {TL_PACKET_FAMILY_TL, TL_PACKET_CLASS_OBJ, TL_PACKET_TYPE_HEADER, 1}, |
| {TL_PACKET_FAMILY_TL, TL_PACKET_CLASS_OBJ, TL_PACKET_TYPE_SUMMARY, 1}, |
| {TL_PACKET_FAMILY_TL, TL_PACKET_CLASS_OBJ, TL_PACKET_TYPE_BODY, 1}, |
| {TL_PACKET_FAMILY_TL, TL_PACKET_CLASS_AUX, TL_PACKET_TYPE_HEADER, 1}, |
| {TL_PACKET_FAMILY_TL, TL_PACKET_CLASS_AUX, TL_PACKET_TYPE_BODY, 1} |
| }; |
| |
| /* The timeline streams generated by kernel. */ |
| static struct tl_stream *tl_stream[TL_STREAM_TYPE_COUNT]; |
| |
| /* Autoflush timer. */ |
| static struct timer_list autoflush_timer; |
| |
| /* If non-zero autoflush timer is active. */ |
| static atomic_t autoflush_timer_active; |
| |
| /* Reader lock. Only one reader is allowed to have access to the timeline |
| * streams at any given time. */ |
| static DEFINE_MUTEX(tl_reader_lock); |
| |
| /* Timeline stream event queue. */ |
| static DECLARE_WAIT_QUEUE_HEAD(tl_event_queue); |
| |
| /* The timeline stream file operations functions. */ |
| static ssize_t kbasep_tlstream_read( |
| struct file *filp, |
| char __user *buffer, |
| size_t size, |
| loff_t *f_pos); |
| static unsigned int kbasep_tlstream_poll(struct file *filp, poll_table *wait); |
| static int kbasep_tlstream_release(struct inode *inode, struct file *filp); |
| |
| /* The timeline stream file operations structure. */ |
| static const struct file_operations kbasep_tlstream_fops = { |
| .release = kbasep_tlstream_release, |
| .read = kbasep_tlstream_read, |
| .poll = kbasep_tlstream_poll, |
| }; |
| |
| /* Descriptors of timeline messages transmitted in object events stream. */ |
| static const struct tp_desc tp_desc_obj[] = { |
| { |
| KBASE_TL_NEW_CTX, |
| __stringify(KBASE_TL_NEW_CTX), |
| "object ctx is created", |
| "@pII", |
| "ctx,ctx_nr,tgid" |
| }, |
| { |
| KBASE_TL_NEW_GPU, |
| __stringify(KBASE_TL_NEW_GPU), |
| "object gpu is created", |
| "@pII", |
| "gpu,gpu_id,core_count" |
| }, |
| { |
| KBASE_TL_NEW_LPU, |
| __stringify(KBASE_TL_NEW_LPU), |
| "object lpu is created", |
| "@pII", |
| "lpu,lpu_nr,lpu_fn" |
| }, |
| { |
| KBASE_TL_NEW_ATOM, |
| __stringify(KBASE_TL_NEW_ATOM), |
| "object atom is created", |
| "@pI", |
| "atom,atom_nr" |
| }, |
| { |
| KBASE_TL_NEW_AS, |
| __stringify(KBASE_TL_NEW_AS), |
| "address space object is created", |
| "@pI", |
| "address_space,as_nr" |
| }, |
| { |
| KBASE_TL_DEL_CTX, |
| __stringify(KBASE_TL_DEL_CTX), |
| "context is destroyed", |
| "@p", |
| "ctx" |
| }, |
| { |
| KBASE_TL_DEL_ATOM, |
| __stringify(KBASE_TL_DEL_ATOM), |
| "atom is destroyed", |
| "@p", |
| "atom" |
| }, |
| { |
| KBASE_TL_LIFELINK_LPU_GPU, |
| __stringify(KBASE_TL_LIFELINK_LPU_GPU), |
| "lpu is deleted with gpu", |
| "@pp", |
| "lpu,gpu" |
| }, |
| { |
| KBASE_TL_LIFELINK_AS_GPU, |
| __stringify(KBASE_TL_LIFELINK_AS_GPU), |
| "address space is deleted with gpu", |
| "@pp", |
| "address_space,gpu" |
| }, |
| { |
| KBASE_TL_RET_CTX_LPU, |
| __stringify(KBASE_TL_RET_CTX_LPU), |
| "context is retained by lpu", |
| "@pp", |
| "ctx,lpu" |
| }, |
| { |
| KBASE_TL_RET_ATOM_CTX, |
| __stringify(KBASE_TL_RET_ATOM_CTX), |
| "atom is retained by context", |
| "@pp", |
| "atom,ctx" |
| }, |
| { |
| KBASE_TL_RET_ATOM_LPU, |
| __stringify(KBASE_TL_RET_ATOM_LPU), |
| "atom is retained by lpu", |
| "@pps", |
| "atom,lpu,attrib_match_list" |
| }, |
| { |
| KBASE_TL_NRET_CTX_LPU, |
| __stringify(KBASE_TL_NRET_CTX_LPU), |
| "context is released by lpu", |
| "@pp", |
| "ctx,lpu" |
| }, |
| { |
| KBASE_TL_NRET_ATOM_CTX, |
| __stringify(KBASE_TL_NRET_ATOM_CTX), |
| "atom is released by context", |
| "@pp", |
| "atom,ctx" |
| }, |
| { |
| KBASE_TL_NRET_ATOM_LPU, |
| __stringify(KBASE_TL_NRET_ATOM_LPU), |
| "atom is released by lpu", |
| "@pp", |
| "atom,lpu" |
| }, |
| { |
| KBASE_TL_RET_AS_CTX, |
| __stringify(KBASE_TL_RET_AS_CTX), |
| "address space is retained by context", |
| "@pp", |
| "address_space,ctx" |
| }, |
| { |
| KBASE_TL_NRET_AS_CTX, |
| __stringify(KBASE_TL_NRET_AS_CTX), |
| "address space is released by context", |
| "@pp", |
| "address_space,ctx" |
| }, |
| { |
| KBASE_TL_RET_ATOM_AS, |
| __stringify(KBASE_TL_RET_ATOM_AS), |
| "atom is retained by address space", |
| "@pp", |
| "atom,address_space" |
| }, |
| { |
| KBASE_TL_NRET_ATOM_AS, |
| __stringify(KBASE_TL_NRET_ATOM_AS), |
| "atom is released by address space", |
| "@pp", |
| "atom,address_space" |
| }, |
| { |
| KBASE_TL_DEP_ATOM_ATOM, |
| __stringify(KBASE_TL_DEP_ATOM_ATOM), |
| "atom2 depends on atom1", |
| "@pp", |
| "atom1,atom2" |
| }, |
| { |
| KBASE_TL_NDEP_ATOM_ATOM, |
| __stringify(KBASE_TL_NDEP_ATOM_ATOM), |
| "atom2 no longer depends on atom1", |
| "@pp", |
| "atom1,atom2" |
| }, |
| { |
| KBASE_TL_RDEP_ATOM_ATOM, |
| __stringify(KBASE_TL_RDEP_ATOM_ATOM), |
| "resolved dependecy of atom2 depending on atom1", |
| "@pp", |
| "atom1,atom2" |
| }, |
| { |
| KBASE_TL_ATTRIB_ATOM_CONFIG, |
| __stringify(KBASE_TL_ATTRIB_ATOM_CONFIG), |
| "atom job slot attributes", |
| "@pLLI", |
| "atom,descriptor,affinity,config" |
| }, |
| { |
| KBASE_TL_ATTRIB_ATOM_PRIORITY, |
| __stringify(KBASE_TL_ATTRIB_ATOM_PRIORITY), |
| "atom priority", |
| "@pI", |
| "atom,prio" |
| }, |
| { |
| KBASE_TL_ATTRIB_ATOM_STATE, |
| __stringify(KBASE_TL_ATTRIB_ATOM_STATE), |
| "atom state", |
| "@pI", |
| "atom,state" |
| }, |
| { |
| KBASE_TL_ATTRIB_ATOM_PRIORITY_CHANGE, |
| __stringify(KBASE_TL_ATTRIB_ATOM_PRIORITY_CHANGE), |
| "atom caused priority change", |
| "@p", |
| "atom" |
| }, |
| { |
| KBASE_TL_ATTRIB_AS_CONFIG, |
| __stringify(KBASE_TL_ATTRIB_AS_CONFIG), |
| "address space attributes", |
| "@pLLL", |
| "address_space,transtab,memattr,transcfg" |
| }, |
| { |
| KBASE_TL_EVENT_LPU_SOFTSTOP, |
| __stringify(KBASE_TL_EVENT_LPU_SOFTSTOP), |
| "softstop event on given lpu", |
| "@p", |
| "lpu" |
| }, |
| { |
| KBASE_TL_EVENT_ATOM_SOFTSTOP_EX, |
| __stringify(KBASE_TL_EVENT_ATOM_SOFTSTOP_EX), |
| "atom softstopped", |
| "@p", |
| "atom" |
| }, |
| { |
| KBASE_TL_EVENT_ATOM_SOFTSTOP_ISSUE, |
| __stringify(KBASE_TL_EVENT_SOFTSTOP_ISSUE), |
| "atom softstop issued", |
| "@p", |
| "atom" |
| }, |
| { |
| KBASE_JD_GPU_SOFT_RESET, |
| __stringify(KBASE_JD_GPU_SOFT_RESET), |
| "gpu soft reset", |
| "@p", |
| "gpu" |
| }, |
| }; |
| |
| /* Descriptors of timeline messages transmitted in auxiliary events stream. */ |
| static const struct tp_desc tp_desc_aux[] = { |
| { |
| KBASE_AUX_PM_STATE, |
| __stringify(KBASE_AUX_PM_STATE), |
| "PM state", |
| "@IL", |
| "core_type,core_state_bitset" |
| }, |
| { |
| KBASE_AUX_PAGEFAULT, |
| __stringify(KBASE_AUX_PAGEFAULT), |
| "Page fault", |
| "@IL", |
| "ctx_nr,page_cnt_change" |
| }, |
| { |
| KBASE_AUX_PAGESALLOC, |
| __stringify(KBASE_AUX_PAGESALLOC), |
| "Total alloc pages change", |
| "@IL", |
| "ctx_nr,page_cnt" |
| }, |
| { |
| KBASE_AUX_DEVFREQ_TARGET, |
| __stringify(KBASE_AUX_DEVFREQ_TARGET), |
| "New device frequency target", |
| "@L", |
| "target_freq" |
| } |
| }; |
| |
| #if MALI_UNIT_TEST |
| /* Number of bytes read by user. */ |
| static atomic_t tlstream_bytes_collected = {0}; |
| |
| /* Number of bytes generated by tracepoint messages. */ |
| static atomic_t tlstream_bytes_generated = {0}; |
| #endif /* MALI_UNIT_TEST */ |
| |
| /*****************************************************************************/ |
| |
| /* Indicator of whether the timeline stream file descriptor is used. */ |
| atomic_t kbase_tlstream_enabled = {0}; |
| |
| /*****************************************************************************/ |
| |
| /** |
| * kbasep_tlstream_get_timestamp - return timestamp |
| * |
| * Function returns timestamp value based on raw monotonic timer. Value will |
| * wrap around zero in case of overflow. |
| * Return: timestamp value |
| */ |
| static u64 kbasep_tlstream_get_timestamp(void) |
| { |
| struct timespec ts; |
| u64 timestamp; |
| |
| getrawmonotonic(&ts); |
| timestamp = (u64)ts.tv_sec * NSECS_IN_SEC + ts.tv_nsec; |
| return timestamp; |
| } |
| |
| /** |
| * kbasep_tlstream_write_bytes - write data to message buffer |
| * @buffer: buffer where data will be written |
| * @pos: position in the buffer where to place data |
| * @bytes: pointer to buffer holding data |
| * @len: length of data to be written |
| * |
| * Return: updated position in the buffer |
| */ |
| static size_t kbasep_tlstream_write_bytes( |
| char *buffer, |
| size_t pos, |
| const void *bytes, |
| size_t len) |
| { |
| KBASE_DEBUG_ASSERT(buffer); |
| KBASE_DEBUG_ASSERT(bytes); |
| |
| memcpy(&buffer[pos], bytes, len); |
| |
| return pos + len; |
| } |
| |
| /** |
| * kbasep_tlstream_write_string - write string to message buffer |
| * @buffer: buffer where data will be written |
| * @pos: position in the buffer where to place data |
| * @string: pointer to buffer holding the source string |
| * @max_write_size: number of bytes that can be stored in buffer |
| * |
| * Return: updated position in the buffer |
| */ |
| static size_t kbasep_tlstream_write_string( |
| char *buffer, |
| size_t pos, |
| const char *string, |
| size_t max_write_size) |
| { |
| u32 string_len; |
| |
| KBASE_DEBUG_ASSERT(buffer); |
| KBASE_DEBUG_ASSERT(string); |
| /* Timeline string consists of at least string length and nul |
| * terminator. */ |
| KBASE_DEBUG_ASSERT(max_write_size >= sizeof(string_len) + sizeof(char)); |
| max_write_size -= sizeof(string_len); |
| |
| string_len = strlcpy( |
| &buffer[pos + sizeof(string_len)], |
| string, |
| max_write_size); |
| string_len += sizeof(char); |
| |
| /* Make sure that the source string fit into the buffer. */ |
| KBASE_DEBUG_ASSERT(string_len <= max_write_size); |
| |
| /* Update string length. */ |
| memcpy(&buffer[pos], &string_len, sizeof(string_len)); |
| |
| return pos + sizeof(string_len) + string_len; |
| } |
| |
| /** |
| * kbasep_tlstream_write_timestamp - write timestamp to message buffer |
| * @buffer: buffer where data will be written |
| * @pos: position in the buffer where to place data |
| * |
| * Return: updated position in the buffer |
| */ |
| static size_t kbasep_tlstream_write_timestamp(void *buffer, size_t pos) |
| { |
| u64 timestamp = kbasep_tlstream_get_timestamp(); |
| |
| return kbasep_tlstream_write_bytes( |
| buffer, pos, |
| ×tamp, sizeof(timestamp)); |
| } |
| |
| /** |
| * kbasep_tlstream_put_bits - put bits in a word |
| * @word: pointer to the words being modified |
| * @value: value that shall be written to given position |
| * @bitpos: position where value shall be written (in bits) |
| * @bitlen: length of value (in bits) |
| */ |
| static void kbasep_tlstream_put_bits( |
| u32 *word, |
| u32 value, |
| unsigned int bitpos, |
| unsigned int bitlen) |
| { |
| const u32 mask = ((1 << bitlen) - 1) << bitpos; |
| |
| KBASE_DEBUG_ASSERT(word); |
| KBASE_DEBUG_ASSERT((0 != bitlen) && (32 >= bitlen)); |
| KBASE_DEBUG_ASSERT((bitpos + bitlen) <= 32); |
| |
| *word &= ~mask; |
| *word |= ((value << bitpos) & mask); |
| } |
| |
| /** |
| * kbasep_tlstream_packet_header_setup - setup the packet header |
| * @buffer: pointer to the buffer |
| * @pkt_family: packet's family |
| * @pkt_type: packet's type |
| * @pkt_class: packet's class |
| * @stream_id: stream id |
| * @numbered: non-zero if this stream is numbered |
| * |
| * Function sets up immutable part of packet header in the given buffer. |
| */ |
| static void kbasep_tlstream_packet_header_setup( |
| char *buffer, |
| enum tl_packet_family pkt_family, |
| enum tl_packet_class pkt_class, |
| enum tl_packet_type pkt_type, |
| unsigned int stream_id, |
| int numbered) |
| { |
| u32 word0 = 0; |
| u32 word1 = 0; |
| |
| KBASE_DEBUG_ASSERT(buffer); |
| KBASE_DEBUG_ASSERT(pkt_family == TL_PACKET_FAMILY_TL); |
| KBASE_DEBUG_ASSERT( |
| (pkt_type == TL_PACKET_TYPE_HEADER) || |
| (pkt_type == TL_PACKET_TYPE_SUMMARY) || |
| (pkt_type == TL_PACKET_TYPE_BODY)); |
| KBASE_DEBUG_ASSERT( |
| (pkt_class == TL_PACKET_CLASS_OBJ) || |
| (pkt_class == TL_PACKET_CLASS_AUX)); |
| |
| kbasep_tlstream_put_bits( |
| &word0, pkt_family, |
| PACKET_FAMILY_POS, PACKET_FAMILY_LEN); |
| kbasep_tlstream_put_bits( |
| &word0, pkt_class, |
| PACKET_CLASS_POS, PACKET_CLASS_LEN); |
| kbasep_tlstream_put_bits( |
| &word0, pkt_type, |
| PACKET_TYPE_POS, PACKET_TYPE_LEN); |
| kbasep_tlstream_put_bits( |
| &word0, stream_id, |
| PACKET_STREAMID_POS, PACKET_STREAMID_LEN); |
| |
| if (numbered) |
| kbasep_tlstream_put_bits( |
| &word1, 1, |
| PACKET_SEQBIT_POS, PACKET_SEQBIT_LEN); |
| |
| memcpy(&buffer[0], &word0, sizeof(word0)); |
| memcpy(&buffer[sizeof(word0)], &word1, sizeof(word1)); |
| } |
| |
| /** |
| * kbasep_tlstream_packet_header_update - update the packet header |
| * @buffer: pointer to the buffer |
| * @data_size: amount of data carried in this packet |
| * |
| * Function updates mutable part of packet header in the given buffer. |
| * Note that value of data_size must not including size of the header. |
| */ |
| static void kbasep_tlstream_packet_header_update( |
| char *buffer, |
| size_t data_size) |
| { |
| u32 word0; |
| u32 word1; |
| |
| KBASE_DEBUG_ASSERT(buffer); |
| CSTD_UNUSED(word0); |
| |
| memcpy(&word1, &buffer[sizeof(word0)], sizeof(word1)); |
| |
| kbasep_tlstream_put_bits( |
| &word1, data_size, |
| PACKET_LENGTH_POS, PACKET_LENGTH_LEN); |
| |
| memcpy(&buffer[sizeof(word0)], &word1, sizeof(word1)); |
| } |
| |
| /** |
| * kbasep_tlstream_packet_number_update - update the packet number |
| * @buffer: pointer to the buffer |
| * @counter: value of packet counter for this packet's stream |
| * |
| * Function updates packet number embedded within the packet placed in the |
| * given buffer. |
| */ |
| static void kbasep_tlstream_packet_number_update(char *buffer, u32 counter) |
| { |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| memcpy(&buffer[PACKET_HEADER_SIZE], &counter, sizeof(counter)); |
| } |
| |
| /** |
| * kbasep_timeline_stream_reset - reset stream |
| * @stream: pointer to the stream structure |
| * |
| * Function discards all pending messages and resets packet counters. |
| */ |
| static void kbasep_timeline_stream_reset(struct tl_stream *stream) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < PACKET_COUNT; i++) { |
| if (stream->numbered) |
| atomic_set( |
| &stream->buffer[i].size, |
| PACKET_HEADER_SIZE + |
| PACKET_NUMBER_SIZE); |
| else |
| atomic_set(&stream->buffer[i].size, PACKET_HEADER_SIZE); |
| } |
| |
| atomic_set(&stream->wbi, 0); |
| atomic_set(&stream->rbi, 0); |
| } |
| |
| /** |
| * kbasep_timeline_stream_init - initialize timeline stream |
| * @stream: pointer to the stream structure |
| * @stream_type: stream type |
| */ |
| static void kbasep_timeline_stream_init( |
| struct tl_stream *stream, |
| enum tl_stream_type stream_type) |
| { |
| unsigned int i; |
| |
| KBASE_DEBUG_ASSERT(stream); |
| KBASE_DEBUG_ASSERT(TL_STREAM_TYPE_COUNT > stream_type); |
| |
| spin_lock_init(&stream->lock); |
| |
| /* All packets carrying tracepoints shall be numbered. */ |
| if (TL_PACKET_TYPE_BODY == tl_stream_cfg[stream_type].pkt_type) |
| stream->numbered = 1; |
| else |
| stream->numbered = 0; |
| |
| for (i = 0; i < PACKET_COUNT; i++) |
| kbasep_tlstream_packet_header_setup( |
| stream->buffer[i].data, |
| tl_stream_cfg[stream_type].pkt_family, |
| tl_stream_cfg[stream_type].pkt_class, |
| tl_stream_cfg[stream_type].pkt_type, |
| tl_stream_cfg[stream_type].stream_id, |
| stream->numbered); |
| |
| kbasep_timeline_stream_reset(tl_stream[stream_type]); |
| } |
| |
| /** |
| * kbasep_timeline_stream_term - terminate timeline stream |
| * @stream: pointer to the stream structure |
| */ |
| static void kbasep_timeline_stream_term(struct tl_stream *stream) |
| { |
| KBASE_DEBUG_ASSERT(stream); |
| } |
| |
| /** |
| * kbasep_tlstream_msgbuf_submit - submit packet to the user space |
| * @stream: pointer to the stream structure |
| * @wb_idx_raw: write buffer index |
| * @wb_size: length of data stored in current buffer |
| * |
| * Function updates currently written buffer with packet header. Then write |
| * index is incremented and buffer is handled to user space. Parameters |
| * of new buffer are returned using provided arguments. |
| * |
| * Return: length of data in new buffer |
| * |
| * Warning: User must update the stream structure with returned value. |
| */ |
| static size_t kbasep_tlstream_msgbuf_submit( |
| struct tl_stream *stream, |
| unsigned int wb_idx_raw, |
| unsigned int wb_size) |
| { |
| unsigned int rb_idx_raw = atomic_read(&stream->rbi); |
| unsigned int wb_idx = wb_idx_raw % PACKET_COUNT; |
| |
| /* Set stream as flushed. */ |
| atomic_set(&stream->autoflush_counter, -1); |
| |
| kbasep_tlstream_packet_header_update( |
| stream->buffer[wb_idx].data, |
| wb_size - PACKET_HEADER_SIZE); |
| |
| if (stream->numbered) |
| kbasep_tlstream_packet_number_update( |
| stream->buffer[wb_idx].data, |
| wb_idx_raw); |
| |
| /* Increasing write buffer index will expose this packet to the reader. |
| * As stream->lock is not taken on reader side we must make sure memory |
| * is updated correctly before this will happen. */ |
| smp_wmb(); |
| wb_idx_raw++; |
| atomic_set(&stream->wbi, wb_idx_raw); |
| |
| /* Inform user that packets are ready for reading. */ |
| wake_up_interruptible(&tl_event_queue); |
| |
| /* Detect and mark overflow in this stream. */ |
| if (PACKET_COUNT == wb_idx_raw - rb_idx_raw) { |
| /* Reader side depends on this increment to correctly handle |
| * overflows. The value shall be updated only if it was not |
| * modified by the reader. The data holding buffer will not be |
| * updated before stream->lock is released, however size of the |
| * buffer will. Make sure this increment is globally visible |
| * before information about selected write buffer size. */ |
| atomic_cmpxchg(&stream->rbi, rb_idx_raw, rb_idx_raw + 1); |
| } |
| |
| wb_size = PACKET_HEADER_SIZE; |
| if (stream->numbered) |
| wb_size += PACKET_NUMBER_SIZE; |
| |
| return wb_size; |
| } |
| |
| /** |
| * kbasep_tlstream_msgbuf_acquire - lock selected stream and reserves buffer |
| * @stream_type: type of the stream that shall be locked |
| * @msg_size: message size |
| * @flags: pointer to store flags passed back on stream release |
| * |
| * Function will lock the stream and reserve the number of bytes requested |
| * in msg_size for the user. |
| * |
| * Return: pointer to the buffer where message can be stored |
| * |
| * Warning: Stream must be released with kbasep_tlstream_msgbuf_release(). |
| * Only atomic operations are allowed while stream is locked |
| * (i.e. do not use any operation that may sleep). |
| */ |
| static char *kbasep_tlstream_msgbuf_acquire( |
| enum tl_stream_type stream_type, |
| size_t msg_size, |
| unsigned long *flags) __acquires(&stream->lock) |
| { |
| struct tl_stream *stream; |
| unsigned int wb_idx_raw; |
| unsigned int wb_idx; |
| size_t wb_size; |
| |
| KBASE_DEBUG_ASSERT(TL_STREAM_TYPE_COUNT > stream_type); |
| KBASE_DEBUG_ASSERT( |
| PACKET_SIZE - PACKET_HEADER_SIZE - PACKET_NUMBER_SIZE >= |
| msg_size); |
| |
| stream = tl_stream[stream_type]; |
| |
| spin_lock_irqsave(&stream->lock, *flags); |
| |
| wb_idx_raw = atomic_read(&stream->wbi); |
| wb_idx = wb_idx_raw % PACKET_COUNT; |
| wb_size = atomic_read(&stream->buffer[wb_idx].size); |
| |
| /* Select next buffer if data will not fit into current one. */ |
| if (PACKET_SIZE < wb_size + msg_size) { |
| wb_size = kbasep_tlstream_msgbuf_submit( |
| stream, wb_idx_raw, wb_size); |
| wb_idx = (wb_idx_raw + 1) % PACKET_COUNT; |
| } |
| |
| /* Reserve space in selected buffer. */ |
| atomic_set(&stream->buffer[wb_idx].size, wb_size + msg_size); |
| |
| #if MALI_UNIT_TEST |
| atomic_add(msg_size, &tlstream_bytes_generated); |
| #endif /* MALI_UNIT_TEST */ |
| |
| return &stream->buffer[wb_idx].data[wb_size]; |
| } |
| |
| /** |
| * kbasep_tlstream_msgbuf_release - unlock selected stream |
| * @stream_type: type of the stream that shall be locked |
| * @flags: value obtained during stream acquire |
| * |
| * Function releases stream that has been previously locked with a call to |
| * kbasep_tlstream_msgbuf_acquire(). |
| */ |
| static void kbasep_tlstream_msgbuf_release( |
| enum tl_stream_type stream_type, |
| unsigned long flags) __releases(&stream->lock) |
| { |
| struct tl_stream *stream; |
| |
| KBASE_DEBUG_ASSERT(TL_STREAM_TYPE_COUNT > stream_type); |
| |
| stream = tl_stream[stream_type]; |
| |
| /* Mark stream as containing unflushed data. */ |
| atomic_set(&stream->autoflush_counter, 0); |
| |
| spin_unlock_irqrestore(&stream->lock, flags); |
| } |
| |
| /*****************************************************************************/ |
| |
| /** |
| * kbasep_tlstream_flush_stream - flush stream |
| * @stype: type of stream to be flushed |
| * |
| * Flush pending data in timeline stream. |
| */ |
| static void kbasep_tlstream_flush_stream(enum tl_stream_type stype) |
| { |
| struct tl_stream *stream = tl_stream[stype]; |
| unsigned long flags; |
| unsigned int wb_idx_raw; |
| unsigned int wb_idx; |
| size_t wb_size; |
| size_t min_size = PACKET_HEADER_SIZE; |
| |
| if (stream->numbered) |
| min_size += PACKET_NUMBER_SIZE; |
| |
| spin_lock_irqsave(&stream->lock, flags); |
| |
| wb_idx_raw = atomic_read(&stream->wbi); |
| wb_idx = wb_idx_raw % PACKET_COUNT; |
| wb_size = atomic_read(&stream->buffer[wb_idx].size); |
| |
| if (wb_size > min_size) { |
| wb_size = kbasep_tlstream_msgbuf_submit( |
| stream, wb_idx_raw, wb_size); |
| wb_idx = (wb_idx_raw + 1) % PACKET_COUNT; |
| atomic_set(&stream->buffer[wb_idx].size, wb_size); |
| } |
| spin_unlock_irqrestore(&stream->lock, flags); |
| } |
| |
| /** |
| * kbasep_tlstream_autoflush_timer_callback - autoflush timer callback |
| * @data: unused |
| * |
| * Timer is executed periodically to check if any of the stream contains |
| * buffer ready to be submitted to user space. |
| */ |
| static void kbasep_tlstream_autoflush_timer_callback(unsigned long data) |
| { |
| enum tl_stream_type stype; |
| int rcode; |
| |
| CSTD_UNUSED(data); |
| |
| for (stype = 0; stype < TL_STREAM_TYPE_COUNT; stype++) { |
| struct tl_stream *stream = tl_stream[stype]; |
| unsigned long flags; |
| unsigned int wb_idx_raw; |
| unsigned int wb_idx; |
| size_t wb_size; |
| size_t min_size = PACKET_HEADER_SIZE; |
| |
| int af_cnt = atomic_read(&stream->autoflush_counter); |
| |
| /* Check if stream contain unflushed data. */ |
| if (0 > af_cnt) |
| continue; |
| |
| /* Check if stream should be flushed now. */ |
| if (af_cnt != atomic_cmpxchg( |
| &stream->autoflush_counter, |
| af_cnt, |
| af_cnt + 1)) |
| continue; |
| if (!af_cnt) |
| continue; |
| |
| /* Autoflush this stream. */ |
| if (stream->numbered) |
| min_size += PACKET_NUMBER_SIZE; |
| |
| spin_lock_irqsave(&stream->lock, flags); |
| |
| wb_idx_raw = atomic_read(&stream->wbi); |
| wb_idx = wb_idx_raw % PACKET_COUNT; |
| wb_size = atomic_read(&stream->buffer[wb_idx].size); |
| |
| if (wb_size > min_size) { |
| wb_size = kbasep_tlstream_msgbuf_submit( |
| stream, wb_idx_raw, wb_size); |
| wb_idx = (wb_idx_raw + 1) % PACKET_COUNT; |
| atomic_set(&stream->buffer[wb_idx].size, |
| wb_size); |
| } |
| spin_unlock_irqrestore(&stream->lock, flags); |
| } |
| |
| if (atomic_read(&autoflush_timer_active)) |
| rcode = mod_timer( |
| &autoflush_timer, |
| jiffies + msecs_to_jiffies(AUTOFLUSH_INTERVAL)); |
| CSTD_UNUSED(rcode); |
| } |
| |
| /** |
| * kbasep_tlstream_packet_pending - check timeline streams for pending packets |
| * @stype: pointer to variable where stream type will be placed |
| * @rb_idx_raw: pointer to variable where read buffer index will be placed |
| * |
| * Function checks all streams for pending packets. It will stop as soon as |
| * packet ready to be submitted to user space is detected. Variables under |
| * pointers, passed as the parameters to this function will be updated with |
| * values pointing to right stream and buffer. |
| * |
| * Return: non-zero if any of timeline streams has at last one packet ready |
| */ |
| static int kbasep_tlstream_packet_pending( |
| enum tl_stream_type *stype, |
| unsigned int *rb_idx_raw) |
| { |
| int pending = 0; |
| |
| KBASE_DEBUG_ASSERT(stype); |
| KBASE_DEBUG_ASSERT(rb_idx_raw); |
| |
| for ( |
| *stype = 0; |
| (*stype < TL_STREAM_TYPE_COUNT) && !pending; |
| (*stype)++) { |
| if (NULL != tl_stream[*stype]) { |
| *rb_idx_raw = atomic_read(&tl_stream[*stype]->rbi); |
| /* Read buffer index may be updated by writer in case of |
| * overflow. Read and write buffer indexes must be |
| * loaded in correct order. */ |
| smp_rmb(); |
| if (atomic_read(&tl_stream[*stype]->wbi) != *rb_idx_raw) |
| pending = 1; |
| } |
| } |
| (*stype)--; |
| |
| return pending; |
| } |
| |
| /** |
| * kbasep_tlstream_read - copy data from streams to buffer provided by user |
| * @filp: pointer to file structure (unused) |
| * @buffer: pointer to the buffer provided by user |
| * @size: maximum amount of data that can be stored in the buffer |
| * @f_pos: pointer to file offset (unused) |
| * |
| * Return: number of bytes stored in the buffer |
| */ |
| static ssize_t kbasep_tlstream_read( |
| struct file *filp, |
| char __user *buffer, |
| size_t size, |
| loff_t *f_pos) |
| { |
| ssize_t copy_len = 0; |
| |
| KBASE_DEBUG_ASSERT(filp); |
| KBASE_DEBUG_ASSERT(f_pos); |
| |
| if (!buffer) |
| return -EINVAL; |
| |
| if ((0 > *f_pos) || (PACKET_SIZE > size)) |
| return -EINVAL; |
| |
| mutex_lock(&tl_reader_lock); |
| |
| while (copy_len < size) { |
| enum tl_stream_type stype; |
| unsigned int rb_idx_raw = 0; |
| unsigned int rb_idx; |
| size_t rb_size; |
| |
| /* If we don't have any data yet, wait for packet to be |
| * submitted. If we already read some packets and there is no |
| * packet pending return back to user. */ |
| if (0 < copy_len) { |
| if (!kbasep_tlstream_packet_pending( |
| &stype, |
| &rb_idx_raw)) |
| break; |
| } else { |
| if (wait_event_interruptible( |
| tl_event_queue, |
| kbasep_tlstream_packet_pending( |
| &stype, |
| &rb_idx_raw))) { |
| copy_len = -ERESTARTSYS; |
| break; |
| } |
| } |
| |
| /* Check if this packet fits into the user buffer. |
| * If so copy its content. */ |
| rb_idx = rb_idx_raw % PACKET_COUNT; |
| rb_size = atomic_read(&tl_stream[stype]->buffer[rb_idx].size); |
| if (rb_size > size - copy_len) |
| break; |
| if (copy_to_user( |
| &buffer[copy_len], |
| tl_stream[stype]->buffer[rb_idx].data, |
| rb_size)) { |
| copy_len = -EFAULT; |
| break; |
| } |
| |
| /* If the rbi still points to the packet we just processed |
| * then there was no overflow so we add the copied size to |
| * copy_len and move rbi on to the next packet |
| */ |
| smp_rmb(); |
| if (atomic_read(&tl_stream[stype]->rbi) == rb_idx_raw) { |
| copy_len += rb_size; |
| atomic_inc(&tl_stream[stype]->rbi); |
| |
| #if MALI_UNIT_TEST |
| atomic_add(rb_size, &tlstream_bytes_collected); |
| #endif /* MALI_UNIT_TEST */ |
| } |
| } |
| |
| mutex_unlock(&tl_reader_lock); |
| |
| return copy_len; |
| } |
| |
| /** |
| * kbasep_tlstream_poll - poll timeline stream for packets |
| * @filp: pointer to file structure |
| * @wait: pointer to poll table |
| * Return: POLLIN if data can be read without blocking, otherwise zero |
| */ |
| static unsigned int kbasep_tlstream_poll(struct file *filp, poll_table *wait) |
| { |
| enum tl_stream_type stream_type; |
| unsigned int rb_idx; |
| |
| KBASE_DEBUG_ASSERT(filp); |
| KBASE_DEBUG_ASSERT(wait); |
| |
| poll_wait(filp, &tl_event_queue, wait); |
| if (kbasep_tlstream_packet_pending(&stream_type, &rb_idx)) |
| return POLLIN; |
| return 0; |
| } |
| |
| /** |
| * kbasep_tlstream_release - release timeline stream descriptor |
| * @inode: pointer to inode structure |
| * @filp: pointer to file structure |
| * |
| * Return always return zero |
| */ |
| static int kbasep_tlstream_release(struct inode *inode, struct file *filp) |
| { |
| KBASE_DEBUG_ASSERT(inode); |
| KBASE_DEBUG_ASSERT(filp); |
| CSTD_UNUSED(inode); |
| CSTD_UNUSED(filp); |
| |
| /* Stop autoflush timer before releasing access to streams. */ |
| atomic_set(&autoflush_timer_active, 0); |
| del_timer_sync(&autoflush_timer); |
| |
| atomic_set(&kbase_tlstream_enabled, 0); |
| return 0; |
| } |
| |
| /** |
| * kbasep_tlstream_timeline_header - prepare timeline header stream packet |
| * @stream_type: type of the stream that will carry header data |
| * @tp_desc: pointer to array with tracepoint descriptors |
| * @tp_count: number of descriptors in the given array |
| * |
| * Functions fills in information about tracepoints stored in body stream |
| * associated with this header stream. |
| */ |
| static void kbasep_tlstream_timeline_header( |
| enum tl_stream_type stream_type, |
| const struct tp_desc *tp_desc, |
| u32 tp_count) |
| { |
| const u8 tv = SWTRACE_VERSION; /* protocol version */ |
| const u8 ps = sizeof(void *); /* pointer size */ |
| size_t msg_size = sizeof(tv) + sizeof(ps) + sizeof(tp_count); |
| char *buffer; |
| size_t pos = 0; |
| unsigned long flags; |
| unsigned int i; |
| |
| KBASE_DEBUG_ASSERT(TL_STREAM_TYPE_COUNT > stream_type); |
| KBASE_DEBUG_ASSERT(tp_desc); |
| |
| /* Calculate the size of the timeline message. */ |
| for (i = 0; i < tp_count; i++) { |
| msg_size += sizeof(tp_desc[i].id); |
| msg_size += |
| strnlen(tp_desc[i].id_str, STRLEN_MAX) + |
| sizeof(char) + sizeof(u32); |
| msg_size += |
| strnlen(tp_desc[i].name, STRLEN_MAX) + |
| sizeof(char) + sizeof(u32); |
| msg_size += |
| strnlen(tp_desc[i].arg_types, STRLEN_MAX) + |
| sizeof(char) + sizeof(u32); |
| msg_size += |
| strnlen(tp_desc[i].arg_names, STRLEN_MAX) + |
| sizeof(char) + sizeof(u32); |
| } |
| |
| KBASE_DEBUG_ASSERT(PACKET_SIZE - PACKET_HEADER_SIZE >= msg_size); |
| |
| buffer = kbasep_tlstream_msgbuf_acquire(stream_type, msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &tv, sizeof(tv)); |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &ps, sizeof(ps)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &tp_count, sizeof(tp_count)); |
| |
| for (i = 0; i < tp_count; i++) { |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, |
| &tp_desc[i].id, sizeof(tp_desc[i].id)); |
| pos = kbasep_tlstream_write_string( |
| buffer, pos, |
| tp_desc[i].id_str, msg_size - pos); |
| pos = kbasep_tlstream_write_string( |
| buffer, pos, |
| tp_desc[i].name, msg_size - pos); |
| pos = kbasep_tlstream_write_string( |
| buffer, pos, |
| tp_desc[i].arg_types, msg_size - pos); |
| pos = kbasep_tlstream_write_string( |
| buffer, pos, |
| tp_desc[i].arg_names, msg_size - pos); |
| } |
| |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(stream_type, flags); |
| |
| /* We don't expect any more data to be read in this stream. |
| * As header stream must be read before its associated body stream, |
| * make this packet visible to the user straightaway. */ |
| kbasep_tlstream_flush_stream(stream_type); |
| } |
| |
| /*****************************************************************************/ |
| |
| int kbase_tlstream_init(void) |
| { |
| enum tl_stream_type i; |
| |
| /* Prepare stream structures. */ |
| for (i = 0; i < TL_STREAM_TYPE_COUNT; i++) { |
| tl_stream[i] = kmalloc(sizeof(**tl_stream), GFP_KERNEL); |
| if (!tl_stream[i]) |
| break; |
| kbasep_timeline_stream_init(tl_stream[i], i); |
| } |
| if (TL_STREAM_TYPE_COUNT > i) { |
| for (; i > 0; i--) { |
| kbasep_timeline_stream_term(tl_stream[i - 1]); |
| kfree(tl_stream[i - 1]); |
| } |
| return -ENOMEM; |
| } |
| |
| /* Initialize autoflush timer. */ |
| atomic_set(&autoflush_timer_active, 0); |
| setup_timer(&autoflush_timer, |
| kbasep_tlstream_autoflush_timer_callback, |
| 0); |
| |
| return 0; |
| } |
| |
| void kbase_tlstream_term(void) |
| { |
| enum tl_stream_type i; |
| |
| for (i = 0; i < TL_STREAM_TYPE_COUNT; i++) { |
| kbasep_timeline_stream_term(tl_stream[i]); |
| kfree(tl_stream[i]); |
| } |
| } |
| |
| int kbase_tlstream_acquire(struct kbase_context *kctx, int *fd, u32 flags) |
| { |
| u32 tlstream_enabled = TLSTREAM_ENABLED | flags; |
| |
| if (0 == atomic_cmpxchg(&kbase_tlstream_enabled, 0, tlstream_enabled)) { |
| int rcode; |
| |
| *fd = anon_inode_getfd( |
| "[mali_tlstream]", |
| &kbasep_tlstream_fops, |
| kctx, |
| O_RDONLY | O_CLOEXEC); |
| if (0 > *fd) { |
| atomic_set(&kbase_tlstream_enabled, 0); |
| return *fd; |
| } |
| |
| /* Reset and initialize header streams. */ |
| kbasep_timeline_stream_reset( |
| tl_stream[TL_STREAM_TYPE_OBJ_HEADER]); |
| kbasep_timeline_stream_reset( |
| tl_stream[TL_STREAM_TYPE_OBJ_SUMMARY]); |
| kbasep_timeline_stream_reset( |
| tl_stream[TL_STREAM_TYPE_AUX_HEADER]); |
| kbasep_tlstream_timeline_header( |
| TL_STREAM_TYPE_OBJ_HEADER, |
| tp_desc_obj, |
| ARRAY_SIZE(tp_desc_obj)); |
| kbasep_tlstream_timeline_header( |
| TL_STREAM_TYPE_AUX_HEADER, |
| tp_desc_aux, |
| ARRAY_SIZE(tp_desc_aux)); |
| |
| /* Start autoflush timer. */ |
| atomic_set(&autoflush_timer_active, 1); |
| rcode = mod_timer( |
| &autoflush_timer, |
| jiffies + msecs_to_jiffies(AUTOFLUSH_INTERVAL)); |
| CSTD_UNUSED(rcode); |
| |
| } else { |
| *fd = -EBUSY; |
| } |
| |
| return 0; |
| } |
| |
| void kbase_tlstream_flush_streams(void) |
| { |
| enum tl_stream_type stype; |
| |
| for (stype = 0; stype < TL_STREAM_TYPE_COUNT; stype++) |
| kbasep_tlstream_flush_stream(stype); |
| } |
| |
| void kbase_tlstream_reset_body_streams(void) |
| { |
| kbasep_timeline_stream_reset( |
| tl_stream[TL_STREAM_TYPE_OBJ]); |
| kbasep_timeline_stream_reset( |
| tl_stream[TL_STREAM_TYPE_AUX]); |
| } |
| |
| #if MALI_UNIT_TEST |
| void kbase_tlstream_stats(u32 *bytes_collected, u32 *bytes_generated) |
| { |
| KBASE_DEBUG_ASSERT(bytes_collected); |
| KBASE_DEBUG_ASSERT(bytes_generated); |
| *bytes_collected = atomic_read(&tlstream_bytes_collected); |
| *bytes_generated = atomic_read(&tlstream_bytes_generated); |
| } |
| #endif /* MALI_UNIT_TEST */ |
| |
| /*****************************************************************************/ |
| |
| void __kbase_tlstream_tl_summary_new_ctx(void *context, u32 nr, u32 tgid) |
| { |
| const u32 msg_id = KBASE_TL_NEW_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(context) + sizeof(nr) + |
| sizeof(tgid); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &nr, sizeof(nr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &tgid, sizeof(tgid)); |
| |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| void __kbase_tlstream_tl_summary_new_gpu(void *gpu, u32 id, u32 core_count) |
| { |
| const u32 msg_id = KBASE_TL_NEW_GPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(gpu) + sizeof(id) + |
| sizeof(core_count); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &gpu, sizeof(gpu)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &id, sizeof(id)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &core_count, sizeof(core_count)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| void __kbase_tlstream_tl_summary_new_lpu(void *lpu, u32 nr, u32 fn) |
| { |
| const u32 msg_id = KBASE_TL_NEW_LPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(lpu) + sizeof(nr) + |
| sizeof(fn); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &nr, sizeof(nr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &fn, sizeof(fn)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| void __kbase_tlstream_tl_summary_lifelink_lpu_gpu(void *lpu, void *gpu) |
| { |
| const u32 msg_id = KBASE_TL_LIFELINK_LPU_GPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(lpu) + sizeof(gpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &gpu, sizeof(gpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| void __kbase_tlstream_tl_summary_new_as(void *as, u32 nr) |
| { |
| const u32 msg_id = KBASE_TL_NEW_AS; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(as) + sizeof(nr); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &nr, sizeof(nr)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| void __kbase_tlstream_tl_summary_lifelink_as_gpu(void *as, void *gpu) |
| { |
| const u32 msg_id = KBASE_TL_LIFELINK_AS_GPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(as) + sizeof(gpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ_SUMMARY, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &gpu, sizeof(gpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ_SUMMARY, flags); |
| } |
| |
| /*****************************************************************************/ |
| |
| void __kbase_tlstream_tl_new_ctx(void *context, u32 nr, u32 tgid) |
| { |
| const u32 msg_id = KBASE_TL_NEW_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(context) + sizeof(nr) + |
| sizeof(tgid); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &nr, sizeof(nr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &tgid, sizeof(tgid)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_new_atom(void *atom, u32 nr) |
| { |
| const u32 msg_id = KBASE_TL_NEW_ATOM; |
| const size_t msg_size = sizeof(msg_id) + sizeof(u64) + sizeof(atom) + |
| sizeof(nr); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &nr, sizeof(nr)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_del_ctx(void *context) |
| { |
| const u32 msg_id = KBASE_TL_DEL_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(context); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_del_atom(void *atom) |
| { |
| const u32 msg_id = KBASE_TL_DEL_ATOM; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ret_ctx_lpu(void *context, void *lpu) |
| { |
| const u32 msg_id = KBASE_TL_RET_CTX_LPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(context) + sizeof(lpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ret_atom_ctx(void *atom, void *context) |
| { |
| const u32 msg_id = KBASE_TL_RET_ATOM_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(context); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ret_atom_lpu( |
| void *atom, void *lpu, const char *attrib_match_list) |
| { |
| const u32 msg_id = KBASE_TL_RET_ATOM_LPU; |
| const size_t msg_s0 = sizeof(u32) + sizeof(char) + |
| strnlen(attrib_match_list, STRLEN_MAX); |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + |
| sizeof(atom) + sizeof(lpu) + msg_s0; |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| pos = kbasep_tlstream_write_string( |
| buffer, pos, attrib_match_list, msg_s0); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_nret_ctx_lpu(void *context, void *lpu) |
| { |
| const u32 msg_id = KBASE_TL_NRET_CTX_LPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(context) + sizeof(lpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_nret_atom_ctx(void *atom, void *context) |
| { |
| const u32 msg_id = KBASE_TL_NRET_ATOM_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(context); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &context, sizeof(context)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_dep_atom_atom(void *atom1, void *atom2) |
| { |
| const u32 msg_id = KBASE_TL_DEP_ATOM_ATOM; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom1) + sizeof(atom2); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom1, sizeof(atom1)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom2, sizeof(atom2)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ndep_atom_atom(void *atom1, void *atom2) |
| { |
| const u32 msg_id = KBASE_TL_NDEP_ATOM_ATOM; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom1) + sizeof(atom2); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom1, sizeof(atom1)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom2, sizeof(atom2)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_rdep_atom_atom(void *atom1, void *atom2) |
| { |
| const u32 msg_id = KBASE_TL_RDEP_ATOM_ATOM; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom1) + sizeof(atom2); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom1, sizeof(atom1)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom2, sizeof(atom2)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_nret_atom_lpu(void *atom, void *lpu) |
| { |
| const u32 msg_id = KBASE_TL_NRET_ATOM_LPU; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(lpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ret_as_ctx(void *as, void *ctx) |
| { |
| const u32 msg_id = KBASE_TL_RET_AS_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(as) + sizeof(ctx); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &ctx, sizeof(ctx)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_nret_as_ctx(void *as, void *ctx) |
| { |
| const u32 msg_id = KBASE_TL_NRET_AS_CTX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(as) + sizeof(ctx); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &ctx, sizeof(ctx)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_ret_atom_as(void *atom, void *as) |
| { |
| const u32 msg_id = KBASE_TL_RET_ATOM_AS; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(as); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_nret_atom_as(void *atom, void *as) |
| { |
| const u32 msg_id = KBASE_TL_NRET_ATOM_AS; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(as); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_attrib_atom_config( |
| void *atom, u64 jd, u64 affinity, u32 config) |
| { |
| const u32 msg_id = KBASE_TL_ATTRIB_ATOM_CONFIG; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + |
| sizeof(jd) + sizeof(affinity) + sizeof(config); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &jd, sizeof(jd)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &affinity, sizeof(affinity)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &config, sizeof(config)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_attrib_atom_priority(void *atom, u32 prio) |
| { |
| const u32 msg_id = KBASE_TL_ATTRIB_ATOM_PRIORITY; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(prio); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &prio, sizeof(prio)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_attrib_atom_state(void *atom, u32 state) |
| { |
| const u32 msg_id = KBASE_TL_ATTRIB_ATOM_STATE; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom) + sizeof(state); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &state, sizeof(state)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_attrib_atom_priority_change(void *atom) |
| { |
| const u32 msg_id = KBASE_TL_ATTRIB_ATOM_PRIORITY_CHANGE; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_attrib_as_config( |
| void *as, u64 transtab, u64 memattr, u64 transcfg) |
| { |
| const u32 msg_id = KBASE_TL_ATTRIB_AS_CONFIG; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(as) + |
| sizeof(transtab) + sizeof(memattr) + sizeof(transcfg); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &as, sizeof(as)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &transtab, sizeof(transtab)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &memattr, sizeof(memattr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &transcfg, sizeof(transcfg)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_event_lpu_softstop(void *lpu) |
| { |
| const u32 msg_id = KBASE_TL_EVENT_LPU_SOFTSTOP; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(lpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &lpu, sizeof(lpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_event_atom_softstop_ex(void *atom) |
| { |
| const u32 msg_id = KBASE_TL_EVENT_ATOM_SOFTSTOP_EX; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_tl_event_atom_softstop_issue(void *atom) |
| { |
| const u32 msg_id = KBASE_TL_EVENT_ATOM_SOFTSTOP_ISSUE; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(atom); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &atom, sizeof(atom)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| void __kbase_tlstream_jd_gpu_soft_reset(void *gpu) |
| { |
| const u32 msg_id = KBASE_JD_GPU_SOFT_RESET; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(gpu); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_OBJ, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &gpu, sizeof(gpu)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_OBJ, flags); |
| } |
| |
| /*****************************************************************************/ |
| |
| void __kbase_tlstream_aux_pm_state(u32 core_type, u64 state) |
| { |
| const u32 msg_id = KBASE_AUX_PM_STATE; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(core_type) + |
| sizeof(state); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_AUX, |
| msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &core_type, sizeof(core_type)); |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &state, sizeof(state)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_AUX, flags); |
| } |
| |
| void __kbase_tlstream_aux_pagefault(u32 ctx_nr, u64 page_count_change) |
| { |
| const u32 msg_id = KBASE_AUX_PAGEFAULT; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(ctx_nr) + |
| sizeof(page_count_change); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_AUX, msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &ctx_nr, sizeof(ctx_nr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, |
| &page_count_change, sizeof(page_count_change)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_AUX, flags); |
| } |
| |
| void __kbase_tlstream_aux_pagesalloc(u32 ctx_nr, u64 page_count) |
| { |
| const u32 msg_id = KBASE_AUX_PAGESALLOC; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(ctx_nr) + |
| sizeof(page_count); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_AUX, msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &ctx_nr, sizeof(ctx_nr)); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &page_count, sizeof(page_count)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_AUX, flags); |
| } |
| |
| void __kbase_tlstream_aux_devfreq_target(u64 target_freq) |
| { |
| const u32 msg_id = KBASE_AUX_DEVFREQ_TARGET; |
| const size_t msg_size = |
| sizeof(msg_id) + sizeof(u64) + sizeof(target_freq); |
| unsigned long flags; |
| char *buffer; |
| size_t pos = 0; |
| |
| buffer = kbasep_tlstream_msgbuf_acquire( |
| TL_STREAM_TYPE_AUX, msg_size, &flags); |
| KBASE_DEBUG_ASSERT(buffer); |
| |
| pos = kbasep_tlstream_write_bytes(buffer, pos, &msg_id, sizeof(msg_id)); |
| pos = kbasep_tlstream_write_timestamp(buffer, pos); |
| pos = kbasep_tlstream_write_bytes( |
| buffer, pos, &target_freq, sizeof(target_freq)); |
| KBASE_DEBUG_ASSERT(msg_size == pos); |
| |
| kbasep_tlstream_msgbuf_release(TL_STREAM_TYPE_AUX, flags); |
| } |
| |