diff -urN old/include/linux/shmem_fs.h new/include/linux/shmem_fs.h --- old/include/linux/shmem_fs.h Wed May 23 07:44:24 2001 +++ new/include/linux/shmem_fs.h Wed May 23 07:45:03 2001 @@ -33,6 +33,13 @@ unsigned long max_inodes; /* How many inodes are allowed */ unsigned long free_inodes; /* How many are left for allocation */ spinlock_t stat_lock; + + /* Improved resource limits */ + unsigned long reserved_blocks; /* Blocks reserved for root */ + unsigned long reserved_inodes; /* Inodes reserved for root */ + unsigned long reserved_mem; /* Memory reserved for rest of system */ + uid_t reserved_uid; /* User with acces to reserved blocks + * and inodes */ }; #endif diff -urN old/mm/shmem.c new/mm/shmem.c --- old/mm/shmem.c Wed May 23 07:44:02 2001 +++ new/mm/shmem.c Wed May 23 07:48:38 2001 @@ -14,6 +14,13 @@ * which makes it a completely usable filesystem. */ +/* + * Improved resource limits. + * + * Copyright (C) 2001 Kasper Dupont + * + */ + #include #include #include @@ -49,6 +56,34 @@ #define BLOCKS_PER_PAGE (PAGE_SIZE/512) +/* shmem_sb_sys_free - compute free space in super block and system + * + * @shmem_sb: Super block to get parameters from + * + * Compute amount of memory we can be allowed to use, it is the + * minimum of two different limits. The first limit is the free + * space according to super block. The second limit is the free + * memory in system - the reserved memory according to super block. + * + * Must be called with shmem_sb->stat_lock held. + */ + +static inline unsigned long shmem_sb_sys_free(struct shmem_sb_info * shmem_sb) +{ + unsigned long free_mem = + nr_free_pages() + + nr_swap_pages + + atomic_read(&buffermem_pages); + unsigned long reserved_mem = + shmem_sb->reserved_mem; + unsigned long free_blocks = + shmem_sb->free_blocks; + + if (free_memswapped--; spin_unlock (&info->lock); } else { + unsigned long currently_free; + struct shmem_sb_info * shmem_sb = &inode->i_sb->u.shmem_sb; spin_unlock (&info->lock); - spin_lock (&inode->i_sb->u.shmem_sb.stat_lock); - if (inode->i_sb->u.shmem_sb.free_blocks == 0) + spin_lock (&shmem_sb->stat_lock); + currently_free=shmem_sb_sys_free(shmem_sb); + if (currently_free == 0) + goto no_space; + if ((currently_free <= shmem_sb->reserved_blocks)&& + (current->fsuid != shmem_sb->reserved_uid)&& + (!capable(CAP_SYS_RESOURCE))) goto no_space; - inode->i_sb->u.shmem_sb.free_blocks--; - spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock); + shmem_sb->free_blocks--; + spin_unlock (&shmem_sb->stat_lock); /* Ok, get a new page. We don't have to worry about the * info->lock spinlock here: we cannot race against @@ -506,6 +548,13 @@ spin_unlock (&sb->u.shmem_sb.stat_lock); return NULL; } + if ((sb->u.shmem_sb.free_inodes <= sb->u.shmem_sb.reserved_inodes) + &&(current->fsuid != sb->u.shmem_sb.reserved_uid) + &&(!capable(CAP_SYS_RESOURCE))) + { + spin_unlock (&sb->u.shmem_sb.stat_lock); + return NULL; + } sb->u.shmem_sb.free_inodes--; spin_unlock (&sb->u.shmem_sb.stat_lock); @@ -758,21 +807,22 @@ static int shmem_statfs(struct super_block *sb, struct statfs *buf) { + struct shmem_sb_info *info = &sb->u.shmem_sb; buf->f_type = TMPFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; - spin_lock (&sb->u.shmem_sb.stat_lock); - if (sb->u.shmem_sb.max_blocks == ULONG_MAX) { - /* - * This is only a guestimate and not honoured. - * We need it to make some programs happy which like to - * test the free space of a file system. - */ - buf->f_bavail = buf->f_bfree = nr_free_pages() + nr_swap_pages + atomic_read(&buffermem_pages); - buf->f_blocks = buf->f_bfree + ULONG_MAX - sb->u.shmem_sb.free_blocks; - } else { - buf->f_blocks = sb->u.shmem_sb.max_blocks; - buf->f_bavail = buf->f_bfree = sb->u.shmem_sb.free_blocks; - } + spin_lock (&info->stat_lock); + /* + * This is only a guestimate and not honoured. + * We need it to make some programs happy which like to + * test the free space of a file system. + */ + buf->f_bfree = shmem_sb_sys_free(info); + if (buf->f_bfree < info->reserved_blocks) + buf->f_bavail=0; + else + buf->f_bavail=buf->f_bfree-info->reserved_blocks; + buf->f_blocks = buf->f_bfree + info->max_blocks - info->free_blocks; + buf->f_files = sb->u.shmem_sb.max_inodes; buf->f_ffree = sb->u.shmem_sb.free_inodes; spin_unlock (&sb->u.shmem_sb.stat_lock); @@ -977,7 +1027,8 @@ return res; } -static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, unsigned long *inodes) +static int shmem_parse_options(char *options,int *mode, + struct shmem_sb_info *args) { char *this_char, *value; @@ -989,22 +1040,22 @@ *value++ = 0; if (!strcmp(this_char,"size")) { unsigned long long size; - if (!value || !*value || !blocks) + if (!value || !*value) return 1; size = memparse(value,&value); if (*value) return 1; - *blocks = size >> PAGE_CACHE_SHIFT; + args->max_blocks = size >> PAGE_CACHE_SHIFT; } else if (!strcmp(this_char,"nr_blocks")) { - if (!value || !*value || !blocks) + if (!value || !*value) return 1; - *blocks = memparse(value,&value); + args->max_blocks = memparse(value,&value); if (*value) return 1; } else if (!strcmp(this_char,"nr_inodes")) { - if (!value || !*value || !inodes) + if (!value || !*value) return 1; - *inodes = memparse(value,&value); + args->max_inodes = memparse(value,&value); if (*value) return 1; } else if (!strcmp(this_char,"mode")) { @@ -1013,36 +1064,84 @@ *mode = simple_strtoul(value,&value,8); if (*value) return 1; + } else if (!strcmp(this_char,"reserved_blocks")) { + if (!value || !*value) + return 1; + args->reserved_blocks = memparse(value,&value); + if (*value) + return 1; + } else if (!strcmp(this_char,"reserved_inodes")) { + if (!value || !*value) + return 1; + args->reserved_inodes = memparse(value,&value); + if (*value) + return 1; + } else if (!strcmp(this_char,"reserved_mem")) { + if (!value || !*value) + return 1; + args->reserved_mem = memparse(value,&value); + if (*value) + return 1; + } else if (!strcmp(this_char,"reserved_uid")) { + if (!value || !*value) + return 1; + args->reserved_uid = memparse(value,&value); + if (*value) + return 1; } else return 1; } + + if (args->max_blocks<5) args->max_blocks=5; + if (args->max_inodes<9) args->max_inodes=9; + if (args->reserved_blocks > args->max_blocks-2) + args->reserved_blocks = args->max_blocks-2; + if (args->reserved_inodes > args->max_inodes-4) + args->reserved_inodes = args->max_inodes-4; + return 0; } static int shmem_remount_fs (struct super_block *sb, int *flags, char *data) { int error; - unsigned long max_blocks, blocks; - unsigned long max_inodes, inodes; + unsigned long blocks; + unsigned long inodes; struct shmem_sb_info *info = &sb->u.shmem_sb; + struct shmem_sb_info args; + + /* FIXME: We unlock the stats while parsing options. + * Is that a good thing to do? In fact I don't + * think it makes any difference. Somebody should + * consider this closely, can anything go wrong, + * can it be made more efficient. + */ + + spin_lock(&info->stat_lock); + args=*info; + spin_unlock(&info->stat_lock); - if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes)) + if (shmem_parse_options (data, NULL, &args)) return -EINVAL; spin_lock(&info->stat_lock); blocks = info->max_blocks - info->free_blocks; inodes = info->max_inodes - info->free_inodes; error = -EINVAL; - if (max_blocks < blocks) + if (args.max_blocks < blocks) goto out; - if (max_inodes < inodes) + if (args.max_inodes < inodes) goto out; error = 0; - info->max_blocks = max_blocks; - info->free_blocks = max_blocks - blocks; - info->max_inodes = max_inodes; - info->free_inodes = max_inodes - inodes; + info->max_blocks = args.max_blocks; + info->free_blocks = args.max_blocks - blocks; + info->max_inodes = args.max_inodes; + info->free_inodes = args.max_inodes - inodes; + info->reserved_blocks = args.reserved_blocks; + info->reserved_inodes = args.reserved_inodes; + info->reserved_mem = args.reserved_mem; + info->reserved_uid = args.reserved_uid; out: spin_unlock(&info->stat_lock); return error; @@ -1058,22 +1157,27 @@ { struct inode * inode; struct dentry * root; - unsigned long blocks = ULONG_MAX; /* unlimited */ - unsigned long inodes = ULONG_MAX; /* unlimited */ + struct shmem_sb_info * info = &sb->u.shmem_sb; int mode = S_IRWXUGO | S_ISVTX; + info->max_blocks=ULONG_MAX; /* unlimited */ + info->max_inodes=ULONG_MAX; /* unlimited */ + info->reserved_blocks = 0; + info->reserved_inodes = 0; + info->reserved_mem = 8192; /* 32MB */ + info->reserved_uid = 0; /* root */ + #ifdef CONFIG_TMPFS - if (shmem_parse_options (data, &mode, &blocks, &inodes)) { + if (shmem_parse_options (data, &mode, info )) { printk(KERN_ERR "tmpfs invalid option\n"); return NULL; } #endif - spin_lock_init (&sb->u.shmem_sb.stat_lock); - sb->u.shmem_sb.max_blocks = blocks; - sb->u.shmem_sb.free_blocks = blocks; - sb->u.shmem_sb.max_inodes = inodes; - sb->u.shmem_sb.free_inodes = inodes; + spin_lock_init (&info->stat_lock); + info->free_blocks = info->max_blocks; + info->free_inodes = info->max_inodes; + sb->s_maxbytes = (unsigned long long)(SHMEM_NR_DIRECT + (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)) << PAGE_CACHE_SHIFT; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT;