firehose_buffer_private.h   [plain text]


/*
 * Copyright (c) 2015 Apple Inc. All rights reserved.
 *
 * @APPLE_APACHE_LICENSE_HEADER_START@
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @APPLE_APACHE_LICENSE_HEADER_END@
 */

#ifndef __FIREHOSE_BUFFER_PRIVATE__
#define __FIREHOSE_BUFFER_PRIVATE__

#if OS_FIREHOSE_SPI
#ifdef KERNEL
#include <stdint.h>
#else
#include <os/base.h>
#include <os/base_private.h>
#include <dispatch/dispatch.h>
#endif

#define OS_FIREHOSE_SPI_VERSION 20160318

/*!
 * @group Firehose SPI
 * SPI intended for logd only
 * Layout of structs is subject to change without notice
 */

#define FIREHOSE_BUFFER_CHUNK_SIZE				4096ul
#define FIREHOSE_BUFFER_LIBTRACE_HEADER_SIZE	2048ul
#define FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT		16

typedef union {
	uint64_t fbc_atomic_pos;
#define FIREHOSE_BUFFER_POS_ENTRY_OFFS_INC		(1ULL <<  0)
#define FIREHOSE_BUFFER_POS_PRIVATE_OFFS_INC	(1ULL << 16)
#define FIREHOSE_BUFFER_POS_REFCNT_INC			(1ULL << 32)
#define FIREHOSE_BUFFER_POS_FULL_BIT			(1ULL << 56)
#define FIREHOSE_BUFFER_POS_USABLE_FOR_STREAM(pos, stream) \
		((((pos).fbc_atomic_pos >> 48) & 0x1ff) == (uint16_t)stream)
	struct {
		uint16_t fbc_next_entry_offs;
		uint16_t fbc_private_offs;
		uint8_t  fbc_refcnt;
		uint8_t  fbc_qos_bits;
		uint8_t  fbc_stream;
		uint8_t  fbc_flag_full : 1;
		uint8_t  fbc_flag_io : 1;
		uint8_t  _fbc_flag_unused : 6;
	};
} firehose_buffer_pos_u;

typedef struct firehose_buffer_chunk_s {
	uint8_t  fbc_start[0];
	firehose_buffer_pos_u volatile fbc_pos;
	uint64_t fbc_timestamp;
	uint8_t  fbc_data[FIREHOSE_BUFFER_CHUNK_SIZE
			- sizeof(firehose_buffer_pos_u)
			- sizeof(uint64_t)];
} *firehose_buffer_chunk_t;

typedef struct firehose_buffer_range_s {
	uint16_t fbr_offset; // offset from the start of the buffer
	uint16_t fbr_length;
} *firehose_buffer_range_t;

#ifdef KERNEL

// implemented by the kernel
extern void __firehose_buffer_push_to_logd(firehose_buffer_t fb, bool for_io);
extern void __firehose_critical_region_enter(void);
extern void __firehose_critical_region_leave(void);
extern void __firehose_allocate(vm_offset_t *addr, vm_size_t size);

// exported for the kernel
firehose_tracepoint_t
__firehose_buffer_tracepoint_reserve(uint64_t stamp, firehose_stream_t stream,
		uint16_t pubsize, uint16_t privsize, uint8_t **privptr);

firehose_tracepoint_t
__firehose_buffer_tracepoint_reserve_with_chunk(firehose_buffer_chunk_t fbc,
		uint64_t stamp, firehose_stream_t stream,
		uint16_t pubsize, uint16_t privsize, uint8_t **privptr);

void
__firehose_buffer_tracepoint_flush(firehose_tracepoint_t vat,
		firehose_tracepoint_id_u vatid);

void
__firehose_buffer_tracepoint_flush_chunk(firehose_buffer_chunk_t fbc,
		firehose_tracepoint_t vat, firehose_tracepoint_id_u vatid);

firehose_buffer_t
__firehose_buffer_create(size_t *size);

void
__firehose_merge_updates(firehose_push_reply_t update);

#else

#define __firehose_critical_region_enter()
#define __firehose_critical_region_leave()

OS_EXPORT
const uint32_t _firehose_spi_version;

OS_ALWAYS_INLINE
static inline const uint8_t *
_firehose_tracepoint_reader_init(firehose_buffer_chunk_t fbc,
		const uint8_t **endptr)
{
	const uint8_t *start = fbc->fbc_data;
	const uint8_t *end = fbc->fbc_start + fbc->fbc_pos.fbc_next_entry_offs;

	if (end > fbc->fbc_start + FIREHOSE_BUFFER_CHUNK_SIZE) {
		end = start;
	}
	*endptr = end;
	return start;
}

OS_ALWAYS_INLINE
static inline firehose_tracepoint_t
_firehose_tracepoint_reader_next(const uint8_t **ptr, const uint8_t *end)
{
	const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
	firehose_tracepoint_t ft;

	do {
		ft = (firehose_tracepoint_t)*ptr;
		if (ft->ft_data >= end) {
			// reached the end
			return NULL;
		}
		if (!ft->ft_length) {
			// tracepoint write didn't even start
			return NULL;
		}
		if (ft->ft_length > end - ft->ft_data) {
			// invalid length
			return NULL;
		}
		*ptr += roundup(ft_size + ft->ft_length, 8);
		// test whether write of the tracepoint was finished
	} while (os_unlikely(ft->ft_id.ftid_value == 0));

	return ft;
}

#define firehose_tracepoint_foreach(ft, fbc) \
		for (const uint8_t *end, *p = _firehose_tracepoint_reader_init(fbc, &end); \
				((ft) = _firehose_tracepoint_reader_next(&p, end)); )

OS_ALWAYS_INLINE
static inline bool
firehose_buffer_range_validate(firehose_buffer_chunk_t fbc,
		firehose_tracepoint_t ft, firehose_buffer_range_t range)
{
	if (range->fbr_offset + range->fbr_length > FIREHOSE_BUFFER_CHUNK_SIZE) {
		return false;
	}
	if (fbc->fbc_start + range->fbr_offset < ft->ft_data + ft->ft_length) {
		return false;
	}
	return true;
}

#endif // !KERNEL

#endif // OS_FIREHOSE_SPI

#endif // __FIREHOSE_BUFFER_PRIVATE__