linux-old/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/sched.h>
  12#include <linux/slab.h>
  13#include <linux/vmalloc.h>
  14
  15#include <asm/bitops.h>
  16
  17
  18/*
  19 * Allocate an fd array, using kmalloc or vmalloc.
  20 * Note: the array isn't cleared at allocation time.
  21 */
  22struct file ** alloc_fd_array(int num)
  23{
  24        struct file **new_fds;
  25        int size = num * sizeof(struct file *);
  26
  27        if (size <= PAGE_SIZE)
  28                new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
  29        else 
  30                new_fds = (struct file **) vmalloc(size);
  31        return new_fds;
  32}
  33
  34void free_fd_array(struct file **array, int num)
  35{
  36        int size = num * sizeof(struct file *);
  37
  38        if (!array) {
  39                printk(KERN_ERR "%s array = 0 (num = %d)\n",
  40                                __FUNCTION__, 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        write_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        write_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                        write_unlock(&files->file_lock);
 114                        free_fd_array(old_fds, i);
 115                        write_lock(&files->file_lock);
 116                }
 117        } else {
 118                /* Somebody expanded the array while we slept ... */
 119                write_unlock(&files->file_lock);
 120                free_fd_array(new_fds, nfds);
 121                write_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 (!array) {
 149                printk(KERN_ERR "%s array = 0 (num = %d)\n",
 150                                __FUNCTION__, num);
 151                return;
 152        }
 153        
 154        if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
 155                return;
 156        else if (size <= PAGE_SIZE)
 157                kfree(array);
 158        else
 159                vfree(array);
 160}
 161
 162/*
 163 * Expand the fdset in the files_struct.  Called with the files spinlock
 164 * held for write.
 165 */
 166int expand_fdset(struct files_struct *files, int nr)
 167{
 168        fd_set *new_openset = 0, *new_execset = 0;
 169        int error, nfds = 0;
 170
 171        error = -EMFILE;
 172        if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)
 173                goto out;
 174
 175        nfds = files->max_fdset;
 176        write_unlock(&files->file_lock);
 177
 178        /* Expand to the max in easy steps */
 179        do {
 180                if (nfds < (PAGE_SIZE * 8))
 181                        nfds = PAGE_SIZE * 8;
 182                else {
 183                        nfds = nfds * 2;
 184                        if (nfds > NR_OPEN)
 185                                nfds = NR_OPEN;
 186                }
 187        } while (nfds <= nr);
 188
 189        error = -ENOMEM;
 190        new_openset = alloc_fdset(nfds);
 191        new_execset = alloc_fdset(nfds);
 192        write_lock(&files->file_lock);
 193        if (!new_openset || !new_execset)
 194                goto out;
 195
 196        error = 0;
 197        
 198        /* Copy the existing tables and install the new pointers */
 199        if (nfds > files->max_fdset) {
 200                int i = files->max_fdset / (sizeof(unsigned long) * 8);
 201                int count = (nfds - files->max_fdset) / 8;
 202                
 203                /* 
 204                 * Don't copy the entire array if the current fdset is
 205                 * not yet initialised.  
 206                 */
 207                if (i) {
 208                        memcpy (new_openset, files->open_fds, files->max_fdset/8);
 209                        memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
 210                        memset (&new_openset->fds_bits[i], 0, count);
 211                        memset (&new_execset->fds_bits[i], 0, count);
 212                }
 213                
 214                nfds = xchg(&files->max_fdset, nfds);
 215                new_openset = xchg(&files->open_fds, new_openset);
 216                new_execset = xchg(&files->close_on_exec, new_execset);
 217                write_unlock(&files->file_lock);
 218                free_fdset (new_openset, nfds);
 219                free_fdset (new_execset, nfds);
 220                write_lock(&files->file_lock);
 221                return 0;
 222        } 
 223        /* Somebody expanded the array while we slept ... */
 224
 225out:
 226        write_unlock(&files->file_lock);
 227        if (new_openset)
 228                free_fdset(new_openset, nfds);
 229        if (new_execset)
 230                free_fdset(new_execset, nfds);
 231        write_lock(&files->file_lock);
 232        return error;
 233}
 234
 235
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.