linux/fs/udf/ialloc.c
Jan Kara 146bca72c7 udf: Don't write integrity descriptor too often
We update information in logical volume integrity descriptor after each
allocation (as LVID contains free space, number of directories and files on
disk etc.). If the filesystem is on some phase change media, this leads to its
quick degradation as such media is able to handle only 10000 overwrites or so.
We solve the problem by writing new information into LVID only on umount,
remount-ro and sync. This solves the problem at the price of longer media
inconsistency (previously media became consistent after pdflush flushed dirty
LVID buffer) but that should be acceptable.

Report by and patch written in cooperation with
Rich Coe <Richard.Coe@med.ge.com>.

Signed-off-by: Jan Kara <jack@suse.cz>
2009-04-02 13:36:28 +02:00

167 lines
4.1 KiB
C

/*
* ialloc.c
*
* PURPOSE
* Inode allocation handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License (GPL). Copies of the GPL can be obtained from:
* ftp://prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work.
*
* (C) 1998-2001 Ben Fennema
*
* HISTORY
*
* 02/24/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/quotaops.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "udf_i.h"
#include "udf_sb.h"
void udf_free_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct udf_sb_info *sbi = UDF_SB(sb);
/*
* Note: we must free any quota before locking the superblock,
* as writing the quota to disk may need the lock as well.
*/
vfs_dq_free_inode(inode);
vfs_dq_drop(inode);
clear_inode(inode);
mutex_lock(&sbi->s_alloc_mutex);
if (sbi->s_lvid_bh) {
struct logicalVolIntegrityDescImpUse *lvidiu =
udf_sb_lvidiu(sbi);
if (S_ISDIR(inode->i_mode))
le32_add_cpu(&lvidiu->numDirs, -1);
else
le32_add_cpu(&lvidiu->numFiles, -1);
udf_updated_lvid(sb);
}
mutex_unlock(&sbi->s_alloc_mutex);
udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
}
struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
{
struct super_block *sb = dir->i_sb;
struct udf_sb_info *sbi = UDF_SB(sb);
struct inode *inode;
int block;
uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
struct udf_inode_info *iinfo;
struct udf_inode_info *dinfo = UDF_I(dir);
inode = new_inode(sb);
if (!inode) {
*err = -ENOMEM;
return NULL;
}
*err = -ENOSPC;
iinfo = UDF_I(inode);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) {
iinfo->i_efe = 1;
if (UDF_VERS_USE_EXTENDED_FE > sbi->s_udfrev)
sbi->s_udfrev = UDF_VERS_USE_EXTENDED_FE;
iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize -
sizeof(struct extendedFileEntry),
GFP_KERNEL);
} else {
iinfo->i_efe = 0;
iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize -
sizeof(struct fileEntry),
GFP_KERNEL);
}
if (!iinfo->i_ext.i_data) {
iput(inode);
*err = -ENOMEM;
return NULL;
}
block = udf_new_block(dir->i_sb, NULL,
dinfo->i_location.partitionReferenceNum,
start, err);
if (*err) {
iput(inode);
return NULL;
}
mutex_lock(&sbi->s_alloc_mutex);
if (sbi->s_lvid_bh) {
struct logicalVolIntegrityDesc *lvid =
(struct logicalVolIntegrityDesc *)
sbi->s_lvid_bh->b_data;
struct logicalVolIntegrityDescImpUse *lvidiu =
udf_sb_lvidiu(sbi);
struct logicalVolHeaderDesc *lvhd;
uint64_t uniqueID;
lvhd = (struct logicalVolHeaderDesc *)
(lvid->logicalVolContentsUse);
if (S_ISDIR(mode))
le32_add_cpu(&lvidiu->numDirs, 1);
else
le32_add_cpu(&lvidiu->numFiles, 1);
iinfo->i_unique = uniqueID = le64_to_cpu(lvhd->uniqueID);
if (!(++uniqueID & 0x00000000FFFFFFFFUL))
uniqueID += 16;
lvhd->uniqueID = cpu_to_le64(uniqueID);
udf_updated_lvid(sb);
}
mutex_unlock(&sbi->s_alloc_mutex);
inode->i_mode = mode;
inode->i_uid = current_fsuid();
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else {
inode->i_gid = current_fsgid();
}
iinfo->i_location.logicalBlockNum = block;
iinfo->i_location.partitionReferenceNum =
dinfo->i_location.partitionReferenceNum;
inode->i_ino = udf_get_lb_pblock(sb, &iinfo->i_location, 0);
inode->i_blocks = 0;
iinfo->i_lenEAttr = 0;
iinfo->i_lenAlloc = 0;
iinfo->i_use = 0;
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB))
iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else
iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
inode->i_mtime = inode->i_atime = inode->i_ctime =
iinfo->i_crtime = current_fs_time(inode->i_sb);
insert_inode_hash(inode);
mark_inode_dirty(inode);
if (vfs_dq_alloc_inode(inode)) {
vfs_dq_drop(inode);
inode->i_flags |= S_NOQUOTA;
inode->i_nlink = 0;
iput(inode);
*err = -EDQUOT;
return NULL;
}
*err = 0;
return inode;
}