/* $OpenBSD: kstat.h,v 1.5 2024/03/26 00:53:51 dlg Exp $ */

/*
 * Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _SYS_KSTAT_H_
#define _SYS_KSTAT_H_

#include <sys/ioccom.h>

#define KSTAT_STRLEN		32

#define KSTAT_T_RAW		0
#define KSTAT_T_KV		1
#define KSTAT_T_COUNTERS	2

struct kstat_req {
	unsigned int		 ks_rflags;
#define KSTATIOC_F_IGNVER		(1 << 0)
	/* the current version of the kstat subsystem */
	unsigned int		 ks_version;

	uint64_t		 ks_id;

	char			 ks_provider[KSTAT_STRLEN];
	unsigned int		 ks_instance;
	char			 ks_name[KSTAT_STRLEN];
	unsigned int		 ks_unit;

	struct timespec		 ks_created;
	struct timespec		 ks_updated;
	struct timespec		 ks_interval;
	unsigned int		 ks_type;
	unsigned int		 ks_state;

	void			*ks_data;
	size_t			 ks_datalen;
	unsigned int		 ks_dataver;
};

/* ioctls */

#define KSTATIOC_VERSION	_IOR('k', 1, unsigned int)
#define KSTATIOC_FIND_ID	_IOWR('k', 2, struct kstat_req)
#define KSTATIOC_NFIND_ID	_IOWR('k', 3, struct kstat_req)
#define KSTATIOC_FIND_PROVIDER	_IOWR('k', 4, struct kstat_req)
#define KSTATIOC_NFIND_PROVIDER	_IOWR('k', 5, struct kstat_req)
#define KSTATIOC_FIND_NAME	_IOWR('k', 6, struct kstat_req)
#define KSTATIOC_NFIND_NAME	_IOWR('k', 7, struct kstat_req)

/* named data */

#define KSTAT_KV_NAMELEN	16
#define KSTAT_KV_ALIGN		sizeof(uint64_t)

enum kstat_kv_type {
	KSTAT_KV_T_NULL,
	KSTAT_KV_T_BOOL,
	KSTAT_KV_T_COUNTER64,
	KSTAT_KV_T_COUNTER32,
	KSTAT_KV_T_UINT64,
	KSTAT_KV_T_INT64,
	KSTAT_KV_T_UINT32,
	KSTAT_KV_T_INT32,
	KSTAT_KV_T_ISTR,	/* inline string */
	KSTAT_KV_T_STR,		/* trailing string */
	KSTAT_KV_T_BYTES,	/* trailing bytes */
	KSTAT_KV_T_TEMP,	/* temperature (uK) */
	KSTAT_KV_T_COUNTER16,
	KSTAT_KV_T_UINT16,
	KSTAT_KV_T_INT16,
	KSTAT_KV_T_FREQ,	/* frequency (Hz) */
	KSTAT_KV_T_VOLTS_DC,	/* voltage (uV DC) */
	KSTAT_KV_T_VOLTS_AC,	/* voltage (uV AC) */
	KSTAT_KV_T_AMPS,	/* current (uA) */
	KSTAT_KV_T_WATTS,	/* power (uW) */
};

/* units only apply to integer types */
enum kstat_kv_unit {
	KSTAT_KV_U_NONE = 0,
	KSTAT_KV_U_PACKETS,	/* packets */
	KSTAT_KV_U_BYTES,	/* bytes */
	KSTAT_KV_U_CYCLES,	/* cycles */
};

struct kstat_kv {
	char			 kv_key[KSTAT_KV_NAMELEN];
	union {
		char			v_istr[16];
		unsigned int		v_bool;	
		uint64_t		v_u64;
		int64_t			v_s64;
		uint32_t		v_u32;
		int32_t			v_s32;
		uint16_t		v_u16;
		int16_t			v_s16;
		size_t			v_len;
	}			 kv_v;
	enum kstat_kv_type	 kv_type;
	enum kstat_kv_unit	 kv_unit;
} __aligned(KSTAT_KV_ALIGN);

#define kstat_kv_istr(_kv)	(_kv)->kv_v.v_istr
#define kstat_kv_bool(_kv)	(_kv)->kv_v.v_bool
#define kstat_kv_u64(_kv)	(_kv)->kv_v.v_u64
#define kstat_kv_s64(_kv)	(_kv)->kv_v.v_s64
#define kstat_kv_u32(_kv)	(_kv)->kv_v.v_u32
#define kstat_kv_s32(_kv)	(_kv)->kv_v.v_s32
#define kstat_kv_u16(_kv)	(_kv)->kv_v.v_u16
#define kstat_kv_s16(_kv)	(_kv)->kv_v.v_s16
#define kstat_kv_len(_kv)	(_kv)->kv_v.v_len
#define kstat_kv_temp(_kv)	(_kv)->kv_v.v_u64
#define kstat_kv_freq(_kv)	(_kv)->kv_v.v_u64
#define kstat_kv_volts(_kv)	(_kv)->kv_v.v_u64
#define kstat_kv_amps(_kv)	(_kv)->kv_v.v_u64
#define kstat_kv_watts(_kv)	(_kv)->kv_v.v_u64

#ifdef _KERNEL

#include <sys/tree.h>

struct kstat_lock_ops;

struct kstat {
	uint64_t		  ks_id;

	const char		 *ks_provider;
	unsigned int		  ks_instance;
	const char		 *ks_name;
	unsigned int		  ks_unit;

	unsigned int		  ks_type;
	unsigned int		  ks_flags;
#define KSTAT_F_REALLOC			(1 << 0)
	unsigned int		  ks_state;
#define KSTAT_S_CREATED			0
#define KSTAT_S_INSTALLED		1

	struct timespec		  ks_created;
	RBT_ENTRY(kstat)	  ks_id_entry;
	RBT_ENTRY(kstat)	  ks_pv_entry;
	RBT_ENTRY(kstat)	  ks_nm_entry;

	/* the driver can update these between kstat creation and install */
	unsigned int		  ks_dataver;
	void			 *ks_softc;
	void			 *ks_ptr;
	int			(*ks_read)(struct kstat *);
	int			(*ks_copy)(struct kstat *, void *);

	const struct kstat_lock_ops *
				  ks_lock_ops;
	void			 *ks_lock;

	/* the data that is updated by ks_read */
	void			 *ks_data;
	size_t			  ks_datalen;
	struct timespec		  ks_updated;
	struct timespec		  ks_interval;
};

struct kstat	*kstat_create(const char *, unsigned int,
		     const char *, unsigned int,
		     unsigned int, unsigned int);

void		 kstat_set_rlock(struct kstat *, struct rwlock *);
void		 kstat_set_wlock(struct kstat *, struct rwlock *);
void		 kstat_set_mutex(struct kstat *, struct mutex *);
void		 kstat_set_cpu(struct kstat *, struct cpu_info *);

int		 kstat_read_nop(struct kstat *);

void		 kstat_install(struct kstat *);
void		 kstat_remove(struct kstat *);
void		 kstat_destroy(struct kstat *);

/*
 * kstat_kv api
 */

#define KSTAT_KV_UNIT_INITIALIZER(_key, _type, _unit) {	\
	.kv_key = (_key),				\
	.kv_type = (_type),				\
	.kv_unit = (_unit),				\
}

#define KSTAT_KV_INITIALIZER(_key, _type)		\
    KSTAT_KV_UNIT_INITIALIZER((_key), (_type), KSTAT_KV_U_NONE)

void	kstat_kv_init(struct kstat_kv *, const char *, enum kstat_kv_type);
void	kstat_kv_unit_init(struct kstat_kv *, const char *,
	    enum kstat_kv_type, enum kstat_kv_unit);

#endif /* _KERNEL */

#endif /* _SYS_KSTAT_H_ */
