/*
 * Lets the VM system know about a change in size for a file.
 * We adjust our own internal size and flush any cached pages in
 * the associated object that are affected by the size change.
 *
 * NOTE: This routine may be invoked as a result of a pager put
 * operation (possibly at object termination time), so we must be careful.
 *
 * NOTE: vp->v_filesize is initialized to NOOFFSET (-1), be sure that
 * we do not blow up on the case.  nsize will always be >= 0, however.
 */
void
vnode_pager_setsize(struct vnode *vp, vm_ooffset_t nsize)
{
	vm_pindex_t nobjsize;
	vm_pindex_t oobjsize;
	vm_object_t object;

	object = vp->v_object;
	if (object == NULL)
		return;
	vm_object_hold(object);
	KKASSERT(vp->v_object == object);

	/*
	 * Hasn't changed size
	 */
	if (nsize == vp->v_filesize) {
		vm_object_drop(object);
		return;
	}

	/*
	 * Has changed size.  Adjust the VM object's size and v_filesize
	 * before we start scanning pages to prevent new pages from being
	 * allocated during the scan.
	 */
	nobjsize = OFF_TO_IDX(nsize + PAGE_MASK);
	oobjsize = object->size;
	object->size = nobjsize;

	/*
	 * File has shrunk. Toss any cached pages beyond the new EOF.
	 */
	if (nsize < vp->v_filesize) {
		vp->v_filesize = nsize;
		if (nobjsize < oobjsize) {
			vm_object_page_remove(object, nobjsize, oobjsize,
					      FALSE);
		}
		/*
		 * This gets rid of garbage at the end of a page that is now
		 * only partially backed by the vnode.  Since we are setting
		 * the entire page valid & clean after we are done we have
		 * to be sure that the portion of the page within the file
		 * bounds is already valid.  If it isn't then making it
		 * valid would create a corrupt block.
		 */
		if (nsize & PAGE_MASK) {
			vm_offset_t kva;
			vm_page_t m;

			m = vm_page_lookup_busy_wait(object, OFF_TO_IDX(nsize),
						     TRUE, "vsetsz");

			if (m && m->valid) {
				int base = (int)nsize & PAGE_MASK;
				int size = PAGE_SIZE - base;
				struct lwbuf *lwb;
				struct lwbuf lwb_cache;

				/*
				 * Clear out partial-page garbage in case
				 * the page has been mapped.
				 *
				 * This is byte aligned.
				 */
				lwb = lwbuf_alloc(m, &lwb_cache);
				kva = lwbuf_kva(lwb);
				bzero((caddr_t)kva + base, size);
				lwbuf_free(lwb);

				/*
				 * XXX work around SMP data integrity race
				 * by unmapping the page from user processes.
				 * The garbage we just cleared may be mapped
				 * to a user process running on another cpu
				 * and this code is not running through normal
				 * I/O channels which handle SMP issues for
				 * us, so unmap page to synchronize all cpus.
				 *
				 * XXX should vm_pager_unmap_page() have
				 * dealt with this?
				 */
				vm_page_protect(m, VM_PROT_NONE);

				/*
				 * Clear out partial-page dirty bits.  This
				 * has the side effect of setting the valid
				 * bits, but that is ok.  There are a bunch
				 * of places in the VM system where we expected
				 * m->dirty == VM_PAGE_BITS_ALL.  The file EOF
				 * case is one of them.  If the page is still
				 * partially dirty, make it fully dirty.
				 *
				 * NOTE: We do not clear out the valid
				 * bits.  This would prevent bogus_page
				 * replacement from working properly.
				 *
				 * NOTE: We do not want to clear the dirty
				 * bit for a partial DEV_BSIZE'd truncation!
				 * This is DEV_BSIZE aligned!
				 */
				vm_page_clear_dirty_beg_nonincl(m, base, size);
				if (m->dirty != 0)
					m->dirty = VM_PAGE_BITS_ALL;
				vm_page_wakeup(m);
			} else if (m) {
				vm_page_wakeup(m);
			}
		}
	} else {
		vp->v_filesize = nsize;
	}
	vm_object_drop(object);
}
예제 #2
0
/*
 * vm_contig_pg_alloc:
 *
 * Allocate contiguous pages from the VM.  This function does not
 * map the allocated pages into the kernel map, otherwise it is
 * impossible to make large allocations (i.e. >2G).
 *
 * Malloc()'s data structures have been used for collection of
 * statistics and for allocations of less than a page.
 */
static int
vm_contig_pg_alloc(unsigned long size, vm_paddr_t low, vm_paddr_t high,
                   unsigned long alignment, unsigned long boundary, int mflags)
{
    int i, q, start, pass;
    vm_offset_t phys;
    vm_page_t pga = vm_page_array;
    vm_page_t m;
    int pqtype;

    size = round_page(size);
    if (size == 0)
        panic("vm_contig_pg_alloc: size must not be 0");
    if ((alignment & (alignment - 1)) != 0)
        panic("vm_contig_pg_alloc: alignment must be a power of 2");
    if ((boundary & (boundary - 1)) != 0)
        panic("vm_contig_pg_alloc: boundary must be a power of 2");

    /*
     * See if we can get the pages from the contiguous page reserve
     * alist.  The returned pages will be allocated and wired but not
     * busied.
     */
    m = vm_page_alloc_contig(low, high, alignment, boundary, size);
    if (m)
        return (m - &pga[0]);

    /*
     * Three passes (0, 1, 2).  Each pass scans the VM page list for
     * free or cached pages.  After each pass if the entire scan failed
     * we attempt to flush inactive pages and reset the start index back
     * to 0.  For passes 1 and 2 we also attempt to flush active pages.
     */
    start = 0;
    for (pass = 0; pass < 3; pass++) {
        /*
         * Find first page in array that is free, within range,
         * aligned, and such that the boundary won't be crossed.
         */
again:
        for (i = start; i < vmstats.v_page_count; i++) {
            m = &pga[i];
            phys = VM_PAGE_TO_PHYS(m);
            pqtype = m->queue - m->pc;
            if (((pqtype == PQ_FREE) || (pqtype == PQ_CACHE)) &&
                    (phys >= low) && (phys < high) &&
                    ((phys & (alignment - 1)) == 0) &&
                    (((phys ^ (phys + size - 1)) & ~(boundary - 1)) == 0) &&
                    m->busy == 0 && m->wire_count == 0 &&
                    m->hold_count == 0 &&
                    (m->flags & (PG_BUSY | PG_NEED_COMMIT)) == 0)
            {
                break;
            }
        }

        /*
         * If we cannot find the page in the given range, or we have
         * crossed the boundary, call the vm_contig_pg_clean() function
         * for flushing out the queues, and returning it back to
         * normal state.
         */
        if ((i == vmstats.v_page_count) ||
                ((VM_PAGE_TO_PHYS(&pga[i]) + size) > high)) {

            /*
             * Best effort flush of all inactive pages.
             * This is quite quick, for now stall all
             * callers, even if they've specified M_NOWAIT.
             */
            for (q = 0; q < PQ_L2_SIZE; ++q) {
                vm_contig_pg_clean(PQ_INACTIVE + q,
                                   vmstats.v_inactive_count);
                lwkt_yield();
            }

            /*
             * Best effort flush of active pages.
             *
             * This is very, very slow.
             * Only do this if the caller has agreed to M_WAITOK.
             *
             * If enough pages are flushed, we may succeed on
             * next (final) pass, if not the caller, contigmalloc(),
             * will fail in the index < 0 case.
             */
            if (pass > 0 && (mflags & M_WAITOK)) {
                for (q = 0; q < PQ_L2_SIZE; ++q) {
                    vm_contig_pg_clean(PQ_ACTIVE + q,
                                       vmstats.v_active_count);
                }
                lwkt_yield();
            }

            /*
             * We're already too high in the address space
             * to succeed, reset to 0 for the next iteration.
             */
            start = 0;
            continue;	/* next pass */
        }
        start = i;

        /*
         * Check successive pages for contiguous and free.
         *
         * (still in critical section)
         */
        for (i = start + 1; i < (start + size / PAGE_SIZE); i++) {
            m = &pga[i];
            pqtype = m->queue - m->pc;
            if ((VM_PAGE_TO_PHYS(&m[0]) !=
                    (VM_PAGE_TO_PHYS(&m[-1]) + PAGE_SIZE)) ||
                    ((pqtype != PQ_FREE) && (pqtype != PQ_CACHE)) ||
                    m->busy || m->wire_count ||
                    m->hold_count ||
                    (m->flags & (PG_BUSY | PG_NEED_COMMIT)))
            {
                start++;
                goto again;
            }
        }

        /*
         * Try to allocate the pages, wiring them as we go.
         *
         * (still in critical section)
         */
        for (i = start; i < (start + size / PAGE_SIZE); i++) {
            m = &pga[i];

            if (vm_page_busy_try(m, TRUE)) {
                vm_contig_pg_free(start,
                                  (i - start) * PAGE_SIZE);
                start++;
                goto again;
            }
            pqtype = m->queue - m->pc;
            if (pqtype == PQ_CACHE &&
                    m->hold_count == 0 &&
                    m->wire_count == 0 &&
                    (m->flags & (PG_UNMANAGED | PG_NEED_COMMIT)) == 0) {
                vm_page_protect(m, VM_PROT_NONE);
                KKASSERT((m->flags & PG_MAPPED) == 0);
                KKASSERT(m->dirty == 0);
                vm_page_free(m);
                --i;
                continue;	/* retry the page */
            }
            if (pqtype != PQ_FREE || m->hold_count) {
                vm_page_wakeup(m);
                vm_contig_pg_free(start,
                                  (i - start) * PAGE_SIZE);
                start++;
                goto again;
            }
            KKASSERT((m->valid & m->dirty) == 0);
            KKASSERT(m->wire_count == 0);
            KKASSERT(m->object == NULL);
            vm_page_unqueue_nowakeup(m);
            m->valid = VM_PAGE_BITS_ALL;
            if (m->flags & PG_ZERO)
                vm_page_zero_count--;
            KASSERT(m->dirty == 0,
                    ("vm_contig_pg_alloc: page %p was dirty", m));
            KKASSERT(m->wire_count == 0);
            KKASSERT(m->busy == 0);

            /*
             * Clear all flags except PG_BUSY, PG_ZERO, and
             * PG_WANTED, then unbusy the now allocated page.
             */
            vm_page_flag_clear(m, ~(PG_BUSY | PG_SBUSY |
                                    PG_ZERO | PG_WANTED));
            vm_page_wire(m);
            vm_page_wakeup(m);
        }

        /*
         * Our job is done, return the index page of vm_page_array.
         */
        return (start); /* aka &pga[start] */
    }

    /*
     * Failed.
     */
    return (-1);
}