Home | History | Annotate | Line # | Download | only in lib
pagealign_alloc.c revision 1.1.1.1.2.2
      1 /* Memory allocation aligned to system page boundaries.
      2 
      3    Copyright (C) 2005 Free Software Foundation, Inc.
      4 
      5    This program is free software; you can redistribute it and/or modify it
      6    under the terms of the GNU General Public License as published
      7    by the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public
     16    License along with this program; if not, write to the Free Software
     17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
     18    USA.  */
     19 
     20 /* Written by Derek R. Price <derek (at) ximbiot.com>.  */
     21 
     22 #ifdef HAVE_CONFIG_H
     23 # include <config.h>
     24 #endif
     25 
     26 #include "pagealign_alloc.h"
     27 
     28 #include <errno.h>
     29 #include <stdlib.h>
     30 
     31 #include <fcntl.h>
     32 
     33 #if HAVE_UNISTD_H
     34 # include <unistd.h>
     35 #endif
     36 
     37 #if HAVE_MMAP
     38 # include <sys/mman.h>
     39 #endif
     40 
     41 #include "error.h"
     42 #include "exit.h"
     43 #include "getpagesize.h"
     44 #include "xalloc.h"
     45 #include "gettext.h"
     46 
     47 #define _(str) gettext (str)
     48 
     49 #if HAVE_MMAP
     50 /* Define MAP_FILE when it isn't otherwise.  */
     51 # ifndef MAP_FILE
     52 #  define MAP_FILE 0
     53 # endif
     54 /* Define MAP_FAILED for old systems which neglect to.  */
     55 # ifndef MAP_FAILED
     56 #  define MAP_FAILED ((void *)-1)
     57 # endif
     58 #endif
     59 
     60 
     61 #if HAVE_MMAP || ! HAVE_POSIX_MEMALIGN
     62 
     63 # if HAVE_MMAP
     64 /* For each memory region, we store its size.  */
     65 typedef size_t info_t;
     66 # else
     67 /* For each memory region, we store the original pointer returned by
     68    malloc().  */
     69 typedef void * info_t;
     70 # endif
     71 
     72 /* A simple linked list of allocated memory regions.  It is probably not the
     73    most efficient way to store these, but anyway...  */
     74 typedef struct memnode_s memnode_t;
     75 struct memnode_s
     76 {
     77   void *aligned_ptr;
     78   info_t info;
     79   memnode_t *next;
     80 };
     81 
     82 /* The list of currently allocated memory regions.  */
     83 static memnode_t *memnode_table = NULL;
     84 
     85 
     86 static void
     87 new_memnode (void *aligned_ptr, info_t info)
     88 {
     89   memnode_t *new_node = (memnode_t *) xmalloc (sizeof (memnode_t));
     90   new_node->aligned_ptr = aligned_ptr;
     91   new_node->info = info;
     92   new_node->next = memnode_table;
     93   memnode_table = new_node;
     94 }
     95 
     96 
     97 /* Dispose of the memnode containing a map for the ALIGNED_PTR in question
     98    and return the content of the node's INFO field.  */
     99 static info_t
    100 get_memnode (void *aligned_ptr)
    101 {
    102   info_t ret;
    103   memnode_t *c;
    104   memnode_t **p_next = &memnode_table;
    105 
    106   for (c = *p_next; c != NULL; p_next = &c->next, c = c->next)
    107     if (c->aligned_ptr == aligned_ptr)
    108       break;
    109 
    110   if (c == NULL)
    111     /* An attempt to free untracked memory.  A wrong pointer was passed
    112        to pagealign_free().  */
    113     abort ();
    114 
    115   /* Remove this entry from the list, save the return value, and free it.  */
    116   *p_next = c->next;
    117   ret = c->info;
    118   free (c);
    119 
    120   return ret;
    121 }
    122 
    123 #endif /* HAVE_MMAP || !HAVE_POSIX_MEMALIGN */
    124 
    125 
    126 void *
    127 pagealign_alloc (size_t size)
    128 {
    129   void *ret;
    130 #if HAVE_MMAP
    131 # ifdef HAVE_MAP_ANONYMOUS
    132   const int fd = -1;
    133   const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
    134 # else /* !HAVE_MAP_ANONYMOUS */
    135   static int fd = -1;  /* Only open /dev/zero once in order to avoid limiting
    136 			  the amount of memory we may allocate based on the
    137 			  number of open file descriptors.  */
    138   const int flags = MAP_FILE | MAP_PRIVATE;
    139   if (fd == -1)
    140     {
    141       fd = open ("/dev/zero", O_RDONLY, 0666);
    142       if (fd < 0)
    143 	error (EXIT_FAILURE, errno, _("Failed to open /dev/zero for read"));
    144     }
    145 # endif /* HAVE_MAP_ANONYMOUS */
    146   ret = mmap (NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
    147   if (ret == MAP_FAILED)
    148     return NULL;
    149   new_memnode (ret, size);
    150 #elif HAVE_POSIX_MEMALIGN
    151   int status = posix_memalign (&ret, getpagesize (), size);
    152   if (status)
    153     {
    154       errno = status;
    155       return NULL;
    156     }
    157 #else /* !HAVE_MMAP && !HAVE_POSIX_MEMALIGN */
    158   size_t pagesize = getpagesize ();
    159   void *unaligned_ptr = malloc (size + pagesize - 1);
    160   if (unaligned_ptr == NULL)
    161     {
    162       /* Set errno.  We don't know whether malloc already set errno: some
    163 	 implementations of malloc do, some don't.  */
    164       errno = ENOMEM;
    165       return NULL;
    166     }
    167   ret = (char *) unaligned_ptr
    168         + ((- (unsigned long) unaligned_ptr) & (pagesize - 1));
    169   new_memnode (ret, unaligned_ptr);
    170 #endif /* HAVE_MMAP && HAVE_POSIX_MEMALIGN */
    171   return ret;
    172 }
    173 
    174 
    175 void *
    176 pagealign_xalloc (size_t size)
    177 {
    178   void *ret;
    179 
    180   ret = pagealign_alloc (size);
    181   if (ret == NULL)
    182     xalloc_die ();
    183   return ret;
    184 }
    185 
    186 
    187 void
    188 pagealign_free (void *aligned_ptr)
    189 {
    190 #if HAVE_MMAP
    191   if (munmap (aligned_ptr, get_memnode (aligned_ptr)) < 0)
    192     error (EXIT_FAILURE, errno, "Failed to unmap memory");
    193 #elif HAVE_POSIX_MEMALIGN
    194   free (aligned_ptr);
    195 #else
    196   free (get_memnode (aligned_ptr));
    197 #endif
    198 }
    199