summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2003-08-03 06:08:48 +0000
committeralc <alc@FreeBSD.org>2003-08-03 06:08:48 +0000
commit52878a67703b2e2eaa2388ced81978247f684260 (patch)
treef246a7e7ffca9c388d94d330b074415ddce07ce2
parent697f428114a3cc2de7307efefe55db5f47f41d83 (diff)
downloadFreeBSD-src-52878a67703b2e2eaa2388ced81978247f684260.zip
FreeBSD-src-52878a67703b2e2eaa2388ced81978247f684260.tar.gz
Revise obj_alloc(). Most notably, use the object's lock to prevent two
concurrent invocations from acquiring the same address(es). Also, in case of an incomplete allocation, free any allocated pages. In collaboration with: tegge
-rw-r--r--sys/vm/uma_core.c47
1 files changed, 30 insertions, 17 deletions
diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c
index 1ec706a..95b3f11 100644
--- a/sys/vm/uma_core.c
+++ b/sys/vm/uma_core.c
@@ -821,38 +821,51 @@ page_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait)
* A pointer to the alloced memory or possibly
* NULL if M_NOWAIT is set.
*
- * TODO: If we fail during a multi-page allocation release the pages that have
- * already been allocated.
*/
static void *
obj_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait)
{
- vm_offset_t zkva;
- vm_offset_t retkva;
+ vm_object_t object;
+ vm_offset_t retkva, zkva;
vm_page_t p;
- int pages;
+ int pages, startpages;
+ object = zone->uz_obj;
retkva = 0;
- pages = zone->uz_pages;
/*
* This looks a little weird since we're getting one page at a time
*/
- while (bytes > 0) {
- VM_OBJECT_LOCK(zone->uz_obj);
- p = vm_page_alloc(zone->uz_obj, pages,
- VM_ALLOC_INTERRUPT);
- VM_OBJECT_UNLOCK(zone->uz_obj);
- if (p == NULL)
- return (NULL);
-
- zkva = zone->uz_kva + pages * PAGE_SIZE;
+ VM_OBJECT_LOCK(object);
+ p = TAILQ_LAST(&object->memq, pglist);
+ pages = p != NULL ? p->pindex + 1 : 0;
+ startpages = pages;
+ zkva = zone->uz_kva + pages * PAGE_SIZE;
+ for (; bytes > 0; bytes -= PAGE_SIZE) {
+ p = vm_page_alloc(object, pages,
+ VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED);
+ if (p == NULL) {
+ if (pages != startpages)
+ pmap_qremove(retkva, pages - startpages);
+ while (pages != startpages) {
+ pages--;
+ p = TAILQ_LAST(&object->memq, pglist);
+ vm_page_lock_queues();
+ vm_page_unwire(p, 0);
+ vm_page_free(p);
+ vm_page_unlock_queues();
+ }
+ retkva = 0;
+ goto done;
+ }
+ pmap_qenter(zkva, &p, 1);
if (retkva == 0)
retkva = zkva;
- pmap_qenter(zkva, &p, 1);
- bytes -= PAGE_SIZE;
+ zkva += PAGE_SIZE;
pages += 1;
}
+done:
+ VM_OBJECT_UNLOCK(object);
*flags = UMA_SLAB_PRIV;
OpenPOWER on IntegriCloud