BIGPHYSエリアについて

BIGPHYSエリアはどのようなものでどのように使うかについて述べます。 BIGPHYSエリアはカーネルの管理から外されている物理メモリです。 カーネルの中でカーネル関数を呼ぶことにより利用できます。 ユーザとのコミュニケーションはいろいろと方法がありますが、 基本的にはBIGPHYSエリアを管理するドライバを作成し、その ドライバをユーザが呼び出すことでデータのやりとりを 行ないます。DMAなどで直接このBIGPHYSエリアに書き込めば、 余分なコピーの発生を防ぐことができます。いわゆるゼロコピー ドライバを作成することが可能になります。 BIGPHYSエリアは標準には実装されていませんので、カーネルに パッチを当てる必要があります。詳しくはこのページの後半に 述べますので、見てください。まずはそのBIGPHYSエリアを 使ったドライバの実装について述べることにします。

BIGPHYSエリアをサポートするカーネル関数

BIGPHYSエリアをサポートする デバイスドライバの例

以下のコードはカーネル2.2用です。2.4では試されていません。
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/kernel.h>
#include <linux/bigphysarea.h>

#define lnxuio_major  (42) /* uio dev major number */

#define lnxuio_allocpage _IOW('x',7,size_t)
#define lnxuio_deallocpage _IOW('x',8,size_t)
#define lnxuio_getptr _IOW('x',9,size_t)

#define UIO_MAX_BUFFERS 100

#define lnxuio_fail 0
#define lnxuio_ok 1

/*
* contiguous memory block descriptor
*/
typedef struct uio_mem_t {
        unsigned long kaddr;           /* kernel virtual address       */
        unsigned long uaddr;           /* user   virtual address       */
        unsigned long paddr;           /* physical address             */
        int size;                      /* size in bytes                */
} uio_mem_t;

#define COUNT 100 /* max number of memory areas */

static int current_count = 0;
static int total_count = 0;

/*
 *  global bookkeeping arrays (common to all processes ..)
 */

static void *addr[COUNT];
static int   size[COUNT];
static int   upid[COUNT];

static int lnxuio_open( struct inode* ino, struct file* filep)
{
  MOD_INC_USE_COUNT;
  return 0;
}
static int lnxuio_close( struct inode* ino, struct file* filep)
{
  MOD_DEC_USE_COUNT; 
}
static int lnxuio_ioctl(struct inode *inode, struct file *file,
                        unsigned int cmd, unsigned long arg)
{

  unsigned int cur_minor = MINOR(inode->i_rdev);
  int i;
  int found_flag;
  int pagecount;
  struct uio_mem_t l_uio; 

  copy_from_user(&l_uio , (struct uio_mem_t *) arg, sizeof(struct uio_mem_t)); 

  switch (cmd) {
  case lnxuio_allocpage :

    pagecount = (int)((l_uio.size -1)/4096 + 1); /* pages */
    if (total_count >= COUNT) {
      return -ENFILE;
    }
    /* find a free slot */
    for (i=0; i < COUNT; i++) if (size[i]==-1) break;
    current_count = i;
    addr[current_count] = 
      bigphysarea_alloc_pages(pagecount, 0, GFP_KERNEL);

    if (addr[current_count] == 0) {
      return -ENOMEM;
    }
    l_uio.uaddr = (unsigned)virt_to_bus((unsigned*)addr[current_count]);
    l_uio.paddr = (unsigned)virt_to_phys((unsigned*)addr[current_count]);
    l_uio.kaddr = (unsigned)addr[current_count];
    size[current_count] = pagecount;
    upid[current_count] = current->pid;
    total_count++;
    copy_to_user((struct uio_mem_t *) arg, &l_uio, sizeof(struct uio_mem_t));
    return 0;
    break;
  case lnxuio_deallocpage :
    found_flag = 0;
    for (i = 0; i < COUNT; i++) {
      if ( (unsigned)addr[i] == l_uio.kaddr) {
        bigphysarea_free_pages(addr[i]);
        current_count = i;
        found_flag = 1;
      } 
    }
    if (found_flag == 0) {
      return -ENXIO;
    }
    size[current_count] = -1;
    total_count--;
    return 0;
    break;
  default :
    return -EINVAL;
}

int uio_table_read (char *buf, char **start, off_t offset, int len, int unused)
{
  int i;
  len=0;
  len+=sprintf(buf+len,"PhysAddr \t Size \t Pid \t lnxuio v1.2\n");

  for (i = 0; i < COUNT; i++) 
    if (size[i] != -1) {
      len+=sprintf(buf+len,"%x\t",(unsigned)addr[i]);
      len+=sprintf(buf+len,"%d \t",size[i]);
      len+=sprintf(buf+len,"%d \n",upid[i]);
    }
  return len;
}

static struct file_operations fops = {
  ioctl: lnxuio_ioctl,
  open:  lnxuio_open,
  release: lnxuio_close,
}

static struct proc_dir_entry uio_proc_file = {
 0,9 ,"uio_table", S_IFREG | S_IRUGO,1,0,0,0, NULL, &uio_table_read};

int init_module(void)
{
int i;
  if (register_chrdev(lnxuio_major, "physmem-manager", &fops)) {
    printk(" lnxuio: register physmem-manager failed. \n");
    return -EIO;
  }
  proc_register(&proc_root, &uio_proc_file) ; 
  for (i = 0; i < COUNT; i++) size[i] = -1;
 
  printk(" lnxuio: physmem-manager loaded; dev major %d\n",lnxuio_major);
  
  return 0;
}

void cleanup_module(void)
{
  int i; 

  if (unregister_chrdev(lnxuio_major, "physmem-manager") != 0) {
    printk(" lnxuio: cleanup_module failed\n");
  }
  else {
    for (i=0; i < COUNT; i++) if (size[i] != -1) {
      bigphysarea_free_pages(addr[i]);
    }

    proc_unregister(&proc_root,uio_proc_file.low_ino);
    printk(" lnxuio: physmem-manager removed\n");
  }
}

BIGPHYSエリアパッチのホームページ

以下の文章は Paulineさん のホームページに載っているBIGPHYSの記事や他のWEBサイトから取って来て まとめたものです。

Bigphysarea is a driver which allocates a big piece of memory during boottime and returns the entire piece or parts of it to a requesting driver. The driver is not created by Pauline, but is maintained by Pauline for the newer kernels.

Some hardware requires memory in physical continuous blocks. Howevery, the longer your Linux system is running, the more main memory is getting allocated and freed in different sizes. This leads to the fenomenen called 'memory fragmentation'. And the end result of this all is a machine which has enough memory left, but scattered all over the place, thereby failing our drivers with an 'out of memory'. Prime examples of these hardware are soundcards which need to be initialized and the above mentioned framegrabber.

Bytes      Timestamp       Filename
________ ____________________ ____________________________
7136 Aug 21 14:06:29 1998 bigphysarea-2.1.116.tar.gz
7165 Feb 17 11:18:40 1999 bigphysarea-2.2.1.tar.gz
7198 May 20 22:56:11 1999 bigphysarea-2.2.9.tar.gz
7173 Oct 23 10:22:54 1999 bigphysarea-2.2.12.tar.gz
6676 Dec 28 11:29:02 1999 bigphysarea-2.3.34.tar.gz
6787 Jan 24 15:10:10 2000 bigphysarea-2.3.40.tar.gz
7158 Oct 20 09:11:15 2000 bigphysarea-2.2.13.tar.gz
7473 Oct 20 09:11:59 2000 bigphysarea-2.4.0.tar.gz  [LATEST]
When there is no patch for the exact kernel version you are running, try the patch with the lower number. Changes are it will work without problems.

上記のURL以外に次のURLからでもキットを持ってくることが できます。 http://sci-serv.inrialpes.fr/Linux/patches/

Patching the Kernel


cd DIS/src/kernel

tar zxvof bigphysarea-2.x.x.tar.gz

cp bigphysarea-2.x.x/bigphysarea-patch   /usr/src/linux

cd /usr/src/linux

patch -p1 < bigphysarea-patch

Configuration parameter : CONFIG_BIGPHYS_AREA

If you have a distribution kit including bigphysarea, you do not need the patch. Please set YES into the flag.
  Enables kernel support for reserving large areas of physical memory
  at boot-time for use by certain device drivers (such as video 
  framegrabbers, etc.) which require it. To use this feature, boot
  the kernel with the boot-time option 'bigphysarea=nnn' where 
  'nnn' is the number of pages (a page is usually 4K) to reserve.

Building and installing a new kernel

Update your /etc/lilo.conf (or other boot selection system you use) 
to include the following lines:
image = /vmlinux
     label = Linux
     append = "bigphysarea=1024"
  
(Other values for the size of the bigphys area can be chosen, 
as required - `1024' equals 4096K, or 4M.
Run lilo to install the new loader and kernel and reboot.

lilo

reboot

Verifying the kernel configuration

cat /proc/bigphysarea

This should return something like:
Big physical area, size 4096 kB
                                   free list:           used list:
number of blocks:          1                0
size of largest block:     4096 kB      0 kB
total:                              4096 kB      0 kB