anon.h revision 870154656b6ec47dea8a31e79fa48256dc521bf4
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#ifndef _VM_ANON_H
#define _VM_ANON_H
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/cred.h>
#include <vm/seg.h>
#include <vm/vpage.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* VM - Anonymous pages.
*/
typedef unsigned long anoff_t; /* anon offsets */
/*
* Each anonymous page, either in memory or in swap, has an anon structure.
* The structure (slot) provides a level of indirection between anonymous pages
* and their backing store.
*
* (an_vp, an_off) names the vnode of the anonymous page for this slot.
*
* (an_pvp, an_poff) names the location of the physical backing store
* for the page this slot represents. If the name is null there is no
* associated physical store. The physical backing store location can
* change while the slot is in use.
*
* an_hash is a hash list of anon slots. The list is hashed by
* (an_vp, an_off) of the associated anonymous page and provides a
* method of going from the name of an anonymous page to its
* associated anon slot.
*
* an_refcnt holds a reference count which is the number of separate
* copies that will need to be created in case of copy-on-write.
* A refcnt > 0 protects the existence of the slot. The refcnt is
* initialized to 1 when the anon slot is created in anon_alloc().
* If a client obtains an anon slot and allows multiple threads to
* share it, then it is the client's responsibility to insure that
* it does not allow one thread to try to reference the slot at the
* same time as another is trying to decrement the last count and
* destroy the anon slot. E.g., the seg_vn segment type protects
* against this with higher level locks.
*/
struct anon {
struct vnode *an_vp; /* vnode of anon page */
struct vnode *an_pvp; /* vnode of physical backing store */
anoff_t an_off; /* offset of anon page */
anoff_t an_poff; /* offset in vnode */
struct anon *an_hash; /* hash table of anon slots */
int an_refcnt; /* # of people sharing slot */
};
#ifdef _KERNEL
/*
* The swapinfo_lock protects:
* swapinfo list
* individual swapinfo structures
*
* The anoninfo_lock protects:
* anoninfo counters
*
* The anonhash_lock protects:
* anon hash lists
* anon slot fields
*
* Fields in the anon slot which are read-only for the life of the slot
* (an_vp, an_off) do not require the anonhash_lock be held to access them.
* If you access a field without the anonhash_lock held you must be holding
* the slot with an_refcnt to make sure it isn't destroyed.
* To write (an_pvp, an_poff) in a given slot you must also hold the
* p_iolock of the anonymous page for slot.
*/
extern kmutex_t anoninfo_lock;
extern kmutex_t swapinfo_lock;
extern kmutex_t anonhash_lock[];
extern pad_mutex_t anon_array_lock[];
extern kcondvar_t anon_array_cv[];
/*
* Global hash table to provide a function from (vp, off) -> ap
*/
extern size_t anon_hash_size;
extern struct anon **anon_hash;
#define ANON_HASH_SIZE anon_hash_size
#define ANON_HASHAVELEN 4
#define ANON_HASH(VP, OFF) \
((((uintptr_t)(VP) >> 7) ^ ((OFF) >> PAGESHIFT)) & (ANON_HASH_SIZE - 1))
#define AH_LOCK_SIZE 64
#define AH_LOCK(vp, off) (ANON_HASH((vp), (off)) & (AH_LOCK_SIZE -1))
#endif /* _KERNEL */
/*
* Declaration for the Global counters to accurately
* track the kernel foot print in memory.
*/
extern pgcnt_t segvn_pages_locked;
extern pgcnt_t pages_locked;
extern pgcnt_t pages_claimed;
extern pgcnt_t pages_useclaim;
extern pgcnt_t obp_pages;
/*
* Anonymous backing store accounting structure for swapctl.
*
* ani_max = maximum amount of swap space
* (including potentially available physical memory)
* ani_free = amount of unallocated anonymous memory
* (some of which might be reserved and including
* potentially available physical memory)
* ani_resv = amount of claimed (reserved) anonymous memory
*
* The swap data can be aquired more efficiently through the
* kstats interface.
* Total slots currently available for reservation =
* MAX(ani_max - ani_resv, 0) + (availrmem - swapfs_minfree)
*/
struct anoninfo {
pgcnt_t ani_max;
pgcnt_t ani_free;
pgcnt_t ani_resv;
};
#ifdef _SYSCALL32
struct anoninfo32 {
size32_t ani_max;
size32_t ani_free;
size32_t ani_resv;
};
#endif /* _SYSCALL32 */
/*
* Define the NCPU pool of the ani_free counters. Update the counter
* of the cpu on which the thread is running and in every clock intr
* sync anoninfo.ani_free with the current total off all the NCPU entries.
*/
typedef struct ani_free {
kmutex_t ani_lock;
pgcnt_t ani_count;
uchar_t pad[64 - sizeof (kmutex_t) - sizeof (pgcnt_t)];
/* XXX 64 = cacheline size */
} ani_free_t;
#define ANI_MAX_POOL 128
extern ani_free_t ani_free_pool[];
#define ANI_ADD(inc) { \
ani_free_t *anifp; \
int index; \
index = (CPU->cpu_id & (ANI_MAX_POOL - 1)); \
anifp = &ani_free_pool[index]; \
mutex_enter(&anifp->ani_lock); \
anifp->ani_count += inc; \
mutex_exit(&anifp->ani_lock); \
}
/*
* Anon array pointers are allocated in chunks. Each chunk
* has PAGESIZE/sizeof(u_long *) of anon pointers.
* There are two levels of arrays for anon array pointers larger
* than a chunk. The first level points to anon array chunks.
* The second level consists of chunks of anon pointers.
*
* If anon array is smaller than a chunk then the whole anon array
* is created (memory is allocated for whole anon array).
* If anon array is larger than a chunk only first level array is
* allocated. Then other arrays (chunks) are allocated only when
* they are initialized with anon pointers.
*/
struct anon_hdr {
kmutex_t serial_lock; /* serialize array chunk allocation */
pgcnt_t size; /* number of pointers to (anon) pages */
void **array_chunk; /* pointers to anon pointers or chunks of */
/* anon pointers */
int flags; /* ANON_ALLOC_FORCE force preallocation of */
/* whole anon array */
};
#ifdef _LP64
#define ANON_PTRSHIFT 3
#define ANON_PTRMASK ~7
#else
#define ANON_PTRSHIFT 2
#define ANON_PTRMASK ~3
#endif
#define ANON_CHUNK_SIZE (PAGESIZE >> ANON_PTRSHIFT)
#define ANON_CHUNK_SHIFT (PAGESHIFT - ANON_PTRSHIFT)
#define ANON_CHUNK_OFF (ANON_CHUNK_SIZE - 1)
/*
* Anon flags.
*/
#define ANON_SLEEP 0x0 /* ok to block */
#define ANON_NOSLEEP 0x1 /* non-blocking call */
#define ANON_ALLOC_FORCE 0x2 /* force single level anon array */
#define ANON_GROWDOWN 0x4 /* anon array should grow downward */
/*
* The anon_map structure is used by various clients of the anon layer to
* manage anonymous memory. When anonymous memory is shared,
* then the different clients sharing it will point to the
* same anon_map structure. Also, if a segment is unmapped
* in the middle where an anon_map structure exists, the
* newly created segment will also share the anon_map structure,
* although the two segments will use different ranges of the
* anon array. When mappings are private (or shared with
* a reference count of 1), an unmap operation will free up
* a range of anon slots in the array given by the anon_map
* structure. Because of fragmentation due to this unmapping,
* we have to store the size of the anon array in the anon_map
* structure so that we can free everything when the referernce
* count goes to zero.
*
* A new rangelock scheme is introduced to make the anon layer scale.
* A reader/writer lock per anon_amp and an array of system-wide hash
* locks, anon_array_lock[] are introduced to replace serial_lock and
* anonmap lock. The writer lock is held when we want to singlethreaD
* the reference to the anon array pointers or when references to
* anon_map's members, whereas reader lock and anon_array_lock are
* held to allows multiple threads to reference different part of
* anon array. A global set of condition variables, anon_array_cv,
* are used with anon_array_lock[] to make the hold time of the locks
* short.
*
* szc is used to calculate the index of hash locks and cv's. We
* could've just used seg->s_szc if not for the possible sharing of
* anon_amp between SYSV shared memory and ISM, so now we introduce
* szc in the anon_map structure. For MAP_SHARED, the amp->szc is either
* 0 (base page size) or page_num_pagesizes() - 1, while MAP_PRIVATE
* the amp->szc could be anything in [0, page_num_pagesizes() - 1].
*/
struct anon_map {
krwlock_t a_rwlock; /* protect anon_map and anon array */
size_t size; /* size in bytes mapped by the anon array */
struct anon_hdr *ahp; /* anon array header pointer, containing */
/* anon pointer array(s) */
size_t swresv; /* swap space reserved for this anon_map */
uint_t refcnt; /* reference count on this structure */
ushort_t a_szc; /* max szc among shared processes */
void *locality; /* lgroup locality info */
};
#ifdef _KERNEL
#define ANON_BUSY 0x1
#define ANON_ISBUSY(slot) (*(slot) & ANON_BUSY)
#define ANON_SETBUSY(slot) (*(slot) |= ANON_BUSY)
#define ANON_CLRBUSY(slot) (*(slot) &= ~ANON_BUSY)
#define ANON_MAP_SHIFT 6 /* log2(sizeof (struct anon_map)) */
#define ANON_ARRAY_SHIFT 7 /* log2(ANON_LOCKSIZE) */
#define ANON_LOCKSIZE 128
#define ANON_LOCK_ENTER(lock, type) rw_enter((lock), (type))
#define ANON_LOCK_EXIT(lock) rw_exit((lock))
#define ANON_ARRAY_HASH(amp, idx)\
((((idx) + ((idx) >> ANON_ARRAY_SHIFT) +\
((idx) >> (ANON_ARRAY_SHIFT << 1)) +\
((idx) >> (ANON_ARRAY_SHIFT + (ANON_ARRAY_SHIFT << 1)))) ^\
((uintptr_t)(amp) >> ANON_MAP_SHIFT)) & (ANON_LOCKSIZE - 1))
typedef struct anon_sync_obj {
kmutex_t *sync_mutex;
kcondvar_t *sync_cv;
ulong_t *sync_data;
} anon_sync_obj_t;
/*
* Anonymous backing store accounting structure for kernel.
* ani_max = total reservable slots on physical (disk-backed) swap
* ani_phys_resv = total phys slots reserved for use by clients
* ani_mem_resv = total mem slots reserved for use by clients
* ani_free = # unallocated physical slots + # of reserved unallocated
* memory slots
*/
/*
* Initial total swap slots available for reservation
*/
#define TOTAL_AVAILABLE_SWAP \
(k_anoninfo.ani_max + MAX((spgcnt_t)(availrmem - swapfs_minfree), 0))
/*
* Swap slots currently available for reservation
*/
#define CURRENT_TOTAL_AVAILABLE_SWAP \
((k_anoninfo.ani_max - k_anoninfo.ani_phys_resv) + \
MAX((spgcnt_t)(availrmem - swapfs_minfree), 0))
struct k_anoninfo {
pgcnt_t ani_max; /* total reservable slots on phys */
/* (disk) swap */
pgcnt_t ani_free; /* # of unallocated phys and mem slots */
pgcnt_t ani_phys_resv; /* # of reserved phys (disk) slots */
pgcnt_t ani_mem_resv; /* # of reserved mem slots */
pgcnt_t ani_locked_swap; /* # of swap slots locked in reserved */
/* mem swap */
};
extern struct k_anoninfo k_anoninfo;
extern void anon_init(void);
extern struct anon *anon_alloc(struct vnode *, anoff_t);
extern void anon_dup(struct anon_hdr *, ulong_t,
struct anon_hdr *, ulong_t, size_t);
extern void anon_dup_fill_holes(struct anon_hdr *, ulong_t,
struct anon_hdr *, ulong_t, size_t, uint_t, int);
extern int anon_fill_cow_holes(struct seg *, caddr_t, struct anon_hdr *,
ulong_t, struct vnode *, u_offset_t, size_t, uint_t,
uint_t, struct vpage [], struct cred *);
extern void anon_free(struct anon_hdr *, ulong_t, size_t);
extern void anon_free_pages(struct anon_hdr *, ulong_t, size_t, uint_t);
extern void anon_disclaim(struct anon_map *, ulong_t, size_t, int);
extern int anon_getpage(struct anon **, uint_t *, struct page **,
size_t, struct seg *, caddr_t, enum seg_rw, struct cred *);
extern int swap_getconpage(struct vnode *, u_offset_t, size_t,
uint_t *, page_t *[], size_t, page_t *,
spgcnt_t *, struct seg *, caddr_t,
enum seg_rw, struct cred *);
extern int anon_map_getpages(struct anon_map *, ulong_t,
uint_t, struct seg *, caddr_t, uint_t,
uint_t *, page_t *[], uint_t *,
struct vpage [], enum seg_rw, int, int, struct cred *);
extern int anon_map_privatepages(struct anon_map *, ulong_t,
uint_t, struct seg *, caddr_t, uint_t,
page_t *[], struct vpage [], int, struct cred *);
extern struct page *anon_private(struct anon **, struct seg *,
caddr_t, uint_t, struct page *,
int, struct cred *);
extern struct page *anon_zero(struct seg *, caddr_t,
struct anon **, struct cred *);
extern int anon_map_createpages(struct anon_map *, ulong_t,
size_t, struct page **,
struct seg *, caddr_t,
enum seg_rw, struct cred *);
extern int anon_map_demotepages(struct anon_map *, ulong_t,
struct seg *, caddr_t, uint_t,
struct vpage [], struct cred *);
extern int anon_resvmem(size_t, uint_t);
extern void anon_unresv(size_t);
extern struct anon_map *anonmap_alloc(size_t, size_t);
extern void anonmap_free(struct anon_map *);
extern void anon_decref(struct anon *);
extern int non_anon(struct anon_hdr *, ulong_t, u_offset_t *, size_t *);
extern pgcnt_t anon_pages(struct anon_hdr *, ulong_t, pgcnt_t);
extern int anon_swap_adjust(pgcnt_t);
extern void anon_swap_restore(pgcnt_t);
extern struct anon_hdr *anon_create(pgcnt_t, int);
extern void anon_release(struct anon_hdr *, pgcnt_t);
extern struct anon *anon_get_ptr(struct anon_hdr *, ulong_t);
extern ulong_t *anon_get_slot(struct anon_hdr *, ulong_t);
extern struct anon *anon_get_next_ptr(struct anon_hdr *, ulong_t *);
extern int anon_set_ptr(struct anon_hdr *, ulong_t, struct anon *, int);
extern int anon_copy_ptr(struct anon_hdr *, ulong_t,
struct anon_hdr *, ulong_t, pgcnt_t, int);
extern pgcnt_t anon_grow(struct anon_hdr *, ulong_t *, pgcnt_t, pgcnt_t, int);
extern void anon_array_enter(struct anon_map *, ulong_t,
anon_sync_obj_t *);
extern int anon_array_try_enter(struct anon_map *, ulong_t,
anon_sync_obj_t *);
extern void anon_array_exit(anon_sync_obj_t *);
/*
* anon_resv checks to see if there is enough swap space to fulfill a
* request and if so, reserves the appropriate anonymous memory resources.
* anon_checkspace just checks to see if there is space to fulfill the request,
* without taking any resources. Both return 1 if successful and 0 if not.
*/
#define anon_resv(size) anon_resvmem((size), 1)
#define anon_checkspace(size) anon_resvmem((size), 0)
/*
* Flags to anon_private
*/
#define STEAL_PAGE 0x1 /* page can be stolen */
#define LOCK_PAGE 0x2 /* page must be ``logically'' locked */
/*
* Flags to anon_disclaim
*/
#define ANON_PGLOOKUP_BLK 0x1 /* block on locked pages */
/*
* SEGKP ANON pages that are locked are assumed to be LWP stack pages
* and thus count towards the user pages locked count.
* This value is protected by the same lock as availrmem.
*/
extern pgcnt_t anon_segkp_pages_locked;
extern int anon_debug;
#ifdef ANON_DEBUG
#define A_ANON 0x01
#define A_RESV 0x02
#define A_MRESV 0x04
/* vararg-like debugging macro. */
#define ANON_PRINT(f, printf_args) \
if (anon_debug & f) \
printf printf_args
#else /* ANON_DEBUG */
#define ANON_PRINT(f, printf_args)
#endif /* ANON_DEBUG */
#endif /* _KERNEL */
#ifdef __cplusplus
}
#endif
#endif /* _VM_ANON_H */