/*
 * Copyright (c) 2018-2021 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#ifndef _KERN_LOCK_GROUP_H
#define _KERN_LOCK_GROUP_H

#include <kern/assert.h>
#include <kern/queue.h>
#include <kern/lock_types.h>
#if XNU_KERNEL_PRIVATE
#include <kern/startup.h>
#include <os/refcnt.h>
#endif /* XNU_KERNEL_PRIVATE */

__BEGIN_DECLS

/*!
 * @typedef lck_grp_t
 *
 * @abstract
 * The opaque type of a lock group.
 *
 * @discussion
 * A lock group is used to denote a set of locks that serve
 * a similar purpose, and hold an equivalent "rank" in the lock hierarchy.
 *
 * This structure can then provide some statistics and anchor checks
 * in development kernels for an entire family of locks.
 */
typedef struct _lck_grp_        lck_grp_t;
#define LCK_GRP_NULL            ((lck_grp_t *)NULL)

/*!
 * @typedef lck_grp_attr_t
 *
 * @abstract
 * The opaque type for attributes to a group.
 *
 * @discussion
 * A lock group attribute is meant to configure
 * a group, as a group configuration becomes
 * immutable once made.
 */
typedef struct _lck_grp_attr_   lck_grp_attr_t;
#define LCK_GRP_ATTR_NULL       ((lck_grp_attr_t *)NULL)

extern lck_grp_attr_t  *lck_grp_attr_alloc_init(
	void);

extern void             lck_grp_attr_setdefault(
	lck_grp_attr_t         *attr);

extern void             lck_grp_attr_setstat(
	lck_grp_attr_t         *attr);

extern void             lck_grp_attr_free(
	lck_grp_attr_t         *attr);

extern lck_grp_t       *lck_grp_alloc_init(
	const char             *grp_name,
	lck_grp_attr_t         *attr);

extern void             lck_grp_free(
	lck_grp_t              *grp);

#if XNU_KERNEL_PRIVATE
__exported_push_hidden

/*
 * Arguments wrapped in LCK_GRP_ARG() will be elided
 * when LOCK_STATS is not set.
 *
 * Arguments wrapped with LCK_GRP_PROBEARG() will be
 * NULL when LOCK_STATS is not set
 */
#if LOCK_STATS
#if !CONFIG_DTRACE
#error invalid configuration: LOCK_STATS needs dtrace
#endif
#define LCK_GRP_ARG(expr)       , expr
#define LCK_GRP_PROBEARG(grp)   grp
#define LCK_GRP_USE_ARG         1
#else
#define LCK_GRP_ARG(expr)
#define LCK_GRP_PROBEARG(grp)   LCK_GRP_NULL
#define LCK_GRP_USE_ARG         0
#endif /* LOCK_STATS */

__enum_decl(lck_debug_feature_t, uint32_t, {
	LCK_DEBUG_LOCKSTAT,
	LCK_DEBUG_LOCKPROF,

	LCK_DEBUG_MAX,
});

extern uint32_t LcksOpts;

extern struct lck_debug_state {
	uint32_t                lds_value;
	long                    lds_counts[LCK_DEBUG_MAX];
} lck_debug_state;

__options_decl(lck_grp_options_t, uint32_t, {
	LCK_GRP_ATTR_NONE       = 0x00000000,

#if MACH_KERNEL_PRIVATE
	LCK_GRP_ATTR_ID_MASK    = 0x0000ffff,
	LCK_GRP_ATTR_STAT       = 0x00010000, /* enable non time stats         */
	LCK_GRP_ATTR_TIME_STAT  = 0x00020000, /* enable time stats             */
	LCK_GRP_ATTR_DEBUG      = 0x00040000, /* profile locks of this group   */
	LCK_GRP_ATTR_ALLOCATED  = 0x80000000,
#endif
});

#if CONFIG_DTRACE
typedef struct _lck_grp_stat_ {
	uint64_t lgs_count;
	uint32_t lgs_enablings;
	/*
	 * Protected by dtrace_lock
	 */
	uint32_t lgs_probeid;
	uint64_t lgs_limit;
} lck_grp_stat_t;

typedef struct _lck_grp_stats_ {
	lck_grp_stat_t          lgss_spin_held;
	lck_grp_stat_t          lgss_spin_miss;
	lck_grp_stat_t          lgss_spin_spin;

	lck_grp_stat_t          lgss_ticket_held;
	lck_grp_stat_t          lgss_ticket_miss;
	lck_grp_stat_t          lgss_ticket_spin;

	lck_grp_stat_t          lgss_mtx_held;
	lck_grp_stat_t          lgss_mtx_direct_wait;
	lck_grp_stat_t          lgss_mtx_miss;
	lck_grp_stat_t          lgss_mtx_wait;
} lck_grp_stats_t;
#endif /* CONFIG_DTRACE */

#define LCK_GRP_MAX_NAME        64

struct _lck_grp_ {
	os_ref_atomic_t         lck_grp_refcnt;
	uint32_t                lck_grp_attr_id;
	uint32_t                lck_grp_spincnt;
	uint32_t                lck_grp_ticketcnt;
	uint32_t                lck_grp_mtxcnt;
	uint32_t                lck_grp_rwcnt;
	char                    lck_grp_name[LCK_GRP_MAX_NAME];
#if CONFIG_DTRACE
	lck_grp_stats_t         lck_grp_stats;
#endif /* CONFIG_DTRACE */
};

struct _lck_grp_attr_ {
	lck_grp_options_t       grp_attr_val;
};

struct lck_grp_spec {
	lck_grp_t              *grp;
	char                    grp_name[LCK_GRP_MAX_NAME];
	lck_grp_options_t       grp_flags;
};

/*
 * Auto-initializing lock group declarations
 * -----------------------------------------
 *
 * Use LCK_GRP_DECLARE to declare an automatically initialized group.
 */
#define LCK_GRP_DECLARE_ATTR(var, name, flags) \
	__PLACE_IN_SECTION("__DATA,__lock_grp") lck_grp_t var; \
	static __startup_data struct lck_grp_spec \
	__startup_lck_grp_spec_ ## var = { &var, name, flags }; \
	STARTUP_ARG(LOCKS, STARTUP_RANK_SECOND, lck_grp_startup_init, \
	    &__startup_lck_grp_spec_ ## var)

#define LCK_GRP_DECLARE(var, name) \
	LCK_GRP_DECLARE_ATTR(var, name, LCK_GRP_ATTR_NONE);

extern bool             lck_grp_has_stats(
	lck_grp_t              *grp);

extern void             lck_grp_startup_init(
	struct lck_grp_spec    *spec);

extern void             lck_grp_init(
	lck_grp_t              *grp,
	const char*             grp_name,
	lck_grp_attr_t         *attr);

extern lck_grp_t       *lck_grp_init_flags(
	lck_grp_t              *grp,
	const char*             grp_name,
	lck_grp_options_t       grp_flags);

extern lck_grp_t       *lck_grp_resolve(
	uint32_t                grp_attr_id) __pure2;

extern void             lck_grp_assert_id(
	lck_grp_t              *grp,
	uint32_t                grp_attr_id);

#define LCK_GRP_ASSERT_ID(...)  MACH_ASSERT_DO(lck_grp_assert_id(__VA_ARGS__))

extern void             lck_grp_reference(
	lck_grp_t              *grp,
	uint32_t               *cnt);

extern void             lck_grp_deallocate(
	lck_grp_t              *grp,
	uint32_t               *cnt);

extern void             lck_grp_foreach(
	bool                  (^block)(lck_grp_t *));


extern void             lck_grp_enable_feature(
	lck_debug_feature_t     feat);

extern void             lck_grp_disable_feature(
	lck_debug_feature_t     feat);

__pure2
static inline uint32_t
lck_opts_get(void)
{
	return LcksOpts;
}

__exported_pop


#endif /* XNU_KERNEL_PRIVATE */

__END_DECLS

#endif /* _KERN_LOCK_GROUP_H */
