File: /usr/src/linux/fs/file.c

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