udf: Replace bkl with the UDF_I(inode)->i_data_sem for protect udf_inode_info struct
Replace bkl with the UDF_I(inode)->i_data_sem rw semaphore in udf_release_file(), udf_symlink(), udf_symlink_filler(), udf_get_block(), udf_block_map(), and udf_setattr(). The rule now is that any operation on regular file's or symlink's extents (or generally allocation information including goal block) needs to hold i_data_sem. This work was supported by a hardware donation from the CE Linux Forum. Signed-off-by: Alessio Igor Bogani <abogani@texware.it> Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
d1668fe390
commit
4d0fb621d3
@ -204,10 +204,10 @@ static int udf_release_file(struct inode *inode, struct file *filp)
|
|||||||
{
|
{
|
||||||
if (filp->f_mode & FMODE_WRITE) {
|
if (filp->f_mode & FMODE_WRITE) {
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
lock_kernel();
|
down_write(&UDF_I(inode)->i_data_sem);
|
||||||
udf_discard_prealloc(inode);
|
udf_discard_prealloc(inode);
|
||||||
udf_truncate_tail_extent(inode);
|
udf_truncate_tail_extent(inode);
|
||||||
unlock_kernel();
|
up_write(&UDF_I(inode)->i_data_sem);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -301,10 +301,9 @@ static int udf_get_block(struct inode *inode, sector_t block,
|
|||||||
err = -EIO;
|
err = -EIO;
|
||||||
new = 0;
|
new = 0;
|
||||||
bh = NULL;
|
bh = NULL;
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
|
|
||||||
|
down_write(&iinfo->i_data_sem);
|
||||||
if (block == iinfo->i_next_alloc_block + 1) {
|
if (block == iinfo->i_next_alloc_block + 1) {
|
||||||
iinfo->i_next_alloc_block++;
|
iinfo->i_next_alloc_block++;
|
||||||
iinfo->i_next_alloc_goal++;
|
iinfo->i_next_alloc_goal++;
|
||||||
@ -323,7 +322,7 @@ static int udf_get_block(struct inode *inode, sector_t block,
|
|||||||
map_bh(bh_result, inode->i_sb, phys);
|
map_bh(bh_result, inode->i_sb, phys);
|
||||||
|
|
||||||
abort:
|
abort:
|
||||||
unlock_kernel();
|
up_write(&iinfo->i_data_sem);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1021,16 +1020,16 @@ void udf_truncate(struct inode *inode)
|
|||||||
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
|
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
||||||
|
down_write(&iinfo->i_data_sem);
|
||||||
if (inode->i_sb->s_blocksize <
|
if (inode->i_sb->s_blocksize <
|
||||||
(udf_file_entry_alloc_offset(inode) +
|
(udf_file_entry_alloc_offset(inode) +
|
||||||
inode->i_size)) {
|
inode->i_size)) {
|
||||||
udf_expand_file_adinicb(inode, inode->i_size, &err);
|
udf_expand_file_adinicb(inode, inode->i_size, &err);
|
||||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
||||||
inode->i_size = iinfo->i_lenAlloc;
|
inode->i_size = iinfo->i_lenAlloc;
|
||||||
unlock_kernel();
|
up_write(&iinfo->i_data_sem);
|
||||||
return;
|
return;
|
||||||
} else
|
} else
|
||||||
udf_truncate_extents(inode);
|
udf_truncate_extents(inode);
|
||||||
@ -1041,10 +1040,13 @@ void udf_truncate(struct inode *inode)
|
|||||||
offset - udf_file_entry_alloc_offset(inode));
|
offset - udf_file_entry_alloc_offset(inode));
|
||||||
iinfo->i_lenAlloc = inode->i_size;
|
iinfo->i_lenAlloc = inode->i_size;
|
||||||
}
|
}
|
||||||
|
up_write(&iinfo->i_data_sem);
|
||||||
} else {
|
} else {
|
||||||
block_truncate_page(inode->i_mapping, inode->i_size,
|
block_truncate_page(inode->i_mapping, inode->i_size,
|
||||||
udf_get_block);
|
udf_get_block);
|
||||||
|
down_write(&iinfo->i_data_sem);
|
||||||
udf_truncate_extents(inode);
|
udf_truncate_extents(inode);
|
||||||
|
up_write(&iinfo->i_data_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb);
|
inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb);
|
||||||
@ -1052,7 +1054,6 @@ void udf_truncate(struct inode *inode)
|
|||||||
udf_sync_inode(inode);
|
udf_sync_inode(inode);
|
||||||
else
|
else
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
unlock_kernel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __udf_read_inode(struct inode *inode)
|
static void __udf_read_inode(struct inode *inode)
|
||||||
@ -2043,7 +2044,7 @@ long udf_block_map(struct inode *inode, sector_t block)
|
|||||||
struct extent_position epos = {};
|
struct extent_position epos = {};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
lock_kernel();
|
down_read(&UDF_I(inode)->i_data_sem);
|
||||||
|
|
||||||
if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) ==
|
if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) ==
|
||||||
(EXT_RECORDED_ALLOCATED >> 30))
|
(EXT_RECORDED_ALLOCATED >> 30))
|
||||||
@ -2051,7 +2052,7 @@ long udf_block_map(struct inode *inode, sector_t block)
|
|||||||
else
|
else
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
unlock_kernel();
|
up_read(&UDF_I(inode)->i_data_sem);
|
||||||
brelse(epos.bh);
|
brelse(epos.bh);
|
||||||
|
|
||||||
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
|
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
|
||||||
|
@ -893,18 +893,18 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
|
|||||||
struct udf_inode_info *iinfo;
|
struct udf_inode_info *iinfo;
|
||||||
struct super_block *sb = dir->i_sb;
|
struct super_block *sb = dir->i_sb;
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err);
|
inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err);
|
||||||
if (!inode)
|
if (!inode)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
iinfo = UDF_I(inode);
|
||||||
|
down_write(&iinfo->i_data_sem);
|
||||||
name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
|
name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_no_entry;
|
goto out_no_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
iinfo = UDF_I(inode);
|
|
||||||
inode->i_data.a_ops = &udf_symlink_aops;
|
inode->i_data.a_ops = &udf_symlink_aops;
|
||||||
inode->i_op = &udf_symlink_inode_operations;
|
inode->i_op = &udf_symlink_inode_operations;
|
||||||
|
|
||||||
@ -1024,6 +1024,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
|
|||||||
udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
|
udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
|
||||||
if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
|
if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
|
||||||
mark_inode_dirty(dir);
|
mark_inode_dirty(dir);
|
||||||
|
up_write(&iinfo->i_data_sem);
|
||||||
if (fibh.sbh != fibh.ebh)
|
if (fibh.sbh != fibh.ebh)
|
||||||
brelse(fibh.ebh);
|
brelse(fibh.ebh);
|
||||||
brelse(fibh.sbh);
|
brelse(fibh.sbh);
|
||||||
@ -1032,10 +1033,10 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
kfree(name);
|
kfree(name);
|
||||||
unlock_kernel();
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
out_no_entry:
|
out_no_entry:
|
||||||
|
up_write(&iinfo->i_data_sem);
|
||||||
inode_dec_link_count(inode);
|
inode_dec_link_count(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -135,6 +135,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
|
|||||||
ei->i_next_alloc_block = 0;
|
ei->i_next_alloc_block = 0;
|
||||||
ei->i_next_alloc_goal = 0;
|
ei->i_next_alloc_goal = 0;
|
||||||
ei->i_strat4096 = 0;
|
ei->i_strat4096 = 0;
|
||||||
|
init_rwsem(&ei->i_data_sem);
|
||||||
|
|
||||||
return &ei->vfs_inode;
|
return &ei->vfs_inode;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/smp_lock.h>
|
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include "udf_i.h"
|
#include "udf_i.h"
|
||||||
|
|
||||||
@ -78,13 +77,16 @@ static int udf_symlink_filler(struct file *file, struct page *page)
|
|||||||
int err = -EIO;
|
int err = -EIO;
|
||||||
unsigned char *p = kmap(page);
|
unsigned char *p = kmap(page);
|
||||||
struct udf_inode_info *iinfo;
|
struct udf_inode_info *iinfo;
|
||||||
|
uint32_t pos;
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
|
pos = udf_block_map(inode, 0);
|
||||||
|
|
||||||
|
down_read(&iinfo->i_data_sem);
|
||||||
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
|
||||||
symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
|
symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
|
||||||
} else {
|
} else {
|
||||||
bh = sb_bread(inode->i_sb, udf_block_map(inode, 0));
|
bh = sb_bread(inode->i_sb, pos);
|
||||||
|
|
||||||
if (!bh)
|
if (!bh)
|
||||||
goto out;
|
goto out;
|
||||||
@ -95,14 +97,14 @@ static int udf_symlink_filler(struct file *file, struct page *page)
|
|||||||
udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
|
udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
|
|
||||||
unlock_kernel();
|
up_read(&iinfo->i_data_sem);
|
||||||
SetPageUptodate(page);
|
SetPageUptodate(page);
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
unlock_kernel();
|
up_read(&iinfo->i_data_sem);
|
||||||
SetPageError(page);
|
SetPageError(page);
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
#ifndef _UDF_I_H
|
#ifndef _UDF_I_H
|
||||||
#define _UDF_I_H
|
#define _UDF_I_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The i_data_sem and i_mutex serve for protection of allocation information
|
||||||
|
* of a regular files and symlinks. This includes all extents belonging to
|
||||||
|
* the file/symlink, a fact whether data are in-inode or in external data
|
||||||
|
* blocks, preallocation, goal block information... When extents are read,
|
||||||
|
* i_mutex or i_data_sem must be held (for reading is enough in case of
|
||||||
|
* i_data_sem). When extents are changed, i_data_sem must be held for writing
|
||||||
|
* and also i_mutex must be held.
|
||||||
|
*
|
||||||
|
* For directories i_mutex is used for all the necessary protection.
|
||||||
|
*/
|
||||||
|
|
||||||
struct udf_inode_info {
|
struct udf_inode_info {
|
||||||
struct timespec i_crtime;
|
struct timespec i_crtime;
|
||||||
/* Physical address of inode */
|
/* Physical address of inode */
|
||||||
@ -21,6 +33,7 @@ struct udf_inode_info {
|
|||||||
struct long_ad *i_lad;
|
struct long_ad *i_lad;
|
||||||
__u8 *i_data;
|
__u8 *i_data;
|
||||||
} i_ext;
|
} i_ext;
|
||||||
|
struct rw_semaphore i_data_sem;
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user