#define DIAGNOSTIC 1
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/ubc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/ubc.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <mach/mach_types.h>
#include <mach/memory_object_types.h>
#include <kern/zalloc.h>
#if DIAGNOSTIC
#if defined(assert)
#undef assert()
#endif
#define assert(cond) \
if (!(cond)) panic("%s:%d (%s)", __FILE__, __LINE__, # cond)
#else
#include <kern/assert.h>
#endif
struct zone *ubc_info_zone;
#if DIAGNOSTIC
#define USHOULDNOT(fun) panic("%s: should not", (fun));
#else
#define USHOULDNOT(fun)
#endif
static void *_ubc_getobject(struct vnode *, int);
static void ubc_lock(struct vnode *);
static void ubc_unlock(struct vnode *);
static void
ubc_getobjref(struct vnode *vp)
{
register struct ubc_info *uip;
void *pager_cport;
void *object;
uip = vp->v_ubcinfo;
if (pager_cport = (void *)vnode_pager_lookup(vp, uip->ui_pager))
object = (void *)vm_object_lookup(pager_cport);
if (object != uip->ui_object) {
#if 0
Debugger("ubc_getobjref: object changed");
#endif
uip->ui_object = object;
}
if (uip->ui_object == NULL)
panic("ubc_getobjref: lost object");
}
void
ubc_init()
{
int i;
i = (vm_size_t) sizeof (struct ubc_info);
ubc_info_zone = zinit (i, 10000*i, 8192, "ubc_info zone");
return;
}
int
ubc_info_init(struct vnode *vp)
{
register struct ubc_info *uip;
void * pager;
struct vattr vattr;
struct proc *p = current_proc();
int error = 0;
kern_return_t kret;
void * pager_cport;
assert(vp);
assert(UBCISVALID(vp));
ubc_lock(vp);
if (ISSET(vp->v_flag, VUINIT)) {
while (ISSET(vp->v_flag, VUINIT)) {
SET(vp->v_flag, VUWANT);
ubc_unlock(vp);
(void) tsleep((caddr_t)vp, PINOD, "ubcinfo", 0);
ubc_lock(vp);
}
ubc_unlock(vp);
return (0);
} else {
SET(vp->v_flag, VUINIT);
}
uip = vp->v_ubcinfo;
if ((uip == UBC_INFO_NULL) || (uip == UBC_NOINFO)) {
ubc_unlock(vp);
uip = (struct ubc_info *) zalloc(ubc_info_zone);
bzero(uip, sizeof(struct ubc_info));
ubc_lock(vp);
SET(uip->ui_flags, UI_INITED);
uip->ui_vnode = vp;
uip->ui_ucred = NOCRED;
}
assert(uip->ui_flags != UI_NONE);
assert(uip->ui_vnode == vp);
#if 0
if(ISSET(uip->ui_flags, UI_HASPAGER))
goto done;
#endif
vp->v_ubcinfo = uip;
SET(uip->ui_flags, UI_HASPAGER);
ubc_unlock(vp);
pager = (void *)vnode_pager_setup(vp, uip->ui_pager);
assert(pager);
kret = vm_object_create_nomap(pager, (vm_object_offset_t)uip->ui_size);
if (kret != KERN_SUCCESS)
panic("ubc_info_init: vm_object_create_nomap returned %d", kret);
if (_ubc_getobject(vp, 0) == NULL)
panic("ubc_info_init: lost vmobject : uip = 0X%08x", uip);
vm_object_deallocate(uip->ui_object);
error = vget(vp, LK_INTERLOCK, p);
if (error)
panic("ubc_info_init: vget error = %d", error);
error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
ubc_lock(vp);
uip->ui_size = (error ? 0: vattr.va_size);
done:
CLR(vp->v_flag, VUINIT);
if (ISSET(vp->v_flag, VUWANT)) {
CLR(vp->v_flag, VUWANT);
ubc_unlock(vp);
wakeup((caddr_t)vp);
} else
ubc_unlock(vp);
return(error);
}
void
ubc_info_free(struct vnode *vp)
{
register struct ubc_info *uip;
struct ucred *credp;
assert(vp);
uip = vp->v_ubcinfo;
vp->v_ubcinfo = UBC_INFO_NULL;
credp = uip->ui_ucred;
if (credp != NOCRED) {
uip->ui_ucred = NOCRED;
crfree(credp);
}
zfree(ubc_info_zone, (vm_offset_t)uip);
return;
}
int
ubc_setsize(struct vnode *vp, off_t nsize)
{
off_t osize;
off_t lastpg, olastpgend, lastoff;
struct ubc_info *uip;
void *object;
kern_return_t kret;
int didhold;
#if DIAGNOSTIC
assert(vp);
assert(nsize >= (off_t)0);
#endif
if (UBCINVALID(vp))
return(0);
if (!UBCINFOEXISTS(vp))
return(0);
uip = vp->v_ubcinfo;
osize = uip->ui_size;
uip->ui_size = nsize;
if (nsize >= osize)
return(0);
didhold = ubc_hold(vp);
lastpg = trunc_page_64(nsize);
olastpgend = round_page_64(osize);
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
lastoff = (nsize & PAGE_MASK_64);
if (!lastoff) {
ubc_getobjref(vp);
kret = memory_object_lock_request(object,
(vm_object_offset_t)lastpg,
(memory_object_size_t)(olastpgend - lastpg),
MEMORY_OBJECT_RETURN_NONE,TRUE,
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret != KERN_SUCCESS)
printf("ubc_setsize: invalidate failed (error = %d)\n", kret);
if (didhold)
ubc_rele(vp);
return ((kret == KERN_SUCCESS) ? 1 : 0);
}
ubc_getobjref(vp);
kret = memory_object_lock_request(object,
(vm_object_offset_t)lastpg,
PAGE_SIZE_64,
MEMORY_OBJECT_RETURN_DIRTY,FALSE,
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret == KERN_SUCCESS) {
ubc_getobjref(vp);
kret = memory_object_lock_request(object,
(vm_object_offset_t)lastpg,
(memory_object_size_t)(olastpgend - lastpg),
MEMORY_OBJECT_RETURN_NONE,TRUE,
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret != KERN_SUCCESS)
printf("ubc_setsize: invalidate failed (error = %d)\n", kret);
} else
printf("ubc_setsize: flush failed (error = %d)\n", kret);
if (didhold)
ubc_rele(vp);
return ((kret == KERN_SUCCESS) ? 1 : 0);
}
off_t
ubc_getsize(struct vnode *vp)
{
return (vp->v_ubcinfo->ui_size);
}
static void
ubc_lock(struct vnode *vp)
{
simple_lock(&vp->v_interlock);
}
static void
ubc_unlock(struct vnode *vp)
{
simple_unlock(&vp->v_interlock);
}
int
ubc_uncache(struct vnode *vp)
{
void *object;
kern_return_t kret;
struct ubc_info *uip;
memory_object_perf_info_data_t perf;
int didhold;
assert(vp);
if (!UBCINFOEXISTS(vp))
return (0);
uip = vp->v_ubcinfo;
assert(uip != UBC_INFO_NULL);
vagevp(vp);
SET(uip->ui_flags, UI_DONTCACHE);
didhold = ubc_hold(vp);
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
ubc_getobjref(vp);
perf.cluster_size = PAGE_SIZE;
perf.may_cache = FALSE;
kret = memory_object_change_attributes(object,
MEMORY_OBJECT_PERFORMANCE_INFO,
(memory_object_info_t) &perf,
MEMORY_OBJECT_PERF_INFO_COUNT,
MACH_PORT_NULL, 0);
if (didhold)
ubc_rele(vp);
if (kret != KERN_SUCCESS) {
#if DIAGNOSTIC
panic("ubc_uncache: memory_object_change_attributes "
"kret = %d", kret);
#endif
return (0);
}
return (1);
}
int
ubc_umount(struct mount *mp)
{
struct proc *p = current_proc();
struct vnode *vp, *nvp;
int ret = 1;
loop:
simple_lock(&mntvnode_slock);
for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
if (vp->v_mount != mp) {
simple_unlock(&mntvnode_slock);
goto loop;
}
nvp = vp->v_mntvnodes.le_next;
simple_unlock(&mntvnode_slock);
if (UBCINFOEXISTS(vp)) {
ret &= ubc_clean(vp, 0);
ret &= ubc_uncache(vp);
ubc_release(vp);
}
simple_lock(&mntvnode_slock);
}
simple_unlock(&mntvnode_slock);
return (ret);
}
void
ubc_unmountall()
{
struct mount *mp, *nmp;
for (mp = mountlist.cqh_last; mp != (void *)&mountlist; mp = nmp) {
nmp = mp->mnt_list.cqe_prev;
(void) ubc_umount(mp);
}
}
struct ucred *
ubc_getcred(struct vnode *vp)
{
struct ubc_info *uip;
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
if (UBCINVALID(vp)) {
return (NOCRED);
}
return (uip->ui_ucred);
}
int
ubc_setcred(struct vnode *vp, struct proc *p)
{
struct ubc_info *uip;
struct ucred *credp;
assert(vp);
assert(p);
uip = vp->v_ubcinfo;
assert(uip);
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_setcred");
return (0);
}
credp = uip->ui_ucred;
if (credp == NOCRED) {
crhold(p->p_ucred);
uip->ui_ucred = p->p_ucred;
}
return (1);
}
void *
ubc_getpager(struct vnode *vp)
{
struct ubc_info *uip;
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_getpager");
return (0);
}
return (uip->ui_pager);
}
static void *
_ubc_getobject(struct vnode *vp, int flags)
{
struct ubc_info *uip;
void *object;
uip = vp->v_ubcinfo;
object = uip->ui_object;
if ((object == NULL) && ISSET(uip->ui_flags, UI_HASPAGER)
&& !(flags & UBC_NOREACTIVATE)) {
void *pager_cport;
if (ISSET(uip->ui_flags, UI_HASOBJREF))
panic("ubc_getobject: lost object");
if (pager_cport = (void *)vnode_pager_lookup(vp, uip->ui_pager)) {
object = (void *)vm_object_lookup(pager_cport);
#if 0
if ((uip->ui_object) && (uip->ui_object != object))
Debugger("_ubc_getobject: object changed");
#endif
uip->ui_object = object;
}
if (object != NULL)
SET(uip->ui_flags, UI_HASOBJREF);
}
if ((flags & UBC_HOLDOBJECT)
&& (object != NULL)) {
if (!ISSET(uip->ui_flags, UI_HASOBJREF)) {
ubc_getobjref(vp);
SET(uip->ui_flags, UI_HASOBJREF);
}
}
return (uip->ui_object);
}
void *
ubc_getobject(struct vnode *vp, int flags)
{
struct ubc_info *uip;
void *object;
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
if (UBCINVALID(vp)) {
return (0);
}
object = _ubc_getobject(vp, flags);
assert(object);
if (!ISSET(uip->ui_flags, (UI_HASOBJREF|UI_WASMAPPED))
&& !(uip->ui_holdcnt)) {
if (!(flags & UBC_PAGINGOP))
panic("ubc_getobject: lost reference");
}
}
int
ubc_setpager(struct vnode *vp, void *pager)
{
struct ubc_info *uip;
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_setpager");
return (0);
}
uip->ui_pager = pager;
return (1);
}
int
ubc_setflags(struct vnode * vp, int flags)
{
struct ubc_info *uip;
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_setflags");
return (EINVAL);
}
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
SET(uip->ui_flags, flags);
return(0);
}
int
ubc_clearflags(struct vnode * vp, int flags)
{
struct ubc_info *uip;
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_clearflags");
return (EINVAL);
}
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
CLR(uip->ui_flags, flags);
return(0);
}
int
ubc_issetflags(struct vnode * vp, int flags)
{
struct ubc_info *uip;
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_issetflags");
return (EINVAL);
}
assert(vp);
uip = vp->v_ubcinfo;
assert(uip);
return(ISSET(uip->ui_flags, flags));
}
off_t
ubc_blktooff(struct vnode *vp, daddr_t blkno)
{
off_t file_offset;
int error;
assert(vp);
if (UBCINVALID(vp)) {
USHOULDNOT("ubc_blktooff");
return ((off_t)-1);
}
error = VOP_BLKTOOFF(vp, blkno, &file_offset);
if (error)
file_offset = -1;
return (file_offset);
}
daddr_t
ubc_offtoblk(struct vnode *vp, off_t offset)
{
daddr_t blkno;
int error=0;
assert(vp);
if (UBCINVALID(vp)) {
return ((daddr_t)-1);
}
error = VOP_OFFTOBLK(vp, offset, &blkno);
if (error)
blkno = -1;
return (blkno);
}
int
ubc_clean(struct vnode *vp, int invalidate)
{
off_t size;
struct ubc_info *uip;
void *object;
kern_return_t kret;
int flags = 0;
int didhold;
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp))
return(0);
if (!UBCINFOEXISTS(vp))
return(0);
if (invalidate)
flags = (MEMORY_OBJECT_DATA_FLUSH | MEMORY_OBJECT_DATA_NO_CHANGE);
didhold = ubc_hold(vp);
uip = vp->v_ubcinfo;
size = uip->ui_size;
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
ubc_getobjref(vp);
vp->v_flag &= ~VHASDIRTY;
vp->v_clen = 0;
kret = memory_object_lock_request(object,
(vm_object_offset_t)0,
(memory_object_size_t)round_page_64(size),
MEMORY_OBJECT_RETURN_ALL, flags,
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret != KERN_SUCCESS) {
printf("ubc_clean: clean failed (error = %d)\n", kret);
}
if (didhold)
ubc_rele(vp);
return ((kret == KERN_SUCCESS) ? 1 : 0);
}
int
ubc_pushdirty(struct vnode *vp)
{
off_t size;
struct ubc_info *uip;
void *object;
kern_return_t kret;
int didhold;
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp))
return(0);
if (!UBCINFOEXISTS(vp))
return(0);
didhold = ubc_hold(vp);
uip = vp->v_ubcinfo;
size = uip->ui_size;
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
ubc_getobjref(vp);
vp->v_flag &= ~VHASDIRTY;
vp->v_clen = 0;
kret = memory_object_lock_request(object,
(vm_object_offset_t)0,
(memory_object_size_t)round_page_64(size),
MEMORY_OBJECT_RETURN_DIRTY,FALSE,
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret != KERN_SUCCESS) {
printf("ubc_pushdirty: flush failed (error = %d)\n", kret);
}
if (didhold)
ubc_rele(vp);
return ((kret == KERN_SUCCESS) ? 1 : 0);
}
int
ubc_hold(struct vnode *vp)
{
struct ubc_info *uip;
void *object;
if (UBCINVALID(vp))
return (0);
if (!UBCINFOEXISTS(vp)) {
if ((vp->v_flag & VXLOCK) || (vp->v_flag & VTERMINATE))
return (0);
vp->v_ubcinfo = UBC_INFO_NULL;
ubc_info_init(vp);
}
uip = vp->v_ubcinfo;
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
if (uip->ui_holdcnt++ == 0)
ubc_getobjref(vp);
if (uip->ui_holdcnt < 0)
panic("ubc_hold: ui_holdcnt");
return (1);
}
void
ubc_rele(struct vnode *vp)
{
struct ubc_info *uip;
void *object;
if (UBCINVALID(vp))
return;
if (!UBCINFOEXISTS(vp)) {
if ((vp->v_flag & VXLOCK) || (vp->v_flag & VTERMINATE))
return;
panic("ubc_rele: can not");
}
uip = vp->v_ubcinfo;
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
if (uip->ui_holdcnt == 0)
panic("ubc_rele: ui_holdcnt");
if (--uip->ui_holdcnt == 0) {
if (object)
vm_object_deallocate(object);
#if DIAGNOSTIC
else
printf("ubc_rele: null object for %x", vp);
#endif
}
return;
}
void
ubc_map(struct vnode *vp)
{
struct ubc_info *uip;
void *object;
ubc_lock(vp);
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp)) {
ubc_unlock(vp);
return;
}
if (!UBCINFOEXISTS(vp))
panic("ubc_map: can not");
uip = vp->v_ubcinfo;
SET(uip->ui_flags, UI_WASMAPPED);
uip->ui_mapped = 1;
ubc_unlock(vp);
#if 1
#else
if (ISSET(uip->ui_flags, UI_HASOBJREF)) {
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
CLR(uip->ui_flags, UI_HASOBJREF);
vm_object_deallocate(object);
}
#endif
return;
}
int
ubc_release(struct vnode *vp)
{
struct ubc_info *uip;
void *object;
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp))
return (0);
if (!UBCINFOEXISTS(vp))
panic("ubc_release: can not");
uip = vp->v_ubcinfo;
if (uip->ui_holdcnt)
return (0);
if (ISSET(uip->ui_flags, UI_HASOBJREF)) {
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
CLR(uip->ui_flags, UI_HASOBJREF);
vm_object_deallocate(object);
return (1);
} else
return (0);
}
int
ubc_invalidate(struct vnode *vp, off_t offset, size_t size)
{
struct ubc_info *uip;
void *object;
kern_return_t kret;
off_t toff;
size_t tsize;
int didhold;
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp))
return;
if (!UBCINFOEXISTS(vp))
panic("ubc_invalidate: can not");
didhold = ubc_hold(vp);
toff = trunc_page_64(offset);
tsize = (size_t)(round_page_64(offset+size) - toff);
uip = vp->v_ubcinfo;
object = _ubc_getobject(vp, UBC_NOREACTIVATE);
assert(object);
ubc_getobjref(vp);
kret = memory_object_lock_request(object,
(vm_object_offset_t)toff,
(memory_object_size_t)tsize,
MEMORY_OBJECT_RETURN_NONE,
(MEMORY_OBJECT_DATA_NO_CHANGE| MEMORY_OBJECT_DATA_FLUSH),
VM_PROT_NO_CHANGE,MACH_PORT_NULL);
if (kret != KERN_SUCCESS)
printf("ubc_invalidate: invalidate failed (error = %d)\n", kret);
if (didhold)
ubc_rele(vp);
return ((kret == KERN_SUCCESS) ? 1 : 0);
}
int
ubc_isinuse(struct vnode *vp, int tookref)
{
int busycount = tookref ? 2 : 1;
if (!UBCINFOEXISTS(vp))
return(0);
if (vp->v_usecount > busycount)
return (1);
if ((vp->v_usecount == busycount)
&& (vp->v_ubcinfo->ui_mapped == 1))
return(1);
else
return(0);
}
void
ubc_unmap(struct vnode *vp)
{
struct ubc_info *uip;
#if DIAGNOSTIC
assert(vp);
#endif
if (UBCINVALID(vp)) {
return;
}
if (!UBCINFOEXISTS(vp))
panic("ubc_unmap: can not");
ubc_lock(vp);
uip = vp->v_ubcinfo;
uip->ui_mapped = 0;
ubc_unlock(vp);
return;
}