Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Wed, 5 Dec 2001 02:56:44 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Wed, 5 Dec 2001 02:56:27 -0500 Received: from fgwmail5.fujitsu.co.jp ([192.51.44.35]:22450 "EHLO fgwmail5.fujitsu.co.jp") by vger.kernel.org with ESMTP id ; Wed, 5 Dec 2001 02:56:18 -0500 Date: Wed, 05 Dec 2001 16:56:08 +0900 Message-ID: From: Tachino Nobuhiro To: Alan Cox Cc: padraig@antefacto.com (Padraig Brady), scho1208@yahoo.com (Roy S.C. Ho), david@gibson.dropbear.id.au, tachino@open.nm.fujitsu.co.jp, linux-kernel@vger.kernel.org Subject: Re: question about kernel 2.4 ramdisk In-Reply-To: In-Reply-To: <3C0D2843.5060708@antefacto.com> User-Agent: Wanderlust/2.7.6 (Too Funky) EMIKO/1.14.1 (Choanoflagellata) LIMIT/1.14.7 (Fujiidera) APEL/10.3 Emacs/21.1 (i586-kondara-linux-gnu) MULE/5.0 (SAKAKI) MIME-Version: 1.0 (generated by EMIKO 1.14.1 - "Choanoflagellata") Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org At Tue, 4 Dec 2001 20:14:16 +0000 (GMT), Alan Cox wrote: > > > wrt the ramfs leak (the referenced patch below worked for me), > > is the ramfs usage limits patch + this fix going into > > the official 2.4 soon as it was in the ac series for ages? > > The -ac ramfs changes need the mm operations changes. Someone has to go > merge that with Andrea-vm then you can get ramfs fixed and accounting sorted > out in shmfs How about the patch below? This is almost the same as the ramfs in 2.4.13-ac7 but shmfs style accounting merged. It works for me, but I definitely believe expert's code review is required. diff -Nur linux-2.4.17-pre2.org/fs/ramfs/inode.c linux-2.4.17-pre2/fs/ramfs/inode.c --- linux-2.4.17-pre2.org/fs/ramfs/inode.c Mon Dec 3 10:46:08 2001 +++ linux-2.4.17-pre2/fs/ramfs/inode.c Wed Dec 5 15:34:03 2001 @@ -29,22 +29,187 @@ #include #include #include +#include +#include #include +#include + +#if PAGE_CACHE_SIZE % 1024 +#error Oh no, PAGE_CACHE_SIZE is not divisible by 1k! I cannot cope. +#endif + +#define IBLOCKS_PER_PAGE (PAGE_CACHE_SIZE / 512) +#define K_PER_PAGE (PAGE_CACHE_SIZE / 1024) /* some random number */ #define RAMFS_MAGIC 0x858458f6 +static struct inode_operations ramfs_inode_operations; +static struct inode_operations ramfs_symlink_inode_operations; static struct super_operations ramfs_ops; static struct address_space_operations ramfs_aops; static struct file_operations ramfs_dir_operations; static struct file_operations ramfs_file_operations; static struct inode_operations ramfs_dir_inode_operations; +/* + * ramfs super-block data in memory + */ +struct ramfs_sb_info { + /* Prevent races accessing the used block + * counts. Conceptually, this could probably be a semaphore, + * but the only thing we do while holding the lock is + * arithmetic, so there's no point */ + spinlock_t ramfs_lock; + + /* It is important that at least the free counts below be + signed. free_XXX may become negative if a limit is changed + downwards (by a remount) below the current usage. */ + + /* maximum number of pages in a file */ + long max_file_pages; + + /* max total number of data pages */ + long max_pages; + /* free_pages = max_pages - total number of pages currently in use */ + long free_pages; + + /* max number of inodes */ + long max_inodes; + /* free_inodes = max_inodes - total number of inodes currently in use */ + long free_inodes; + + /* max number of dentries */ + long max_dentries; + /* free_dentries = max_dentries - total number of dentries in use */ + long free_dentries; +}; + +#define RAMFS_SB(sb) ((struct ramfs_sb_info *)((sb)->u.generic_sbp)) + +/* + * Resource limit helper functions + */ + +static inline void lock_rsb(struct ramfs_sb_info *rsb) +{ + spin_lock(&(rsb->ramfs_lock)); +} + +static inline void unlock_rsb(struct ramfs_sb_info *rsb) +{ + spin_unlock(&(rsb->ramfs_lock)); +} + +/* Decrements the free inode count and returns true, or returns false + * if there are no free inodes */ +static int ramfs_alloc_inode(struct super_block *sb) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(sb); + int ret = 1; + + lock_rsb(rsb); + if (!rsb->max_inodes || rsb->free_inodes > 0) + rsb->free_inodes--; + else + ret = 0; + unlock_rsb(rsb); + + return ret; +} + +/* Increments the free inode count */ +static void ramfs_dealloc_inode(struct super_block *sb) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(sb); + + lock_rsb(rsb); + rsb->free_inodes++; + unlock_rsb(rsb); +} + +/* Decrements the free dentry count and returns true, or returns false + * if there are no free dentries */ +static int ramfs_alloc_dentry(struct super_block *sb) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(sb); + int ret = 1; + + lock_rsb(rsb); + if (!rsb->max_dentries || rsb->free_dentries > 0) + rsb->free_dentries--; + else + ret = 0; + unlock_rsb(rsb); + + return ret; +} + +/* Increments the free dentry count */ +static void ramfs_dealloc_dentry(struct super_block *sb) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(sb); + + lock_rsb(rsb); + rsb->free_dentries++; + unlock_rsb(rsb); +} + +/* If the given page can be added to the give inode for ramfs, return + * true and update the filesystem's free page count and the inode's + * i_blocks field. Always returns true if the file is already used by + * ramfs (ie. PageDirty(page) is true) */ +int ramfs_alloc_page(struct inode *inode, struct page *page) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(inode->i_sb); + int ret = 1; + + lock_rsb(rsb); + + if ( (rsb->free_pages > 0) && + ( !rsb->max_file_pages || + (inode->i_data.nrpages <= rsb->max_file_pages) ) ) { + inode->i_blocks += IBLOCKS_PER_PAGE; + rsb->free_pages--; + } else + ret = 0; + + unlock_rsb(rsb); + + return ret; +} + +static void ramfs_recalc_inode(struct inode *inode) +{ + struct ramfs_sb_info *rsb = RAMFS_SB(inode->i_sb); + unsigned long freed; + + freed = inode->i_blocks/IBLOCKS_PER_PAGE - inode->i_mapping->nrpages; + if (freed) { + inode->i_blocks -= freed * IBLOCKS_PER_PAGE; + lock_rsb(rsb); + rsb->free_pages += freed; + unlock_rsb(rsb); + } +} + static int ramfs_statfs(struct super_block *sb, struct statfs *buf) { + struct ramfs_sb_info *rsb = RAMFS_SB(sb); + + lock_rsb(rsb); + buf->f_blocks = rsb->max_pages; + buf->f_files = rsb->max_inodes; + + buf->f_bfree = rsb->free_pages; + buf->f_bavail = buf->f_bfree; + buf->f_ffree = rsb->free_inodes; + unlock_rsb(rsb); + buf->f_type = RAMFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; +// buf->f_spare[0] = rsb->free_dentries; /* debug */ buf->f_namelen = 255; return 0; } @@ -77,8 +242,12 @@ static int ramfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { + struct inode *inode = (struct inode *)page->mapping->host; void *addr = kmap(page); if (!Page_Uptodate(page)) { + if (!ramfs_alloc_page(inode, page)) + return -ENOSPC; + memset(addr, 0, PAGE_CACHE_SIZE); flush_dcache_page(page); SetPageUptodate(page); @@ -98,9 +267,20 @@ return 0; } +static void ramfs_truncate(struct inode *inode) +{ + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + ramfs_recalc_inode(inode); +} + struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) { - struct inode * inode = new_inode(sb); + struct inode * inode; + + if (! ramfs_alloc_inode(sb)) + return NULL; + + inode = new_inode(sb); if (inode) { inode->i_mode = mode; @@ -116,6 +296,7 @@ init_special_inode(inode, mode, dev); break; case S_IFREG: + inode->i_op = &ramfs_inode_operations; inode->i_fop = &ramfs_file_operations; break; case S_IFDIR: @@ -123,26 +304,38 @@ inode->i_fop = &ramfs_dir_operations; break; case S_IFLNK: - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &ramfs_symlink_inode_operations; break; } - } + } else + ramfs_dealloc_inode(sb); + return inode; } /* - * File creation. Allocate an inode, and we're done.. + * File creation. Allocate an inode, update free inode and dentry counts + * and we're done.. */ static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev) { - struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev); + struct super_block *sb = dir->i_sb; + struct inode * inode; int error = -ENOSPC; + if (! ramfs_alloc_dentry(sb)) + return error; + + inode = ramfs_get_inode(dir->i_sb, mode, dev); + if (inode) { d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ error = 0; + } else { + ramfs_dealloc_dentry(sb); } + return error; } @@ -161,11 +354,15 @@ */ static int ramfs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) { + struct super_block *sb = dir->i_sb; struct inode *inode = old_dentry->d_inode; if (S_ISDIR(inode->i_mode)) return -EPERM; + if (! ramfs_alloc_dentry(sb)) + return -ENOSPC; + inode->i_nlink++; atomic_inc(&inode->i_count); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ @@ -212,6 +409,7 @@ */ static int ramfs_unlink(struct inode * dir, struct dentry *dentry) { + struct super_block *sb = dir->i_sb; int retval = -ENOTEMPTY; if (ramfs_empty(dentry)) { @@ -219,6 +417,9 @@ inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ + + ramfs_dealloc_dentry(sb); + retval = 0; } return retval; @@ -234,6 +435,8 @@ */ static int ramfs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry) { + struct super_block *sb = new_dir->i_sb; + int error = -ENOTEMPTY; if (ramfs_empty(new_dentry)) { @@ -241,6 +444,7 @@ if (inode) { inode->i_nlink--; dput(new_dentry); + ramfs_dealloc_dentry(sb); } error = 0; } @@ -260,11 +464,185 @@ return error; } +static void ramfs_delete_inode(struct inode *inode) +{ + ramfs_truncate(inode); + ramfs_dealloc_inode(inode->i_sb); + clear_inode(inode); +} + +static void ramfs_put_super(struct super_block *sb) +{ + kfree(sb->u.generic_sbp); +} + +struct ramfs_params { + long pages; + long filepages; + long inodes; + long dentries; +}; + +static int parse_options(char * options, struct ramfs_params *p) +{ + char save = 0, *savep = NULL, *optname, *value; + + p->pages = -1; + p->filepages = -1; + p->inodes = -1; + p->dentries = -1; + + for (optname = strtok(options,","); optname; + optname = strtok(NULL,",")) { + if ((value = strchr(optname,'=')) != NULL) { + save = *value; + savep = value; + *value++ = 0; + } + + if (!strcmp(optname, "maxfilesize") && value) { + p->filepages = simple_strtoul(value, &value, 0) + / K_PER_PAGE; + if (*value) + return -EINVAL; + } else if (!strcmp(optname, "maxsize") && value) { + p->pages = simple_strtoul(value, &value, 0) + / K_PER_PAGE; + if (*value) + return -EINVAL; + } else if (!strcmp(optname, "maxinodes") && value) { + p->inodes = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } else if (!strcmp(optname, "maxdentries") && value) { + p->dentries = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } + + if (optname != options) + *(optname-1) = ','; + if (value) + *savep = save; +/* if (ret == 0) */ +/* break; */ + } + + return 0; +} + +static void init_limits(struct ramfs_sb_info *rsb, struct ramfs_params *p) +{ + struct sysinfo si; + + si_meminfo(&si); + + /* By default we set the limits to be: + - Allow this ramfs to take up to half of all available RAM + - No limit on filesize (except no file may be bigger that + the total max size, obviously) + - dentries limited to one per 4k of data space + - No limit to the number of inodes (except that there + are never more inodes than dentries). + */ + rsb->max_pages = (si.totalram / 2); + + if (p->pages >= 0) + rsb->max_pages = p->pages; + + rsb->max_file_pages = 0; + if (p->filepages >= 0) + rsb->max_file_pages = p->filepages; + + rsb->max_dentries = rsb->max_pages * K_PER_PAGE / 4; + if (p->dentries >= 0) + rsb->max_dentries = p->dentries; + + rsb->max_inodes = 0; + if (p->inodes >= 0) + rsb->max_inodes = p->inodes; + + rsb->free_pages = rsb->max_pages; + rsb->free_inodes = rsb->max_inodes; + rsb->free_dentries = rsb->max_dentries; + + return; +} + +/* reset_limits is called during a remount to change the usage limits. + + This will suceed, even if the new limits are lower than current + usage. This is the intended behaviour - new allocations will fail + until usage falls below the new limit */ +static void reset_limits(struct ramfs_sb_info *rsb, struct ramfs_params *p) +{ + lock_rsb(rsb); + + if (p->pages >= 0) { + int used_pages = rsb->max_pages - rsb->free_pages; + + rsb->max_pages = p->pages; + rsb->free_pages = rsb->max_pages - used_pages; + } + + if (p->filepages >= 0) { + rsb->max_file_pages = p->filepages; + } + + + if (p->dentries >= 0) { + int used_dentries = rsb->max_dentries - rsb->free_dentries; + + rsb->max_dentries = p->dentries; + rsb->free_dentries = rsb->max_dentries - used_dentries; + } + + if (p->inodes >= 0) { + int used_inodes = rsb->max_inodes - rsb->free_inodes; + + rsb->max_inodes = p->inodes; + rsb->free_inodes = rsb->max_inodes - used_inodes; + } + + unlock_rsb(rsb); +} + +static int ramfs_remount(struct super_block * sb, int * flags, char * data) +{ + struct ramfs_params params; + struct ramfs_sb_info * rsb = RAMFS_SB(sb); + + if (parse_options((char *)data, ¶ms) != 0) + return -EINVAL; + + reset_limits(rsb, ¶ms); + + printk(KERN_DEBUG "ramfs: remounted with options: %s\n", + data ? (char *)data : "" ); + printk(KERN_DEBUG "ramfs: max_pages=%ld max_file_pages=%ld \ +max_inodes=%ld max_dentries=%ld\n", + rsb->max_pages, rsb->max_file_pages, + rsb->max_inodes, rsb->max_dentries); + + return 0; +} + static int ramfs_sync_file(struct file * file, struct dentry *dentry, int datasync) { return 0; } + +static struct inode_operations ramfs_inode_operations = { + truncate: ramfs_truncate, +}; + +static struct inode_operations ramfs_symlink_inode_operations = { + truncate: ramfs_truncate, + readlink: page_readlink, + follow_link: page_follow_link, +}; + static struct address_space_operations ramfs_aops = { readpage: ramfs_readpage, writepage: fail_writepage, @@ -273,6 +651,7 @@ }; static struct file_operations ramfs_file_operations = { + llseek: generic_file_llseek, read: generic_file_read, write: generic_file_write, mmap: generic_file_mmap, @@ -300,17 +679,36 @@ static struct super_operations ramfs_ops = { statfs: ramfs_statfs, put_inode: force_delete, + delete_inode: ramfs_delete_inode, + put_super: ramfs_put_super, + remount_fs: ramfs_remount, }; static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent) { struct inode * inode; struct dentry * root; + struct ramfs_sb_info * rsb; + struct ramfs_params params; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = RAMFS_MAGIC; sb->s_op = &ramfs_ops; + sb->s_maxbytes = MAX_NON_LFS; /* Why? */ + + rsb = kmalloc(sizeof(struct ramfs_sb_info), GFP_KERNEL); + RAMFS_SB(sb) = rsb; + if (!rsb) + return NULL; + + spin_lock_init(&rsb->ramfs_lock); + + if (parse_options((char *)data, ¶ms) != 0) + return NULL; + + init_limits(rsb, ¶ms); + inode = ramfs_get_inode(sb, S_IFDIR | 0755, 0); if (!inode) return NULL; @@ -321,6 +719,13 @@ return NULL; } sb->s_root = root; + + printk(KERN_DEBUG "ramfs: mounted with options: %s\n", + data ? (char *)data : "" ); + printk(KERN_DEBUG "ramfs: max_pages=%ld max_file_pages=%ld \ +max_inodes=%ld max_dentries=%ld\n", + rsb->max_pages, rsb->max_file_pages, + rsb->max_inodes, rsb->max_dentries); return sb; } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/