version 1.36, 2010/01/30 12:06:20 |
version 1.36.2.36, 2010/11/19 02:30:41 |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
#include "opt_xip.h" |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/proc.h> |
#include <sys/proc.h> |
Line 48 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 50 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/kauth.h> |
#include <sys/kauth.h> |
#include <sys/fstrans.h> |
#include <sys/fstrans.h> |
#include <sys/buf.h> |
#include <sys/buf.h> |
|
#include <sys/once.h> |
|
|
#include <miscfs/genfs/genfs.h> |
#include <miscfs/genfs/genfs.h> |
#include <miscfs/genfs/genfs_node.h> |
#include <miscfs/genfs/genfs_node.h> |
Line 56 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 59 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <uvm/uvm.h> |
#include <uvm/uvm.h> |
#include <uvm/uvm_pager.h> |
#include <uvm/uvm_pager.h> |
|
|
|
static int genfs_do_getpages(void *); |
|
#ifdef XIP |
|
static int genfs_do_getpages_xip(void *); |
|
static int genfs_do_getpages_xip1(struct vnode *, voff_t, struct vm_page **, |
|
int *, int, vm_prot_t, int, int); |
|
static int genfs_do_getpages_xip_io(struct vnode *, voff_t, struct vm_page **, |
|
int *, int, vm_prot_t, int, int); |
|
static int genfs_do_putpages_xip(struct vnode *, off_t, off_t, int, |
|
struct vm_page **); |
|
#endif |
static int genfs_do_directio(struct vmspace *, vaddr_t, size_t, struct vnode *, |
static int genfs_do_directio(struct vmspace *, vaddr_t, size_t, struct vnode *, |
off_t, enum uio_rw); |
off_t, enum uio_rw); |
static void genfs_dio_iodone(struct buf *); |
static void genfs_dio_iodone(struct buf *); |
|
|
static int genfs_do_io(struct vnode *, off_t, vaddr_t, size_t, int, enum uio_rw, |
static int genfs_do_io(struct vnode *, off_t, vaddr_t, size_t, int, enum uio_rw, |
void (*)(struct buf *)); |
void (*)(struct buf *)); |
static inline void genfs_rel_pages(struct vm_page **, int); |
static void genfs_rel_pages(struct vm_page **, int); |
|
static void genfs_markdirty(struct vnode *); |
|
|
int genfs_maxdio = MAXPHYS; |
int genfs_maxdio = MAXPHYS; |
|
|
static inline void |
static void |
genfs_rel_pages(struct vm_page **pgs, int npages) |
genfs_rel_pages(struct vm_page **pgs, int npages) |
{ |
{ |
int i; |
int i; |
Line 85 genfs_rel_pages(struct vm_page **pgs, in |
|
Line 99 genfs_rel_pages(struct vm_page **pgs, in |
|
mutex_exit(&uvm_pageqlock); |
mutex_exit(&uvm_pageqlock); |
} |
} |
|
|
|
static void |
|
genfs_markdirty(struct vnode *vp) |
|
{ |
|
struct genfs_node * const gp = VTOG(vp); |
|
|
|
KASSERT(mutex_owned(&vp->v_interlock)); |
|
gp->g_dirtygen++; |
|
if ((vp->v_iflag & VI_ONWORKLST) == 0) { |
|
vn_syncer_add_to_worklist(vp, filedelay); |
|
} |
|
if ((vp->v_iflag & (VI_WRMAP|VI_WRMAPDIRTY)) == VI_WRMAP) { |
|
vp->v_iflag |= VI_WRMAPDIRTY; |
|
} |
|
} |
|
|
/* |
/* |
* generic VM getpages routine. |
* generic VM getpages routine. |
* Return PG_BUSY pages for the given range, |
* Return PG_BUSY pages for the given range, |
Line 94 genfs_rel_pages(struct vm_page **pgs, in |
|
Line 123 genfs_rel_pages(struct vm_page **pgs, in |
|
int |
int |
genfs_getpages(void *v) |
genfs_getpages(void *v) |
{ |
{ |
|
#ifdef XIP |
|
struct vop_getpages_args /* { |
|
struct vnode *a_vp; |
|
voff_t a_offset; |
|
struct vm_page **a_m; |
|
int *a_count; |
|
int a_centeridx; |
|
vm_prot_t a_access_type; |
|
int a_advice; |
|
int a_flags; |
|
} */ * const ap = v; |
|
|
|
if ((ap->a_vp->v_vflag & VV_XIP) != 0) |
|
return genfs_do_getpages_xip(v); |
|
else |
|
#endif |
|
return genfs_do_getpages(v); |
|
} |
|
|
|
static int |
|
genfs_do_getpages(void *v) |
|
{ |
struct vop_getpages_args /* { |
struct vop_getpages_args /* { |
struct vnode *a_vp; |
struct vnode *a_vp; |
voff_t a_offset; |
voff_t a_offset; |
Line 109 genfs_getpages(void *v) |
|
Line 160 genfs_getpages(void *v) |
|
int i, error, npages; |
int i, error, npages; |
const int flags = ap->a_flags; |
const int flags = ap->a_flags; |
struct vnode * const vp = ap->a_vp; |
struct vnode * const vp = ap->a_vp; |
struct genfs_node * const gp = VTOG(vp); |
|
struct uvm_object * const uobj = &vp->v_uobj; |
struct uvm_object * const uobj = &vp->v_uobj; |
kauth_cred_t const cred = curlwp->l_cred; /* XXXUBC curlwp */ |
kauth_cred_t const cred = curlwp->l_cred; /* XXXUBC curlwp */ |
const bool async = (flags & PGO_SYNCIO) == 0; |
const bool async = (flags & PGO_SYNCIO) == 0; |
Line 117 genfs_getpages(void *v) |
|
Line 167 genfs_getpages(void *v) |
|
bool has_trans = false; |
bool has_trans = false; |
const bool overwrite = (flags & PGO_OVERWRITE) != 0; |
const bool overwrite = (flags & PGO_OVERWRITE) != 0; |
const bool blockalloc = memwrite && (flags & PGO_NOBLOCKALLOC) == 0; |
const bool blockalloc = memwrite && (flags & PGO_NOBLOCKALLOC) == 0; |
|
const bool glocked = (flags & PGO_GLOCKHELD) != 0; |
UVMHIST_FUNC("genfs_getpages"); UVMHIST_CALLED(ubchist); |
UVMHIST_FUNC("genfs_getpages"); UVMHIST_CALLED(ubchist); |
|
|
UVMHIST_LOG(ubchist, "vp %p off 0x%x/%x count %d", |
UVMHIST_LOG(ubchist, "vp %p off 0x%x/%x count %d", |
|
|
#if defined(DIAGNOSTIC) |
#if defined(DIAGNOSTIC) |
GOP_SIZE(vp, vp->v_writesize, &writeeof, GOP_SIZE_MEM); |
GOP_SIZE(vp, vp->v_writesize, &writeeof, GOP_SIZE_MEM); |
if (newsize > round_page(writeeof)) { |
if (newsize > round_page(writeeof)) { |
panic("%s: past eof", __func__); |
panic("%s: past eof: %" PRId64 " vs. %" PRId64, |
|
__func__, newsize, round_page(writeeof)); |
} |
} |
#endif /* defined(DIAGNOSTIC) */ |
#endif /* defined(DIAGNOSTIC) */ |
} else { |
} else { |
|
|
} |
} |
} |
} |
|
|
if (memwrite) { |
|
gp->g_dirtygen++; |
|
if ((vp->v_iflag & VI_ONWORKLST) == 0) { |
|
vn_syncer_add_to_worklist(vp, filedelay); |
|
} |
|
if ((vp->v_iflag & (VI_WRMAP|VI_WRMAPDIRTY)) == VI_WRMAP) { |
|
vp->v_iflag |= VI_WRMAPDIRTY; |
|
} |
|
} |
|
|
|
/* |
/* |
* For PGO_LOCKED requests, just return whatever's in memory. |
* For PGO_LOCKED requests, just return whatever's in memory. |
*/ |
*/ |
|
|
int nfound; |
int nfound; |
struct vm_page *pg; |
struct vm_page *pg; |
|
|
|
KASSERT(!glocked); |
npages = *ap->a_count; |
npages = *ap->a_count; |
#if defined(DEBUG) |
#if defined(DEBUG) |
for (i = 0; i < npages; i++) { |
for (i = 0; i < npages; i++) { |
|
|
for (i = 0; i < npages; i++) { |
for (i = 0; i < npages; i++) { |
pg = ap->a_m[i]; |
pg = ap->a_m[i]; |
|
|
if (pg != NULL || pg != PGO_DONTCARE) { |
if (pg != NULL && pg != PGO_DONTCARE) { |
ap->a_m[i] = NULL; |
ap->a_m[i] = NULL; |
} |
} |
|
KASSERT(pg == NULL || pg == PGO_DONTCARE); |
} |
} |
} else { |
} else { |
genfs_node_unlock(vp); |
genfs_node_unlock(vp); |
} |
} |
error = (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0); |
error = (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0); |
|
if (error == 0 && memwrite) { |
|
genfs_markdirty(vp); |
|
} |
goto out_err; |
goto out_err; |
} |
} |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
|
|
* check if our idea of v_size is still valid. |
* check if our idea of v_size is still valid. |
*/ |
*/ |
|
|
if (blockalloc) { |
KASSERT(!glocked || genfs_node_wrlocked(vp)); |
rw_enter(&gp->g_glock, RW_WRITER); |
if (!glocked) { |
} else { |
if (blockalloc) { |
rw_enter(&gp->g_glock, RW_READER); |
genfs_node_wrlock(vp); |
|
} else { |
|
genfs_node_rdlock(vp); |
|
} |
} |
} |
mutex_enter(&uobj->vmobjlock); |
mutex_enter(&uobj->vmobjlock); |
if (vp->v_size < origvsize) { |
if (vp->v_size < origvsize) { |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
if (pgs != pgs_onstack) |
if (pgs != pgs_onstack) |
kmem_free(pgs, pgs_size); |
kmem_free(pgs, pgs_size); |
goto startover; |
goto startover; |
|
|
|
|
if (uvn_findpages(uobj, origoffset, &npages, &pgs[ridx], |
if (uvn_findpages(uobj, origoffset, &npages, &pgs[ridx], |
async ? UFP_NOWAIT : UFP_ALL) != orignmempages) { |
async ? UFP_NOWAIT : UFP_ALL) != orignmempages) { |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
KASSERT(async != 0); |
KASSERT(async != 0); |
genfs_rel_pages(&pgs[ridx], orignmempages); |
genfs_rel_pages(&pgs[ridx], orignmempages); |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
|
|
} |
} |
} |
} |
if (i == npages) { |
if (i == npages) { |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0); |
UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0); |
npages += ridx; |
npages += ridx; |
goto out; |
goto out; |
|
|
*/ |
*/ |
|
|
if (overwrite) { |
if (overwrite) { |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
UVMHIST_LOG(ubchist, "PGO_OVERWRITE",0,0,0,0); |
UVMHIST_LOG(ubchist, "PGO_OVERWRITE",0,0,0,0); |
|
|
for (i = 0; i < npages; i++) { |
for (i = 0; i < npages; i++) { |
|
|
npgs = npages; |
npgs = npages; |
if (uvn_findpages(uobj, startoffset, &npgs, pgs, |
if (uvn_findpages(uobj, startoffset, &npgs, pgs, |
async ? UFP_NOWAIT : UFP_ALL) != npages) { |
async ? UFP_NOWAIT : UFP_ALL) != npages) { |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
KASSERT(async != 0); |
KASSERT(async != 0); |
genfs_rel_pages(pgs, npages); |
genfs_rel_pages(pgs, npages); |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
|
|
nestiobuf_done(mbp, skipbytes, error); |
nestiobuf_done(mbp, skipbytes, error); |
if (async) { |
if (async) { |
UVMHIST_LOG(ubchist, "returning 0 (async)",0,0,0,0); |
UVMHIST_LOG(ubchist, "returning 0 (async)",0,0,0,0); |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
error = 0; |
error = 0; |
goto out_err_free; |
goto out_err_free; |
} |
} |
|
|
} |
} |
} |
} |
} |
} |
genfs_node_unlock(vp); |
if (!glocked) { |
|
genfs_node_unlock(vp); |
|
} |
|
|
putiobuf(mbp); |
putiobuf(mbp); |
} |
} |
|
|
} |
} |
} |
} |
mutex_exit(&uvm_pageqlock); |
mutex_exit(&uvm_pageqlock); |
|
if (memwrite) { |
|
genfs_markdirty(vp); |
|
} |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
if (ap->a_m != NULL) { |
if (ap->a_m != NULL) { |
memcpy(ap->a_m, &pgs[ridx], |
memcpy(ap->a_m, &pgs[ridx], |
|
|
out_err: |
out_err: |
if (has_trans) |
if (has_trans) |
fstrans_done(vp->v_mount); |
fstrans_done(vp->v_mount); |
return (error); |
return error; |
} |
} |
|
|
|
#ifdef XIP |
|
/* |
|
* genfs_do_getpages_xip |
|
* Return "direct pages" of XIP vnode. The block addresses of XIP |
|
* vnode pages are returned back to the VM fault handler as the |
|
* actually mapped physical addresses. |
|
*/ |
|
static int |
|
genfs_do_getpages_xip(void *v) |
|
{ |
|
struct vop_getpages_args /* { |
|
struct vnode *a_vp; |
|
voff_t a_offset; |
|
struct vm_page **a_m; |
|
int *a_count; |
|
int a_centeridx; |
|
vm_prot_t a_access_type; |
|
int a_advice; |
|
int a_flags; |
|
} */ * const ap = v; |
|
|
|
UVMHIST_FUNC("genfs_do_getpages_xip"); UVMHIST_CALLED(ubchist); |
|
|
|
return genfs_do_getpages_xip1( |
|
ap->a_vp, |
|
ap->a_offset, |
|
ap->a_m, |
|
ap->a_count, |
|
ap->a_centeridx, |
|
ap->a_access_type, |
|
ap->a_advice, |
|
ap->a_flags); |
|
} |
|
|
|
static int |
|
genfs_do_getpages_xip1( |
|
struct vnode *vp, |
|
voff_t offset, |
|
struct vm_page **pps, |
|
int *npagesp, |
|
int centeridx, |
|
vm_prot_t access_type, |
|
int advice, |
|
int flags) |
|
{ |
|
|
|
KASSERT((vp->v_vflag & VV_XIP) != 0); |
|
|
|
if ((flags & PGO_LOCKED) != 0) { |
|
*npagesp = 0; |
|
return 0; |
|
} else |
|
return genfs_do_getpages_xip_io( |
|
vp, |
|
offset, |
|
pps, |
|
npagesp, |
|
centeridx, |
|
access_type, |
|
advice, |
|
flags); |
|
} |
|
|
|
static int |
|
genfs_do_getpages_xip_io( |
|
struct vnode *vp, |
|
voff_t offset, |
|
struct vm_page **pps, |
|
int *npagesp, |
|
int centeridx, |
|
vm_prot_t access_type, |
|
int advice, |
|
int flags) |
|
{ |
|
struct uvm_object * const uobj = &vp->v_uobj; |
|
|
|
int error; |
|
off_t eof, sbkoff, ebkoff, off; |
|
int npages; |
|
int fs_bshift, fs_bsize, dev_bshift, dev_bsize; |
|
int i; |
|
struct vm_page *zero_page; |
|
|
|
UVMHIST_FUNC("genfs_do_getpages_xip_io"); UVMHIST_CALLED(ubchist); |
|
|
|
GOP_SIZE(vp, vp->v_size, &eof, GOP_SIZE_MEM); |
|
npages = MIN(*npagesp, round_page(eof - offset) >> PAGE_SHIFT); |
|
|
|
fs_bshift = vp->v_mount->mnt_fs_bshift; |
|
fs_bsize = 1 << fs_bshift; |
|
dev_bshift = vp->v_mount->mnt_dev_bshift; |
|
dev_bsize = 1 << dev_bshift; |
|
|
|
sbkoff = offset & ~(fs_bsize - 1); |
|
ebkoff = ((offset + PAGE_SIZE * npages) + (fs_bsize - 1)) & |
|
~(fs_bsize - 1); |
|
|
|
zero_page = NULL; |
|
|
|
UVMHIST_LOG(ubchist, "xip npages=%d sbkoff=%lx ebkoff=%lx", |
|
npages, (long)sbkoff, (long)ebkoff, 0); |
|
|
|
KASSERT(mutex_owned(&uobj->vmobjlock)); |
|
mutex_exit(&uobj->vmobjlock); |
|
|
|
off = offset; |
|
for (i = 0; i < npages; i++) { |
|
daddr_t lbn, blkno; |
|
int run; |
|
struct vnode *devvp; |
|
|
|
lbn = (off & ~(fs_bsize - 1)) >> fs_bshift; |
|
|
|
error = VOP_BMAP(vp, lbn, &devvp, &blkno, &run); |
|
KASSERT(error == 0); |
|
UVMHIST_LOG(ubchist, "xip VOP_BMAP: lbn=%ld blkno=%ld run=%d", |
|
(long)lbn, (long)blkno, run, 0); |
|
|
|
/* |
|
* XIP page metadata assignment |
|
* - Unallocated block is redirected to the dedicated zero'ed |
|
* page. |
|
*/ |
|
if (blkno < 0) { |
|
zero_page = uvm_page_zeropage_alloc(); |
|
KASSERT(zero_page != NULL); |
|
pps[i] = zero_page; |
|
} else { |
|
daddr_t blk_off, fs_off; |
|
|
|
blk_off = blkno << dev_bshift; |
|
fs_off = off - (lbn << fs_bshift); |
|
|
|
pps[i] = uvn_findpage_xip(devvp, &vp->v_uobj, |
|
blk_off + fs_off); |
|
KASSERT(pps[i] != NULL); |
|
} |
|
|
|
UVMHIST_LOG(ubchist, "xip pgs %d => phys_addr=0x%lx (%p)", |
|
i, |
|
(long)pps[i]->phys_addr, |
|
pps[i], |
|
0); |
|
|
|
off += PAGE_SIZE; |
|
} |
|
|
|
mutex_enter(&uobj->vmobjlock); |
|
|
|
for (i = 0; i < npages; i++) { |
|
struct vm_page *pg = pps[i]; |
|
|
|
KASSERT((pg->flags & PG_RDONLY) != 0); |
|
if (pg == zero_page) |
|
continue; |
|
KASSERT((pg->flags & PG_BUSY) == 0); |
|
KASSERT((pg->flags & PG_CLEAN) != 0); |
|
KASSERT((pg->flags & PG_DEVICE) != 0); |
|
pg->flags |= PG_BUSY; |
|
pg->flags &= ~PG_FAKE; |
|
pg->uobject = &vp->v_uobj; |
|
} |
|
|
|
mutex_exit(&uobj->vmobjlock); |
|
|
|
*npagesp = npages; |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
/* |
/* |
* generic VM putpages routine. |
* generic VM putpages routine. |
* Write the given range of pages to backing store. |
* Write the given range of pages to backing store. |
Line 769 genfs_putpages(void *v) |
|
Line 1007 genfs_putpages(void *v) |
|
int a_flags; |
int a_flags; |
} */ * const ap = v; |
} */ * const ap = v; |
|
|
|
#ifdef XIP |
|
if ((ap->a_vp->v_vflag & VV_XIP) != 0) |
|
return genfs_do_putpages_xip(ap->a_vp, ap->a_offlo, ap->a_offhi, |
|
ap->a_flags, NULL); |
|
else |
|
#endif |
return genfs_do_putpages(ap->a_vp, ap->a_offlo, ap->a_offhi, |
return genfs_do_putpages(ap->a_vp, ap->a_offlo, ap->a_offhi, |
ap->a_flags, NULL); |
ap->a_flags, NULL); |
} |
} |
|
|
dirtygen = gp->g_dirtygen; |
dirtygen = gp->g_dirtygen; |
freeflag = pagedaemon ? PG_PAGEOUT : PG_RELEASED; |
freeflag = pagedaemon ? PG_PAGEOUT : PG_RELEASED; |
if (by_list) { |
if (by_list) { |
curmp.uobject = uobj; |
curmp.flags = PG_MARKER; |
curmp.offset = (voff_t)-1; |
endmp.flags = PG_MARKER; |
curmp.flags = PG_BUSY; |
|
endmp.uobject = uobj; |
|
endmp.offset = (voff_t)-1; |
|
endmp.flags = PG_BUSY; |
|
pg = TAILQ_FIRST(&uobj->memq); |
pg = TAILQ_FIRST(&uobj->memq); |
TAILQ_INSERT_TAIL(&uobj->memq, &endmp, listq.queue); |
TAILQ_INSERT_TAIL(&uobj->memq, &endmp, listq.queue); |
} else { |
} else { |
|
|
* if the current page is not interesting, move on to the next. |
* if the current page is not interesting, move on to the next. |
*/ |
*/ |
|
|
KASSERT(pg == NULL || pg->uobject == uobj); |
KASSERT(pg == NULL || pg->uobject == uobj || |
|
(pg->flags & PG_MARKER) != 0); |
KASSERT(pg == NULL || |
KASSERT(pg == NULL || |
(pg->flags & (PG_RELEASED|PG_PAGEOUT)) == 0 || |
(pg->flags & (PG_RELEASED|PG_PAGEOUT)) == 0 || |
(pg->flags & PG_BUSY) != 0); |
(pg->flags & (PG_BUSY|PG_MARKER)) != 0); |
if (by_list) { |
if (by_list) { |
if (pg == &endmp) { |
if (pg == &endmp) { |
break; |
break; |
} |
} |
|
if (pg->flags & PG_MARKER) { |
|
pg = TAILQ_NEXT(pg, listq.queue); |
|
continue; |
|
} |
if (pg->offset < startoff || pg->offset >= endoff || |
if (pg->offset < startoff || pg->offset >= endoff || |
pg->flags & (PG_RELEASED|PG_PAGEOUT)) { |
pg->flags & (PG_RELEASED|PG_PAGEOUT)) { |
if (pg->flags & (PG_RELEASED|PG_PAGEOUT)) { |
if (pg->flags & (PG_RELEASED|PG_PAGEOUT)) { |
|
|
(vp->v_iflag & VI_ONWORKLST) != 0) { |
(vp->v_iflag & VI_ONWORKLST) != 0) { |
#if defined(DEBUG) |
#if defined(DEBUG) |
TAILQ_FOREACH(pg, &uobj->memq, listq.queue) { |
TAILQ_FOREACH(pg, &uobj->memq, listq.queue) { |
|
if ((pg->flags & PG_MARKER) != 0) { |
|
continue; |
|
} |
if ((pg->flags & PG_CLEAN) == 0) { |
if ((pg->flags & PG_CLEAN) == 0) { |
printf("%s: %p: !CLEAN\n", __func__, pg); |
printf("%s: %p: !CLEAN\n", __func__, pg); |
} |
} |
|
|
return (error); |
return (error); |
} |
} |
|
|
|
#ifdef XIP |
|
int |
|
genfs_do_putpages_xip(struct vnode *vp, off_t startoff, off_t endoff, |
|
int flags, struct vm_page **busypg) |
|
{ |
|
struct uvm_object *uobj = &vp->v_uobj; |
|
#ifdef DIAGNOSTIC |
|
struct genfs_node * const gp = VTOG(vp); |
|
#endif |
|
|
|
UVMHIST_FUNC("genfs_do_putpages_xip"); UVMHIST_CALLED(ubchist); |
|
|
|
KASSERT(mutex_owned(&uobj->vmobjlock)); |
|
KASSERT((vp->v_iflag & VI_ONWORKLST) == 0); |
|
KASSERT(vp->v_numoutput == 0); |
|
KASSERT(gp->g_dirtygen == 0); |
|
|
|
UVMHIST_LOG(ubchist, "vp %p pages %d off 0x%x len 0x%x", |
|
vp, uobj->uo_npages, startoff, endoff - startoff); |
|
|
|
/* |
|
* XIP pages are read-only, and never become dirty. They're also never |
|
* queued. PGO_DEACTIVATE and PGO_CLEANIT are meaningless for XIP |
|
* pages, so we ignore them. |
|
*/ |
|
if ((flags & PGO_FREE) == 0) |
|
goto done; |
|
|
|
/* |
|
* For PGO_FREE (or (PGO_CLEANIT | PGO_FREE)), we invalidate MMU |
|
* mappings of both XIP pages and XIP zero pages. |
|
* |
|
* Zero page is freed when one of its mapped offset is freed, even if |
|
* one file (vnode) has many holes and mapping its zero page to all |
|
* of those hole pages. |
|
* |
|
* We don't know which pages are currently mapped in the given vnode, |
|
* because XIP pages are not added to vnode. What we can do is to |
|
* locate pages by querying the filesystem as done in getpages. Call |
|
* genfs_do_getpages_xip1(). |
|
*/ |
|
|
|
off_t off, eof; |
|
struct vm_page *zero_page; |
|
bool put_zero_page; |
|
|
|
off = trunc_page(startoff); |
|
if (endoff == 0 || (flags & PGO_ALLPAGES)) |
|
GOP_SIZE(vp, vp->v_size, &eof, GOP_SIZE_MEM); |
|
else |
|
eof = endoff; |
|
|
|
zero_page = uvm_pagelookup(uobj, 0); |
|
KASSERT(zero_page != NULL || uobj->uo_npages == 0); |
|
KASSERT(zero_page == NULL || uobj->uo_npages == 1); |
|
put_zero_page = false; |
|
|
|
while (off < eof) { |
|
int npages, orignpages, error, i; |
|
struct vm_page *pgs[maxpages], *pg; |
|
|
|
npages = round_page(eof - off) >> PAGE_SHIFT; |
|
if (npages > maxpages) |
|
npages = maxpages; |
|
|
|
orignpages = npages; |
|
KASSERT(mutex_owned(&uobj->vmobjlock)); |
|
error = genfs_do_getpages_xip1(vp, off, pgs, &npages, 0, |
|
VM_PROT_ALL, 0, 0); |
|
KASSERT(error == 0); |
|
KASSERT(npages == orignpages); |
|
mutex_enter(&uobj->vmobjlock); |
|
for (i = 0; i < npages; i++) { |
|
pg = pgs[i]; |
|
if (pg == NULL || pg == PGO_DONTCARE) |
|
continue; |
|
if (pg == uvm_page_zeropage) |
|
/* Do nothing for holes. */ |
|
continue; |
|
/* |
|
* Freeing normal XIP pages; nothing to do. |
|
*/ |
|
pmap_page_protect(pg, VM_PROT_NONE); |
|
KASSERT((pg->flags & PG_BUSY) != 0); |
|
KASSERT((pg->flags & PG_RDONLY) != 0); |
|
KASSERT((pg->flags & PG_CLEAN) != 0); |
|
KASSERT((pg->flags & PG_FAKE) == 0); |
|
KASSERT((pg->flags & PG_DEVICE) != 0); |
|
pg->flags &= ~PG_BUSY; |
|
} |
|
off += npages << PAGE_SHIFT; |
|
} |
|
|
|
KASSERT(uobj->uo_npages == 0); |
|
|
|
done: |
|
KASSERT(mutex_owned(&uobj->vmobjlock)); |
|
mutex_exit(&uobj->vmobjlock); |
|
return 0; |
|
} |
|
#endif |
|
|
int |
int |
genfs_gop_write(struct vnode *vp, struct vm_page **pgs, int npages, int flags) |
genfs_gop_write(struct vnode *vp, struct vm_page **pgs, int npages, int flags) |
{ |
{ |
Line 1463 genfs_compat_getpages(void *v) |
|
Line 1813 genfs_compat_getpages(void *v) |
|
orignpages = *ap->a_count; |
orignpages = *ap->a_count; |
pgs = ap->a_m; |
pgs = ap->a_m; |
|
|
if (memwrite && (vp->v_iflag & VI_ONWORKLST) == 0) { |
|
vn_syncer_add_to_worklist(vp, filedelay); |
|
} |
|
if (ap->a_flags & PGO_LOCKED) { |
if (ap->a_flags & PGO_LOCKED) { |
uvn_findpages(uobj, origoffset, ap->a_count, ap->a_m, |
uvn_findpages(uobj, origoffset, ap->a_count, ap->a_m, |
UFP_NOWAIT|UFP_NOALLOC| (memwrite ? UFP_NORDONLY : 0)); |
UFP_NOWAIT|UFP_NOALLOC| (memwrite ? UFP_NORDONLY : 0)); |
|
|
return (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0); |
error = ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0; |
|
if (error == 0 && memwrite) { |
|
genfs_markdirty(vp); |
|
} |
|
return error; |
} |
} |
if (origoffset + (ap->a_centeridx << PAGE_SHIFT) >= vp->v_size) { |
if (origoffset + (ap->a_centeridx << PAGE_SHIFT) >= vp->v_size) { |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
return (EINVAL); |
return EINVAL; |
} |
} |
if ((ap->a_flags & PGO_SYNCIO) == 0) { |
if ((ap->a_flags & PGO_SYNCIO) == 0) { |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
Line 1523 genfs_compat_getpages(void *v) |
|
Line 1874 genfs_compat_getpages(void *v) |
|
uvm_page_unbusy(pgs, npages); |
uvm_page_unbusy(pgs, npages); |
} |
} |
mutex_exit(&uvm_pageqlock); |
mutex_exit(&uvm_pageqlock); |
|
if (error == 0 && memwrite) { |
|
genfs_markdirty(vp); |
|
} |
mutex_exit(&uobj->vmobjlock); |
mutex_exit(&uobj->vmobjlock); |
return (error); |
return error; |
} |
} |
|
|
int |
int |