X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Ffile_table.c;h=4a90726243762d9ecad9977c2492e65900566d23;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=8996d4d1189241a9188af9889129bca651c50f31;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/file_table.c b/fs/file_table.c index 8996d4d11..4a9072624 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -14,51 +14,74 @@ #include #include #include +#include #include +#include #include +#include +#include +#include #include +#include + +#include /* sysctl tunables... */ struct files_stat_struct files_stat = { .max_files = NR_FILE }; -EXPORT_SYMBOL(files_stat); /* Needed by unix.o */ +/* public. Not pretty! */ +__cacheline_aligned_in_smp DEFINE_SPINLOCK(files_lock); -/* public *and* exported. Not pretty! */ -spinlock_t __cacheline_aligned_in_smp files_lock = SPIN_LOCK_UNLOCKED; +static struct percpu_counter nr_files __cacheline_aligned_in_smp; -EXPORT_SYMBOL(files_lock); +static inline void file_free_rcu(struct rcu_head *head) +{ + struct file *f = container_of(head, struct file, f_u.fu_rcuhead); + kmem_cache_free(filp_cachep, f); +} -static spinlock_t filp_count_lock = SPIN_LOCK_UNLOCKED; +static inline void file_free(struct file *f) +{ + percpu_counter_dec(&nr_files); + call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); +} -/* slab constructors and destructors are called from arbitrary - * context and must be fully threaded - use a local spinlock - * to protect files_stat.nr_files +/* + * Return the total number of open files in the system */ -void filp_ctor(void * objp, struct kmem_cache_s *cachep, unsigned long cflags) +static int get_nr_files(void) { - if ((cflags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) { - unsigned long flags; - spin_lock_irqsave(&filp_count_lock, flags); - files_stat.nr_files++; - spin_unlock_irqrestore(&filp_count_lock, flags); - } + return percpu_counter_read_positive(&nr_files); } -void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags) +/* + * Return the maximum number of open files in the system + */ +int get_max_files(void) { - unsigned long flags; - spin_lock_irqsave(&filp_count_lock, flags); - files_stat.nr_files--; - spin_unlock_irqrestore(&filp_count_lock, flags); + return files_stat.max_files; } +EXPORT_SYMBOL_GPL(get_max_files); -static inline void file_free(struct file *f) +/* + * Handle nr_files sysctl + */ +#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) +int proc_nr_files(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) { - kmem_cache_free(filp_cachep, f); + files_stat.nr_files = get_nr_files(); + return proc_dointvec(table, write, filp, buffer, lenp, ppos); +} +#else +int proc_nr_files(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return -ENOSYS; } +#endif /* Find an unused file structure and return a pointer to it. * Returns NULL, if there are no more free file structures or @@ -66,94 +89,60 @@ static inline void file_free(struct file *f) */ struct file *get_empty_filp(void) { -static int old_max; + struct task_struct *tsk; + static int old_max; struct file * f; /* * Privileged users can go above max_files */ - if (files_stat.nr_files < files_stat.max_files || - capable(CAP_SYS_ADMIN)) { - f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); - if (f) { - memset(f, 0, sizeof(*f)); - if (security_file_alloc(f)) { - file_free(f); - goto fail; - } - eventpoll_init_file(f); - atomic_set(&f->f_count, 1); - f->f_uid = current->fsuid; - f->f_gid = current->fsgid; - f->f_owner.lock = RW_LOCK_UNLOCKED; - /* f->f_version: 0 */ - INIT_LIST_HEAD(&f->f_list); - vx_files_inc(f); - return f; - } + if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) { + /* + * percpu_counters are inaccurate. Do an expensive check before + * we go and fail. + */ + if (percpu_counter_sum(&nr_files) >= files_stat.max_files) + goto over; } + f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); + if (f == NULL) + goto fail; + + percpu_counter_inc(&nr_files); + memset(f, 0, sizeof(*f)); + if (security_file_alloc(f)) + goto fail_sec; + + tsk = current; + INIT_LIST_HEAD(&f->f_u.fu_list); + atomic_set(&f->f_count, 1); + rwlock_init(&f->f_owner.lock); + f->f_uid = tsk->fsuid; + f->f_gid = tsk->fsgid; + eventpoll_init_file(f); + /* f->f_version: 0 */ + f->f_xid = vx_current_xid(); + vx_files_inc(f); + return f; + +over: /* Ran out of filps - report that */ - if (files_stat.max_files >= old_max) { + if (get_nr_files() > old_max) { printk(KERN_INFO "VFS: file-max limit %d reached\n", - files_stat.max_files); - old_max = files_stat.max_files; - } else { - /* Big problems... */ - printk(KERN_WARNING "VFS: filp allocation failed\n"); + get_max_files()); + old_max = get_nr_files(); } + goto fail; + +fail_sec: + file_free(f); fail: return NULL; } EXPORT_SYMBOL(get_empty_filp); -/* - * Clear and initialize a (private) struct file for the given dentry, - * allocate the security structure, and call the open function (if any). - * The file should be released using close_private_file. - */ -int open_private_file(struct file *filp, struct dentry *dentry, int flags) -{ - int error; - memset(filp, 0, sizeof(*filp)); - eventpoll_init_file(filp); - filp->f_flags = flags; - filp->f_mode = ((flags+1) & O_ACCMODE) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; - atomic_set(&filp->f_count, 1); - filp->f_dentry = dentry; - filp->f_mapping = dentry->d_inode->i_mapping; - filp->f_uid = current->fsuid; - filp->f_gid = current->fsgid; - filp->f_op = dentry->d_inode->i_fop; - INIT_LIST_HEAD(&filp->f_list); - error = security_file_alloc(filp); - if (!error) - if (filp->f_op && filp->f_op->open) { - error = filp->f_op->open(dentry->d_inode, filp); - if (error) - security_file_free(filp); - } - return error; -} - -EXPORT_SYMBOL(open_private_file); - -/* - * Release a private file by calling the release function (if any) and - * freeing the security structure. - */ -void close_private_file(struct file *file) -{ - struct inode * inode = file->f_dentry->d_inode; - - if (file->f_op && file->f_op->release) - file->f_op->release(inode, file); - security_file_free(file); -} - -EXPORT_SYMBOL(close_private_file); - void fastcall fput(struct file *file) { if (atomic_dec_and_test(&file->f_count)) @@ -171,6 +160,9 @@ void fastcall __fput(struct file *file) struct vfsmount *mnt = file->f_vfsmnt; struct inode *inode = dentry->d_inode; + might_sleep(); + + fsnotify_close(file); /* * The function eventpoll_release() should be the first called * in the file cleanup chain. @@ -181,12 +173,13 @@ void fastcall __fput(struct file *file) if (file->f_op && file->f_op->release) file->f_op->release(inode, file); security_file_free(file); - if (unlikely(inode->i_cdev != NULL)) + if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) cdev_put(inode->i_cdev); fops_put(file->f_op); if (file->f_mode & FMODE_WRITE) put_write_access(inode); vx_files_dec(file); + file->f_xid = 0; file_kill(file); file->f_dentry = NULL; file->f_vfsmnt = NULL; @@ -200,11 +193,17 @@ struct file fastcall *fget(unsigned int fd) struct file *file; struct files_struct *files = current->files; - spin_lock(&files->file_lock); + rcu_read_lock(); file = fcheck_files(files, fd); - if (file) - get_file(file); - spin_unlock(&files->file_lock); + if (file) { + if (!atomic_inc_not_zero(&file->f_count)) { + /* File object ref couldn't be taken */ + rcu_read_unlock(); + return NULL; + } + } + rcu_read_unlock(); + return file; } @@ -226,43 +225,48 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) if (likely((atomic_read(&files->count) == 1))) { file = fcheck_files(files, fd); } else { - spin_lock(&files->file_lock); + rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - get_file(file); - *fput_needed = 1; + if (atomic_inc_not_zero(&file->f_count)) + *fput_needed = 1; + else + /* Didn't get the reference, someone's freed */ + file = NULL; } - spin_unlock(&files->file_lock); + rcu_read_unlock(); } + return file; } +EXPORT_SYMBOL_GPL(fget_light); void put_filp(struct file *file) { if (atomic_dec_and_test(&file->f_count)) { security_file_free(file); + vx_files_dec(file); + file->f_xid = 0; file_kill(file); file_free(file); } } -EXPORT_SYMBOL(put_filp); - void file_move(struct file *file, struct list_head *list) { if (!list) return; file_list_lock(); - list_move(&file->f_list, list); + list_move(&file->f_u.fu_list, list); file_list_unlock(); } void file_kill(struct file *file) { - if (!list_empty(&file->f_list)) { + if (!list_empty(&file->f_u.fu_list)) { file_list_lock(); - list_del_init(&file->f_list); + list_del_init(&file->f_u.fu_list); file_list_unlock(); } } @@ -274,7 +278,7 @@ int fs_may_remount_ro(struct super_block *sb) /* Check that no files are currently opened for writing. */ file_list_lock(); list_for_each(p, &sb->s_files) { - struct file *file = list_entry(p, struct file, f_list); + struct file *file = list_entry(p, struct file, f_u.fu_list); struct inode *inode = file->f_dentry->d_inode; /* File with pending delete? */ @@ -303,4 +307,6 @@ void __init files_init(unsigned long mempages) files_stat.max_files = n; if (files_stat.max_files < NR_FILE) files_stat.max_files = NR_FILE; + files_defer_init(); + percpu_counter_init(&nr_files, 0); }