⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dquot.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Implementation of the diskquota system for the LINUX operating * system. QUOTA is implemented using the BSD system call interface as * the means of communication with the user level. Currently only the * ext2 filesystem has support for disk quotas. Other filesystems may * be added in the future. This file contains the generic routines * called by the different filesystems on allocation of an inode or * block. These routines take care of the administration needed to * have a consistent diskquota tracking system. The ideas of both * user and group quotas are based on the Melbourne quota system as * used on BSD derived systems. The internal implementation is  * based on one of the several variants of the LINUX inode-subsystem * with added complexity of the diskquota system. *  * Version: $Id: dquot.c,v 6.3 1996/11/17 18:35:34 mvw Exp mvw $ *  * Author:	Marco van Wieringen <mvw@planets.elm.net> * * Fixes:   Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96 * *		Revised list management to avoid races *		-- Bill Hawes, <whawes@star.net>, 9/98 * *		Fixed races in dquot_transfer(), dqget() and dquot_alloc_...(). *		As the consequence the locking was moved from dquot_decr_...(), *		dquot_incr_...() to calling functions. *		invalidate_dquots() now writes modified dquots. *		Serialized quota_off() and quota_on() for mount point. *		Fixed a few bugs in grow_dquots(). *		Fixed deadlock in write_dquot() - we no longer account quotas on *		quota files *		remove_dquot_ref() moved to inode.c - it now traverses through inodes *		add_dquot_ref() restarts after blocking *		Added check for bogus uid and fixed check for group in quotactl. *		Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99 * *		Used struct list_head instead of own list struct *		Invalidation of dquots with dq_count > 0 no longer possible *		Improved free_dquots list management *		Quota and i_blocks are now updated in one place to avoid races *		Warnings are now delayed so we won't block in critical section *		Write updated not to require dquot lock *		Jan Kara, <jack@suse.cz>, 9/2000 * *		Added dynamic quota structure allocation *		Jan Kara <jack@suse.cz> 12/2000 * * (C) Copyright 1994 - 1997 Marco van Wieringen  */#include <linux/errno.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/stat.h>#include <linux/tty.h>#include <linux/file.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/init.h>#include <asm/uaccess.h>#define __DQUOT_VERSION__	"dquot_6.4.0"int nr_dquots, nr_free_dquots;static char *quotatypes[] = INITQFNAMES;static inline struct quota_mount_options *sb_dqopt(struct super_block *sb){	return &sb->s_dquot;}/* * Dquot List Management: * The quota code uses three lists for dquot management: the inuse_list, * free_dquots, and dquot_hash[] array. A single dquot structure may be * on all three lists, depending on its current state. * * All dquots are placed to the end of inuse_list when first created, and this * list is used for the sync and invalidate operations, which must look * at every dquot. * * Unused dquots (dq_count == 0) are added to the free_dquots list when * freed, and this list is searched whenever we need an available dquot. * Dquots are removed from the list as soon as they are used again, and * nr_free_dquots gives the number of dquots on the list. When dquot is * invalidated it's completely released from memory. * * Dquots with a specific identity (device, type and id) are placed on * one of the dquot_hash[] hash chains. The provides an efficient search * mechanism to locate a specific dquot. *//* * Note that any operation which operates on dquot data (ie. dq_dqb) mustn't * block while it's updating/reading it. Otherwise races would occur. * * Locked dquots might not be referenced in inodes - operations like * add_dquot_space() does dqduplicate() and would complain. Currently * dquot it locked only once in its existence - when it's being read * to memory on first dqget() and at that time it can't be referenced * from inode. Write operations on dquots don't hold dquot lock as they * copy data to internal buffers before writing anyway and copying as well * as any data update should be atomic. Also nobody can change used * entries in dquot structure as this is done only when quota is destroyed * and invalidate_dquots() waits for dquot to have dq_count == 0. */static LIST_HEAD(inuse_list);static LIST_HEAD(free_dquots);static struct list_head dquot_hash[NR_DQHASH];static struct dqstats dqstats;static void dqput(struct dquot *);static struct dquot *dqduplicate(struct dquot *);static inline char is_enabled(struct quota_mount_options *dqopt, short type){	switch (type) {		case USRQUOTA:			return((dqopt->flags & DQUOT_USR_ENABLED) != 0);		case GRPQUOTA:			return((dqopt->flags & DQUOT_GRP_ENABLED) != 0);	}	return(0);}static inline char sb_has_quota_enabled(struct super_block *sb, short type){	return is_enabled(sb_dqopt(sb), type);}static inline int const hashfn(kdev_t dev, unsigned int id, short type){	return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;}static inline void insert_dquot_hash(struct dquot *dquot){	struct list_head *head = dquot_hash + hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type);	list_add(&dquot->dq_hash, head);}static inline void remove_dquot_hash(struct dquot *dquot){	list_del(&dquot->dq_hash);	INIT_LIST_HEAD(&dquot->dq_hash);}static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigned int id, short type){	struct list_head *head;	struct dquot *dquot;	for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {		dquot = list_entry(head, struct dquot, dq_hash);		if (dquot->dq_dev == dev && dquot->dq_id == id && dquot->dq_type == type)			return dquot;	}	return NODQUOT;}/* Add a dquot to the head of the free list */static inline void put_dquot_head(struct dquot *dquot){	list_add(&dquot->dq_free, &free_dquots);	nr_free_dquots++;}/* Add a dquot to the tail of the free list */static inline void put_dquot_last(struct dquot *dquot){	list_add(&dquot->dq_free, free_dquots.prev);	nr_free_dquots++;}/* Move dquot to the head of free list (it must be already on it) */static inline void move_dquot_head(struct dquot *dquot){	list_del(&dquot->dq_free);	list_add(&dquot->dq_free, &free_dquots);}static inline void remove_free_dquot(struct dquot *dquot){	if (list_empty(&dquot->dq_free))		return;	list_del(&dquot->dq_free);	INIT_LIST_HEAD(&dquot->dq_free);	nr_free_dquots--;}static inline void put_inuse(struct dquot *dquot){	/* We add to the back of inuse list so we don't have to restart	 * when traversing this list and we block */	list_add(&dquot->dq_inuse, inuse_list.prev);	nr_dquots++;}static inline void remove_inuse(struct dquot *dquot){	nr_dquots--;	list_del(&dquot->dq_inuse);}static void __wait_on_dquot(struct dquot *dquot){	DECLARE_WAITQUEUE(wait, current);	add_wait_queue(&dquot->dq_wait_lock, &wait);repeat:	set_current_state(TASK_UNINTERRUPTIBLE);	if (dquot->dq_flags & DQ_LOCKED) {		schedule();		goto repeat;	}	remove_wait_queue(&dquot->dq_wait_lock, &wait);	current->state = TASK_RUNNING;}static inline void wait_on_dquot(struct dquot *dquot){	if (dquot->dq_flags & DQ_LOCKED)		__wait_on_dquot(dquot);}static inline void lock_dquot(struct dquot *dquot){	wait_on_dquot(dquot);	dquot->dq_flags |= DQ_LOCKED;}static inline void unlock_dquot(struct dquot *dquot){	dquot->dq_flags &= ~DQ_LOCKED;	wake_up(&dquot->dq_wait_lock);}static void __wait_dquot_unused(struct dquot *dquot){	DECLARE_WAITQUEUE(wait, current);	add_wait_queue(&dquot->dq_wait_free, &wait);repeat:	set_current_state(TASK_UNINTERRUPTIBLE);	if (dquot->dq_count) {		schedule();		goto repeat;	}	remove_wait_queue(&dquot->dq_wait_free, &wait);	current->state = TASK_RUNNING;}/* *	We don't have to be afraid of deadlocks as we never have quotas on quota files... */static void write_dquot(struct dquot *dquot){	short type = dquot->dq_type;	struct file *filp;	mm_segment_t fs;	loff_t offset;	ssize_t ret;	struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;	struct dqblk dqbuf;	down(sem);	filp = dquot->dq_sb->s_dquot.files[type];	offset = dqoff(dquot->dq_id);	fs = get_fs();	set_fs(KERNEL_DS);	/*	 * Note: clear the DQ_MOD flag unconditionally,	 * so we don't loop forever on failure.	 */	memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk));	dquot->dq_flags &= ~DQ_MOD;	ret = 0;	if (filp)		ret = filp->f_op->write(filp, (char *)&dqbuf, 					sizeof(struct dqblk), &offset);	if (ret != sizeof(struct dqblk))		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",			kdevname(dquot->dq_dev));	set_fs(fs);	up(sem);	dqstats.writes++;}static void read_dquot(struct dquot *dquot){	short type = dquot->dq_type;	struct file *filp;	mm_segment_t fs;	loff_t offset;	filp = dquot->dq_sb->s_dquot.files[type];	if (filp == (struct file *)NULL)		return;	lock_dquot(dquot);	if (!dquot->dq_sb)	/* Invalidated quota? */		goto out_lock;	/* Now we are sure filp is valid - the dquot isn't invalidated */	down(&dquot->dq_sb->s_dquot.dqio_sem);	offset = dqoff(dquot->dq_id);	fs = get_fs();	set_fs(KERNEL_DS);	filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset);	up(&dquot->dq_sb->s_dquot.dqio_sem);	set_fs(fs);	if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&	    dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)		dquot->dq_flags |= DQ_FAKE;	dqstats.reads++;out_lock:	unlock_dquot(dquot);}/* Invalidate all dquots on the list, wait for all users. Note that this function is called * after quota is disabled so no new quota might be created. As we only insert to the end of * inuse list, we don't have to restart searching... */static void invalidate_dquots(struct super_block *sb, short type){	struct dquot *dquot;	struct list_head *head;restart:	for (head = inuse_list.next; head != &inuse_list; head = head->next) {		dquot = list_entry(head, struct dquot, dq_inuse);		if (dquot->dq_sb != sb)			continue;		if (dquot->dq_type != type)			continue;		dquot->dq_flags |= DQ_INVAL;		if (dquot->dq_count)			/*			 *  Wait for any users of quota. As we have already cleared the flags in			 *  superblock and cleared all pointers from inodes we are assured			 *  that there will be no new users of this quota.			 */			__wait_dquot_unused(dquot);		/* Quota now have no users and it has been written on last dqput() */		remove_dquot_hash(dquot);		remove_free_dquot(dquot);		remove_inuse(dquot);		kmem_cache_free(dquot_cachep, dquot);		goto restart;	}}int sync_dquots(kdev_t dev, short type){	struct list_head *head;	struct dquot *dquot;	lock_kernel();restart:	for (head = inuse_list.next; head != &inuse_list; head = head->next) {		dquot = list_entry(head, struct dquot, dq_inuse);		if (dev && dquot->dq_dev != dev)			continue;                if (type != -1 && dquot->dq_type != type)			continue;		if (!dquot->dq_sb)	/* Invalidated? */			continue;		if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED)))			continue;		/* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */		dquot->dq_count++;		if (dquot->dq_flags & DQ_LOCKED)			wait_on_dquot(dquot);		if (dquot->dq_flags & DQ_MOD)			write_dquot(dquot);		dqput(dquot);		goto restart;	}	dqstats.syncs++;	unlock_kernel();	return 0;}/* Free unused dquots from cache */static void prune_dqcache(int count){	struct list_head *head;	struct dquot *dquot;	head = free_dquots.prev;	while (head != &free_dquots && count) {		dquot = list_entry(head, struct dquot, dq_free);		remove_dquot_hash(dquot);		remove_free_dquot(dquot);		remove_inuse(dquot);		kmem_cache_free(dquot_cachep, dquot);		count--;		head = free_dquots.prev;	}}int shrink_dqcache_memory(int priority, unsigned int gfp_mask){	lock_kernel();	prune_dqcache(nr_free_dquots / (priority + 1));	unlock_kernel();	kmem_cache_shrink(dquot_cachep);	return 0;}/* NOTE: If you change this function please check whether dqput_blocks() works right... */static void dqput(struct dquot *dquot){	if (!dquot)		return;	if (!dquot->dq_count) {		printk("VFS: dqput: trying to free free dquot\n");		printk("VFS: device %s, dquot of %s %d\n",			kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],			dquot->dq_id);		return;	}	dqstats.drops++;we_slept:	if (dquot->dq_count > 1) {		/* We have more than one user... We can simply decrement use count */		dquot->dq_count--;		return;	}	if (dquot->dq_flags & DQ_MOD) {		write_dquot(dquot);		goto we_slept;	}	/* sanity check */	if (!list_empty(&dquot->dq_free)) {		printk(KERN_ERR "dqput: dquot already on free list??\n");		dquot->dq_count--;	/* J.K. Just decrementing use count seems safer... */		return;	}	dquot->dq_count--;	/* If dquot is going to be invalidated invalidate_dquots() is going to free it so */	if (!(dquot->dq_flags & DQ_INVAL))		put_dquot_last(dquot);	/* Place at end of LRU free queue */	wake_up(&dquot->dq_wait_free);}static struct dquot *get_empty_dquot(void){	struct dquot *dquot;	dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);	if(!dquot)		return NODQUOT;	memset((caddr_t)dquot, 0, sizeof(struct dquot));	init_waitqueue_head(&dquot->dq_wait_free);	init_waitqueue_head(&dquot->dq_wait_lock);	INIT_LIST_HEAD(&dquot->dq_free);	INIT_LIST_HEAD(&dquot->dq_inuse);	INIT_LIST_HEAD(&dquot->dq_hash);	dquot->dq_count = 1;	/* all dquots go on the inuse_list */	put_inuse(dquot);	return dquot;}static struct dquot *dqget(struct super_block *sb, unsigned int id, short type){	unsigned int hashent = hashfn(sb->s_dev, id, type);	struct dquot *dquot, *empty = NODQUOT;	struct quota_mount_options *dqopt = sb_dqopt(sb);we_slept:        if (!is_enabled(dqopt, type)) {		if (empty)			dqput(empty);                return NODQUOT;	}	if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NODQUOT) {		if (empty == NODQUOT) {			if ((empty = get_empty_dquot()) == NODQUOT)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -