Skip to contentSkip to conversation

Use kernel module instead Big Physical Area patch

DISCUSSION Constantine Gavrilov 2 participants 3 messages Aug 5, 2001

lore
Share & tools
lore
#0 lore raw

Hi,

I wrote a small kernel module that defines and exports bfree() and bmalloc() functions. The idea is to use this kernel module as a replacement for big physcial area kernel patch.

For example, zoran kernel driver relies on the big physical area API for v4l (used for video-in-a-window) to work. While the driver will compile and load with a non-patched kernel, v4l will not work reliably without big physical area support since the chip needs about 2megs of contiguous memory to display in a window. This means one really has to use a patched kernel.

This module removes this requirement and allows v4l to work reliably with a non-patched kernel. The idea is to load the module from initrd or right after boot. I have used __get_free_pages() and mem_map_reserve() to pre-allocate the memory. I have left the user-mode code that I used to debug allocation and garbage collection. Compiled into a user-space program, the code will allocate/free random chunks from a pre-allocated space and print out the used list.

Comments?

Questions:
1) On a 256 MB machine, I was able to pre-allocate 512 pages using __get_free_pages(...,get_order(size)). It is enough for one card, but not for more. Any ideas on how to pre-allocate more?
2) __get_free_pages(...,get_order(size)) can be used to pre-allocate 2^n contiguous pages (2,4,16,...512, ...., 1024). Can I use something else from a kernel module to request a number that is not 2^n (say 750)?

Signature · 8 lines
----------------------------------------
Constantine Gavrilov
Linux Leader
Optibase Ltd
7 Shenkar St, Herzliya 46120, Israel
Phone: (972-9)-970-9140
Fax:   (972-9)-958-6099
----------------------------------------

/***************************************************************************
memreserve.c - description
-------------------
begin : Tue Jul 31 2001
copyright : (C) 2001 by Optibase Ltd
email : [email protected]
***************************************************************************/

/***************************************************************************

 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *

***************************************************************************/

#ifndef __KERNEL__
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>

/* module stuff */
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define EXPORT_SYMBOL(x)
#define MODULE_PARM(x,y)
#define GFP_USER
#define GFP_DMA

/* memory management stuff */
#define kmalloc(x,y) (malloc(x))
#define kfree(x) (free(x))
#define __get_free_pages(x,y) (malloc(y))
#define free_pages(x, y) (free((void *)x))
#define mem_map_reserve(x) (printf("reserve print: 0x%08lx\n",x)) #define mem_map_unreserve(x) (printf("unreserve print: 0x%08lx\n",x)) #define virt_to_page(x) (x)
#define phys_to_virt(x) (x)
#define get_order(x) (x)
/* from asm/page.h */

#define PAGE_SHIFT  12
#define PAGE_SIZE   (1UL << PAGE_SHIFT)
#define PAGE_MASK   (~(PAGE_SIZE-1))

#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)

/* lock stuff */
#define write_lock(x) (pthread_mutex_lock(x)) #define write_unlock(x) (pthread_mutex_unlock(x)) #define RW_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER #define rwlock_t pthread_mutex_t

/* printk stuff */
#define printk printf
#define KERN_WARNING
#define KERN_INFO
#define KERN_ERR
#define KERN_DEBUG

#else
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/page.h>
#include <asm/io.h>
#include <linux/mm.h>
#include <linux/wrapper.h>
#endif

void bfree(void*, unsigned long);
void* bmalloc(unsigned long);

EXPORT_SYMBOL(bfree);
EXPORT_SYMBOL(bmalloc);

int reserve;
static unsigned long reserve_start;

MODULE_PARM(reserve, "i");

struct reserve_list {
	unsigned long mem;
	unsigned long size;
	struct reserve_list *next;
};

struct reserve_list *used_list=NULL;

static rwlock_t alloc_ops=RW_LOCK_UNLOCKED;

#ifdef __KERNEL__
int init_module(void)

{
	unsigned long adr;
	unsigned long size;
#else
int init_module(int x)
{
	unsigned long adr;
	unsigned long size;

	reserve=x;
#endif
		
	if (!reserve) {
		printk(KERN_ERR "memreserve: Supply a positive reserve param\n");
		return -EINVAL;
	}
	size=reserve*PAGE_SIZE;
	reserve_start = (unsigned long)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));
	if (reserve_start) {
		adr = reserve_start;
		while (size > 0) {
			mem_map_reserve(virt_to_page(phys_to_virt(adr)));
			adr += PAGE_SIZE;
			size -= PAGE_SIZE;
		}
	}
	else {
		printk(KERN_ERR "memreserve: Cannot allocate contiguous memory of %lu bytes\n", size);
		return -EINVAL;
	}
	printk(KERN_INFO "memreserve: Allocated contiguous memory of %lu bytes\n", reserve*PAGE_SIZE);
	return 0;
}

void cleanup_module(void)

{
	unsigned long adr;
	unsigned long size;
	
	if(used_list != NULL) {
		printk(KERN_ERR "memreserve: Memory leak somewhere -- more than zero member in the used list\n");
		printk(KERN_ERR "memreserve: Check your usage count\n");
		printk(KERN_ERR "memreserve: Will not free memory\n");
		return;
	}
	if (reserve_start) {
		adr = reserve_start;
		size = reserve * PAGE_SIZE;
		while (size > 0) {
			mem_map_unreserve(virt_to_page(phys_to_virt(adr)));
			adr += PAGE_SIZE;
			size -= PAGE_SIZE;
		}
		free_pages(reserve_start,get_order(reserve * PAGE_SIZE));
	}
}

void* bmalloc(unsigned long req_size)

{
	/*we need a lock here against simultaneous entry*/

	struct reserve_list *list, *newmember, *prevmember;
	unsigned long prevaddr, size;
	
	write_lock(&alloc_ops);
	size=PAGE_ALIGN(req_size);
	prevaddr=reserve_start;
	prevmember=NULL;
	
	printk(KERN_DEBUG "memreserve: Rounded requested size %lu (0x%08lx) to 0x%08lx (%lu pages)\n", req_size, req_size,size, size/PAGE_SIZE);
	for(list=used_list; list != NULL; list=list->next)
	{
		if( (list->mem - prevaddr) >= size) {
			newmember=(struct reserve_list *)kmalloc(sizeof(struct reserve_list), GFP_USER);
			if (!newmember) {
				printk(KERN_ERR "memreserve: Out of memory\n");
				write_unlock(&alloc_ops);
				return NULL;
			}
			newmember->next=list;
			newmember->mem=prevaddr;
			newmember->size=size;
			if(prevmember != NULL)
				prevmember->next=newmember;
			else
				used_list=newmember;
			MOD_INC_USE_COUNT;
			write_unlock(&alloc_ops);
			return (void *)prevaddr; /* we got our address */
		}
		prevmember=list;
		prevaddr=list->mem+list->size;
	}

/* list is NULL here; this code both works for both first and the last list member */ if(size <= reserve_start + (PAGE_SIZE * reserve) - prevaddr) {
newmember=(struct reserve_list *)kmalloc(sizeof(struct reserve_list), GFP_USER); if (!newmember) {
printk(KERN_ERR "memreserve: Out of memory\n"); write_unlock(&alloc_ops);
return NULL;
}
newmember->mem=prevaddr;
newmember->size=size;
if(prevmember == NULL) /* this is the first member */
used_list=newmember;
else /* this is the last member */

   			prevmember->next=newmember;
		newmember->next=NULL;
   		MOD_INC_USE_COUNT;
   		write_unlock(&alloc_ops);
   		return (void *)prevaddr; /* we got our address */
   	}
   	else
   		printk(KERN_WARNING "memreserve: Could not find big enough region in the free list\n");
   	write_unlock(&alloc_ops);
	return NULL;
}

void bfree(void *mem, unsigned long req_size)

{
	/*we need a lock here against simultaneous entry*/
	struct reserve_list *list, *prevaddr;
	unsigned long size;
	
	write_lock(&alloc_ops);
	size=PAGE_ALIGN(req_size);
	prevaddr=NULL;
	
	for(list=used_list; list != NULL; list=list->next) {
		if(list->mem == (unsigned long)mem && list->size == size) {
			if(prevaddr == NULL)
				used_list=list->next;
			else
				prevaddr->next=list->next;
			kfree(list);
			MOD_DEC_USE_COUNT;
			write_unlock(&alloc_ops);
			return;
		}
		prevaddr=list;
	}
	printk(KERN_WARNING "memreserve: Someone requested illegal deallocation\n");
	write_unlock(&alloc_ops);
}

void print_used_list(void)

{
	/*we need a lock here against simultaneous entry*/
	struct reserve_list *list;
	
	write_lock(&alloc_ops);
	printk(KERN_INFO "Reserve start is 0x%08lx, reserve size is 0x%08lx\n", reserve_start, reserve*PAGE_SIZE);
	for(list=used_list; list != NULL; list=list->next) {
		printk(KERN_INFO "memreserve: Address is 0x%08lx (%lu pages), size is 0x%08lx (%lu pages)\n", list->mem, list->mem/PAGE_SIZE, list->size, list->size/PAGE_SIZE);
	}
	write_unlock(&alloc_ops);
}

#ifndef __KERNEL__
int main()

{
	int n=0;
	int i=0;
	int k;
	struct reserve_list *list;
	unsigned long choose_size(unsigned long);
	int choose_index(int);
	int choose_bool(void);
#define MAX_ALLOC 100
	
	if(init_module(MAX_ALLOC) != 0) {
		printf("Could not get memory\n");
		exit(1);
	}
	while(n>=0) {
		if(n<=3) {
			if (bmalloc(choose_size(MAX_ALLOC)/10) != NULL)
				n++;
		}
		else {
			if(choose_bool() == 0) {
				if (bmalloc(choose_size(MAX_ALLOC)/10) != NULL)
					n++;
			}
			else {
				i=choose_index(n);
				list=used_list;
				for(k=0;k<(i-1);k++)
					list=list->next;
				printf("Free index %d\n", i);
				bfree((void *)list->mem, list->size);
				n--;
			}
		}
		print_used_list(); getchar();
	}
	cleanup_module();
	return 0;
}

unsigned long choose_size(unsigned long MAX_SIZE)

{
	double pos;
	
	pos=((double)MAX_SIZE*(double)PAGE_SIZE*(double)random())/(double)(RAND_MAX+1.0);
	return 1+(unsigned long) pos;
}

int choose_index(int MAX_INDEX)

{
	double pos;
	
	pos=((double)MAX_INDEX*(double)random())/(double)(RAND_MAX+1.0);
	return 1+(int) pos;
}

int choose_bool(void)

{
	long pos;
	
	pos=random();
	
	if(pos <= (RAND_MAX/2))
		return 0;
	else
		return 1;
}

#endif

#1 lore raw
Quoted 5 lines · Constantine Gavrilov

Hi,

I wrote a small kernel module that defines and exports bfree() and bmalloc() functions. The idea is to use this kernel module as a replacement for big physcial area kernel patch.

Ahum, but this is _not_ a kernel replacement for the bigphysarea patch. If also requires the patch to be integrated in the main-stream kernel. Following your reasoning you could just as well get Linus to accept the bigphysarea patch and voila, all the problems you describe are gone.

Quoted 6 lines

For example, zoran kernel driver relies on the big physical area API for v4l (used for video-in-a-window) to work. While the driver will compile and load with a non-patched kernel, v4l will not work reliably without big physical area support since the chip needs about 2megs of contiguous memory to display in a window. This means one really has to use a patched kernel.

Check, or load the zoran module very early in the boot process.

Quoted 1 line

This module removes this requirement and allows v4l to work reliably

^^^ zoran driver

Quoted 3 lines

with a non-patched kernel. The idea is to load the module from initrd or right after boot. I have used __get_free_pages() and mem_map_reserve() to pre-allocate the memory.

Wasn't it easier to just take bigphysarea and made a module out of it? It seems you already took a lot of the code, so why not take the last step?

Quoted 4 lines

I have left the user-mode code that I used to debug allocation and garbage collection. Compiled into a user-space program, the code will allocate/free random chunks from a pre-allocated space and print out the used list.

/proc/bigphysarea?

Quoted 9 lines

Comments?

Questions:
1) On a 256 MB machine, I was able to pre-allocate 512 pages using __get_free_pages(...,get_order(size)). It is enough for one card, but not for more. Any ideas on how to pre-allocate more?
2) __get_free_pages(...,get_order(size)) can be used to pre-allocate 2^n contiguous pages (2,4,16,...512, ...., 1024). Can I use something else from a kernel module to request a number that is not 2^n (say 750)?

Congrats. Now you know why I used bootmem, which as a bad sideeffect prevents one to make bigphysarea a module :(

On the last question: no. get_free_pages() works with _pages_. kmalloc() works with sizes. (but you know the problems with that one.)

Quoted 5 lines

/***************************************************************************
memreserve.c - description
-------------------
begin : Tue Jul 31 2001
copyright : (C) 2001 by Optibase Ltd

Eh, if you insist of using this much of the bigphysarea ideas, you could at least give us some credit/mention?

Quoted 1 line

email : [email protected]

Met vriendelijke groet,
Pauline Middelink

Signature · 2 lines
GPG Key fingerprint = 2D5B 87A7 DDA6 0378 5DEA  BD3B 9A50 B416 E2D0 C3C2
For more details look at my website http://www.polyware.nl/~middelink
#2 lore raw
In reply to Constantine Gavrilov
Quoted 4 lines · Pauline Middelink
Quoted 2 lines · Constantine Gavrilov

Hi,

Quoted 9 lines
Quoted 2 lines

I wrote a small kernel module that defines and exports bfree() and bmalloc() functions. The idea is to use this kernel module as a replacement for big physcial area kernel patch.

Ahum, but this is _not_ a kernel replacement for the bigphysarea patch. If also requires the patch to be integrated in the main-stream kernel. Following your reasoning you could just as well get Linus to accept the bigphysarea patch and voila, all the problems you describe are gone.

You missed the point here. Of course big physical area is preferrable and it is guaranteed to work for larger pre-allocations as well. If you ask me, I am for big physical area API in the Linus kernel. It is a clean drop-in and will not touch anything. Advantages are obvious, of course. And no, this code does not require modifications to the kernel source since it is a module. I do not have to re-compile the kernel with a big physical area api, I just need to compile a module against the kernel I have. The idea here is if I ship a 3-d party module that requires contiguous memory support, I can add this code to the source of the module and it will work with the distribution kernels. The idea is to allow the 3d party (or included) modules to work with non-patched distribution kernels.

Quoted 5 lines
Quoted 2 lines

For example, zoran kernel driver relies on the big physical area API for v4l (used for video-in-a-window) to work. While the driver will compile and load with a non-patched kernel, v4l will not work reliably without big physical area support since the chip needs about 2megs of contiguous memory to display in a window. This means one really has to use a patched kernel.

Check, or load the zoran module very early in the boot process.

For Zoran case, loading early will not really help because memory is not allocated unil one opens /dev/video. However, this can be easily fixed. In any case, I want to be able to load/unload my card drivers. Our usage of VPXpress cards based on Zoran chips requires that. So, the module pre-allocating the ememory is more flexible.

Quoted 5 lines

Wasn't it easier to just take bigphysarea and made a module out of it? It seems you already took a lot of the code, so why not take the last step?

I used some code from Zoran driver and none from the big physical area patch (please see below). The code re-implements bfree() and balloc() interfaces that Zoran driver uses. It is the bree() and balloc() functions in Zoran driver that my module re-implemented that were using the big physical area functions. I got myself confused somewhat. To call it a big physical area replacement, I had to implement bigphysarea_alloc, bigphysarea_free, bigphysarea_alloc_pages, bigphysarea_free_pages and possibly add the proc support. I can still do that.

Quoted 5 lines
Quoted 2 lines

I have left the user-mode code that I used to debug allocation and garbage collection. Compiled into a user-space program, the code will allocate/free random chunks from a pre-allocated space and print out the used list.

/proc/bigphysarea?

What good does it make if you your garbage collection structures get corrupted and kernel oopses?

Quoted 10 lines

Eh, if you insist of using this much of the bigphysarea ideas, you could at least give us some credit/mention?

Quoted 2 lines

email : [email protected]

Met vriendelijke groet,
Pauline Middelink

Well, duly noted. Everyone knows what big physical area API is and who supports it. Is it not so? I did not mean to rob you of credit. I do use ideas of big physical area API, but no code was used from the patch itself. The sample pre-allocation and release code was taken from YOUR Zoran driver. This is what I use from your code in my init_module():
................................................
reserve_start = (unsigned
long)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));

  if (reserve_start) {
      adr = reserve_start;
      while (size > 0) {
          mem_map_reserve(virt_to_page(phys_to_virt(adr)));
          adr += PAGE_SIZE;
          size -= PAGE_SIZE;
      }

}
....................................................

And this is what I use from your code in my cleanup_module():
.....................................................

if (reserve_start) {
      adr = reserve_start;
      size = reserve * PAGE_SIZE;
      while (size > 0) {
          mem_map_unreserve(virt_to_page(phys_to_virt(adr)));
          adr += PAGE_SIZE;
          size -= PAGE_SIZE;
      }
      free_pages(reserve_start,get_order(reserve * PAGE_SIZE));

}
.......................................................

The rest basically is the play with data structures. Yes, I do re-implement bfree() and balloc(). In your Zoran driver, these functions were defined to use bigphysarea_alloc_pages and bigphysarea_free_pages interfaces from big physical area patch that you maitain for the new kernels. I have done it in order to allow the use of drivers that rely on this patch with non-patched kernels. I did this work for the possible inclusion into the future drivers for our Xpress cards. These cards use Zoran chips and we have used your driver code. We have given you due credit in the driver source and in the documentation (for example, see http://www.optibase.com/linux/docs.asp).

Signature · 8 lines
----------------------------------------
Constantine Gavrilov
Linux Leader
Optibase Ltd
7 Shenkar St, Herzliya 46120, Israel
Phone: (972-9)-970-9140
Fax:   (972-9)-958-6099
----------------------------------------