Hi, I wrote a RPC in GNUMach to allocate contiguous physical memory for DMA buffers. The code is shown as below. I have been using this function for quite a long time, but it seems it can cause some kind of memory leak. If I run the pcnet32 driver, which uses this function to allocate DMA buffers, for many times, the system seems to run out of memory. I checked the code, but didn't find the place which could cause memory leak. After a process is terminated, the memory and relevant objects allocated by vm_dma_buff_alloc should be freed automatically. I don't have much experience of Mach development. Maybe that's why I fail to find the problem.
By the way, feel free to point out any other problems in this function. antrik said this implementation isn't consistent with Mach's memory management and I should make it work like device_map, so there will be a pager in the kernel. I basically agree with that. What should the interface be? any suggestions? Best regards, Zheng Da +void vm_pages_release(npages, pages, external) + int npages; + vm_page_t *pages; + boolean_t external; +{ + int i; + + for (i = 0; i < npages; i++) + { + vm_page_release (pages[i], external); + } +} + +kern_return_t vm_dma_buff_alloc(host_priv, map, size, result_vaddr, result_paddr) + host_t host_priv; + vm_map_t map; + vm_size_t size; + vm_address_t *result_vaddr; + vm_address_t *result_paddr; +{ + extern vm_size_t vm_page_big_pagenum; + extern vm_offset_t phys_first_addr; + extern vm_offset_t phys_last_addr; + + int npages; + int i; + vm_page_t *pages; + vm_object_t object; + vm_map_entry_t entry; + kern_return_t kr; + vm_address_t vaddr; + vm_offset_t offset = 0; + + if (host_priv == HOST_NULL) + return KERN_INVALID_HOST; + + if (map == VM_MAP_NULL) + return KERN_INVALID_TASK; + + size = round_page(size); + + /* We allocate the contiguous physical pages for the buffer. */ + + npages = size / PAGE_SIZE; + pages = (vm_page_t) kalloc (npages * sizeof (vm_page_t)); + if (pages == NULL) + { + return KERN_RESOURCE_SHORTAGE; + } + + if (vm_page_big_pagenum == 0) + vm_page_big_pagenum = atop(phys_last_addr - phys_first_addr); + + kr = vm_page_grab_contiguous_pages(npages, pages, NULL, TRUE); + if (kr) + { + kfree (pages, npages * sizeof (vm_page_t)); + return kr; + } + + /* Allocate the object + * and find the virtual address for the DMA buffer */ + + object = vm_object_allocate(size); + vm_map_lock(map); + /* TODO user_wired_count might need to be set as 1 */ + kr = vm_map_find_entry(map, &vaddr, size, (vm_offset_t) 0, + VM_OBJECT_NULL, &entry); + if (kr != KERN_SUCCESS) + { + vm_map_unlock(map); + vm_object_deallocate(object); + kfree (pages, npages * sizeof (vm_page_t)); + vm_pages_release (npages, pages, TRUE); + return kr; + } + + entry->object.vm_object = object; + entry->offset = 0; + + /* We can unlock map now. */ + vm_map_unlock(map); + + /* We have physical pages we need and now we need to do the mapping. */ + + pmap_pageable (map->pmap, vaddr, vaddr + size, FALSE); + + *result_vaddr = vaddr; + *result_paddr = pages[0]->phys_addr; + + for (i = 0; i < npages; i++) + { + vm_object_lock(object); + vm_page_lock_queues(); + vm_page_insert(pages[i], object, offset); + vm_page_wire(pages[i]); + vm_page_unlock_queues(); + vm_object_unlock(object); + + /* Enter it in the kernel pmap */ + PMAP_ENTER(map->pmap, vaddr, pages[i], VM_PROT_DEFAULT, TRUE); + + vm_object_lock(object); + PAGE_WAKEUP_DONE(pages[i]); + vm_object_unlock(object); + + vaddr += PAGE_SIZE; + offset += PAGE_SIZE; + } + + kfree ((vm_offset_t) pages, npages * sizeof (vm_page_t)); + return KERN_SUCCESS; +}