patch-2.3.12 linux/fs/file.c

Next file: linux/fs/iobuf.c
Previous file: linux/fs/fcntl.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.11/linux/fs/file.c linux/fs/file.c
@@ -0,0 +1,240 @@
+/*
+ *  linux/fs/open.c
+ *
+ *  Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes
+ *
+ *  Manage the dynamic fd arrays in the process files_struct.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+
+#include <asm/bitops.h>
+
+
+/*
+ * Allocate an fd array, using get_free_page() if possible.
+ * Note: the array isn't cleared at allocation time.
+ */
+struct file ** alloc_fd_array(int num)
+{
+	struct file **new_fds;
+	int size = num * sizeof(struct file *);
+
+	if (size < PAGE_SIZE)
+		new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
+	else if (size == PAGE_SIZE)
+		new_fds = (struct file **) __get_free_page(GFP_KERNEL);
+	else
+		new_fds = (struct file **) vmalloc(size);
+	return new_fds;
+}
+
+void free_fd_array(struct file **array, int num)
+{
+	int size = num * sizeof(struct file *);
+
+	if (!array) {
+		printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
+		return;
+	}
+
+	if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
+		return;
+	else if (size < PAGE_SIZE)
+		kfree(array);
+	else if (size == PAGE_SIZE)
+		free_page((unsigned long) array);
+	else
+		vfree(array);
+}
+
+/*
+ * Expand the fd array in the files_struct.  Called with the files
+ * spinlock held for write.
+ */
+
+int expand_fd_array(struct files_struct *files, int nr)
+{
+	struct file **new_fds;
+	int error, nfds;
+
+	
+	error = -EMFILE;
+	if (files->max_fds >= NR_OPEN || nr > NR_OPEN)
+		goto out;
+
+	nfds = files->max_fds;
+	write_unlock(&files->file_lock);
+
+	/* 
+	 * Expand to the max in easy steps, and keep expanding it until
+	 * we have enough for the requested fd array size. 
+	 */
+
+	do {
+#if NR_OPEN_DEFAULT < 256
+		if (nfds < 256)
+			nfds = 256;
+		else 
+#endif
+		if (nfds < (PAGE_SIZE / sizeof(struct file *)))
+			nfds = PAGE_SIZE / sizeof(struct file *);
+		else {
+			nfds = nfds * 2;
+			if (nfds > NR_OPEN)
+				nfds = NR_OPEN;
+		}
+	} while (nfds < nr);
+
+	error = -ENOMEM;
+	new_fds = alloc_fd_array(nfds);
+	write_lock(&files->file_lock);
+	if (!new_fds)
+		goto out;
+
+	/* Copy the existing array and install the new pointer */
+
+	if (nfds > files->max_fds) {
+		struct file **old_fds;
+		int i;
+		
+		old_fds = xchg(&files->fd, new_fds);
+		i = xchg(&files->max_fds, nfds);
+
+		/* Don't copy/clear the array if we are creating a new
+		   fd array for fork() */
+		if (i) {
+			memcpy(new_fds, old_fds, i * sizeof(struct file *));
+			/* clear the remainder of the array */
+			memset(&new_fds[i], 0,
+			       (nfds-i) * sizeof(struct file *)); 
+
+			write_unlock(&files->file_lock);
+			free_fd_array(old_fds, i);
+			write_lock(&files->file_lock);
+		}
+	} else {
+		/* Somebody expanded the array while we slept ... */
+		write_unlock(&files->file_lock);
+		free_fd_array(new_fds, nfds);
+		write_lock(&files->file_lock);
+	}
+	error = 0;
+out:
+	return error;
+}
+
+/*
+ * Allocate an fdset array, using get_free_page() if possible.
+ * Note: the array isn't cleared at allocation time.
+ */
+fd_set * alloc_fdset(int num)
+{
+	fd_set *new_fdset;
+	int size = num / 8;
+
+	if (size < PAGE_SIZE)
+		new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
+	else if (size == PAGE_SIZE)
+		new_fdset = (fd_set *) __get_free_page(GFP_KERNEL);
+	else
+		new_fdset = (fd_set *) vmalloc(size);
+	return new_fdset;
+}
+
+void free_fdset(fd_set *array, int num)
+{
+	int size = num / 8;
+
+	if (!array) {
+		printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)\n", num);
+		return;
+	}
+	
+	if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
+		return;
+	else if (size < PAGE_SIZE)
+		kfree(array);
+	else if (size == PAGE_SIZE)
+		free_page((unsigned long) array);
+	else
+		vfree(array);
+}
+
+/*
+ * Expand the fdset in the files_struct.  Called with the files spinlock
+ * held for write.
+ */
+int expand_fdset(struct files_struct *files, int nr)
+{
+	fd_set *new_openset = 0, *new_execset = 0;
+	int error, nfds = 0;
+
+	error = -EMFILE;
+	if (files->max_fdset >= NR_OPEN || nr > NR_OPEN)
+		goto out;
+
+	nfds = files->max_fdset;
+	write_unlock(&files->file_lock);
+
+	/* Expand to the max in easy steps */
+	do {
+		if (nfds < (PAGE_SIZE * 8))
+			nfds = PAGE_SIZE * 8;
+		else {
+			nfds = nfds * 2;
+			if (nfds > NR_OPEN)
+				nfds = NR_OPEN;
+		}
+	} while (nfds < nr);
+
+	error = -ENOMEM;
+	new_openset = alloc_fdset(nfds);
+	new_execset = alloc_fdset(nfds);
+	write_lock(&files->file_lock);
+	if (!new_openset || !new_execset)
+		goto out;
+
+	error = 0;
+	
+	/* Copy the existing tables and install the new pointers */
+	if (nfds > files->max_fdset) {
+		int i = files->max_fdset / (sizeof(unsigned long) * 8);
+		int count = (nfds - files->max_fdset) / 8;
+		
+		/* 
+		 * Don't copy the entire array if the current fdset is
+		 * not yet initialised.  
+		 */
+		if (i) {
+			memcpy (new_openset, files->open_fds, files->max_fdset/8);
+			memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
+			memset (&new_openset->fds_bits[i], 0, count);
+			memset (&new_execset->fds_bits[i], 0, count);
+		}
+		
+		nfds = xchg(&files->max_fdset, nfds);
+		new_openset = xchg(&files->open_fds, new_openset);
+		new_execset = xchg(&files->close_on_exec, new_execset);
+		write_unlock(&files->file_lock);
+		free_fdset (new_openset, nfds);
+		free_fdset (new_execset, nfds);
+		write_lock(&files->file_lock);
+		return 0;
+	} 
+	/* Somebody expanded the array while we slept ... */
+
+out:
+	write_unlock(&files->file_lock);
+	if (new_openset)
+		free_fdset(new_openset, nfds);
+	if (new_execset)
+		free_fdset(new_execset, nfds);
+	write_lock(&files->file_lock);
+	return error;
+}
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)