/* * Copyright (c) 1999-2001 Apple Computer, Inc. All rights reserved. * * @APPLE_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 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. * * @APPLE_LICENSE_HEADER_END@ */ /* * File: ubc_subr.c * Author: Umesh Vaishampayan [umeshv@apple.com] * 05-Aug-1999 umeshv Created. * * 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 #if DIAGNOSTIC #if defined(assert) #undef assert() #endif #define assert(cond) \ ((void) ((cond) ? 0 : panic("%s:%d (%s)", __FILE__, __LINE__, # cond))) #else #include #endif /* DIAGNOSTIC */ struct zone *ubc_info_zone; /* lock for changes to struct UBC */ static __inline__ void ubc_lock(struct vnode *vp) { /* For now, just use the v_interlock */ simple_lock(&vp->v_interlock); } /* unlock */ static __inline__ void ubc_unlock(struct vnode *vp) { /* For now, just use the v_interlock */ simple_unlock(&vp->v_interlock); } /* * Initialization of the zone for Unified Buffer Cache. */ __private_extern__ void ubc_init() { int i; i = (vm_size_t) sizeof (struct ubc_info); /* XXX the number of elements should be tied in to maxvnodes */ ubc_info_zone = zinit (i, 10000*i, 8192, "ubc_info zone"); return; } /* * Initialize a ubc_info structure for a vnode. */ 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; memory_object_control_t control; if (!UBCISVALID(vp)) return (EINVAL); 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); } uip = vp->v_ubcinfo; if ((uip == UBC_INFO_NULL) || (uip == UBC_NOINFO)) { ubc_unlock(vp); uip = (struct ubc_info *) zalloc(ubc_info_zone); uip->ui_pager = MEMORY_OBJECT_NULL; uip->ui_control = MEMORY_OBJECT_CONTROL_NULL; uip->ui_flags = UI_INITED; uip->ui_vnode = vp; uip->ui_ucred = NOCRED; uip->ui_refcount = 1; uip->ui_size = 0; uip->ui_mapped = 0; ubc_lock(vp); } #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); ubc_setpager(vp, pager); /* * Note: We can not use VOP_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 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. */ 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 */ /* create a pager reference on the vnode */ error = vnode_pager_vget(vp); if (error) panic("ubc_info_init: vnode_pager_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); return (error); } /* Free the ubc_info */ static void ubc_info_free(struct ubc_info *uip) { struct ucred *credp; credp = uip->ui_ucred; if (credp != NOCRED) { uip->ui_ucred = NOCRED; crfree(credp); } if (uip->ui_control != MEMORY_OBJECT_CONTROL_NULL) memory_object_control_deallocate(uip->ui_control); zfree(ubc_info_zone, (vm_offset_t)uip); return; } void ubc_info_deallocate(struct ubc_info *uip) { assert(uip->ui_refcount > 0); if (uip->ui_refcount-- == 1) ubc_info_free(uip); } /* * Communicate with VM the size change of the file * returns 1 on success, 0 on failure */ int ubc_setsize(struct vnode *vp, off_t nsize) { off_t osize; /* ui_size before change */ off_t lastpg, olastpgend, lastoff; struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; assert(nsize >= (off_t)0); if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); uip = vp->v_ubcinfo; osize = uip->ui_size; /* call ubc_getsize() ??? */ /* Update the size before flushing the VM */ uip->ui_size = nsize; if (nsize >= osize) /* Nothing more to do */ return (1); /* return success */ /* * When the file shrinks, invalidate the pages beyond the * new size. Also get rid of garbage beyond nsize on the * last page. The ui_size already has the nsize. This * insures that the pageout would not write beyond the new * end of the file. */ lastpg = trunc_page_64(nsize); olastpgend = round_page_64(osize); control = uip->ui_control; assert(control); lastoff = (nsize & PAGE_MASK_64); /* * If length is multiple of page size, we should not flush * invalidating is sufficient */ if (!lastoff) { /* invalidate last page and old contents beyond nsize */ kret = memory_object_lock_request(control, (memory_object_offset_t)lastpg, (memory_object_size_t)(olastpgend - lastpg), 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); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* flush the last page */ kret = memory_object_lock_request(control, (memory_object_offset_t)lastpg, PAGE_SIZE_64, MEMORY_OBJECT_RETURN_DIRTY, FALSE, VM_PROT_NO_CHANGE); if (kret == KERN_SUCCESS) { /* invalidate last page and old contents beyond nsize */ kret = memory_object_lock_request(control, (memory_object_offset_t)lastpg, (memory_object_size_t)(olastpgend - lastpg), 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); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* * Get the size of the file */ off_t ubc_getsize(struct vnode *vp) { return (vp->v_ubcinfo->ui_size); } /* * Caller indicate that the object corresponding to the vnode * can not be cached in object cache. Make it so. * returns 1 on success, 0 on failure */ int ubc_uncache(struct vnode *vp) { kern_return_t kret; struct ubc_info *uip; memory_object_control_t control; memory_object_perf_info_data_t perf; 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); control = uip->ui_control; assert(control); perf.cluster_size = PAGE_SIZE; /* XXX use real cluster_size. */ perf.may_cache = FALSE; kret = memory_object_change_attributes(control, MEMORY_OBJECT_PERFORMANCE_INFO, (memory_object_info_t) &perf, MEMORY_OBJECT_PERF_INFO_COUNT); if (kret != KERN_SUCCESS) { printf("ubc_uncache: memory_object_change_attributes_named " "kret = %d", kret); return (0); } ubc_release_named(vp); return (1); } /* * call ubc_clean() and ubc_uncache() on all the vnodes * for this mount point. * returns 1 on success, 0 on failure */ __private_extern__ 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)) { /* * Must get a valid reference on the vnode * before callig UBC functions */ if (vget(vp, 0, p)) { ret = 0; simple_lock(&mntvnode_slock); continue; /* move on to the next vnode */ } ret &= ubc_clean(vp, 0); /* do not invalidate */ ret &= ubc_uncache(vp); vrele(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. */ __private_extern__ void ubc_unmountall() { struct mount *mp, *nmp; /* * 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); } } /* Get the credentials */ struct ucred * ubc_getcred(struct vnode *vp) { struct ubc_info *uip; uip = vp->v_ubcinfo; if (UBCINVALID(vp)) return (NOCRED); return (uip->ui_ucred); } /* * Set the credentials * 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; uip = vp->v_ubcinfo; if (UBCINVALID(vp)) return (0); credp = uip->ui_ucred; if (credp == NOCRED) { crhold(p->p_ucred); uip->ui_ucred = p->p_ucred; } return (1); } /* Get the pager */ __private_extern__ memory_object_t ubc_getpager(struct vnode *vp) { struct ubc_info *uip; uip = vp->v_ubcinfo; if (UBCINVALID(vp)) return (0); return (uip->ui_pager); } /* * 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. * If hold was requested create an object reference of one does not * exist already. */ memory_object_control_t ubc_getobject(struct vnode *vp, int flags) { struct ubc_info *uip; memory_object_control_t control; uip = vp->v_ubcinfo; if (UBCINVALID(vp)) return (0); ubc_lock(vp); control = uip->ui_control; if ((flags & UBC_HOLDOBJECT) && (!ISSET(uip->ui_flags, UI_HASOBJREF))) { /* * Take a temporary reference on the ubc info so that it won't go * away during our recovery attempt. */ uip->ui_refcount++; ubc_unlock(vp); if (memory_object_recover_named(control, TRUE) == KERN_SUCCESS) { ubc_lock(vp); SET(uip->ui_flags, UI_HASOBJREF); ubc_unlock(vp); } else { control = MEMORY_OBJECT_CONTROL_NULL; } ubc_info_deallocate(uip); } else { ubc_unlock(vp); } return (control); } /* Set the pager */ int ubc_setpager(struct vnode *vp, memory_object_t pager) { struct ubc_info *uip; uip = vp->v_ubcinfo; if (UBCINVALID(vp)) return (0); uip->ui_pager = pager; return (1); } int ubc_setflags(struct vnode * vp, int flags) { struct ubc_info *uip; if (UBCINVALID(vp)) return (0); uip = vp->v_ubcinfo; SET(uip->ui_flags, flags); return (1); } int ubc_clearflags(struct vnode * vp, int flags) { struct ubc_info *uip; if (UBCINVALID(vp)) return (0); uip = vp->v_ubcinfo; CLR(uip->ui_flags, flags); return (1); } int ubc_issetflags(struct vnode * vp, int flags) { struct ubc_info *uip; if (UBCINVALID(vp)) return (0); uip = vp->v_ubcinfo; return (ISSET(uip->ui_flags, flags)); } off_t ubc_blktooff(struct vnode *vp, daddr_t blkno) { off_t file_offset; int error; if (UBCINVALID(vp)) 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; if (UBCINVALID(vp)) { return ((daddr_t)-1); } error = VOP_OFFTOBLK(vp, offset, &blkno); if (error) blkno = -1; return (blkno); } /* * 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 */ int ubc_clean(struct vnode *vp, int invalidate) { off_t size; struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; int flags = 0; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); /* * if invalidate was requested, write dirty data and then discard * the resident pages */ if (invalidate) flags = (MEMORY_OBJECT_DATA_FLUSH | MEMORY_OBJECT_DATA_NO_CHANGE); uip = vp->v_ubcinfo; size = uip->ui_size; /* call ubc_getsize() ??? */ control = uip->ui_control; assert(control); vp->v_flag &= ~VHASDIRTY; vp->v_clen = 0; /* Write the dirty data in the file and discard cached pages */ kret = memory_object_lock_request(control, (memory_object_offset_t)0, (memory_object_size_t)round_page_64(size), MEMORY_OBJECT_RETURN_ALL, flags, VM_PROT_NO_CHANGE); if (kret != KERN_SUCCESS) printf("ubc_clean: clean failed (error = %d)\n", kret); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* * 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 */ int ubc_pushdirty(struct vnode *vp) { off_t size; struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); uip = vp->v_ubcinfo; size = uip->ui_size; /* call ubc_getsize() ??? */ control = uip->ui_control; assert(control); vp->v_flag &= ~VHASDIRTY; vp->v_clen = 0; /* Write the dirty data in the file and discard cached pages */ kret = memory_object_lock_request(control, (memory_object_offset_t)0, (memory_object_size_t)round_page_64(size), MEMORY_OBJECT_RETURN_DIRTY, FALSE, VM_PROT_NO_CHANGE); if (kret != KERN_SUCCESS) printf("ubc_pushdirty: flush failed (error = %d)\n", kret); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* * 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 */ int ubc_pushdirty_range(struct vnode *vp, off_t offset, off_t size) { struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); uip = vp->v_ubcinfo; control = uip->ui_control; assert(control); /* Write any dirty pages in the requested range of the file: */ kret = memory_object_lock_request(control, (memory_object_offset_t)offset, (memory_object_size_t)round_page_64(size), MEMORY_OBJECT_RETURN_DIRTY, FALSE, VM_PROT_NO_CHANGE); if (kret != KERN_SUCCESS) printf("ubc_pushdirty_range: flush failed (error = %d)\n", kret); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* * 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(). */ int ubc_hold(struct vnode *vp) { struct ubc_info *uip; memory_object_control_t object; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) { /* must be invalid or dying vnode */ assert(UBCINVALID(vp) || ((vp->v_flag & VXLOCK) || (vp->v_flag & VTERMINATE))); return (0); } uip = vp->v_ubcinfo; assert(uip->ui_control != MEMORY_OBJECT_CONTROL_NULL); ubc_lock(vp); uip->ui_refcount++; if (!ISSET(uip->ui_flags, UI_HASOBJREF)) { ubc_unlock(vp); if (memory_object_recover_named(uip->ui_control, TRUE) != KERN_SUCCESS) { ubc_info_deallocate(uip); return (0); } ubc_lock(vp); SET(uip->ui_flags, UI_HASOBJREF); ubc_unlock(vp); } else { ubc_unlock(vp); } assert(uip->ui_refcount > 0); return (1); } /* * Drop the holdcount. * release the reference on the vm object if the this is "uncached" * ubc_info. */ void ubc_rele(struct vnode *vp) { struct ubc_info *uip; if (UBCINVALID(vp)) return; 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"); } uip = vp->v_ubcinfo; if (uip->ui_refcount == 1) panic("ubc_rele: ui_refcount"); --uip->ui_refcount; if ((uip->ui_refcount == 1) && ISSET(uip->ui_flags, UI_DONTCACHE)) (void) ubc_release_named(vp); return; } /* * The vnode is mapped explicitly, mark it so. */ __private_extern__ void ubc_map(struct vnode *vp) { struct ubc_info *uip; if (UBCINVALID(vp)) return; if (!UBCINFOEXISTS(vp)) return; ubc_lock(vp); uip = vp->v_ubcinfo; SET(uip->ui_flags, UI_WASMAPPED); uip->ui_mapped = 1; ubc_unlock(vp); return; } /* * 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_named(struct vnode *vp) { struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); uip = vp->v_ubcinfo; /* can not release held or mapped vnodes */ if (ISSET(uip->ui_flags, UI_HASOBJREF) && (uip->ui_refcount == 1) && !uip->ui_mapped) { control = uip->ui_control; assert(control); CLR(uip->ui_flags, UI_HASOBJREF); kret = memory_object_release_name(control, MEMORY_OBJECT_RESPECT_CACHE); return ((kret != KERN_SUCCESS) ? 0 : 1); } else return (0); } /* * This function used to called by extensions directly. Some may * still exist with this behavior. In those cases, we will do the * release as part of reclaiming or cleaning the vnode. We don't * need anything explicit - so just stub this out until those callers * get cleaned up. */ int ubc_release( struct vnode *vp) { return 0; } /* * destroy the named reference for a given vnode */ __private_extern__ int ubc_destroy_named( struct vnode *vp) { memory_object_control_t control; struct proc *p; struct ubc_info *uip; kern_return_t kret; /* * 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 (!UBCINFOEXISTS(vp)) return (1); uip = vp->v_ubcinfo; /* can not destroy held vnodes */ if (uip->ui_refcount > 1) return (0); /* * 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. */ control = ubc_getobject(vp, UBC_HOLDOBJECT); if (control != MEMORY_OBJECT_CONTROL_NULL) { if (ISSET(vp->v_flag, VTERMINATE)) panic("ubc_destroy_named: already teminating"); SET(vp->v_flag, VTERMINATE); kret = memory_object_destroy(control, 0); if (kret != KERN_SUCCESS) return (0); /* * memory_object_destroy() is asynchronous * with respect to vnode_pager_no_senders(). * wait for vnode_pager_no_senders() to clear * VTERMINATE */ while (ISSET(vp->v_flag, VTERMINATE)) { SET(vp->v_flag, VTERMWANT); (void)tsleep((caddr_t)&vp->v_ubcinfo, PINOD, "ubc_destroy_named", 0); } } return (1); } /* * 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) { struct ubc_info *uip; memory_object_control_t control; kern_return_t kret; off_t toff; size_t tsize; if (UBCINVALID(vp)) return (0); if (!UBCINFOEXISTS(vp)) return (0); toff = trunc_page_64(offset); tsize = (size_t)(round_page_64(offset+size) - toff); uip = vp->v_ubcinfo; control = uip->ui_control; assert(control); /* invalidate pages in the range requested */ kret = memory_object_lock_request(control, (memory_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); if (kret != KERN_SUCCESS) printf("ubc_invalidate: invalidate failed (error = %d)\n", kret); return ((kret == KERN_SUCCESS) ? 1 : 0); } /* * 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) { int busycount = tookref ? 2 : 1; if (!UBCINFOEXISTS(vp)) return (0); if (tookref == 0) { printf("ubc_isinuse: called without a valid reference" ": v_tag = %d\v", vp->v_tag); vprint("ubc_isinuse", 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); } /* * The backdoor routine to clear the ui_mapped. * MUST only be called by the VM * * Note that this routine is not called under funnel. There are numerous * things about the calling sequence that make this work on SMP. * Any code change in those paths can break this. * */ __private_extern__ void ubc_unmap(struct vnode *vp) { struct ubc_info *uip; boolean_t funnel_state; if (UBCINVALID(vp)) return; if (!UBCINFOEXISTS(vp)) return; ubc_lock(vp); uip = vp->v_ubcinfo; uip->ui_mapped = 0; if ((uip->ui_refcount > 1) || !ISSET(uip->ui_flags, UI_DONTCACHE)) { ubc_unlock(vp); return; } ubc_unlock(vp); funnel_state = thread_funnel_set(kernel_flock, TRUE); (void) ubc_release_named(vp); (void) thread_funnel_set(kernel_flock, funnel_state); } kern_return_t ubc_page_op( struct vnode *vp, off_t f_offset, int ops, vm_offset_t *phys_entryp, int *flagsp) { memory_object_control_t control; control = ubc_getobject(vp, UBC_FLAGS_NONE); if (control == MEMORY_OBJECT_CONTROL_NULL) return KERN_INVALID_ARGUMENT; return (memory_object_page_op(control, (memory_object_offset_t)f_offset, ops, phys_entryp, flagsp)); } 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; off_t file_offset; kern_return_t kr; if (bufsize & 0xfff) return KERN_INVALID_ARGUMENT; control = ubc_getobject(vp, UBC_FLAGS_NONE); if (control == MEMORY_OBJECT_CONTROL_NULL) return KERN_INVALID_ARGUMENT; 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; } kern_return_t ubc_upl_map( upl_t upl, vm_offset_t *dst_addr) { return (vm_upl_map(kernel_map, upl, dst_addr)); } kern_return_t ubc_upl_unmap( upl_t upl) { return(vm_upl_unmap(kernel_map, upl)); } 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; } kern_return_t ubc_upl_commit_range( upl_t upl, vm_offset_t offset, vm_size_t size, int flags) { upl_page_info_t *pl; boolean_t empty; kern_return_t kr; if (flags & UPL_COMMIT_FREE_ON_EMPTY) flags |= UPL_COMMIT_NOTIFY_EMPTY; pl = UPL_GET_INTERNAL_PAGE_LIST(upl); kr = upl_commit_range(upl, offset, size, flags, pl, MAX_UPL_TRANSFER, &empty); if((flags & UPL_COMMIT_FREE_ON_EMPTY) && empty) upl_deallocate(upl); return kr; } kern_return_t ubc_upl_abort_range( upl_t upl, vm_offset_t offset, vm_size_t size, int abort_flags) { kern_return_t kr; boolean_t empty = FALSE; if (abort_flags & UPL_ABORT_FREE_ON_EMPTY) abort_flags |= UPL_ABORT_NOTIFY_EMPTY; kr = upl_abort_range(upl, offset, size, abort_flags, &empty); if((abort_flags & UPL_ABORT_FREE_ON_EMPTY) && empty) upl_deallocate(upl); return kr; } kern_return_t ubc_upl_abort( upl_t upl, int abort_type) { kern_return_t kr; kr = upl_abort(upl, abort_type); upl_deallocate(upl); return kr; } upl_page_info_t * ubc_upl_pageinfo( upl_t upl) { return (UPL_GET_INTERNAL_PAGE_LIST(upl)); }