# This introduces the filehandle abstraction, which provides a centralized # facility for managing file descriptors. This is needed because descriptors # are a scarce resource, especially when host-based filesystems are used within # UML. A find, kernel build, or updatedb will easily consume the available # descriptors. Unrelated, and non-piggy parts of UML, such as process # creation, will then start failing. # This alleviates the problem by allowing file descriptors to be reclaimed # when the limit is reached. Host resources which will persist when # descriptors to them are closed, such as non-deleted files, will have their # file handles marked reclaimable, and will provide the information needed # to reopen the file if it becomes active again. # Also, some names in hostfs are changed temporarily to avoid name clashes. Index: um/arch/um/include/filehandle.h =================================================================== --- um.orig/arch/um/include/filehandle.h 2003-09-15 09:40:47.000000000 -0400 +++ um/arch/um/include/filehandle.h 2004-08-05 20:39:23.000000000 -0400 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __FILEHANDLE_H__ +#define __FILEHANDLE_H__ + +#include "linux/list.h" +#include "linux/fs.h" +#include "os.h" + +struct file_handle { + struct list_head list; + int fd; + char *(*get_name)(struct inode *); + struct inode *inode; + struct openflags flags; +}; + +extern struct file_handle bad_filehandle; + +extern int open_file(char *name, struct openflags flags, int mode); +extern void *open_dir(char *file); +extern int open_filehandle(char *name, struct openflags flags, int mode, + struct file_handle *fh); +extern int read_file(struct file_handle *fh, unsigned long long offset, + char *buf, int len); +extern int write_file(struct file_handle *fh, unsigned long long offset, + const char *buf, int len); +extern int truncate_file(struct file_handle *fh, unsigned long long size); +extern int close_file(struct file_handle *fh); +extern void not_reclaimable(struct file_handle *fh); +extern void is_reclaimable(struct file_handle *fh, + char *(name_proc)(struct inode *), + struct inode *inode); +extern int filehandle_fd(struct file_handle *fh); +extern int make_pipe(struct file_handle *fhs); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ Index: um/arch/um/include/os.h =================================================================== --- um.orig/arch/um/include/os.h 2004-08-05 20:38:09.000000000 -0400 +++ um/arch/um/include/os.h 2004-08-05 20:39:23.000000000 -0400 @@ -133,7 +133,10 @@ extern int os_mode_fd(int fd, int mode); extern int os_seek_file(int fd, __u64 offset); +extern void *os_open_dir(char *dir, int *err_out); extern int os_open_file(char *file, struct openflags flags, int mode); +extern int os_truncate_file(const char *file, unsigned long long len); +extern int os_truncate_fd(int fd, unsigned long long len); extern int os_read_file(int fd, void *buf, int len); extern int os_write_file(int fd, const void *buf, int count); extern int os_file_size(char *file, long long *size_out); Index: um/arch/um/kernel/Makefile =================================================================== --- um.orig/arch/um/kernel/Makefile 2004-08-05 20:38:09.000000000 -0400 +++ um/arch/um/kernel/Makefile 2004-08-05 20:39:23.000000000 -0400 @@ -10,13 +10,13 @@ subdir- = ../util -obj-y = checksum.o config.o exec_kern.o exitcode.o frame_kern.o frame.o \ - helper.o init_task.o irq.o irq_user.o ksyms.o mem.o mem_user.o \ - physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \ - sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \ - syscall_kern.o syscall_user.o sysrq.o sys_call_table.o tempfile.o \ - time.o time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o \ - um_arch.o umid.o user_util.o +obj-y = checksum.o config.o exec_kern.o exitcode.o filehandle.o frame_kern.o \ + frame.o helper.o init_task.o irq.o irq_user.o ksyms.o mem.o \ + mem_user.o physmem.o process.o process_kern.o ptrace.o reboot.o \ + resource.o sigio_user.o sigio_kern.o signal_kern.o signal_user.o \ + smp.o syscall_kern.o syscall_user.o sysrq.o sys_call_table.o \ + tempfile.o time.o time_kern.o tlb.o trap_kern.o trap_user.o \ + uaccess_user.o um_arch.o umid.o user_util.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o obj-$(CONFIG_GPROF) += gprof_syms.o Index: um/arch/um/kernel/filehandle.c =================================================================== --- um.orig/arch/um/kernel/filehandle.c 2003-09-15 09:40:47.000000000 -0400 +++ um/arch/um/kernel/filehandle.c 2004-08-05 20:39:23.000000000 -0400 @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/list.h" +#include "linux/spinlock.h" +#include "linux/fs.h" +#include "linux/errno.h" +#include "filehandle.h" +#include "os.h" +#include "kern_util.h" + +static spinlock_t open_files_lock = SPIN_LOCK_UNLOCKED; +static struct list_head open_files = LIST_HEAD_INIT(open_files); + +#define NUM_RECLAIM 128 + +static void reclaim_fds(void) +{ + struct file_handle *victim; + int closed = NUM_RECLAIM; + + spin_lock(&open_files_lock); + while(!list_empty(&open_files) && closed--){ + victim = list_entry(open_files.prev, struct file_handle, list); + os_close_file(victim->fd); + victim->fd = -1; + list_del_init(&victim->list); + } + spin_unlock(&open_files_lock); +} + +int open_file(char *name, struct openflags flags, int mode) +{ + int fd; + + fd = os_open_file(name, flags, mode); + if(fd != -EMFILE) + return(fd); + + reclaim_fds(); + fd = os_open_file(name, flags, mode); + + return(fd); +} + +void *open_dir(char *file) +{ + void *dir; + int err; + + dir = os_open_dir(file, &err); + if(dir != NULL) + return(dir); + if(err != -EMFILE) + return(ERR_PTR(err)); + + reclaim_fds(); + + dir = os_open_dir(file, &err); + if(dir == NULL) + dir = ERR_PTR(err); + + return(dir); +} + +void not_reclaimable(struct file_handle *fh) +{ + char *name; + + if(fh->get_name == NULL) + return; + + if(list_empty(&fh->list)){ + name = (*fh->get_name)(fh->inode); + if(name != NULL){ + fh->fd = open_file(name, fh->flags, 0); + kfree(name); + } + else printk("File descriptor %d has no name\n", fh->fd); + } + else { + spin_lock(&open_files_lock); + list_del_init(&fh->list); + spin_unlock(&open_files_lock); + } +} + +void is_reclaimable(struct file_handle *fh, char *(name_proc)(struct inode *), + struct inode *inode) +{ + fh->get_name = name_proc; + fh->inode = inode; + + spin_lock(&open_files_lock); + list_add(&fh->list, &open_files); + spin_unlock(&open_files_lock); +} + +static int active_handle(struct file_handle *fh) +{ + int fd; + char *name; + + if(!list_empty(&fh->list)) + list_move(&fh->list, &open_files); + + if(fh->fd != -1) + return(0); + + if(fh->inode == NULL) + return(-ENOENT); + + name = (*fh->get_name)(fh->inode); + if(name == NULL) + return(-ENOMEM); + + fd = open_file(name, fh->flags, 0); + kfree(name); + if(fd < 0) + return(fd); + + fh->fd = fd; + is_reclaimable(fh, fh->get_name, fh->inode); + + return(0); +} + +int filehandle_fd(struct file_handle *fh) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + return(fh->fd); +} + +static void init_fh(struct file_handle *fh, int fd, struct openflags flags) +{ + flags.c = 0; + *fh = ((struct file_handle) { .list = LIST_HEAD_INIT(fh->list), + .fd = fd, + .get_name = NULL, + .inode = NULL, + .flags = flags }); +} + +int open_filehandle(char *name, struct openflags flags, int mode, + struct file_handle *fh) +{ + int fd; + + fd = open_file(name, flags, mode); + if(fd < 0) + return(fd); + + init_fh(fh, fd, flags); + return(0); +} + +int close_file(struct file_handle *fh) +{ + spin_lock(&open_files_lock); + list_del(&fh->list); + spin_unlock(&open_files_lock); + + os_close_file(fh->fd); + + fh->fd = -1; + return(0); +} + +int read_file(struct file_handle *fh, unsigned long long offset, char *buf, + int len) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + err = os_seek_file(fh->fd, offset); + if(err) + return(err); + + return(os_read_file(fh->fd, buf, len)); +} + +int write_file(struct file_handle *fh, unsigned long long offset, + const char *buf, int len) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + if(offset != -1) + err = os_seek_file(fh->fd, offset); + if(err) + return(err); + + return(os_write_file(fh->fd, buf, len)); +} + +int truncate_file(struct file_handle *fh, unsigned long long size) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + return(os_truncate_fd(fh->fd, size)); +} + +int make_pipe(struct file_handle *fhs) +{ + int fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err && (err != -EMFILE)) + return(err); + + if(err){ + reclaim_fds(); + err = os_pipe(fds, 1, 1); + } + if(err) + return(err); + + init_fh(&fhs[0], fds[0], OPENFLAGS()); + init_fh(&fhs[1], fds[1], OPENFLAGS()); + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ Index: um/arch/um/os-Linux/file.c =================================================================== --- um.orig/arch/um/os-Linux/file.c 2004-08-05 20:38:09.000000000 -0400 +++ um/arch/um/os-Linux/file.c 2004-08-05 20:39:23.000000000 -0400 @@ -278,6 +278,25 @@ return(fd); } +void *os_open_dir(char *path, int *err_out) +{ + void *dir; + + dir = opendir(path); + *err_out = -errno; + return(dir); +} + +int os_truncate_fd(int fd, unsigned long long len) +{ + int err; + + err = ftruncate(fd, len); + if(err) + return(-errno); + return(0); +} + int os_connect_socket(char *name) { struct sockaddr_un sock; Index: um/fs/hostfs/hostfs.h =================================================================== --- um.orig/fs/hostfs/hostfs.h 2004-08-05 20:38:09.000000000 -0400 +++ um/fs/hostfs/hostfs.h 2004-08-05 20:39:23.000000000 -0400 @@ -37,15 +37,15 @@ struct timespec *mtime_out, struct timespec *ctime_out, int *blksize_out, unsigned long long *blocks_out); extern int access_file(char *path, int r, int w, int x); -extern int open_file(char *path, int r, int w, int append); +extern int hostfs_open_file(char *path, int r, int w, int append); extern int file_type(const char *path, int *rdev); -extern void *open_dir(char *path, int *err_out); +extern void *hostfs_open_dir(char *path, int *err_out); extern char *read_dir(void *stream, unsigned long long *pos, unsigned long long *ino_out, int *len_out); -extern void close_file(void *stream); +extern void hostfs_close_file(void *stream); extern void close_dir(void *stream); -extern int read_file(int fd, unsigned long long *offset, char *buf, int len); -extern int write_file(int fd, unsigned long long *offset, const char *buf, +extern int hostfs_read_file(int fd, unsigned long long *offset, char *buf, int len); +extern int hostfs_write_file(int fd, unsigned long long *offset, const char *buf, int len); extern int lseek_file(int fd, long long offset, int whence); extern int file_create(char *name, int ur, int uw, int ux, int gr, Index: um/fs/hostfs/hostfs_kern.c =================================================================== --- um.orig/fs/hostfs/hostfs_kern.c 2004-08-05 20:38:09.000000000 -0400 +++ um/fs/hostfs/hostfs_kern.c 2004-08-05 20:39:23.000000000 -0400 @@ -290,7 +290,7 @@ kfree(HOSTFS_I(inode)->host_filename); if(HOSTFS_I(inode)->fd != -1) - close_file(&HOSTFS_I(inode)->fd); + hostfs_close_file(&HOSTFS_I(inode)->fd); kfree(HOSTFS_I(inode)); } @@ -316,7 +316,7 @@ name = dentry_name(file->f_dentry, 0); if(name == NULL) return(-ENOMEM); - dir = open_dir(name, &error); + dir = hostfs_open_dir(name, &error); kfree(name); if(dir == NULL) return(-error); next = file->f_pos; @@ -343,7 +343,7 @@ * so this resets things and reopens the file with the new access. */ if(HOSTFS_I(ino)->fd != -1){ - close_file(&HOSTFS_I(ino)->fd); + hostfs_close_file(&HOSTFS_I(ino)->fd); HOSTFS_I(ino)->fd = -1; } @@ -359,7 +359,7 @@ if(name == NULL) return(-ENOMEM); - fd = open_file(name, r, w, append); + fd = hostfs_open_file(name, r, w, append); kfree(name); if(fd < 0) return(fd); FILE_HOSTFS_I(file)->fd = fd; @@ -403,7 +403,7 @@ buffer = kmap(page); base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; - err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); + err = hostfs_write_file(HOSTFS_I(inode)->fd, &base, buffer, count); if(err != count){ ClearPageUptodate(page); goto out; @@ -431,7 +431,7 @@ start = (long long) page->index << PAGE_CACHE_SHIFT; buffer = kmap(page); - err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, + err = hostfs_read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, PAGE_CACHE_SIZE); if(err < 0) goto out; @@ -458,13 +458,13 @@ buffer = kmap(page); if(from != 0){ tmp = start; - err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer, + err = hostfs_read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer, from); if(err < 0) goto out; } if(to != PAGE_CACHE_SIZE){ start += to; - err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to, + err = hostfs_read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to, PAGE_CACHE_SIZE - to); if(err < 0) goto out; } @@ -485,7 +485,7 @@ start = (long long) (page->index << PAGE_CACHE_SHIFT) + from; buffer = kmap(page); - err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from, + err = hostfs_write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from, to - from); if(err > 0) err = 0; if(!err && (start > inode->i_size)) Index: um/fs/hostfs/hostfs_user.c =================================================================== --- um.orig/fs/hostfs/hostfs_user.c 2004-08-05 20:38:09.000000000 -0400 +++ um/fs/hostfs/hostfs_user.c 2004-08-05 20:39:23.000000000 -0400 @@ -83,7 +83,7 @@ else return(0); } -int open_file(char *path, int r, int w, int append) +int hostfs_open_file(char *path, int r, int w, int append) { int mode = 0, fd; @@ -102,7 +102,7 @@ else return(fd); } -void *open_dir(char *path, int *err_out) +void *hostfs_open_dir(char *path, int *err_out) { DIR *dir; @@ -127,7 +127,7 @@ return(ent->d_name); } -int read_file(int fd, unsigned long long *offset, char *buf, int len) +int hostfs_read_file(int fd, unsigned long long *offset, char *buf, int len) { int n; @@ -137,7 +137,7 @@ return(n); } -int write_file(int fd, unsigned long long *offset, const char *buf, int len) +int hostfs_write_file(int fd, unsigned long long *offset, const char *buf, int len) { int n; @@ -156,7 +156,7 @@ return(0); } -void close_file(void *stream) +void hostfs_close_file(void *stream) { close(*((int *) stream)); }