286 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
 | |
| /*
 | |
|  *
 | |
|  * (C) COPYRIGHT 2015-2023 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 license.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, you can access it online at
 | |
|  * http://www.gnu.org/licenses/gpl-2.0.html.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "mali_kbase_tlstream.h"
 | |
| #include "mali_kbase_tl_serialize.h"
 | |
| #include "mali_kbase_mipe_proto.h"
 | |
| 
 | |
| /**
 | |
|  * kbasep_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_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 words[2] = {
 | |
| 		MIPE_PACKET_HEADER_W0(pkt_family, pkt_class, pkt_type, stream_id),
 | |
| 		MIPE_PACKET_HEADER_W1(0, !!numbered),
 | |
| 	};
 | |
| 	memcpy(buffer, words, sizeof(words));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * kbasep_packet_header_update - update the packet header
 | |
|  * @buffer:    pointer to the buffer
 | |
|  * @data_size: amount of data carried in this packet
 | |
|  * @numbered:   non-zero if the stream is numbered
 | |
|  *
 | |
|  * Function updates mutable part of packet header in the given buffer.
 | |
|  * Note that value of data_size must not include size of the header.
 | |
|  */
 | |
| static void kbasep_packet_header_update(char *buffer, size_t data_size, int numbered)
 | |
| {
 | |
| 	u32 word1 = MIPE_PACKET_HEADER_W1((u32)data_size, !!numbered);
 | |
| 
 | |
| 	KBASE_DEBUG_ASSERT(buffer);
 | |
| 
 | |
| 	/* we copy the contents of word1 to its respective position in the buffer */
 | |
| 	memcpy(&buffer[sizeof(u32)], &word1, sizeof(word1));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * kbasep_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_packet_number_update(char *buffer, u32 counter)
 | |
| {
 | |
| 	KBASE_DEBUG_ASSERT(buffer);
 | |
| 
 | |
| 	memcpy(&buffer[PACKET_HEADER_SIZE], &counter, sizeof(counter));
 | |
| }
 | |
| 
 | |
| void kbase_tlstream_reset(struct kbase_tlstream *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);
 | |
| }
 | |
| 
 | |
| /* Configuration of timeline streams generated by kernel. */
 | |
| static const struct {
 | |
| 	enum tl_packet_family pkt_family;
 | |
| 	enum tl_packet_class pkt_class;
 | |
| 	enum tl_packet_type pkt_type;
 | |
| 	enum tl_stream_id stream_id;
 | |
| } tl_stream_cfg[TL_STREAM_TYPE_COUNT] = {
 | |
| 	{
 | |
| 		TL_PACKET_FAMILY_TL,
 | |
| 		TL_PACKET_CLASS_OBJ,
 | |
| 		TL_PACKET_TYPE_SUMMARY,
 | |
| 		TL_STREAM_ID_KERNEL,
 | |
| 	},
 | |
| 	{
 | |
| 		TL_PACKET_FAMILY_TL,
 | |
| 		TL_PACKET_CLASS_OBJ,
 | |
| 		TL_PACKET_TYPE_BODY,
 | |
| 		TL_STREAM_ID_KERNEL,
 | |
| 	},
 | |
| 	{
 | |
| 		TL_PACKET_FAMILY_TL,
 | |
| 		TL_PACKET_CLASS_AUX,
 | |
| 		TL_PACKET_TYPE_BODY,
 | |
| 		TL_STREAM_ID_KERNEL,
 | |
| 	},
 | |
| #if MALI_USE_CSF
 | |
| 	{
 | |
| 		TL_PACKET_FAMILY_TL,
 | |
| 		TL_PACKET_CLASS_OBJ,
 | |
| 		TL_PACKET_TYPE_BODY,
 | |
| 		TL_STREAM_ID_CSFFW,
 | |
| 	},
 | |
| #endif
 | |
| };
 | |
| 
 | |
| void kbase_tlstream_init(struct kbase_tlstream *stream, enum tl_stream_type stream_type,
 | |
| 			 wait_queue_head_t *ready_read)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 
 | |
| 	KBASE_DEBUG_ASSERT(stream);
 | |
| 	KBASE_DEBUG_ASSERT(stream_type < TL_STREAM_TYPE_COUNT);
 | |
| 
 | |
| 	spin_lock_init(&stream->lock);
 | |
| 
 | |
| 	/* All packets carrying tracepoints shall be numbered. */
 | |
| 	if (tl_stream_cfg[stream_type].pkt_type == TL_PACKET_TYPE_BODY)
 | |
| 		stream->numbered = 1;
 | |
| 	else
 | |
| 		stream->numbered = 0;
 | |
| 
 | |
| 	for (i = 0; i < PACKET_COUNT; i++)
 | |
| 		kbasep_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);
 | |
| 
 | |
| #if MALI_UNIT_TEST
 | |
| 	atomic_set(&stream->bytes_generated, 0);
 | |
| #endif
 | |
| 	stream->ready_read = ready_read;
 | |
| 
 | |
| 	kbase_tlstream_reset(stream);
 | |
| }
 | |
| 
 | |
| void kbase_tlstream_term(struct kbase_tlstream *stream)
 | |
| {
 | |
| 	KBASE_DEBUG_ASSERT(stream);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * kbasep_tlstream_msgbuf_submit - submit packet to user space
 | |
|  * @stream:     Pointer to the stream structure
 | |
|  * @wb_idx_raw: Write buffer index
 | |
|  * @wb_size:    Length of data stored in the current buffer
 | |
|  *
 | |
|  * Updates currently written buffer with the packet header.
 | |
|  * Then write index is incremented and the buffer is handed to user space.
 | |
|  * Parameters of the new buffer are returned using provided arguments.
 | |
|  *
 | |
|  * Return: length of data in the new buffer
 | |
|  *
 | |
|  * Warning: the user must update the stream structure with returned value.
 | |
|  */
 | |
| static size_t kbasep_tlstream_msgbuf_submit(struct kbase_tlstream *stream, unsigned int wb_idx_raw,
 | |
| 					    unsigned int wb_size)
 | |
| {
 | |
| 	unsigned int wb_idx = wb_idx_raw % PACKET_COUNT;
 | |
| 
 | |
| 	/* Set stream as flushed. */
 | |
| 	atomic_set(&stream->autoflush_counter, -1);
 | |
| 
 | |
| 	kbasep_packet_header_update(stream->buffer[wb_idx].data, wb_size - PACKET_HEADER_SIZE,
 | |
| 				    stream->numbered);
 | |
| 
 | |
| 	if (stream->numbered)
 | |
| 		kbasep_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();
 | |
| 	atomic_inc(&stream->wbi);
 | |
| 
 | |
| 	/* Inform user that packets are ready for reading. */
 | |
| 	wake_up_interruptible(stream->ready_read);
 | |
| 
 | |
| 	wb_size = PACKET_HEADER_SIZE;
 | |
| 	if (stream->numbered)
 | |
| 		wb_size += PACKET_NUMBER_SIZE;
 | |
| 
 | |
| 	return wb_size;
 | |
| }
 | |
| 
 | |
| char *kbase_tlstream_msgbuf_acquire(struct kbase_tlstream *stream, size_t msg_size,
 | |
| 				    unsigned long *flags) __acquires(&stream->lock)
 | |
| {
 | |
| 	unsigned int wb_idx_raw;
 | |
| 	unsigned int wb_idx;
 | |
| 	size_t wb_size;
 | |
| 
 | |
| 	KBASE_DEBUG_ASSERT(PACKET_SIZE - PACKET_HEADER_SIZE - PACKET_NUMBER_SIZE >= msg_size);
 | |
| 
 | |
| 	spin_lock_irqsave(&stream->lock, *flags);
 | |
| 
 | |
| 	wb_idx_raw = (unsigned int)atomic_read(&stream->wbi);
 | |
| 	wb_idx = wb_idx_raw % PACKET_COUNT;
 | |
| 	wb_size = (size_t)atomic_read(&stream->buffer[wb_idx].size);
 | |
| 
 | |
| 	/* Select next buffer if data will not fit into current one. */
 | |
| 	if (wb_size + msg_size > PACKET_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, &stream->bytes_generated);
 | |
| #endif /* MALI_UNIT_TEST */
 | |
| 
 | |
| 	return &stream->buffer[wb_idx].data[wb_size];
 | |
| }
 | |
| 
 | |
| void kbase_tlstream_msgbuf_release(struct kbase_tlstream *stream, unsigned long flags)
 | |
| 	__releases(&stream->lock)
 | |
| {
 | |
| 	/* Mark stream as containing unflushed data. */
 | |
| 	atomic_set(&stream->autoflush_counter, 0);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&stream->lock, flags);
 | |
| }
 | |
| 
 | |
| size_t kbase_tlstream_flush_stream(struct kbase_tlstream *stream)
 | |
| {
 | |
| 	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 = (unsigned int)atomic_read(&stream->wbi);
 | |
| 	wb_idx = wb_idx_raw % PACKET_COUNT;
 | |
| 	wb_size = (size_t)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);
 | |
| 	} else {
 | |
| 		/* we return that there is no bytes to be read.*/
 | |
| 		/* Timeline io fsync will use this info the decide whether
 | |
| 		 * fsync should return an error
 | |
| 		 */
 | |
| 		wb_size = 0;
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&stream->lock, flags);
 | |
| 	return wb_size;
 | |
| }
 |