linux/lib/flex_array.c
<<
>>
Prefs
   1/*
   2 * Flexible array managed in PAGE_SIZE parts
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17 *
  18 * Copyright IBM Corporation, 2009
  19 *
  20 * Author: Dave Hansen <dave@linux.vnet.ibm.com>
  21 */
  22
  23#include <linux/flex_array.h>
  24#include <linux/slab.h>
  25#include <linux/stddef.h>
  26
  27struct flex_array_part {
  28        char elements[FLEX_ARRAY_PART_SIZE];
  29};
  30
  31/*
  32 * If a user requests an allocation which is small
  33 * enough, we may simply use the space in the
  34 * flex_array->parts[] array to store the user
  35 * data.
  36 */
  37static inline int elements_fit_in_base(struct flex_array *fa)
  38{
  39        int data_size = fa->element_size * fa->total_nr_elements;
  40        if (data_size <= FLEX_ARRAY_BASE_BYTES_LEFT)
  41                return 1;
  42        return 0;
  43}
  44
  45/**
  46 * flex_array_alloc - allocate a new flexible array
  47 * @element_size:       the size of individual elements in the array
  48 * @total:              total number of elements that this should hold
  49 * @flags:              page allocation flags to use for base array
  50 *
  51 * Note: all locking must be provided by the caller.
  52 *
  53 * @total is used to size internal structures.  If the user ever
  54 * accesses any array indexes >=@total, it will produce errors.
  55 *
  56 * The maximum number of elements is defined as: the number of
  57 * elements that can be stored in a page times the number of
  58 * page pointers that we can fit in the base structure or (using
  59 * integer math):
  60 *
  61 *      (PAGE_SIZE/element_size) * (PAGE_SIZE-8)/sizeof(void *)
  62 *
  63 * Here's a table showing example capacities.  Note that the maximum
  64 * index that the get/put() functions is just nr_objects-1.   This
  65 * basically means that you get 4MB of storage on 32-bit and 2MB on
  66 * 64-bit.
  67 *
  68 *
  69 * Element size | Objects | Objects |
  70 * PAGE_SIZE=4k |  32-bit |  64-bit |
  71 * ---------------------------------|
  72 *      1 bytes | 4186112 | 2093056 |
  73 *      2 bytes | 2093056 | 1046528 |
  74 *      3 bytes | 1395030 |  697515 |
  75 *      4 bytes | 1046528 |  523264 |
  76 *     32 bytes |  130816 |   65408 |
  77 *     33 bytes |  126728 |   63364 |
  78 *   2048 bytes |    2044 |    1022 |
  79 *   2049 bytes |    1022 |     511 |
  80 *       void * | 1046528 |  261632 |
  81 *
  82 * Since 64-bit pointers are twice the size, we lose half the
  83 * capacity in the base structure.  Also note that no effort is made
  84 * to efficiently pack objects across page boundaries.
  85 */
  86struct flex_array *flex_array_alloc(int element_size, unsigned int total,
  87                                        gfp_t flags)
  88{
  89        struct flex_array *ret;
  90        int max_size = FLEX_ARRAY_NR_BASE_PTRS *
  91                                FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
  92
  93        /* max_size will end up 0 if element_size > PAGE_SIZE */
  94        if (total > max_size)
  95                return NULL;
  96        ret = kzalloc(sizeof(struct flex_array), flags);
  97        if (!ret)
  98                return NULL;
  99        ret->element_size = element_size;
 100        ret->total_nr_elements = total;
 101        if (elements_fit_in_base(ret) && !(flags & __GFP_ZERO))
 102                memset(ret->parts[0], FLEX_ARRAY_FREE,
 103                                                FLEX_ARRAY_BASE_BYTES_LEFT);
 104        return ret;
 105}
 106
 107static int fa_element_to_part_nr(struct flex_array *fa,
 108                                        unsigned int element_nr)
 109{
 110        return element_nr / FLEX_ARRAY_ELEMENTS_PER_PART(fa->element_size);
 111}
 112
 113/**
 114 * flex_array_free_parts - just free the second-level pages
 115 * @fa:         the flex array from which to free parts
 116 *
 117 * This is to be used in cases where the base 'struct flex_array'
 118 * has been statically allocated and should not be free.
 119 */
 120void flex_array_free_parts(struct flex_array *fa)
 121{
 122        int part_nr;
 123
 124        if (elements_fit_in_base(fa))
 125                return;
 126        for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++)
 127                kfree(fa->parts[part_nr]);
 128}
 129
 130void flex_array_free(struct flex_array *fa)
 131{
 132        flex_array_free_parts(fa);
 133        kfree(fa);
 134}
 135
 136static unsigned int index_inside_part(struct flex_array *fa,
 137                                        unsigned int element_nr)
 138{
 139        unsigned int part_offset;
 140
 141        part_offset = element_nr %
 142                                FLEX_ARRAY_ELEMENTS_PER_PART(fa->element_size);
 143        return part_offset * fa->element_size;
 144}
 145
 146static struct flex_array_part *
 147__fa_get_part(struct flex_array *fa, int part_nr, gfp_t flags)
 148{
 149        struct flex_array_part *part = fa->parts[part_nr];
 150        if (!part) {
 151                part = kmalloc(sizeof(struct flex_array_part), flags);
 152                if (!part)
 153                        return NULL;
 154                if (!(flags & __GFP_ZERO))
 155                        memset(part, FLEX_ARRAY_FREE,
 156                                sizeof(struct flex_array_part));
 157                fa->parts[part_nr] = part;
 158        }
 159        return part;
 160}
 161
 162/**
 163 * flex_array_put - copy data into the array at @element_nr
 164 * @fa:         the flex array to copy data into
 165 * @element_nr: index of the position in which to insert
 166 *              the new element.
 167 * @src:        address of data to copy into the array
 168 * @flags:      page allocation flags to use for array expansion
 169 *
 170 *
 171 * Note that this *copies* the contents of @src into
 172 * the array.  If you are trying to store an array of
 173 * pointers, make sure to pass in &ptr instead of ptr.
 174 *
 175 * Locking must be provided by the caller.
 176 */
 177int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
 178                        gfp_t flags)
 179{
 180        int part_nr = fa_element_to_part_nr(fa, element_nr);
 181        struct flex_array_part *part;
 182        void *dst;
 183
 184        if (element_nr >= fa->total_nr_elements)
 185                return -ENOSPC;
 186        if (elements_fit_in_base(fa))
 187                part = (struct flex_array_part *)&fa->parts[0];
 188        else {
 189                part = __fa_get_part(fa, part_nr, flags);
 190                if (!part)
 191                        return -ENOMEM;
 192        }
 193        dst = &part->elements[index_inside_part(fa, element_nr)];
 194        memcpy(dst, src, fa->element_size);
 195        return 0;
 196}
 197
 198/**
 199 * flex_array_clear - clear element in array at @element_nr
 200 * @fa:         the flex array of the element.
 201 * @element_nr: index of the position to clear.
 202 *
 203 * Locking must be provided by the caller.
 204 */
 205int flex_array_clear(struct flex_array *fa, unsigned int element_nr)
 206{
 207        int part_nr = fa_element_to_part_nr(fa, element_nr);
 208        struct flex_array_part *part;
 209        void *dst;
 210
 211        if (element_nr >= fa->total_nr_elements)
 212                return -ENOSPC;
 213        if (elements_fit_in_base(fa))
 214                part = (struct flex_array_part *)&fa->parts[0];
 215        else {
 216                part = fa->parts[part_nr];
 217                if (!part)
 218                        return -EINVAL;
 219        }
 220        dst = &part->elements[index_inside_part(fa, element_nr)];
 221        memset(dst, FLEX_ARRAY_FREE, fa->element_size);
 222        return 0;
 223}
 224
 225/**
 226 * flex_array_prealloc - guarantee that array space exists
 227 * @fa:         the flex array for which to preallocate parts
 228 * @start:      index of first array element for which space is allocated
 229 * @end:        index of last (inclusive) element for which space is allocated
 230 * @flags:      page allocation flags
 231 *
 232 * This will guarantee that no future calls to flex_array_put()
 233 * will allocate memory.  It can be used if you are expecting to
 234 * be holding a lock or in some atomic context while writing
 235 * data into the array.
 236 *
 237 * Locking must be provided by the caller.
 238 */
 239int flex_array_prealloc(struct flex_array *fa, unsigned int start,
 240                        unsigned int end, gfp_t flags)
 241{
 242        int start_part;
 243        int end_part;
 244        int part_nr;
 245        struct flex_array_part *part;
 246
 247        if (start >= fa->total_nr_elements || end >= fa->total_nr_elements)
 248                return -ENOSPC;
 249        if (elements_fit_in_base(fa))
 250                return 0;
 251        start_part = fa_element_to_part_nr(fa, start);
 252        end_part = fa_element_to_part_nr(fa, end);
 253        for (part_nr = start_part; part_nr <= end_part; part_nr++) {
 254                part = __fa_get_part(fa, part_nr, flags);
 255                if (!part)
 256                        return -ENOMEM;
 257        }
 258        return 0;
 259}
 260
 261/**
 262 * flex_array_get - pull data back out of the array
 263 * @fa:         the flex array from which to extract data
 264 * @element_nr: index of the element to fetch from the array
 265 *
 266 * Returns a pointer to the data at index @element_nr.  Note
 267 * that this is a copy of the data that was passed in.  If you
 268 * are using this to store pointers, you'll get back &ptr.
 269 *
 270 * Locking must be provided by the caller.
 271 */
 272void *flex_array_get(struct flex_array *fa, unsigned int element_nr)
 273{
 274        int part_nr = fa_element_to_part_nr(fa, element_nr);
 275        struct flex_array_part *part;
 276
 277        if (element_nr >= fa->total_nr_elements)
 278                return NULL;
 279        if (elements_fit_in_base(fa))
 280                part = (struct flex_array_part *)&fa->parts[0];
 281        else {
 282                part = fa->parts[part_nr];
 283                if (!part)
 284                        return NULL;
 285        }
 286        return &part->elements[index_inside_part(fa, element_nr)];
 287}
 288
 289static int part_is_free(struct flex_array_part *part)
 290{
 291        int i;
 292
 293        for (i = 0; i < sizeof(struct flex_array_part); i++)
 294                if (part->elements[i] != FLEX_ARRAY_FREE)
 295                        return 0;
 296        return 1;
 297}
 298
 299/**
 300 * flex_array_shrink - free unused second-level pages
 301 * @fa:         the flex array to shrink
 302 *
 303 * Frees all second-level pages that consist solely of unused
 304 * elements.  Returns the number of pages freed.
 305 *
 306 * Locking must be provided by the caller.
 307 */
 308int flex_array_shrink(struct flex_array *fa)
 309{
 310        struct flex_array_part *part;
 311        int part_nr;
 312        int ret = 0;
 313
 314        if (elements_fit_in_base(fa))
 315                return ret;
 316        for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) {
 317                part = fa->parts[part_nr];
 318                if (!part)
 319                        continue;
 320                if (part_is_free(part)) {
 321                        fa->parts[part_nr] = NULL;
 322                        kfree(part);
 323                        ret++;
 324                }
 325        }
 326        return ret;
 327}
 328