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 #include <linux/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 */ 22 struct 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 34 void free_fd_array(struct file **array, int num) 35 { 36 int size = num * sizeof(struct file *); 37 38 if (!array) { 39 printk (KERN_ERR "free_fd_array: array = 0 (num = %d)\n", num); 40 return; 41 } 42 43 if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */ 44 return; 45 else if (size <= PAGE_SIZE) 46 kfree(array); 47 else 48 vfree(array); 49 } 50 51 /* 52 * Expand the fd array in the files_struct. Called with the files 53 * spinlock held for write. 54 */ 55 56 static int expand_fd_array(struct files_struct *files, int nr) 57 __releases(files->file_lock) 58 __acquires(files->file_lock) 59 { 60 struct file **new_fds; 61 int error, nfds; 62 63 64 error = -EMFILE; 65 if (files->max_fds >= NR_OPEN || nr >= NR_OPEN) 66 goto out; 67 68 nfds = files->max_fds; 69 spin_unlock(&files->file_lock); 70 71 /* 72 * Expand to the max in easy steps, and keep expanding it until 73 * we have enough for the requested fd array size. 74 */ 75 76 do { 77 #if NR_OPEN_DEFAULT < 256 78 if (nfds < 256) 79 nfds = 256; 80 else 81 #endif 82 if (nfds < (PAGE_SIZE / sizeof(struct file *))) 83 nfds = PAGE_SIZE / sizeof(struct file *); 84 else { 85 nfds = nfds * 2; 86 if (nfds > NR_OPEN) 87 nfds = NR_OPEN; 88 } 89 } while (nfds <= nr); 90 91 error = -ENOMEM; 92 new_fds = alloc_fd_array(nfds); 93 spin_lock(&files->file_lock); 94 if (!new_fds) 95 goto out; 96 97 /* Copy the existing array and install the new pointer */ 98 99 if (nfds > files->max_fds) { 100 struct file **old_fds; 101 int i; 102 103 old_fds = xchg(&files->fd, new_fds); 104 i = xchg(&files->max_fds, nfds); 105 106 /* Don't copy/clear the array if we are creating a new 107 fd array for fork() */ 108 if (i) { 109 memcpy(new_fds, old_fds, i * sizeof(struct file *)); 110 /* clear the remainder of the array */ 111 memset(&new_fds[i], 0, 112 (nfds-i) * sizeof(struct file *)); 113 114 spin_unlock(&files->file_lock); 115 free_fd_array(old_fds, i); 116 spin_lock(&files->file_lock); 117 } 118 } else { 119 /* Somebody expanded the array while we slept ... */ 120 spin_unlock(&files->file_lock); 121 free_fd_array(new_fds, nfds); 122 spin_lock(&files->file_lock); 123 } 124 error = 0; 125 out: 126 return error; 127 } 128 129 /* 130 * Allocate an fdset array, using kmalloc or vmalloc. 131 * Note: the array isn't cleared at allocation time. 132 */ 133 fd_set * alloc_fdset(int num) 134 { 135 fd_set *new_fdset; 136 int size = num / 8; 137 138 if (size <= PAGE_SIZE) 139 new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL); 140 else 141 new_fdset = (fd_set *) vmalloc(size); 142 return new_fdset; 143 } 144 145 void free_fdset(fd_set *array, int num) 146 { 147 int size = num / 8; 148 149 if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */ 150 return; 151 else if (size <= PAGE_SIZE) 152 kfree(array); 153 else 154 vfree(array); 155 } 156 157 /* 158 * Expand the fdset in the files_struct. Called with the files spinlock 159 * held for write. 160 */ 161 static int expand_fdset(struct files_struct *files, int nr) 162 __releases(file->file_lock) 163 __acquires(file->file_lock) 164 { 165 fd_set *new_openset = NULL, *new_execset = NULL; 166 int error, nfds = 0; 167 168 error = -EMFILE; 169 if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN) 170 goto out; 171 172 nfds = files->max_fdset; 173 spin_unlock(&files->file_lock); 174 175 /* Expand to the max in easy steps */ 176 do { 177 if (nfds < (PAGE_SIZE * 8)) 178 nfds = PAGE_SIZE * 8; 179 else { 180 nfds = nfds * 2; 181 if (nfds > NR_OPEN) 182 nfds = NR_OPEN; 183 } 184 } while (nfds <= nr); 185 186 error = -ENOMEM; 187 new_openset = alloc_fdset(nfds); 188 new_execset = alloc_fdset(nfds); 189 spin_lock(&files->file_lock); 190 if (!new_openset || !new_execset) 191 goto out; 192 193 error = 0; 194 195 /* Copy the existing tables and install the new pointers */ 196 if (nfds > files->max_fdset) { 197 int i = files->max_fdset / (sizeof(unsigned long) * 8); 198 int count = (nfds - files->max_fdset) / 8; 199 200 /* 201 * Don't copy the entire array if the current fdset is 202 * not yet initialised. 203 */ 204 if (i) { 205 memcpy (new_openset, files->open_fds, files->max_fdset/8); 206 memcpy (new_execset, files->close_on_exec, files->max_fdset/8); 207 memset (&new_openset->fds_bits[i], 0, count); 208 memset (&new_execset->fds_bits[i], 0, count); 209 } 210 211 nfds = xchg(&files->max_fdset, nfds); 212 new_openset = xchg(&files->open_fds, new_openset); 213 new_execset = xchg(&files->close_on_exec, new_execset); 214 spin_unlock(&files->file_lock); 215 free_fdset (new_openset, nfds); 216 free_fdset (new_execset, nfds); 217 spin_lock(&files->file_lock); 218 return 0; 219 } 220 /* Somebody expanded the array while we slept ... */ 221 222 out: 223 spin_unlock(&files->file_lock); 224 if (new_openset) 225 free_fdset(new_openset, nfds); 226 if (new_execset) 227 free_fdset(new_execset, nfds); 228 spin_lock(&files->file_lock); 229 return error; 230 } 231 232 /* 233 * Expand files. 234 * Return <0 on error; 0 nothing done; 1 files expanded, we may have blocked. 235 * Should be called with the files->file_lock spinlock held for write. 236 */ 237 int expand_files(struct files_struct *files, int nr) 238 { 239 int err, expand = 0; 240 241 if (nr >= files->max_fdset) { 242 expand = 1; 243 if ((err = expand_fdset(files, nr))) 244 goto out; 245 } 246 if (nr >= files->max_fds) { 247 expand = 1; 248 if ((err = expand_fd_array(files, nr))) 249 goto out; 250 } 251 err = expand; 252 out: 253 return err; 254 } 255