X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..4452a7af2eac33dbad800bcc91f2399d62c18f53:/bsd/kern/ubc_subr.c diff --git a/bsd/kern/ubc_subr.c b/bsd/kern/ubc_subr.c index c56fe8b2b..deaaa090a 100644 --- a/bsd/kern/ubc_subr.c +++ b/bsd/kern/ubc_subr.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * 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. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * 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 OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * 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_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * File: ubc_subr.c @@ -26,77 +32,60 @@ * * Functions related to Unified Buffer cache. * + * Caller of UBC functions MUST have a valid reference on the vnode. + * */ +#undef DIAGNOSTIC #define DIAGNOSTIC 1 #include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include +#include +#include #include +#include #include #include +#include +#include +#include +#include #include +#include +#include +#include /* last */ #if DIAGNOSTIC #if defined(assert) #undef assert() #endif #define assert(cond) \ - if (!(cond)) panic("%s:%d (%s)", __FILE__, __LINE__, # cond) + ((void) ((cond) ? 0 : panic("%s:%d (%s)", __FILE__, __LINE__, # cond))) #else #include #endif /* DIAGNOSTIC */ -struct zone *ubc_info_zone; - -#if DIAGNOSTIC -#define USHOULDNOT(fun) panic("%s: should not", (fun)); -#else -#define USHOULDNOT(fun) -#endif /* DIAGNOSTIC */ - - -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; +int ubc_info_init_internal(struct vnode *vp, int withfsize, off_t filesize); +static int ubc_umcallback(vnode_t, void *); +int ubc_isinuse_locked(vnode_t, int, int); +static int ubc_msync_internal(vnode_t, off_t, off_t, off_t *, int, int *); - 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 /* 0 */ - uip->ui_object = object; - } - - if (uip->ui_object == NULL) - panic("ubc_getobjref: lost object"); -} +struct zone *ubc_info_zone; /* * Initialization of the zone for Unified Buffer Cache. */ -void +__private_extern__ void ubc_init() { int i; @@ -112,129 +101,123 @@ ubc_init() */ int ubc_info_init(struct vnode *vp) +{ + return(ubc_info_init_internal(vp, 0, 0)); +} +int +ubc_info_init_withsize(struct vnode *vp, off_t filesize) +{ + return(ubc_info_init_internal(vp, 1, filesize)); +} + +int +ubc_info_init_internal(struct vnode *vp, int withfsize, off_t filesize) { 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; + memory_object_control_t control; - assert(vp); - assert(UBCISVALID(vp)); + uip = vp->v_ubcinfo; - ubc_lock(vp); - if (ISSET(vp->v_flag, VUINIT)) { - /* - * other thread is already doing this - * wait till done - */ - while (ISSET(vp->v_flag, VUINIT)) { - SET(vp->v_flag, VUWANT); /* XXX overloaded! */ - 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); - } + if (uip == UBC_INFO_NULL) { - 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); + bzero((char *)uip, sizeof(struct ubc_info)); + uip->ui_vnode = vp; + uip->ui_flags = UI_INITED; uip->ui_ucred = NOCRED; } +#if DIAGNOSTIC + else + Debugger("ubc_info_init: already"); +#endif /* DIAGNOSTIC */ assert(uip->ui_flags != UI_NONE); assert(uip->ui_vnode == vp); -#if 0 - if(ISSET(uip->ui_flags, UI_HASPAGER)) - goto done; -#endif /* 0 */ - /* now set this ubc_info in the vnode */ vp->v_ubcinfo = uip; - SET(uip->ui_flags, UI_HASPAGER); - ubc_unlock(vp); + pager = (void *)vnode_pager_setup(vp, uip->ui_pager); assert(pager); + SET(uip->ui_flags, UI_HASPAGER); + uip->ui_pager = pager; + /* - * Can not use VOP_GETATTR() to get accurate value - * of ui_size. Thanks to NFS. + * Note: We can not use VNOP_GETATTR() to get accurate + * value of ui_size. Thanks to NFS. * nfs_getattr() can call vinvalbuf() and in this case * ubc_info is not set up to deal with that. * So use bogus size. */ - /* create a vm_object association */ - 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); - - /* _ubc_getobject() gets a reference on the memory object */ - if (_ubc_getobject(vp, 0) == NULL) - panic("ubc_info_init: lost vmobject : uip = 0X%08x", uip); - /* - * vm_object_allocate() called from vm_object_create_nomap() - * created the object with a refcount of 1 - * need to drop the reference gained by vm_object_lookup() + * create a vnode - vm_object association + * memory_object_create_named() creates a "named" reference on the + * memory object we hold this reference as long as the vnode is + * "alive." Since memory_object_create_named() took its own reference + * on the vnode pager we passed it, we can drop the reference + * vnode_pager_setup() returned here. */ - vm_object_deallocate(uip->ui_object); + kret = memory_object_create_named(pager, + (memory_object_size_t)uip->ui_size, &control); + vnode_pager_deallocate(pager); + if (kret != KERN_SUCCESS) + panic("ubc_info_init: memory_object_create_named returned %d", kret); + assert(control); + uip->ui_control = control; /* cache the value of the mo control */ + SET(uip->ui_flags, UI_HASOBJREF); /* with a named reference */ +#if 0 /* create a pager reference on the vnode */ - error = vget(vp, LK_INTERLOCK, p); + error = vnode_pager_vget(vp); if (error) - panic("ubc_info_init: vget error = %d", error); - - /* initialize the size */ - 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); + panic("ubc_info_init: vnode_pager_vget error = %d", error); +#endif + if (withfsize == 0) { + struct vfs_context context; + /* initialize the size */ + context.vc_proc = p; + context.vc_ucred = kauth_cred_get(); + error = vnode_size(vp, &uip->ui_size, &context); + if (error) + uip->ui_size = 0; + } else { + uip->ui_size = filesize; + } + vp->v_lflag |= VNAMED_UBC; - return(error); + return (error); } /* Free the ubc_info */ -void -ubc_info_free(struct vnode *vp) +static void +ubc_info_free(struct ubc_info *uip) { - register struct ubc_info *uip; - struct ucred *credp; + if (IS_VALID_CRED(uip->ui_ucred)) { + kauth_cred_unref(&uip->ui_ucred); + } + + if (uip->ui_control != MEMORY_OBJECT_CONTROL_NULL) + memory_object_control_deallocate(uip->ui_control); - assert(vp); + cluster_release(uip); - 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; } +void +ubc_info_deallocate(struct ubc_info *uip) +{ + ubc_info_free(uip); +} + /* * Communicate with VM the size change of the file * returns 1 on success, 0 on failure @@ -245,20 +228,14 @@ ubc_setsize(struct vnode *vp, off_t nsize) off_t osize; /* ui_size before change */ off_t lastpg, olastpgend, lastoff; struct ubc_info *uip; - void *object; + memory_object_control_t control; kern_return_t kret; - int didhold; -#if DIAGNOSTIC - assert(vp); - assert(nsize >= (off_t)0); -#endif - - if (UBCINVALID(vp)) - return(0); + if (nsize < (off_t)0) + return (0); if (!UBCINFOEXISTS(vp)) - return(0); + return (0); uip = vp->v_ubcinfo; osize = uip->ui_size; /* call ubc_getsize() ??? */ @@ -266,7 +243,7 @@ ubc_setsize(struct vnode *vp, off_t nsize) uip->ui_size = nsize; if (nsize >= osize) /* Nothing more to do */ - return(0); + return (1); /* return success */ /* * When the file shrinks, invalidate the pages beyond the @@ -276,11 +253,10 @@ ubc_setsize(struct vnode *vp, off_t nsize) * end of the file. */ - didhold = ubc_hold(vp); lastpg = trunc_page_64(nsize); olastpgend = round_page_64(osize); - object = _ubc_getobject(vp, UBC_NOREACTIVATE); - assert(object); + control = uip->ui_control; + assert(control); lastoff = (nsize & PAGE_MASK_64); /* @@ -288,227 +264,119 @@ ubc_setsize(struct vnode *vp, off_t nsize) * invalidating is sufficient */ if (!lastoff) { - /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); - /* invalidate last page and old contents beyond nsize */ - 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); + kret = memory_object_lock_request(control, + (memory_object_offset_t)lastpg, + (memory_object_size_t)(olastpgend - lastpg), NULL, NULL, + MEMORY_OBJECT_RETURN_NONE, MEMORY_OBJECT_DATA_FLUSH, + VM_PROT_NO_CHANGE); if (kret != KERN_SUCCESS) printf("ubc_setsize: invalidate failed (error = %d)\n", kret); - if (didhold) - ubc_rele(vp); return ((kret == KERN_SUCCESS) ? 1 : 0); } - /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); - /* flush the last page */ - 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); + kret = memory_object_lock_request(control, + (memory_object_offset_t)lastpg, + PAGE_SIZE_64, NULL, NULL, + MEMORY_OBJECT_RETURN_DIRTY, FALSE, + VM_PROT_NO_CHANGE); if (kret == KERN_SUCCESS) { - /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); - /* invalidate last page and old contents beyond nsize */ - 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); + kret = memory_object_lock_request(control, + (memory_object_offset_t)lastpg, + (memory_object_size_t)(olastpgend - lastpg), NULL, NULL, + MEMORY_OBJECT_RETURN_NONE, MEMORY_OBJECT_DATA_FLUSH, + VM_PROT_NO_CHANGE); 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); } /* * Get the size of the file - * For local file systems the size is locally cached. For NFS - * there might be a network transaction for this. */ off_t ubc_getsize(struct vnode *vp) { - /* XXX deal with NFS */ + /* people depend on the side effect of this working this way + * as they call this for directory + */ + if (!UBCINFOEXISTS(vp)) + return ((off_t)0); return (vp->v_ubcinfo->ui_size); } -/* lock for changes to struct UBC */ -static void -ubc_lock(struct vnode *vp) -{ - /* For now, just use the v_interlock */ - simple_lock(&vp->v_interlock); -} - -/* unlock */ -static void -ubc_unlock(struct vnode *vp) -{ - /* For now, just use the v_interlock */ - simple_unlock(&vp->v_interlock); -} - /* - * Caller indicate that the object corresponding to the vnode - * can not be cached in object cache. Make it so. + * call ubc_sync_range(vp, 0, EOF, UBC_PUSHALL) on all the vnodes + * for this mount point. * returns 1 on success, 0 on failure - * - * Caller of ubc_uncache() MUST have a valid reference on the vnode. */ -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); - - /* - * AGE it so that vfree() can make sure that it - * would get recycled soon after the last reference is gone - * This will insure that .nfs turds would not linger - */ - vagevp(vp); - - /* set the "do not cache" bit */ - SET(uip->ui_flags, UI_DONTCACHE); - - didhold = ubc_hold(vp); - - object = _ubc_getobject(vp, UBC_NOREACTIVATE); - assert(object); +__private_extern__ int +ubc_umount(struct mount *mp) +{ + vnode_iterate(mp, 0, ubc_umcallback, 0); + return(0); +} - /* - * memory_object_change_attributes() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); +static int +ubc_umcallback(vnode_t vp, __unused void * args) +{ - perf.cluster_size = PAGE_SIZE; /* XXX use real cluster_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 (UBCINFOEXISTS(vp)) { - if (didhold) - ubc_rele(vp); + cluster_push(vp, 0); - if (kret != KERN_SUCCESS) { -#if DIAGNOSTIC - panic("ubc_uncache: memory_object_change_attributes " - "kret = %d", kret); -#endif /* DIAGNOSTIC */ - return (0); + (void) ubc_msync(vp, (off_t)0, ubc_getsize(vp), NULL, UBC_PUSHALL); } - - return (1); + return (VNODE_RETURNED); } -/* - * call ubc_clean() and ubc_uncache() on all the vnodes - * for this mount point. - * returns 1 on success, 0 on failure - */ -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); /* do not invalidate */ - ret &= ubc_uncache(vp); - ubc_release(vp); - } - simple_lock(&mntvnode_slock); - } - simple_unlock(&mntvnode_slock); - return (ret); -} -/* - * Call ubc_unmount() for all filesystems. - * The list is traversed in reverse order - * of mounting to avoid dependencies. - */ -void -ubc_unmountall() +/* Get the credentials */ +kauth_cred_t +ubc_getcred(struct vnode *vp) { - struct mount *mp, *nmp; + if (UBCINFOEXISTS(vp)) + return (vp->v_ubcinfo->ui_ucred); - /* - * Since this only runs when rebooting, it is not interlocked. - */ - for (mp = mountlist.cqh_last; mp != (void *)&mountlist; mp = nmp) { - nmp = mp->mnt_list.cqe_prev; - (void) ubc_umount(mp); - } + return (NOCRED); } -/* Get the credentials */ -struct ucred * -ubc_getcred(struct vnode *vp) +int +ubc_setthreadcred(struct vnode *vp, struct proc *p, thread_t thread) { struct ubc_info *uip; + kauth_cred_t credp; + struct uthread *uthread = get_bsdthread_info(thread); - assert(vp); + if (!UBCINFOEXISTS(vp)) + return (1); - uip = vp->v_ubcinfo; + vnode_lock(vp); - assert(uip); + uip = vp->v_ubcinfo; + credp = uip->ui_ucred; - if (UBCINVALID(vp)) { - return (NOCRED); + if (!IS_VALID_CRED(credp)) { + /* use per-thread cred, if assumed identity, else proc cred */ + if (uthread == NULL || (uthread->uu_flag & UT_SETUID) == 0) { + uip->ui_ucred = kauth_cred_proc_ref(p); + } else { + uip->ui_ucred = uthread->uu_ucred; + kauth_cred_ref(uip->ui_ucred); + } } + vnode_unlock(vp); - return (uip->ui_ucred); + return (0); } /* @@ -516,640 +384,611 @@ ubc_getcred(struct vnode *vp) * existing credentials are not changed * returns 1 on success and 0 on failure */ - 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); + kauth_cred_t credp; - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_setcred"); + if ( !UBCINFOEXISTS(vp)) return (0); - } + vnode_lock(vp); + + uip = vp->v_ubcinfo; credp = uip->ui_ucred; - if (credp == NOCRED) { - crhold(p->p_ucred); - uip->ui_ucred = p->p_ucred; + + if (!IS_VALID_CRED(credp)) { + uip->ui_ucred = kauth_cred_proc_ref(p); } + vnode_unlock(vp); return (1); } /* Get the pager */ -void * +__private_extern__ memory_object_t ubc_getpager(struct vnode *vp) { - struct ubc_info *uip; - - assert(vp); - - uip = vp->v_ubcinfo; - - assert(uip); + if (UBCINFOEXISTS(vp)) + return (vp->v_ubcinfo->ui_pager); - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_getpager"); - return (0); - } - - return (uip->ui_pager); + return (0); } /* * Get the memory object associated with this vnode * If the vnode was reactivated, memory object would not exist. * Unless "do not rectivate" was specified, look it up using the pager. - * The vm_object_lookup() would create a reference on the memory object. * If hold was requested create an object reference of one does not * exist already. */ -static void * -_ubc_getobject(struct vnode *vp, int flags) +memory_object_control_t +ubc_getobject(struct vnode *vp, __unused int flags) { - struct ubc_info *uip; - void *object; - - uip = vp->v_ubcinfo; - object = uip->ui_object; + if (UBCINFOEXISTS(vp)) + return((vp->v_ubcinfo->ui_control)); - if ((object == NULL) && ISSET(uip->ui_flags, UI_HASPAGER) - && !(flags & UBC_NOREACTIVATE)) { - void *pager_cport; + return (0); +} - 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 /* 0 */ +off_t +ubc_blktooff(vnode_t vp, daddr64_t blkno) +{ + off_t file_offset; + int error; - uip->ui_object = object; - } + if (UBCINVALID(vp)) + return ((off_t)-1); - if (object != NULL) - SET(uip->ui_flags, UI_HASOBJREF); - } + error = VNOP_BLKTOOFF(vp, blkno, &file_offset); + if (error) + file_offset = -1; - 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); + return (file_offset); } -void * -ubc_getobject(struct vnode *vp, int flags) +daddr64_t +ubc_offtoblk(vnode_t vp, off_t offset) { - struct ubc_info *uip; - void *object; - - assert(vp); - uip = vp->v_ubcinfo; - assert(uip); + daddr64_t blkno; + int error = 0; - if (UBCINVALID(vp)) { - return (0); - } + if (UBCINVALID(vp)) + return ((daddr64_t)-1); - object = _ubc_getobject(vp, flags); - assert(object); + error = VNOP_OFFTOBLK(vp, offset, &blkno); + if (error) + blkno = -1; - if (!ISSET(uip->ui_flags, (UI_HASOBJREF|UI_WASMAPPED)) - && !(uip->ui_holdcnt)) { - if (!(flags & UBC_PAGINGOP)) - panic("ubc_getobject: lost reference"); - } + return (blkno); } -/* Set the pager */ int -ubc_setpager(struct vnode *vp, void *pager) +ubc_pages_resident(vnode_t vp) { - struct ubc_info *uip; - - assert(vp); - - uip = vp->v_ubcinfo; + kern_return_t kret; + boolean_t has_pages_resident; + + if ( !UBCINFOEXISTS(vp)) + return (0); + + kret = memory_object_pages_resident(vp->v_ubcinfo->ui_control, &has_pages_resident); + + if (kret != KERN_SUCCESS) + return (0); + + if (has_pages_resident == TRUE) + return (1); + + return (0); +} - assert(uip); - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_setpager"); - return (0); - } - uip->ui_pager = pager; - return (1); +/* + * This interface will eventually be deprecated + * + * clean and/or invalidate a range in the memory object that backs this + * vnode. The start offset is truncated to the page boundary and the + * size is adjusted to include the last page in the range. + * + * returns 1 for success, 0 for failure + */ +int +ubc_sync_range(vnode_t vp, off_t beg_off, off_t end_off, int flags) +{ + return (ubc_msync_internal(vp, beg_off, end_off, NULL, flags, NULL)); } -int -ubc_setflags(struct vnode * vp, int flags) -{ - struct ubc_info *uip; - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_setflags"); - return (EINVAL); - } +/* + * clean and/or invalidate a range in the memory object that backs this + * vnode. The start offset is truncated to the page boundary and the + * size is adjusted to include the last page in the range. + * if a + */ +errno_t +ubc_msync(vnode_t vp, off_t beg_off, off_t end_off, off_t *resid_off, int flags) +{ + int retval; + int io_errno = 0; + + if (resid_off) + *resid_off = beg_off; - assert(vp); + retval = ubc_msync_internal(vp, beg_off, end_off, resid_off, flags, &io_errno); - uip = vp->v_ubcinfo; - - assert(uip); + if (retval == 0 && io_errno == 0) + return (EINVAL); + return (io_errno); +} - SET(uip->ui_flags, flags); - return(0); -} -int -ubc_clearflags(struct vnode * vp, int flags) +/* + * clean and/or invalidate a range in the memory object that backs this + * vnode. The start offset is truncated to the page boundary and the + * size is adjusted to include the last page in the range. + */ +static int +ubc_msync_internal(vnode_t vp, off_t beg_off, off_t end_off, off_t *resid_off, int flags, int *io_errno) { - struct ubc_info *uip; - - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_clearflags"); - return (EINVAL); - } + memory_object_size_t tsize; + kern_return_t kret; + int request_flags = 0; + int flush_flags = MEMORY_OBJECT_RETURN_NONE; + + if ( !UBCINFOEXISTS(vp)) + return (0); + if (end_off <= beg_off) + return (0); + if ((flags & (UBC_INVALIDATE | UBC_PUSHDIRTY | UBC_PUSHALL)) == 0) + return (0); + + if (flags & UBC_INVALIDATE) + /* + * discard the resident pages + */ + request_flags = (MEMORY_OBJECT_DATA_FLUSH | MEMORY_OBJECT_DATA_NO_CHANGE); - assert(vp); + if (flags & UBC_SYNC) + /* + * wait for all the I/O to complete before returning + */ + request_flags |= MEMORY_OBJECT_IO_SYNC; - uip = vp->v_ubcinfo; + if (flags & UBC_PUSHDIRTY) + /* + * we only return the dirty pages in the range + */ + flush_flags = MEMORY_OBJECT_RETURN_DIRTY; - assert(uip); + if (flags & UBC_PUSHALL) + /* + * then return all the interesting pages in the range (both dirty and precious) + * to the pager + */ + flush_flags = MEMORY_OBJECT_RETURN_ALL; - CLR(uip->ui_flags, flags); + beg_off = trunc_page_64(beg_off); + end_off = round_page_64(end_off); + tsize = (memory_object_size_t)end_off - beg_off; - return(0); -} + /* flush and/or invalidate pages in the range requested */ + kret = memory_object_lock_request(vp->v_ubcinfo->ui_control, + beg_off, tsize, resid_off, io_errno, + flush_flags, request_flags, VM_PROT_NO_CHANGE); + + return ((kret == KERN_SUCCESS) ? 1 : 0); +} -int -ubc_issetflags(struct vnode * vp, int flags) +/* + * The vnode is mapped explicitly, mark it so. + */ +__private_extern__ int +ubc_map(vnode_t vp, int flags) { struct ubc_info *uip; + int error = 0; + int need_ref = 0; + struct vfs_context context; - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_issetflags"); - return (EINVAL); - } - - assert(vp); - - uip = vp->v_ubcinfo; - - assert(uip); - - return(ISSET(uip->ui_flags, flags)); -} + if (vnode_getwithref(vp)) + return (0); -off_t -ubc_blktooff(struct vnode *vp, daddr_t blkno) -{ - off_t file_offset; - int error; + if (UBCINFOEXISTS(vp)) { + context.vc_proc = current_proc(); + context.vc_ucred = kauth_cred_get(); - assert(vp); - if (UBCINVALID(vp)) { - USHOULDNOT("ubc_blktooff"); - return ((off_t)-1); - } + error = VNOP_MMAP(vp, flags, &context); - error = VOP_BLKTOOFF(vp, blkno, &file_offset); - if (error) - file_offset = -1; + if (error != EPERM) + error = 0; - return (file_offset); -} -daddr_t -ubc_offtoblk(struct vnode *vp, off_t offset) -{ - daddr_t blkno; - int error=0; + if (error == 0) { + vnode_lock(vp); + + uip = vp->v_ubcinfo; - assert(vp); - if (UBCINVALID(vp)) { - return ((daddr_t)-1); - } + if ( !ISSET(uip->ui_flags, UI_ISMAPPED)) + need_ref = 1; + SET(uip->ui_flags, (UI_WASMAPPED | UI_ISMAPPED)); - error = VOP_OFFTOBLK(vp, offset, &blkno); - if (error) - blkno = -1; + vnode_unlock(vp); + + if (need_ref) + vnode_ref(vp); + } + } + vnode_put(vp); - return (blkno); + return (error); } /* - * Cause the file data in VM to be pushed out to the storage - * it also causes all currently valid pages to be released - * returns 1 on success, 0 on failure + * destroy the named reference for a given vnode */ -int -ubc_clean(struct vnode *vp, int invalidate) +__private_extern__ int +ubc_destroy_named(struct vnode *vp) { - off_t size; + memory_object_control_t control; 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 was requested, write dirty data and then discard - * the resident pages + * We may already have had the object terminated + * and the ubcinfo released as a side effect of + * some earlier processing. If so, pretend we did + * it, because it probably was a result of our + * efforts. */ - if (invalidate) - flags = (MEMORY_OBJECT_DATA_FLUSH | MEMORY_OBJECT_DATA_NO_CHANGE); + if (!UBCINFOEXISTS(vp)) + return (1); - didhold = ubc_hold(vp); uip = vp->v_ubcinfo; - size = uip->ui_size; /* call ubc_getsize() ??? */ - - object = _ubc_getobject(vp, UBC_NOREACTIVATE); - assert(object); /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it + * Terminate the memory object. + * memory_object_destroy() will result in + * vnode_pager_no_senders(). + * That will release the pager reference + * and the vnode will move to the free list. */ - ubc_getobjref(vp); + control = ubc_getobject(vp, UBC_HOLDOBJECT); + if (control != MEMORY_OBJECT_CONTROL_NULL) { - vp->v_flag &= ~VHASDIRTY; - vp->v_clen = 0; + /* + * XXXXX - should we hold the vnode lock here? + */ + if (ISSET(vp->v_flag, VTERMINATE)) + panic("ubc_destroy_named: already teminating"); + SET(vp->v_flag, VTERMINATE); - /* Write the dirty data in the file and discard cached pages */ - 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); + kret = memory_object_destroy(control, 0); + if (kret != KERN_SUCCESS) + return (0); - if (kret != KERN_SUCCESS) { - printf("ubc_clean: clean failed (error = %d)\n", kret); + /* + * memory_object_destroy() is asynchronous + * with respect to vnode_pager_no_senders(). + * wait for vnode_pager_no_senders() to clear + * VTERMINATE + */ + vnode_lock(vp); + while (ISSET(vp->v_lflag, VNAMED_UBC)) { + (void)msleep((caddr_t)&vp->v_lflag, &vp->v_lock, + PINOD, "ubc_destroy_named", 0); + } + vnode_unlock(vp); } - - if (didhold) - ubc_rele(vp); - - return ((kret == KERN_SUCCESS) ? 1 : 0); + return (1); } + /* - * Cause the file data in VM to be pushed out to the storage - * currently valid pages are NOT invalidated - * returns 1 on success, 0 on failure + * Find out whether a vnode is in use by UBC + * Returns 1 if file is in use by UBC, 0 if not */ int -ubc_pushdirty(struct vnode *vp) +ubc_isinuse(struct vnode *vp, int busycount) { - 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; /* call ubc_getsize() ??? */ - - object = _ubc_getobject(vp, UBC_NOREACTIVATE); - assert(object); + if ( !UBCINFOEXISTS(vp)) + return (0); + return(ubc_isinuse_locked(vp, busycount, 0)); +} - /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); - vp->v_flag &= ~VHASDIRTY; - vp->v_clen = 0; +int +ubc_isinuse_locked(struct vnode *vp, int busycount, int locked) +{ + int retval = 0; - /* Write the dirty data in the file and discard cached pages */ - 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 (!locked) + vnode_lock(vp); - if (didhold) - ubc_rele(vp); + if ((vp->v_usecount - vp->v_kusecount) > busycount) + retval = 1; - return ((kret == KERN_SUCCESS) ? 1 : 0); + if (!locked) + vnode_unlock(vp); + return (retval); } + /* - * Make sure the vm object does not vanish - * returns 1 if the hold count was incremented - * returns 0 if the hold count was not incremented - * This return value should be used to balance - * ubc_hold() and ubc_rele(). + * MUST only be called by the VM */ -int -ubc_hold(struct vnode *vp) +__private_extern__ void +ubc_unmap(struct vnode *vp) { + struct vfs_context context; struct ubc_info *uip; - void *object; + int need_rele = 0; - if (UBCINVALID(vp)) - return (0); + if (vnode_getwithref(vp)) + return; - if (!UBCINFOEXISTS(vp)) { - /* nothing more to do for a dying vnode */ - 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"); + if (UBCINFOEXISTS(vp)) { + vnode_lock(vp); - return (1); + uip = vp->v_ubcinfo; + if (ISSET(uip->ui_flags, UI_ISMAPPED)) { + CLR(uip->ui_flags, UI_ISMAPPED); + need_rele = 1; + } + vnode_unlock(vp); + + if (need_rele) { + context.vc_proc = current_proc(); + context.vc_ucred = kauth_cred_get(); + (void)VNOP_MNOMAP(vp, &context); + + vnode_rele(vp); + } + } + /* + * the drop of the vnode ref will cleanup + */ + vnode_put(vp); } -/* relese the reference on the vm object */ -void -ubc_rele(struct vnode *vp) +kern_return_t +ubc_page_op( + struct vnode *vp, + off_t f_offset, + int ops, + ppnum_t *phys_entryp, + int *flagsp) { - struct ubc_info *uip; - void *object; + memory_object_control_t control; - if (UBCINVALID(vp)) - return; + control = ubc_getobject(vp, UBC_FLAGS_NONE); + if (control == MEMORY_OBJECT_CONTROL_NULL) + return KERN_INVALID_ARGUMENT; - if (!UBCINFOEXISTS(vp)) { - /* nothing more to do for a dying vnode */ - if ((vp->v_flag & VXLOCK) || (vp->v_flag & VTERMINATE)) - return; - panic("ubc_rele: can not"); - } + return (memory_object_page_op(control, + (memory_object_offset_t)f_offset, + ops, + phys_entryp, + flagsp)); +} + +__private_extern__ kern_return_t +ubc_page_op_with_control( + memory_object_control_t control, + off_t f_offset, + int ops, + ppnum_t *phys_entryp, + int *flagsp) +{ + return (memory_object_page_op(control, + (memory_object_offset_t)f_offset, + ops, + phys_entryp, + flagsp)); +} + +kern_return_t +ubc_range_op( + struct vnode *vp, + off_t f_offset_beg, + off_t f_offset_end, + int ops, + int *range) +{ + memory_object_control_t control; - uip = vp->v_ubcinfo; + control = ubc_getobject(vp, UBC_FLAGS_NONE); + if (control == MEMORY_OBJECT_CONTROL_NULL) + return KERN_INVALID_ARGUMENT; - /* get the object before loosing to hold count */ - object = _ubc_getobject(vp, UBC_NOREACTIVATE); + return (memory_object_range_op(control, + (memory_object_offset_t)f_offset_beg, + (memory_object_offset_t)f_offset_end, + ops, + range)); +} + +kern_return_t +ubc_create_upl( + struct vnode *vp, + off_t f_offset, + long bufsize, + upl_t *uplp, + upl_page_info_t **plp, + int uplflags) +{ + memory_object_control_t control; + int count; + int ubcflags; + kern_return_t kr; + + if (bufsize & 0xfff) + return KERN_INVALID_ARGUMENT; - if (uip->ui_holdcnt == 0) - panic("ubc_rele: ui_holdcnt"); + if (uplflags & UPL_FOR_PAGEOUT) { + uplflags &= ~UPL_FOR_PAGEOUT; + ubcflags = UBC_FOR_PAGEOUT; + } else + ubcflags = UBC_FLAGS_NONE; - if (--uip->ui_holdcnt == 0) { - /* If the object is already dead do nothing */ - if (object) - vm_object_deallocate(object); -#if DIAGNOSTIC - else - printf("ubc_rele: null object for %x", vp); -#endif /* DIAGNOSTIC */ - } + control = ubc_getobject(vp, ubcflags); + if (control == MEMORY_OBJECT_CONTROL_NULL) + return KERN_INVALID_ARGUMENT; - return; + if (uplflags & UPL_WILL_BE_DUMPED) { + uplflags &= ~UPL_WILL_BE_DUMPED; + uplflags |= (UPL_NO_SYNC|UPL_SET_INTERNAL); + } else + uplflags |= (UPL_NO_SYNC|UPL_CLEAN_IN_PLACE|UPL_SET_INTERNAL); + count = 0; + kr = memory_object_upl_request(control, f_offset, bufsize, + uplp, NULL, &count, uplflags); + if (plp != NULL) + *plp = UPL_GET_INTERNAL_PAGE_LIST(*uplp); + return kr; } + -/* - * The vnode is mapped explicitly - * Mark it so, and release the vm object reference gained in - * ubc_info_init() - */ -void -ubc_map(struct vnode *vp) +kern_return_t +ubc_upl_map( + upl_t upl, + vm_offset_t *dst_addr) { - 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; + return (vm_upl_map(kernel_map, upl, dst_addr)); +} - SET(uip->ui_flags, UI_WASMAPPED); - uip->ui_mapped = 1; - ubc_unlock(vp); -#if 1 - /* - * Do not release the ubc reference on the - * memory object right away. Let vnreclaim - * deal with that - */ -#else - /* - * Release the ubc reference. memory object cahe - * is responsible for caching this object now. - */ - 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 +kern_return_t +ubc_upl_unmap( + upl_t upl) +{ + return(vm_upl_unmap(kernel_map, upl)); +} - return; +kern_return_t +ubc_upl_commit( + upl_t upl) +{ + upl_page_info_t *pl; + kern_return_t kr; + pl = UPL_GET_INTERNAL_PAGE_LIST(upl); + kr = upl_commit(upl, pl, MAX_UPL_TRANSFER); + upl_deallocate(upl); + return kr; } -/* - * Release the memory object reference on the vnode - * only if it is not in use - * Return 1 if the reference was released, 0 otherwise. - */ -int -ubc_release(struct vnode *vp) + +kern_return_t +ubc_upl_commit_range( + upl_t upl, + vm_offset_t offset, + vm_size_t size, + int flags) { - struct ubc_info *uip; - void *object; -#if DIAGNOSTIC - assert(vp); -#endif + upl_page_info_t *pl; + boolean_t empty; + kern_return_t kr; - if (UBCINVALID(vp)) - return (0); + if (flags & UPL_COMMIT_FREE_ON_EMPTY) + flags |= UPL_COMMIT_NOTIFY_EMPTY; - if (!UBCINFOEXISTS(vp)) - panic("ubc_release: can not"); + pl = UPL_GET_INTERNAL_PAGE_LIST(upl); - uip = vp->v_ubcinfo; + kr = upl_commit_range(upl, offset, size, flags, + pl, MAX_UPL_TRANSFER, &empty); - /* can not release held vnodes */ - if (uip->ui_holdcnt) - return (0); + if((flags & UPL_COMMIT_FREE_ON_EMPTY) && empty) + upl_deallocate(upl); - 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); + return kr; } - -/* - * Invalidate a range in the memory object that backs this - * vnode. The offset is truncated to the page boundary and the - * size is adjusted to include the last page in the range. - */ -int -ubc_invalidate(struct vnode *vp, off_t offset, size_t size) + +kern_return_t +ubc_upl_abort_range( + upl_t upl, + vm_offset_t offset, + vm_size_t size, + int abort_flags) { - struct ubc_info *uip; - void *object; - kern_return_t kret; - off_t toff; - size_t tsize; - int didhold; + kern_return_t kr; + boolean_t empty = FALSE; -#if DIAGNOSTIC - assert(vp); -#endif + if (abort_flags & UPL_ABORT_FREE_ON_EMPTY) + abort_flags |= UPL_ABORT_NOTIFY_EMPTY; - if (UBCINVALID(vp)) - return; + kr = upl_abort_range(upl, offset, size, abort_flags, &empty); - 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); - - /* - * memory_object_lock_request() drops an object - * reference. gain a reference before calling it - */ - ubc_getobjref(vp); - - /* invalidate pages in the range requested */ - 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); + if((abort_flags & UPL_ABORT_FREE_ON_EMPTY) && empty) + upl_deallocate(upl); - return ((kret == KERN_SUCCESS) ? 1 : 0); + return kr; } -/* - * Find out whether a vnode is in use by UBC - * Returns 1 if file is in use by UBC, 0 if not - */ -int -ubc_isinuse(struct vnode *vp, int tookref) +kern_return_t +ubc_upl_abort( + upl_t upl, + int abort_type) { - int busycount = tookref ? 2 : 1; + kern_return_t kr; - if (!UBCINFOEXISTS(vp)) - return(0); - - if (vp->v_usecount > busycount) - return (1); + kr = upl_abort(upl, abort_type); + upl_deallocate(upl); + return kr; +} - if ((vp->v_usecount == busycount) - && (vp->v_ubcinfo->ui_mapped == 1)) - return(1); - else - return(0); +upl_page_info_t * +ubc_upl_pageinfo( + upl_t upl) +{ + return (UPL_GET_INTERNAL_PAGE_LIST(upl)); } +/************* UBC APIS **************/ -/* -- UGLY HACK ALERT -- */ -/* - * The backdoor routine to clear the UI_WASMAPPED bit. - * MUST only be called by the VM - * - * Note that this routine is not under funnel. There are numerous - * thing about the calling sequence that make this work on SMP. - * Any code change in those paths can break this. - * - * This will be replaced soon. - */ -void -ubc_unmap(struct vnode *vp) +int +UBCINFOMISSING(struct vnode * vp) { - struct ubc_info *uip; - -#if DIAGNOSTIC - assert(vp); -#endif - - if (UBCINVALID(vp)) { - return; - } - - if (!UBCINFOEXISTS(vp)) - panic("ubc_unmap: can not"); + return((vp) && ((vp)->v_type == VREG) && ((vp)->v_ubcinfo == UBC_INFO_NULL)); +} - ubc_lock(vp); - uip = vp->v_ubcinfo; +int +UBCINFORECLAIMED(struct vnode * vp) +{ + return((vp) && ((vp)->v_type == VREG) && ((vp)->v_ubcinfo == UBC_INFO_NULL)); +} - uip->ui_mapped = 0; - ubc_unlock(vp); - return; +int +UBCINFOEXISTS(struct vnode * vp) +{ + return((vp) && ((vp)->v_type == VREG) && ((vp)->v_ubcinfo != UBC_INFO_NULL)); +} +int +UBCISVALID(struct vnode * vp) +{ + return((vp) && ((vp)->v_type == VREG) && !((vp)->v_flag & VSYSTEM)); +} +int +UBCINVALID(struct vnode * vp) +{ + return(((vp) == NULL) || ((vp) && ((vp)->v_type != VREG)) + || ((vp) && ((vp)->v_flag & VSYSTEM))); +} +int +UBCINFOCHECK(const char * fun, struct vnode * vp) +{ + if ((vp) && ((vp)->v_type == VREG) && + ((vp)->v_ubcinfo == UBC_INFO_NULL)) { + panic("%s: lost ubc_info", (fun)); + return(1); + } else + return(0); }