/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos72X src/bos/kernel/sys/pmzone.h 1.5.7.1                             */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1988,2021              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/* @(#)54       1.5.7.1  src/bos/kernel/sys/pmzone.h, sysproc, bos72X, x2021_38A6 9/16/21 17:05:01 */
/*
 *   COMPONENT_NAME: SYSPROC
 *
 *   ORIGIN: 27, 83
 *
 */
/*
 * LEVEL 1,  5 Years Bull Confidential Information
 */


#ifndef _H_PMZONE
#define _H_PMZONE

#include <sys/lock_def.h>
#include <sys/uthread.h>
#include <sys/thread.h>
#include <sys/proc.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 *      A zone is a collection of fixed size blocks for which there
 *      is fast allocation/deallocation access.  Kernel routines can
 *      use zones to manage data structures dynamically, creating a zone
 *      for each type of data structure to be managed.
 *
 *      Note: The link word must be unused when the element is free. Any other
 *      words may be used even when the element is free (like p_stat which
 *      holds SNONE).
 */

#define PM_ZERO         0x00000001L     /* when allocated, an entry is zeroed */
#define PM_FIFO         0x00000002L     /* the allocation scheme is FIFO */
#define PM_GLOBAL       0x00000004L     /* pm_heap is in global memory */
#define PM_PROC         0x00000008L     /* pm_heap is pvproc type */
#define PM_THREAD       0x00000010L     /* pm_heap is pvthread type */
#define PM_PRIMARY      0x00000020L     /* 1st zone with affinity for srad */
#define PM_LAST         0x00000040L     /* last zone in global region */
#define PM_BIGZONE	0x00000080L	/* zone element is > 1 page; e.g.
					 * utzone, so pin only uthread size
					 */
/*
 * XXX - SCALABILITY ISSUE
 * As the number of logical processors is scaled above 128, multiple SRADs
 * will need to be created. On a machine running more than 128 logical
 * processors that provides affinity information (i.e., not an SPLPAR machine)
 * the SRAD code should create multiple SRADs automatically. However, if 
 * affinity information is not provided, the code will not create multiple
 * SRADs.
 * 
 * Therefore, at such a time arises when multiple SRADs are needed when running
 * on machines that do not provide affinity information (i.e., a SPLPAR machine)
 * the code that creates SRADs will need to modified to automatically create
 * multiple SRADs or the underlying firmware/hypervisor will need to be
 * modified to provide affinity information.
 */
  
#ifdef __64BIT_KERNEL
#define MAXCPUS_SRAD 1536
#define MAXCPUS_LKU  512	/* More will require another reserved zone */
#else
#define MAXCPUS_SRAD 64
#endif

struct pm_heap {
	struct pm_heap  *next;  	/* points to secondary, if any */
	Simple_lock     lock;           /* zone lock */
	Simple_lock     pm_grow_lock;   /* per-srad lock only used in primary*/
	uint            lastin;         /* last freed entry */
	uint            highwater;      /* just beyond last used (monotonic) */
	uint            start;          /* beginning of the zone */
	uint            end;            /* just beyond end of the zone */
	uint		size;           /* size of an element */
	uint		link;           /* offset of link word in an element */
	uint	        flags;          /* zone flags */
	int  		affinity; 	/* primary zone affinity */
	unsigned long   base;           /* base address (PM_GLOBAL only) */
	char   		*highwater_addr; /* address of highwater mark for */
					/* global zones */
	char  		*free_common;   /* common free list for offnode frees*/
	struct pm_heap  *primary;     	/* primary zone for this heap */
	char  *free_anchor[MAXCPUS_SRAD];
					/* per cpu free list - one */
					/* per cpu on a given SRAD */
	eye_catch8b_t   pmh_eyec;	/* heap eyecatcher */
#define EYEC_PM_HEAP __EYEC8('p','m','_','h','e','a','p','S')
};

#ifndef _32KERMODE
#ifdef _KERNEL

struct pm_heap_global {
	char *         start;          /* beginning of the zone */
	char *         end;            /* just beyond end of the zone */
	int            size;           /* size of an element */
	int            zone_size;      /* Size of a zone in this structure */
	int            l2num_zones;    /* log 2 of number of zones */
	int            link;           /* offset of link word in an element */
	long           flags;          /* zone flags */
	int	       zone_hwm;       /* initialized zones,high water mark */
	int	       pad;	       /* pad for double word alignment	   */
	Simple_lock    pmhg_zone_lock; /* Lock to serialize alloc of zones */
	long           *zoneindex;     /* primary zone by srad */
	struct pm_heap **primary;      /* pm zone per srad */
	eye_catch8b_t  pmhg_eyec;      /* global eyecatcher */
#define EYEC_PM_HEAP_GLOBAL __EYEC8('p','m','g','l','o','b','a','l')
};

typedef uint32long64_t  pmbase_t;

/*
 * pvthread/pvproc data structures must be a power of two in size.
 * PM_TZONE_SHIFT is the log 2 of the size of each subzone of the
 * global pvthread region.  
 *
 * The size of a pvthread element is 2^8 bytes.
 * If we use a value of 2^23 for PM_TZONE_SHIFT, we get 2^15 elements per
 * sub-zone. 2^4 subzones will require 2^27 bytes of total memory.
 *
 * The size of a pvproc element is 2^10 bytes.
 * If we use a value of 2^24 for PM_PZONE_SHIFT,
 * we get 2^14 elements per zone.  2^4 subzones will require 2^28 bytes of
 * total memory, or a segment.
 * 
 * Note that the maximum number of threads is 2^19, while the maximum
 * number of procs is 2^18, so we need to have twice as many threads
 * in each subzone as we have procs in a subzone as long as we have the
 * same number of thread subzones as proc subzones.
 */

/* log 2 of pvthread sub-zone size */
#define PM_TZONE_SHIFT  (TIDXSHIFT + TV_LOGSIZE)
/* log 2 of pvproc sub-zone size */
#define PM_PZONE_SHIFT  (PIDXSHIFT + PV_LOGSIZE)
#define PM_LZONE_SHIFT  28 /* Each zone is within a segment for Local zones */

#define MAX_PM_TZONE   (1L << PM_TZONE_SHIFT)
#define MAX_PM_PZONE   (1L << PM_PZONE_SHIFT)
#define MAX_PM_LZONE   (1L << PM_LZONE_SHIFT)

#define PM_TZONE_OFFSET(_x) ((uint)(_x)  &  (MAX_PM_TZONE-1))
#define PM_PZONE_OFFSET(_x) ((uint)(_x)  &  (MAX_PM_PZONE-1))
#define PM_LZONE_OFFSET(_x) ((uint)(_x)  &  (MAX_PM_LZONE-1))
#define PM_TZONE_BASE(_x)   ((ulong)(_x) & ~(MAX_PM_TZONE-1))
#define PM_PZONE_BASE(_x)   ((ulong)(_x) & ~(MAX_PM_PZONE-1))
#define PM_LZONE_BASE(_x)   ((ulong)(_x) & ~(MAX_PM_LZONE-1))

#define PM_ZONE_OFFSET(_x,flags) ((flags & PM_GLOBAL)?  \
                                ((flags & PM_PROC) ? PM_PZONE_OFFSET(_x) : \
                                                     PM_TZONE_OFFSET(_x)) : \
                                PM_LZONE_OFFSET(_x) )

/* Flags for pm_alloc */
#define PM_SKIPFREELIST	0x00000001
#define PM_RESERVED	0x00000002
#define PM_ZONE_LOCKED	0x00000004

/*
 * ZONE_BASE(pm_zone *zone)
 * returns the effective base address for a zone
 */
#define ZONE_BASE(_z) ( \
	((_z)->flags & PM_GLOBAL) ?     (_z)->base : \
	                                PM_LZONE_BASE(_z))

extern struct pm_heap_global pvproc_cb;
extern struct pm_heap_global pvthread_cb;

struct pvproc *get_next_pvproc(struct pvproc *);
struct pvthread *get_next_pvthread(struct pvthread *);
struct pvproc *get_next_pvproc_srad(struct pvproc *,sradid_t);
struct pvthread *get_next_pvthread_srad(struct pvthread *,sradid_t);

void pm_balance(void);
int pm_validate_slot(long, long);
int pm_init(struct pm_heap *zone, char *start, char *end, uint size,
            uint link, short _class, uint occurrence, long flags);
void pm_release(struct pm_heap *zone);
void pm_free(struct pm_heap *zone, char *element);
void pm_zero(char *block, int size);
char *pm_alloc(struct pm_heap *zone, ut_error_t *error, uint flags);
char *pm_alloc_srad(struct pm_heap *zone, ut_error_t *error, int node);
char *pm_allocext(struct pm_heap *zone1, struct pm_heap *zone2, char * elem,
	          ut_error_t *error);

int pm_init_global(struct pm_heap_global *global_zone, 
		    short _class, short occurrence);
void pm_free_global(struct pm_heap_global *global_zone, char *element);
char *pm_alloc_global(struct pm_heap_global *global_zone,
	              ut_error_t *error, sradid_t target_srad);
extern int klvup_is_base_pid(pid_t);

#ifdef __64BIT_KERNEL
#define pm_alloc_local(n, node)	xmalloc_srad((n), 7, pinned_heap, (node))
#define pm_free_local(n)	xmfree((n), pinned_heap)
#else
#define pm_alloc_local(n, node)	xmalloc((n), 7, pinned_heap0)
#define pm_free_local(n)	xmfree((n), pinned_heap0)
#endif

#define round_down(addr,size)   (char *)((pmbase_t)(addr) & ~(size-1))
#define round_up(addr,size)     round_down((pmbase_t)(addr)+(size)-1,(size))
#endif

#endif  /* !_32KERMODE */

#ifdef __cplusplus
}
#endif  /*__cplusplus*/ 

#endif  /* _H_PMZONE */