linux-bk/fs/file.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/file.c
   3 *
   4 *  Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes
   5 *
   6 *  Manage the dynamic fd arrays in the process files_struct.
   7 */
   8
   9#include <linux/fs.h>
  10#include <linux/mm.h>
  11#include <linux/time.h>
  12#include <linux/slab.h>
  13#include <linux/vmalloc.h>
  14#include <linux/file.h>
  15
  16#include <asm/bitops.h>
  17
  18
  19/*
  20 * Allocate an fd array, using kmalloc or vmalloc.
  21 * Note: the array isn't cleared at allocation time.
  22 */
  23struct file ** alloc_fd_array(int num)
  24{
  25        struct file **new_fds;
  26        int size = num * sizeof(struct file *);
  27
  28        if (size <= PAGE_SIZE)
  29                new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
  30        else 
  31                new_fds = (struct file **) vmalloc(size);
  32        return new_fds;
  33}
  34
  35void free_fd_array(struct file **array, int num)
  36{
  37        int size = num * sizeof(struct file *);
  38
  39        if (!array) {
  40                printk (KERN_ERR "free_fd_array: array = 0 (num = %d)\n", num);
  41                return;
  42        }
  43
  44        if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
  45                return;
  46        else if (size <= PAGE_SIZE)
  47                kfree(array);
  48        else
  49                vfree(array);
  50}
  51
  52/*
  53 * Expand the fd array in the files_struct.  Called with the files
  54 * spinlock held for write.
  55 */
  56
  57int expand_fd_array(struct files_struct *files, int nr)
  58{
  59        struct file **new_fds;
  60        int error, nfds;
  61
  62        
  63        error = -EMFILE;
  64        if (files->max_fds >= NR_OPEN || nr >= NR_OPEN)
  65                goto out;
  66
  67        nfds = files->max_fds;
  68        spin_unlock(&files->file_lock);
  69
  70        /* 
  71         * Expand to the max in easy steps, and keep expanding it until
  72         * we have enough for the requested fd array size. 
  73         */
  74
  75        do {
  76#if NR_OPEN_DEFAULT < 256
  77                if (nfds < 256)
  78                        nfds = 256;
  79                else 
  80#endif
  81                if (nfds < (PAGE_SIZE / sizeof(struct file *)))
  82                        nfds = PAGE_SIZE / sizeof(struct file *);
  83                else {
  84                        nfds = nfds * 2;
  85                        if (nfds > NR_OPEN)
  86                                nfds = NR_OPEN;
  87                }
  88        } while (nfds <= nr);
  89
  90        error = -ENOMEM;
  91        new_fds = alloc_fd_array(nfds);
  92        spin_lock(&files->file_lock);
  93        if (!new_fds)
  94                goto out;
  95
  96        /* Copy the existing array and install the new pointer */
  97
  98        if (nfds > files->max_fds) {
  99                struct file **old_fds;
 100                int i;
 101                
 102                old_fds = xchg(&files->fd, new_fds);
 103                i = xchg(&files->max_fds, nfds);
 104
 105                /* Don't copy/clear the array if we are creating a new
 106                   fd array for fork() */
 107                if (i) {
 108                        memcpy(new_fds, old_fds, i * sizeof(struct file *));
 109                        /* clear the remainder of the array */
 110                        memset(&new_fds[i], 0,
 111                               (nfds-i) * sizeof(struct file *)); 
 112
 113                        spin_unlock(&files->file_lock);
 114                        free_fd_array(old_fds, i);
 115                        spin_lock(&files->file_lock);
 116                }
 117        } else {
 118                /* Somebody expanded the array while we slept ... */
 119                spin_unlock(&files->file_lock);
 120                free_fd_array(new_fds, nfds);
 121                spin_lock(&files->file_lock);
 122        }
 123        error = 0;
 124out:
 125        return error;
 126}
 127
 128/*
 129 * Allocate an fdset array, using kmalloc or vmalloc.
 130 * Note: the array isn't cleared at allocation time.
 131 */
 132fd_set * alloc_fdset(int num)
 133{
 134        fd_set *new_fdset;
 135        int size = num / 8;
 136
 137        if (size <= PAGE_SIZE)
 138                new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
 139        else
 140                new_fdset = (fd_set *) vmalloc(size);
 141        return new_fdset;
 142}
 143
 144void free_fdset(fd_set *array, int num)
 145{
 146        int size = num / 8;
 147
 148        if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
 149                return;
 150        else if (size <= PAGE_SIZE)
 151                kfree(array);
 152        else
 153                vfree(array);
 154}
 155
 156/*
 157 * Expand the fdset in the files_struct.  Called with the files spinlock
 158 * held for write.
 159 */
 160int expand_fdset(struct files_struct *files, int nr)
 161{
 162        fd_set *new_openset = 0, *new_execset = 0;
 163        int error, nfds = 0;
 164
 165        error = -EMFILE;
 166        if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)
 167                goto out;
 168
 169        nfds = files->max_fdset;
 170        spin_unlock(&files->file_lock);
 171
 172        /* Expand to the max in easy steps */
 173        do {
 174                if (nfds < (PAGE_SIZE * 8))
 175                        nfds = PAGE_SIZE * 8;
 176                else {
 177                        nfds = nfds * 2;
 178                        if (nfds > NR_OPEN)
 179                                nfds = NR_OPEN;
 180                }
 181        } while (nfds <= nr);
 182
 183        error = -ENOMEM;
 184        new_openset = alloc_fdset(nfds);
 185        new_execset = alloc_fdset(nfds);
 186        spin_lock(&files->file_lock);
 187        if (!new_openset || !new_execset)
 188                goto out;
 189
 190        error = 0;
 191        
 192        /* Copy the existing tables and install the new pointers */
 193        if (nfds > files->max_fdset) {
 194                int i = files->max_fdset / (sizeof(unsigned long) * 8);
 195                int count = (nfds - files->max_fdset) / 8;
 196                
 197                /* 
 198                 * Don't copy the entire array if the current fdset is
 199                 * not yet initialised.  
 200                 */
 201                if (i) {
 202                        memcpy (new_openset, files->open_fds, files->max_fdset/8);
 203                        memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
 204                        memset (&new_openset->fds_bits[i], 0, count);
 205                        memset (&new_execset->fds_bits[i], 0, count);
 206                }
 207                
 208                nfds = xchg(&files->max_fdset, nfds);
 209                new_openset = xchg(&files->open_fds, new_openset);
 210                new_execset = xchg(&files->close_on_exec, new_execset);
 211                spin_unlock(&files->file_lock);
 212                free_fdset (new_openset, nfds);
 213                free_fdset (new_execset, nfds);
 214                spin_lock(&files->file_lock);
 215                return 0;
 216        } 
 217        /* Somebody expanded the array while we slept ... */
 218
 219out:
 220        spin_unlock(&files->file_lock);
 221        if (new_openset)
 222                free_fdset(new_openset, nfds);
 223        if (new_execset)
 224                free_fdset(new_execset, nfds);
 225        spin_lock(&files->file_lock);
 226        return error;
 227}
 228
 229
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.