From: Ian Abbott <abbotti@mev.co.uk>
Date: Wed, 24 Oct 2007 12:37:39 +0000 (+0000)
Subject: Allocate non-DMA buffer pages using __get_free_page().  (Use of virt_to_page
X-Git-Tag: r0_7_75~9
X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=5807f1937fc52b7970f8b25f2b539de32c0a4529;p=comedi.git

Allocate non-DMA buffer pages using __get_free_page().  (Use of virt_to_page
on vmalloc'd memory was incorrect and failed on x86_64 for example.)
Clean up fully on allocation failure.
---

diff --git a/comedi/drivers.c b/comedi/drivers.c
index b773344c..2250f868 100644
--- a/comedi/drivers.c
+++ b/comedi/drivers.c
@@ -435,12 +435,15 @@ int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s,
 {
 	comedi_async *async = s->async;
 
+	/* Round up new_size to multiple of PAGE_SIZE */
+	new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
+
 	/* if no change is required, do nothing */
 	if(async->prealloc_buf && async->prealloc_bufsz == new_size){
 		return 0;
 	}
 	// deallocate old buffer
-	if(async->prealloc_buf && s->async_dma_dir != DMA_NONE)
+	if(async->prealloc_buf)
 	{
 		vunmap(async->prealloc_buf);
 		async->prealloc_buf = NULL;
@@ -451,77 +454,73 @@ int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s,
 		unsigned i;
 		for(i = 0; i < async->n_buf_pages; ++i)
 		{
-			if(async->buf_page_list[i].virt_addr)
+			if(async->buf_page_list[i].virt_addr){
 				mem_map_unreserve(virt_to_page(async->buf_page_list[i].virt_addr));
-			if(async->buf_page_list[i].dma_addr)
-			{
-				dma_free_coherent(dev->hw_dev, PAGE_SIZE,
-					async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr);
+				if(s->async_dma_dir != DMA_NONE){
+					dma_free_coherent(dev->hw_dev, PAGE_SIZE,
+						async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr);
+				}else{
+					free_page((unsigned long)async->buf_page_list[i].virt_addr);
+				}
 			}
 		}
 		vfree(async->buf_page_list);
 		async->buf_page_list = NULL;
 		async->n_buf_pages = 0;
 	}
-	if(async->prealloc_buf && s->async_dma_dir == DMA_NONE)
-	{
-		vfree(async->prealloc_buf);
-		async->prealloc_buf = NULL;
-		async->prealloc_bufsz = 0;
-	}
 	// allocate new buffer
 	if(new_size){
-		int i;
-		unsigned n_pages = (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
-		// size is rounded up to nearest multiple of PAGE_SIZE
-		new_size = n_pages << PAGE_SHIFT;
+		unsigned i = 0;
+		unsigned n_pages = new_size >> PAGE_SHIFT;
+		struct page** pages = NULL;
 
 		async->buf_page_list = vmalloc(sizeof(struct comedi_buf_page) * n_pages);
-		if(async->buf_page_list == NULL)
-		{
-			return -ENOMEM;
-		}
-		memset(async->buf_page_list, 0, sizeof(struct comedi_buf_page) * n_pages);
-		async->n_buf_pages = n_pages;
-
-		if(s->async_dma_dir != DMA_NONE)
-		{
-			struct page** pages = NULL;
-
+		if(async->buf_page_list){
+			memset(async->buf_page_list, 0, sizeof(struct comedi_buf_page) * n_pages);
 			pages = vmalloc(sizeof(struct page*) * n_pages);
-			if(pages == NULL)
-			{
-				return -ENOMEM;
-			}
-			for(i = 0; i < n_pages; i++)
-			{
-				async->buf_page_list[i].virt_addr = dma_alloc_coherent(dev->hw_dev,
-					PAGE_SIZE, &async->buf_page_list[i].dma_addr, GFP_KERNEL | __GFP_COMP);
-				if(async->buf_page_list[i].virt_addr == NULL)
-				{
-					vfree(pages);
-					return -ENOMEM;
+		}
+		if(pages){
+			for(i = 0; i < n_pages; i++){
+				if(s->async_dma_dir != DMA_NONE){
+					async->buf_page_list[i].virt_addr = dma_alloc_coherent(dev->hw_dev,
+						PAGE_SIZE, &async->buf_page_list[i].dma_addr, GFP_KERNEL | __GFP_COMP);
+				}else{
+					async->buf_page_list[i].virt_addr = (void*)__get_free_page(GFP_KERNEL);
+				}
+				if(async->buf_page_list[i].virt_addr == NULL){
+					break;
 				}
 				mem_map_reserve(virt_to_page(async->buf_page_list[i].virt_addr));
 				pages[i] = virt_to_page(async->buf_page_list[i].virt_addr);
 			}
+		}
+		if(i == n_pages){
 			async->prealloc_buf = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
+		}
+		if(pages){
 			vfree(pages);
-			if(async->prealloc_buf == NULL) return -ENOMEM;
-		}else
-		{
-			async->prealloc_buf = vmalloc(new_size);
-			if(async->prealloc_buf == NULL)
-			{
-				return -ENOMEM;
-			}
-			for(i = 0; i < n_pages; i++)
-			{
-				async->buf_page_list[i].virt_addr = async->prealloc_buf + PAGE_SIZE * i;
-				mem_map_reserve(virt_to_page(async->buf_page_list[i].virt_addr));
+		}
+		if(async->prealloc_buf == NULL){
+			/* Some allocation failed above. */
+			if(async->buf_page_list){
+				for(i = 0; i < n_pages; i++){
+					if(async->buf_page_list[i].virt_addr == NULL){
+						break;
+					}
+					mem_map_unreserve(virt_to_page(async->buf_page_list[i].virt_addr));
+					if(s->async_dma_dir != DMA_NONE){
+						dma_free_coherent(dev->hw_dev, PAGE_SIZE,
+							async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr);
+					}else{
+						free_page((unsigned long)async->buf_page_list[i].virt_addr);
+					}
+				}
+				vfree(async->buf_page_list);
+				async->buf_page_list = NULL;
 			}
+			return -ENOMEM;
 		}
+		async->n_buf_pages = n_pages;
 	}
 	async->prealloc_bufsz = new_size;