/*
 * Copyright (c) 2008-2016 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Matthew Dillon <dillon@backplane.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef _CPU_CPUMASK_H_
#define	_CPU_CPUMASK_H_

#include <machine/stdint.h>
#ifdef _KERNEL
#include <cpu/atomic.h>
#endif

/*
 * cpumask_t - a mask representing a set of cpus and supporting routines.
 *
 * WARNING! It is recommended that this mask NOT be made variably-sized
 *	    because it affects a huge number of system structures.  However,
 *	    kernel code (non-module) can be optimized to not operate on the
 *	    whole mask.
 */

typedef struct {
	__uint64_t	ary[4];
} __cpumask_t;

#define CPUMASK_ELEMENTS	4

#define CPUMASK_INITIALIZER_ALLONES	{ .ary = { (__uint64_t)-1, \
					  (__uint64_t)-1, \
					  (__uint64_t)-1, \
					  (__uint64_t)-1 } }
#define CPUMASK_INITIALIZER_ONLYONE	{ .ary = { 1, 0, 0, 0 } }

#define CPUMASK_SIMPLE(cpu)	((__uint64_t)1 << (cpu))

#define CPUMASK_ADDR(mask, cpu)	(&(mask).ary[((cpu) >> 6) & 3])

#define BSRCPUMASK(val)		((val).ary[3] ? 192 + bsrq((val).ary[3]) : \
				((val).ary[2] ? 128 + bsrq((val).ary[2]) : \
				((val).ary[1] ? 64 + bsrq((val).ary[1]) : \
						bsrq((val).ary[0]))))

#define BSFCPUMASK(val)		((val).ary[0] ? bsfq((val).ary[0]) : \
				((val).ary[1] ? 64 + bsfq((val).ary[1]) : \
				((val).ary[2] ? 128 + bsfq((val).ary[2]) : \
						192 + bsfq((val).ary[3]))))

#define CPUMASK_CMPMASKEQ(val1, val2)	((val1).ary[0] == (val2).ary[0] && \
					 (val1).ary[1] == (val2).ary[1] && \
					 (val1).ary[2] == (val2).ary[2] && \
					 (val1).ary[3] == (val2).ary[3])

#define CPUMASK_CMPMASKNEQ(val1, val2)	((val1).ary[0] != (val2).ary[0] || \
					 (val1).ary[1] != (val2).ary[1] || \
					 (val1).ary[2] != (val2).ary[2] || \
					 (val1).ary[3] != (val2).ary[3])

#define CPUMASK_ISUP(val)		((val).ary[0] == 1 && \
					 (val).ary[1] == 0 && \
					 (val).ary[2] == 0 && \
					 (val).ary[3] == 0)

#define CPUMASK_TESTZERO(val)		((val).ary[0] == 0 && \
					 (val).ary[1] == 0 && \
					 (val).ary[2] == 0 && \
					 (val).ary[3] == 0)

#define CPUMASK_TESTNZERO(val)		((val).ary[0] != 0 || \
					 (val).ary[1] != 0 || \
					 (val).ary[2] != 0 || \
					 (val).ary[3] != 0)

#define CPUMASK_TESTBIT(val, i)		((val).ary[((i) >> 6) & 3] & \
					 CPUMASK_SIMPLE((i) & 63))

#define CPUMASK_TESTMASK(val1, val2)	(((val1).ary[0] & (val2.ary[0])) || \
					 ((val1).ary[1] & (val2.ary[1])) || \
					 ((val1).ary[2] & (val2.ary[2])) || \
					 ((val1).ary[3] & (val2.ary[3])))

#define CPUMASK_LOWMASK(val)		((val).ary[0])

#define CPUMASK_ORBIT(mask, i)		((mask).ary[((i) >> 6) & 3] |= \
					 CPUMASK_SIMPLE((i) & 63))

#define CPUMASK_ANDBIT(mask, i)		((mask).ary[((i) >> 6) & 3] &= \
					 CPUMASK_SIMPLE((i) & 63))

#define CPUMASK_NANDBIT(mask, i)	((mask).ary[((i) >> 6) & 3] &= \
					 ~CPUMASK_SIMPLE((i) & 63))

#define CPUMASK_ASSZERO(mask)		do {				\
					(mask).ary[0] = 0;		\
					(mask).ary[1] = 0;		\
					(mask).ary[2] = 0;		\
					(mask).ary[3] = 0;		\
					} while(0)

#define CPUMASK_ASSALLONES(mask)	do {				\
					(mask).ary[0] = (__uint64_t)-1;	\
					(mask).ary[1] = (__uint64_t)-1;	\
					(mask).ary[2] = (__uint64_t)-1;	\
					(mask).ary[3] = (__uint64_t)-1;	\
					} while(0)

#define CPUMASK_ASSBIT(mask, i)		do {				\
						CPUMASK_ASSZERO(mask);	\
						CPUMASK_ORBIT(mask, i); \
					} while(0)

#define CPUMASK_ASSBMASK(mask, i)	do {				\
		if (i < 64) {						\
			(mask).ary[0] = CPUMASK_SIMPLE(i) - 1;		\
			(mask).ary[1] = 0;				\
			(mask).ary[2] = 0;				\
			(mask).ary[3] = 0;				\
		} else if (i < 128) {					\
			(mask).ary[0] = (__uint64_t)-1;			\
			(mask).ary[1] = CPUMASK_SIMPLE((i) - 64) - 1;	\
			(mask).ary[2] = 0;				\
			(mask).ary[3] = 0;				\
		} else if (i < 192) {					\
			(mask).ary[0] = (__uint64_t)-1;			\
			(mask).ary[1] = (__uint64_t)-1;			\
			(mask).ary[2] = CPUMASK_SIMPLE((i) - 128) - 1;	\
			(mask).ary[3] = 0;				\
		} else {						\
			(mask).ary[0] = (__uint64_t)-1;			\
			(mask).ary[1] = (__uint64_t)-1;			\
			(mask).ary[2] = (__uint64_t)-1;			\
			(mask).ary[3] = CPUMASK_SIMPLE((i) - 192) - 1;	\
		}							\
					} while(0)

#define CPUMASK_ASSNBMASK(mask, i)	do {				\
		if (i < 64) {						\
			(mask).ary[0] = ~(CPUMASK_SIMPLE(i) - 1);	\
			(mask).ary[1] = (__uint64_t)-1;			\
			(mask).ary[2] = (__uint64_t)-1;			\
			(mask).ary[3] = (__uint64_t)-1;			\
		} else if (i < 128) {					\
			(mask).ary[0] = 0;				\
			(mask).ary[1] = ~(CPUMASK_SIMPLE((i) - 64) - 1);\
			(mask).ary[2] = (__uint64_t)-1;			\
			(mask).ary[3] = (__uint64_t)-1;			\
		} else if (i < 192) {					\
			(mask).ary[0] = 0;				\
			(mask).ary[1] = 0;				\
			(mask).ary[2] = ~(CPUMASK_SIMPLE((i) - 128) - 1);\
			(mask).ary[3] = (__uint64_t)-1;			\
		} else {						\
			(mask).ary[0] = 0;				\
			(mask).ary[1] = 0;				\
			(mask).ary[2] = 0;				\
			(mask).ary[3] = ~(CPUMASK_SIMPLE((i) - 192) - 1);\
		}							\
					} while(0)

#define CPUMASK_ANDMASK(mask, val)	do {				\
					(mask).ary[0] &= (val).ary[0];	\
					(mask).ary[1] &= (val).ary[1];	\
					(mask).ary[2] &= (val).ary[2];	\
					(mask).ary[3] &= (val).ary[3];	\
					} while(0)

#define CPUMASK_NANDMASK(mask, val)	do {				\
					(mask).ary[0] &= ~(val).ary[0];	\
					(mask).ary[1] &= ~(val).ary[1];	\
					(mask).ary[2] &= ~(val).ary[2];	\
					(mask).ary[3] &= ~(val).ary[3];	\
					} while(0)

#define CPUMASK_ORMASK(mask, val)	do {				\
					(mask).ary[0] |= (val).ary[0];	\
					(mask).ary[1] |= (val).ary[1];	\
					(mask).ary[2] |= (val).ary[2];	\
					(mask).ary[3] |= (val).ary[3];	\
					} while(0)

#define CPUMASK_XORMASK(mask, val)	do {				\
					(mask).ary[0] ^= (val).ary[0];	\
					(mask).ary[1] ^= (val).ary[1];	\
					(mask).ary[2] ^= (val).ary[2];	\
					(mask).ary[3] ^= (val).ary[3];	\
					} while(0)

#define CPUMASK_INVMASK(mask)		do {				\
					(mask).ary[0] ^= -1L;		\
					(mask).ary[1] ^= -1L;		\
					(mask).ary[2] ^= -1L;		\
					(mask).ary[3] ^= -1L;		\
					} while(0)

#ifndef _KERNEL
#define	__CPU_SETSIZE		((int)(sizeof(cpumask_t) * 8))

#define	__CPU_COUNT(set)	(					\
				__builtin_popcountl((set)->ary[0]) +	\
				__builtin_popcountl((set)->ary[1]) +	\
				__builtin_popcountl((set)->ary[2]) +	\
				__builtin_popcountl((set)->ary[3]))

#define	__CPU_CLR(cpu, set)	CPUMASK_NANDBIT(*set, cpu)
#define	__CPU_EQUAL(set1, set2)	CPUMASK_CMPMASKEQ(*set1, *set2)
#define	__CPU_ISSET(cpu, set)	CPUMASK_TESTBIT(*set, cpu)
#define	__CPU_SET(cpu, set)	CPUMASK_ORBIT(*set, cpu)
#define	__CPU_ZERO(set)		CPUMASK_ASSZERO(*set)

#define	__CPU_AND(dst, set1, set2)					\
			do {						\
				if (dst == set1) {			\
					CPUMASK_ANDMASK(*dst, *set2);	\
				} else {				\
					*dst = *set2;			\
					CPUMASK_ANDMASK(*dst, *set1);	\
				}					\
			} while (0)

#define	__CPU_OR(dst, set1, set2)					\
			do {						\
				if (dst == set1) {			\
					CPUMASK_ORMASK(*dst, *set2);	\
				} else {				\
					*dst = *set2;			\
					CPUMASK_ORMASK(*dst, *set1);	\
				}					\
			} while (0)

#define	__CPU_XOR(dst, set1, set2)					\
			do {						\
				if (dst == set1) {			\
					CPUMASK_XORMASK(*dst, *set2);	\
				} else {				\
					*dst = *set2;			\
					CPUMASK_XORMASK(*dst, *set1);	\
				}					\
			} while (0)
#endif

#ifdef _KERNEL
#define ATOMIC_CPUMASK_ORBIT(mask, i)					  \
			atomic_set_cpumask(&(mask).ary[((i) >> 6) & 3],	  \
					   CPUMASK_SIMPLE((i) & 63))

#define ATOMIC_CPUMASK_NANDBIT(mask, i)					  \
			atomic_clear_cpumask(&(mask).ary[((i) >> 6) & 3], \
					   CPUMASK_SIMPLE((i) & 63))

#define ATOMIC_CPUMASK_TESTANDSET(mask, i)				  \
		atomic_testandset_long(&(mask).ary[((i) >> 6) & 3], (i))

#define ATOMIC_CPUMASK_TESTANDCLR(mask, i)				  \
		atomic_testandclear_long(&(mask).ary[((i) >> 6) & 3], (i))

#define ATOMIC_CPUMASK_ORMASK(mask, val) do {				  \
			atomic_set_cpumask(&(mask).ary[0], (val).ary[0]); \
			atomic_set_cpumask(&(mask).ary[1], (val).ary[1]); \
			atomic_set_cpumask(&(mask).ary[2], (val).ary[2]); \
			atomic_set_cpumask(&(mask).ary[3], (val).ary[3]); \
					 } while(0)

#define ATOMIC_CPUMASK_NANDMASK(mask, val) do {				    \
			atomic_clear_cpumask(&(mask).ary[0], (val).ary[0]); \
			atomic_clear_cpumask(&(mask).ary[1], (val).ary[1]); \
			atomic_clear_cpumask(&(mask).ary[2], (val).ary[2]); \
			atomic_clear_cpumask(&(mask).ary[3], (val).ary[3]); \
					 } while(0)

#define ATOMIC_CPUMASK_COPY(mask, val) do {				    \
			atomic_store_rel_cpumask(&(mask).ary[0], (val).ary[0]);\
			atomic_store_rel_cpumask(&(mask).ary[1], (val).ary[1]);\
			atomic_store_rel_cpumask(&(mask).ary[2], (val).ary[2]);\
			atomic_store_rel_cpumask(&(mask).ary[3], (val).ary[3]);\
					 } while(0)
#endif

#endif /* !_CPU_CPUMASK_H_ */
