diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -1620,6 +1620,7 @@ vm_inherit_t inheritance; u_long bdry; u_int bidx; + int cflags; VM_MAP_ASSERT_LOCKED(map); KASSERT(object != kernel_object || @@ -1696,20 +1697,36 @@ } cred = NULL; - if ((cow & (MAP_ACC_NO_CHARGE | MAP_NOFAULT | MAP_CREATE_GUARD)) != 0) - goto charged; - if ((cow & MAP_ACC_CHARGED) || ((prot & VM_PROT_WRITE) && - ((protoeflags & MAP_ENTRY_NEEDS_COPY) || object == NULL))) { - if (!(cow & MAP_ACC_CHARGED) && !swap_reserve(end - start)) - return (KERN_RESOURCE_SHORTAGE); - KASSERT(object == NULL || - (protoeflags & MAP_ENTRY_NEEDS_COPY) != 0 || - object->cred == NULL, - ("overcommit: vm_map_insert o %p", object)); - cred = curthread->td_ucred; + if ((cow & (MAP_ACC_NO_CHARGE | MAP_NOFAULT | MAP_CREATE_GUARD)) != 0) { + cflags = OBJCO_NO_CHARGE; + } else { + cflags = 0; + if ((cow & MAP_ACC_CHARGED) != 0 || + ((prot & VM_PROT_WRITE) != 0 && + ((protoeflags & MAP_ENTRY_NEEDS_COPY) != 0 || + object == NULL))) { + if ((cow & MAP_ACC_CHARGED) == 0) { + if (!swap_reserve(end - start)) + return (KERN_RESOURCE_SHORTAGE); + + /* + * Only inform vm_object_coalesce() + * that the object was charged if + * there is no need for CoW, so the + * swap amount reserved is applicable + * to the prev_entry->object. + */ + if ((protoeflags & MAP_ENTRY_NEEDS_COPY) == 0) + cflags |= OBJCO_CHARGED; + } + KASSERT(object == NULL || + (protoeflags & MAP_ENTRY_NEEDS_COPY) != 0 || + object->cred == NULL, + ("overcommit: vm_map_insert o %p", object)); + cred = curthread->td_ucred; + } } -charged: /* Expand the kernel pmap, if necessary. */ if (map == kernel_map && end > kernel_vm_end) { int rv; @@ -1741,8 +1758,7 @@ vm_object_coalesce(prev_entry->object.vm_object, prev_entry->offset, (vm_size_t)(prev_entry->end - prev_entry->start), - (vm_size_t)(end - prev_entry->end), cred != NULL && - (protoeflags & MAP_ENTRY_NEEDS_COPY) == 0)) { + (vm_size_t)(end - prev_entry->end), cflags)) { /* * We were able to extend the object. Determine if we * can extend the previous map entry to include the diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h --- a/sys/vm/vm_object.h +++ b/sys/vm/vm_object.h @@ -228,6 +228,12 @@ #define OBJPR_NOTMAPPED 0x2 /* Don't unmap pages. */ #define OBJPR_VALIDONLY 0x4 /* Ignore invalid pages. */ +/* + * Options for vm_object_coalesce(). + */ +#define OBJCO_CHARGED 0x1 /* The next_size was charged already */ +#define OBJCO_NO_CHARGE 0x2 /* Do not do swap accounting at all */ + TAILQ_HEAD(object_q, vm_object); extern struct object_q vm_object_list; /* list of allocated objects */ @@ -354,7 +360,7 @@ vm_size_t); vm_object_t vm_object_allocate_dyn(objtype_t, vm_pindex_t, u_short); boolean_t vm_object_coalesce(vm_object_t, vm_ooffset_t, vm_size_t, vm_size_t, - boolean_t); + int); void vm_object_collapse (vm_object_t); void vm_object_deallocate (vm_object_t); void vm_object_destroy (vm_object_t); diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c --- a/sys/vm/vm_object.c +++ b/sys/vm/vm_object.c @@ -2161,7 +2161,7 @@ */ boolean_t vm_object_coalesce(vm_object_t prev_object, vm_ooffset_t prev_offset, - vm_size_t prev_size, vm_size_t next_size, boolean_t reserved) + vm_size_t prev_size, vm_size_t next_size, int cflags) { vm_pindex_t next_end, next_pindex; @@ -2202,8 +2202,7 @@ /* * Account for the charge. */ - if (prev_object->cred != NULL && - next_pindex + next_size > prev_object->size) { + if (prev_object->cred != NULL && (cflags & OBJCO_NO_CHARGE) == 0) { /* * If prev_object was charged, then this mapping, * although not charged now, may become writable @@ -2214,14 +2213,36 @@ * entry, and swap reservation for this entry is * managed in appropriate time. */ - vm_size_t charge = ptoa(next_pindex + next_size - - prev_object->size); - if (!reserved && - !swap_reserve_by_cred(charge, prev_object->cred)) { - VM_OBJECT_WUNLOCK(prev_object); - return (FALSE); + if (next_end > prev_object->size) { + vm_size_t charge = ptoa(next_end - prev_object->size); + + if ((cflags & OBJCO_CHARGED) == 0) { + if (!swap_reserve_by_cred(charge, + prev_object->cred)) { + VM_OBJECT_WUNLOCK(prev_object); + return (FALSE); + } + } else if (prev_object->size > next_pindex) { + /* + * The caller charged, but: + * - the object has already accounted for the + * space, + * - and the object end is between previous + * mapping end and next_end. + */ + swap_release_by_cred(ptoa(prev_object->size - + next_pindex), prev_object->cred); + } + prev_object->charge += charge; + } else if ((cflags & OBJCO_CHARGED) != 0) { + /* + * The caller charged, but the object has + * already accounted for the space. Whole new + * mapping charge should be released, + */ + swap_release_by_cred(ptoa(next_size), + prev_object->cred); } - prev_object->charge += charge; } /*