diff options
author | Jan Kara <jack@suse.cz> | 2009-01-26 15:28:09 +0100 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2009-03-26 02:18:35 +0100 |
commit | 884d179dff3aa98a73c3ba9dee05fd6050d664f0 (patch) | |
tree | 68c18e3ff8decd2af861e807bed96cca2dea30d4 /fs/quota_tree.c | |
parent | 60e58e0f30e723464c2a7d34b71b8675566c572d (diff) |
quota: Move quota files into separate directory
Quota subsystem has more and more files. It's time to create a dir for it.
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/quota_tree.c')
-rw-r--r-- | fs/quota_tree.c | 645 |
1 files changed, 0 insertions, 645 deletions
diff --git a/fs/quota_tree.c b/fs/quota_tree.c deleted file mode 100644 index 953404c95b1..00000000000 --- a/fs/quota_tree.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * vfsv0 quota IO operations on file - */ - -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/dqblk_v2.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/quotaops.h> - -#include <asm/byteorder.h> - -#include "quota_tree.h" - -MODULE_AUTHOR("Jan Kara"); -MODULE_DESCRIPTION("Quota trie support"); -MODULE_LICENSE("GPL"); - -#define __QUOTA_QT_PARANOIA - -typedef char *dqbuf_t; - -static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth) -{ - unsigned int epb = info->dqi_usable_bs >> 2; - - depth = info->dqi_qtree_depth - depth - 1; - while (depth--) - id /= epb; - return id % epb; -} - -/* Number of entries in one blocks */ -static inline int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) -{ - return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader)) - / info->dqi_entry_size; -} - -static dqbuf_t getdqbuf(size_t size) -{ - dqbuf_t buf = kmalloc(size, GFP_NOFS); - if (!buf) - printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n"); - return buf; -} - -static inline void freedqbuf(dqbuf_t buf) -{ - kfree(buf); -} - -static inline ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf) -{ - struct super_block *sb = info->dqi_sb; - - memset(buf, 0, info->dqi_usable_bs); - return sb->s_op->quota_read(sb, info->dqi_type, (char *)buf, - info->dqi_usable_bs, blk << info->dqi_blocksize_bits); -} - -static inline ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf) -{ - struct super_block *sb = info->dqi_sb; - - return sb->s_op->quota_write(sb, info->dqi_type, (char *)buf, - info->dqi_usable_bs, blk << info->dqi_blocksize_bits); -} - -/* Remove empty block from list and return it */ -static int get_free_dqblk(struct qtree_mem_dqinfo *info) -{ - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; - int ret, blk; - - if (!buf) - return -ENOMEM; - if (info->dqi_free_blk) { - blk = info->dqi_free_blk; - ret = read_blk(info, blk, buf); - if (ret < 0) - goto out_buf; - info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); - } - else { - memset(buf, 0, info->dqi_usable_bs); - /* Assure block allocation... */ - ret = write_blk(info, info->dqi_blocks, buf); - if (ret < 0) - goto out_buf; - blk = info->dqi_blocks++; - } - mark_info_dirty(info->dqi_sb, info->dqi_type); - ret = blk; -out_buf: - freedqbuf(buf); - return ret; -} - -/* Insert empty block to the list */ -static int put_free_dqblk(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) -{ - struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; - int err; - - dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); - dh->dqdh_prev_free = cpu_to_le32(0); - dh->dqdh_entries = cpu_to_le16(0); - err = write_blk(info, blk, buf); - if (err < 0) - return err; - info->dqi_free_blk = blk; - mark_info_dirty(info->dqi_sb, info->dqi_type); - return 0; -} - -/* Remove given block from the list of blocks with free entries */ -static int remove_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) -{ - dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs); - struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; - uint nextblk = le32_to_cpu(dh->dqdh_next_free); - uint prevblk = le32_to_cpu(dh->dqdh_prev_free); - int err; - - if (!tmpbuf) - return -ENOMEM; - if (nextblk) { - err = read_blk(info, nextblk, tmpbuf); - if (err < 0) - goto out_buf; - ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = - dh->dqdh_prev_free; - err = write_blk(info, nextblk, tmpbuf); - if (err < 0) - goto out_buf; - } - if (prevblk) { - err = read_blk(info, prevblk, tmpbuf); - if (err < 0) - goto out_buf; - ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free = - dh->dqdh_next_free; - err = write_blk(info, prevblk, tmpbuf); - if (err < 0) - goto out_buf; - } else { - info->dqi_free_entry = nextblk; - mark_info_dirty(info->dqi_sb, info->dqi_type); - } - freedqbuf(tmpbuf); - dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); - /* No matter whether write succeeds block is out of list */ - if (write_blk(info, blk, buf) < 0) - printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk); - return 0; -out_buf: - freedqbuf(tmpbuf); - return err; -} - -/* Insert given block to the beginning of list with free entries */ -static int insert_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk) -{ - dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs); - struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; - int err; - - if (!tmpbuf) - return -ENOMEM; - dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry); - dh->dqdh_prev_free = cpu_to_le32(0); - err = write_blk(info, blk, buf); - if (err < 0) - goto out_buf; - if (info->dqi_free_entry) { - err = read_blk(info, info->dqi_free_entry, tmpbuf); - if (err < 0) - goto out_buf; - ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = - cpu_to_le32(blk); - err = write_blk(info, info->dqi_free_entry, tmpbuf); - if (err < 0) - goto out_buf; - } - freedqbuf(tmpbuf); - info->dqi_free_entry = blk; - mark_info_dirty(info->dqi_sb, info->dqi_type); - return 0; -out_buf: - freedqbuf(tmpbuf); - return err; -} - -/* Is the entry in the block free? */ -int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk) -{ - int i; - - for (i = 0; i < info->dqi_entry_size; i++) - if (disk[i]) - return 0; - return 1; -} -EXPORT_SYMBOL(qtree_entry_unused); - -/* Find space for dquot */ -static uint find_free_dqentry(struct qtree_mem_dqinfo *info, - struct dquot *dquot, int *err) -{ - uint blk, i; - struct qt_disk_dqdbheader *dh; - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - char *ddquot; - - *err = 0; - if (!buf) { - *err = -ENOMEM; - return 0; - } - dh = (struct qt_disk_dqdbheader *)buf; - if (info->dqi_free_entry) { - blk = info->dqi_free_entry; - *err = read_blk(info, blk, buf); - if (*err < 0) - goto out_buf; - } else { - blk = get_free_dqblk(info); - if ((int)blk < 0) { - *err = blk; - freedqbuf(buf); - return 0; - } - memset(buf, 0, info->dqi_usable_bs); - /* This is enough as block is already zeroed and entry list is empty... */ - info->dqi_free_entry = blk; - mark_info_dirty(dquot->dq_sb, dquot->dq_type); - } - /* Block will be full? */ - if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) { - *err = remove_free_dqentry(info, buf, blk); - if (*err < 0) { - printk(KERN_ERR "VFS: find_free_dqentry(): Can't " - "remove block (%u) from entry free list.\n", - blk); - goto out_buf; - } - } - le16_add_cpu(&dh->dqdh_entries, 1); - /* Find free structure in block */ - for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader); - i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot); - i++, ddquot += info->dqi_entry_size); -#ifdef __QUOTA_QT_PARANOIA - if (i == qtree_dqstr_in_blk(info)) { - printk(KERN_ERR "VFS: find_free_dqentry(): Data block full " - "but it shouldn't.\n"); - *err = -EIO; - goto out_buf; - } -#endif - *err = write_blk(info, blk, buf); - if (*err < 0) { - printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota " - "data block %u.\n", blk); - goto out_buf; - } - dquot->dq_off = (blk << info->dqi_blocksize_bits) + - sizeof(struct qt_disk_dqdbheader) + - i * info->dqi_entry_size; - freedqbuf(buf); - return blk; -out_buf: - freedqbuf(buf); - return 0; -} - -/* Insert reference to structure into the trie */ -static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, - uint *treeblk, int depth) -{ - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - int ret = 0, newson = 0, newact = 0; - __le32 *ref; - uint newblk; - - if (!buf) - return -ENOMEM; - if (!*treeblk) { - ret = get_free_dqblk(info); - if (ret < 0) - goto out_buf; - *treeblk = ret; - memset(buf, 0, info->dqi_usable_bs); - newact = 1; - } else { - ret = read_blk(info, *treeblk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't read tree quota block " - "%u.\n", *treeblk); - goto out_buf; - } - } - ref = (__le32 *)buf; - newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); - if (!newblk) - newson = 1; - if (depth == info->dqi_qtree_depth - 1) { -#ifdef __QUOTA_QT_PARANOIA - if (newblk) { - printk(KERN_ERR "VFS: Inserting already present quota " - "entry (block %u).\n", - le32_to_cpu(ref[get_index(info, - dquot->dq_id, depth)])); - ret = -EIO; - goto out_buf; - } -#endif - newblk = find_free_dqentry(info, dquot, &ret); - } else { - ret = do_insert_tree(info, dquot, &newblk, depth+1); - } - if (newson && ret >= 0) { - ref[get_index(info, dquot->dq_id, depth)] = - cpu_to_le32(newblk); - ret = write_blk(info, *treeblk, buf); - } else if (newact && ret < 0) { - put_free_dqblk(info, buf, *treeblk); - } -out_buf: - freedqbuf(buf); - return ret; -} - -/* Wrapper for inserting quota structure into tree */ -static inline int dq_insert_tree(struct qtree_mem_dqinfo *info, - struct dquot *dquot) -{ - int tmp = QT_TREEOFF; - return do_insert_tree(info, dquot, &tmp, 0); -} - -/* - * We don't have to be afraid of deadlocks as we never have quotas on quota files... - */ -int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) -{ - int type = dquot->dq_type; - struct super_block *sb = dquot->dq_sb; - ssize_t ret; - dqbuf_t ddquot = getdqbuf(info->dqi_entry_size); - - if (!ddquot) - return -ENOMEM; - - /* dq_off is guarded by dqio_mutex */ - if (!dquot->dq_off) { - ret = dq_insert_tree(info, dquot); - if (ret < 0) { - printk(KERN_ERR "VFS: Error %zd occurred while " - "creating quota.\n", ret); - freedqbuf(ddquot); - return ret; - } - } - spin_lock(&dq_data_lock); - info->dqi_ops->mem2disk_dqblk(ddquot, dquot); - spin_unlock(&dq_data_lock); - ret = sb->s_op->quota_write(sb, type, (char *)ddquot, - info->dqi_entry_size, dquot->dq_off); - if (ret != info->dqi_entry_size) { - printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", - sb->s_id); - if (ret >= 0) - ret = -ENOSPC; - } else { - ret = 0; - } - dqstats.writes++; - freedqbuf(ddquot); - - return ret; -} -EXPORT_SYMBOL(qtree_write_dquot); - -/* Free dquot entry in data block */ -static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, - uint blk) -{ - struct qt_disk_dqdbheader *dh; - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - int ret = 0; - - if (!buf) - return -ENOMEM; - if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { - printk(KERN_ERR "VFS: Quota structure has offset to other " - "block (%u) than it should (%u).\n", blk, - (uint)(dquot->dq_off >> info->dqi_blocksize_bits)); - goto out_buf; - } - ret = read_blk(info, blk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); - goto out_buf; - } - dh = (struct qt_disk_dqdbheader *)buf; - le16_add_cpu(&dh->dqdh_entries, -1); - if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ - ret = remove_free_dqentry(info, buf, blk); - if (ret >= 0) - ret = put_free_dqblk(info, buf, blk); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't move quota data block (%u) " - "to free list.\n", blk); - goto out_buf; - } - } else { - memset(buf + - (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)), - 0, info->dqi_entry_size); - if (le16_to_cpu(dh->dqdh_entries) == - qtree_dqstr_in_blk(info) - 1) { - /* Insert will write block itself */ - ret = insert_free_dqentry(info, buf, blk); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't insert quota data " - "block (%u) to free entry list.\n", blk); - goto out_buf; - } - } else { - ret = write_blk(info, blk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't write quota data " - "block %u\n", blk); - goto out_buf; - } - } - } - dquot->dq_off = 0; /* Quota is now unattached */ -out_buf: - freedqbuf(buf); - return ret; -} - -/* Remove reference to dquot from tree */ -static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, - uint *blk, int depth) -{ - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - int ret = 0; - uint newblk; - __le32 *ref = (__le32 *)buf; - - if (!buf) - return -ENOMEM; - ret = read_blk(info, *blk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); - goto out_buf; - } - newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); - if (depth == info->dqi_qtree_depth - 1) { - ret = free_dqentry(info, dquot, newblk); - newblk = 0; - } else { - ret = remove_tree(info, dquot, &newblk, depth+1); - } - if (ret >= 0 && !newblk) { - int i; - ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0); - /* Block got empty? */ - for (i = 0; - i < (info->dqi_usable_bs >> 2) && !ref[i]; - i++); - /* Don't put the root block into the free block list */ - if (i == (info->dqi_usable_bs >> 2) - && *blk != QT_TREEOFF) { - put_free_dqblk(info, buf, *blk); - *blk = 0; - } else { - ret = write_blk(info, *blk, buf); - if (ret < 0) - printk(KERN_ERR "VFS: Can't write quota tree " - "block %u.\n", *blk); - } - } -out_buf: - freedqbuf(buf); - return ret; -} - -/* Delete dquot from tree */ -int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) -{ - uint tmp = QT_TREEOFF; - - if (!dquot->dq_off) /* Even not allocated? */ - return 0; - return remove_tree(info, dquot, &tmp, 0); -} -EXPORT_SYMBOL(qtree_delete_dquot); - -/* Find entry in block */ -static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, - struct dquot *dquot, uint blk) -{ - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - loff_t ret = 0; - int i; - char *ddquot; - - if (!buf) - return -ENOMEM; - ret = read_blk(info, blk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); - goto out_buf; - } - for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader); - i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot); - i++, ddquot += info->dqi_entry_size); - if (i == qtree_dqstr_in_blk(info)) { - printk(KERN_ERR "VFS: Quota for id %u referenced " - "but not present.\n", dquot->dq_id); - ret = -EIO; - goto out_buf; - } else { - ret = (blk << info->dqi_blocksize_bits) + sizeof(struct - qt_disk_dqdbheader) + i * info->dqi_entry_size; - } -out_buf: - freedqbuf(buf); - return ret; -} - -/* Find entry for given id in the tree */ -static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, - struct dquot *dquot, uint blk, int depth) -{ - dqbuf_t buf = getdqbuf(info->dqi_usable_bs); - loff_t ret = 0; - __le32 *ref = (__le32 *)buf; - - if (!buf) - return -ENOMEM; - ret = read_blk(info, blk, buf); - if (ret < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); - goto out_buf; - } - ret = 0; - blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); - if (!blk) /* No reference? */ - goto out_buf; - if (depth < info->dqi_qtree_depth - 1) - ret = find_tree_dqentry(info, dquot, blk, depth+1); - else - ret = find_block_dqentry(info, dquot, blk); -out_buf: - freedqbuf(buf); - return ret; -} - -/* Find entry for given id in the tree - wrapper function */ -static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info, - struct dquot *dquot) -{ - return find_tree_dqentry(info, dquot, QT_TREEOFF, 0); -} - -int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) -{ - int type = dquot->dq_type; - struct super_block *sb = dquot->dq_sb; - loff_t offset; - dqbuf_t ddquot; - int ret = 0; - -#ifdef __QUOTA_QT_PARANOIA - /* Invalidated quota? */ - if (!sb_dqopt(dquot->dq_sb)->files[type]) { - printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); - return -EIO; - } -#endif - /* Do we know offset of the dquot entry in the quota file? */ - if (!dquot->dq_off) { - offset = find_dqentry(info, dquot); - if (offset <= 0) { /* Entry not present? */ - if (offset < 0) - printk(KERN_ERR "VFS: Can't read quota " - "structure for id %u.\n", dquot->dq_id); - dquot->dq_off = 0; - set_bit(DQ_FAKE_B, &dquot->dq_flags); - memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); - ret = offset; - goto out; - } - dquot->dq_off = offset; - } - ddquot = getdqbuf(info->dqi_entry_size); - if (!ddquot) - return -ENOMEM; - ret = sb->s_op->quota_read(sb, type, (char *)ddquot, - info->dqi_entry_size, dquot->dq_off); - if (ret != info->dqi_entry_size) { - if (ret >= 0) - ret = -EIO; - printk(KERN_ERR "VFS: Error while reading quota " - "structure for id %u.\n", dquot->dq_id); - set_bit(DQ_FAKE_B, &dquot->dq_flags); - memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); - freedqbuf(ddquot); - goto out; - } - spin_lock(&dq_data_lock); - info->dqi_ops->disk2mem_dqblk(dquot, ddquot); - if (!dquot->dq_dqb.dqb_bhardlimit && - !dquot->dq_dqb.dqb_bsoftlimit && - !dquot->dq_dqb.dqb_ihardlimit && - !dquot->dq_dqb.dqb_isoftlimit) - set_bit(DQ_FAKE_B, &dquot->dq_flags); - spin_unlock(&dq_data_lock); - freedqbuf(ddquot); -out: - dqstats.reads++; - return ret; -} -EXPORT_SYMBOL(qtree_read_dquot); - -/* Check whether dquot should not be deleted. We know we are - * the only one operating on dquot (thanks to dq_lock) */ -int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) -{ - if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) - return qtree_delete_dquot(info, dquot); - return 0; -} -EXPORT_SYMBOL(qtree_release_dquot); |