aboutsummaryrefslogtreecommitdiffstats
path: root/fs/udf
diff options
context:
space:
mode:
authorAlessio Igor Bogani <abogani@texware.it>2010-11-16 12:40:47 -0500
committerJan Kara <jack@suse.cz>2011-01-06 11:03:56 -0500
commit4d0fb621d35007c19a396f2bb629e5aeaacef2d0 (patch)
tree92f6e0d3c38c15b0af507bd4b6d3353e34a337a9 /fs/udf
parentd1668fe390c1e84580575965684a8fa7e4626dee (diff)
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>
Diffstat (limited to 'fs/udf')
-rw-r--r--fs/udf/file.c4
-rw-r--r--fs/udf/inode.c19
-rw-r--r--fs/udf/namei.c7
-rw-r--r--fs/udf/super.c1
-rw-r--r--fs/udf/symlink.c12
-rw-r--r--fs/udf/udf_i.h13
6 files changed, 37 insertions, 19 deletions
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 66b9e7e7e4c5..df0c5561cc7e 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -204,10 +204,10 @@ static int udf_release_file(struct inode *inode, struct file *filp)
204{ 204{
205 if (filp->f_mode & FMODE_WRITE) { 205 if (filp->f_mode & FMODE_WRITE) {
206 mutex_lock(&inode->i_mutex); 206 mutex_lock(&inode->i_mutex);
207 lock_kernel(); 207 down_write(&UDF_I(inode)->i_data_sem);
208 udf_discard_prealloc(inode); 208 udf_discard_prealloc(inode);
209 udf_truncate_tail_extent(inode); 209 udf_truncate_tail_extent(inode);
210 unlock_kernel(); 210 up_write(&UDF_I(inode)->i_data_sem);
211 mutex_unlock(&inode->i_mutex); 211 mutex_unlock(&inode->i_mutex);
212 } 212 }
213 return 0; 213 return 0;
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index fa3c1541151c..b2fe4d7f20eb 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -301,10 +301,9 @@ static int udf_get_block(struct inode *inode, sector_t block,
301 err = -EIO; 301 err = -EIO;
302 new = 0; 302 new = 0;
303 bh = NULL; 303 bh = NULL;
304
305 lock_kernel();
306
307 iinfo = UDF_I(inode); 304 iinfo = UDF_I(inode);
305
306 down_write(&iinfo->i_data_sem);
308 if (block == iinfo->i_next_alloc_block + 1) { 307 if (block == iinfo->i_next_alloc_block + 1) {
309 iinfo->i_next_alloc_block++; 308 iinfo->i_next_alloc_block++;
310 iinfo->i_next_alloc_goal++; 309 iinfo->i_next_alloc_goal++;
@@ -323,7 +322,7 @@ static int udf_get_block(struct inode *inode, sector_t block,
323 map_bh(bh_result, inode->i_sb, phys); 322 map_bh(bh_result, inode->i_sb, phys);
324 323
325abort: 324abort:
326 unlock_kernel(); 325 up_write(&iinfo->i_data_sem);
327 return err; 326 return err;
328} 327}
329 328
@@ -1021,16 +1020,16 @@ void udf_truncate(struct inode *inode)
1021 if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 1020 if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
1022 return; 1021 return;
1023 1022
1024 lock_kernel();
1025 iinfo = UDF_I(inode); 1023 iinfo = UDF_I(inode);
1026 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 1024 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
1025 down_write(&iinfo->i_data_sem);
1027 if (inode->i_sb->s_blocksize < 1026 if (inode->i_sb->s_blocksize <
1028 (udf_file_entry_alloc_offset(inode) + 1027 (udf_file_entry_alloc_offset(inode) +
1029 inode->i_size)) { 1028 inode->i_size)) {
1030 udf_expand_file_adinicb(inode, inode->i_size, &err); 1029 udf_expand_file_adinicb(inode, inode->i_size, &err);
1031 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 1030 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
1032 inode->i_size = iinfo->i_lenAlloc; 1031 inode->i_size = iinfo->i_lenAlloc;
1033 unlock_kernel(); 1032 up_write(&iinfo->i_data_sem);
1034 return; 1033 return;
1035 } else 1034 } else
1036 udf_truncate_extents(inode); 1035 udf_truncate_extents(inode);
@@ -1041,10 +1040,13 @@ void udf_truncate(struct inode *inode)
1041 offset - udf_file_entry_alloc_offset(inode)); 1040 offset - udf_file_entry_alloc_offset(inode));
1042 iinfo->i_lenAlloc = inode->i_size; 1041 iinfo->i_lenAlloc = inode->i_size;
1043 } 1042 }
1043 up_write(&iinfo->i_data_sem);
1044 } else { 1044 } else {
1045 block_truncate_page(inode->i_mapping, inode->i_size, 1045 block_truncate_page(inode->i_mapping, inode->i_size,
1046 udf_get_block); 1046 udf_get_block);
1047 down_write(&iinfo->i_data_sem);
1047 udf_truncate_extents(inode); 1048 udf_truncate_extents(inode);
1049 up_write(&iinfo->i_data_sem);
1048 } 1050 }
1049 1051
1050 inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); 1052 inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb);
@@ -1052,7 +1054,6 @@ void udf_truncate(struct inode *inode)
1052 udf_sync_inode(inode); 1054 udf_sync_inode(inode);
1053 else 1055 else
1054 mark_inode_dirty(inode); 1056 mark_inode_dirty(inode);
1055 unlock_kernel();
1056} 1057}
1057 1058
1058static void __udf_read_inode(struct inode *inode) 1059static void __udf_read_inode(struct inode *inode)
@@ -2043,7 +2044,7 @@ long udf_block_map(struct inode *inode, sector_t block)
2043 struct extent_position epos = {}; 2044 struct extent_position epos = {};
2044 int ret; 2045 int ret;
2045 2046
2046 lock_kernel(); 2047 down_read(&UDF_I(inode)->i_data_sem);
2047 2048
2048 if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) == 2049 if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) ==
2049 (EXT_RECORDED_ALLOCATED >> 30)) 2050 (EXT_RECORDED_ALLOCATED >> 30))
@@ -2051,7 +2052,7 @@ long udf_block_map(struct inode *inode, sector_t block)
2051 else 2052 else
2052 ret = 0; 2053 ret = 0;
2053 2054
2054 unlock_kernel(); 2055 up_read(&UDF_I(inode)->i_data_sem);
2055 brelse(epos.bh); 2056 brelse(epos.bh);
2056 2057
2057 if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV)) 2058 if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 701fcda18415..d5eb000ddddd 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -893,18 +893,18 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
893 struct udf_inode_info *iinfo; 893 struct udf_inode_info *iinfo;
894 struct super_block *sb = dir->i_sb; 894 struct super_block *sb = dir->i_sb;
895 895
896 lock_kernel();
897 inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err); 896 inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err);
898 if (!inode) 897 if (!inode)
899 goto out; 898 goto out;
900 899
900 iinfo = UDF_I(inode);
901 down_write(&iinfo->i_data_sem);
901 name = kmalloc(UDF_NAME_LEN, GFP_NOFS); 902 name = kmalloc(UDF_NAME_LEN, GFP_NOFS);
902 if (!name) { 903 if (!name) {
903 err = -ENOMEM; 904 err = -ENOMEM;
904 goto out_no_entry; 905 goto out_no_entry;
905 } 906 }
906 907
907 iinfo = UDF_I(inode);
908 inode->i_data.a_ops = &udf_symlink_aops; 908 inode->i_data.a_ops = &udf_symlink_aops;
909 inode->i_op = &udf_symlink_inode_operations; 909 inode->i_op = &udf_symlink_inode_operations;
910 910
@@ -1024,6 +1024,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
1024 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); 1024 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
1025 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) 1025 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
1026 mark_inode_dirty(dir); 1026 mark_inode_dirty(dir);
1027 up_write(&iinfo->i_data_sem);
1027 if (fibh.sbh != fibh.ebh) 1028 if (fibh.sbh != fibh.ebh)
1028 brelse(fibh.ebh); 1029 brelse(fibh.ebh);
1029 brelse(fibh.sbh); 1030 brelse(fibh.sbh);
@@ -1032,10 +1033,10 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
1032 1033
1033out: 1034out:
1034 kfree(name); 1035 kfree(name);
1035 unlock_kernel();
1036 return err; 1036 return err;
1037 1037
1038out_no_entry: 1038out_no_entry:
1039 up_write(&iinfo->i_data_sem);
1039 inode_dec_link_count(inode); 1040 inode_dec_link_count(inode);
1040 iput(inode); 1041 iput(inode);
1041 goto out; 1042 goto out;
diff --git a/fs/udf/super.c b/fs/udf/super.c
index d2ec9f31e843..441b892cf85e 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -135,6 +135,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
135 ei->i_next_alloc_block = 0; 135 ei->i_next_alloc_block = 0;
136 ei->i_next_alloc_goal = 0; 136 ei->i_next_alloc_goal = 0;
137 ei->i_strat4096 = 0; 137 ei->i_strat4096 = 0;
138 init_rwsem(&ei->i_data_sem);
138 139
139 return &ei->vfs_inode; 140 return &ei->vfs_inode;
140} 141}
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index 16064787d2b7..b1d4488b0f14 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -27,7 +27,6 @@
27#include <linux/mm.h> 27#include <linux/mm.h>
28#include <linux/stat.h> 28#include <linux/stat.h>
29#include <linux/pagemap.h> 29#include <linux/pagemap.h>
30#include <linux/smp_lock.h>
31#include <linux/buffer_head.h> 30#include <linux/buffer_head.h>
32#include "udf_i.h" 31#include "udf_i.h"
33 32
@@ -78,13 +77,16 @@ static int udf_symlink_filler(struct file *file, struct page *page)
78 int err = -EIO; 77 int err = -EIO;
79 unsigned char *p = kmap(page); 78 unsigned char *p = kmap(page);
80 struct udf_inode_info *iinfo; 79 struct udf_inode_info *iinfo;
80 uint32_t pos;
81 81
82 lock_kernel();
83 iinfo = UDF_I(inode); 82 iinfo = UDF_I(inode);
83 pos = udf_block_map(inode, 0);
84
85 down_read(&iinfo->i_data_sem);
84 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { 86 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
85 symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr; 87 symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
86 } else { 88 } else {
87 bh = sb_bread(inode->i_sb, udf_block_map(inode, 0)); 89 bh = sb_bread(inode->i_sb, pos);
88 90
89 if (!bh) 91 if (!bh)
90 goto out; 92 goto out;
@@ -95,14 +97,14 @@ static int udf_symlink_filler(struct file *file, struct page *page)
95 udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); 97 udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
96 brelse(bh); 98 brelse(bh);
97 99
98 unlock_kernel(); 100 up_read(&iinfo->i_data_sem);
99 SetPageUptodate(page); 101 SetPageUptodate(page);
100 kunmap(page); 102 kunmap(page);
101 unlock_page(page); 103 unlock_page(page);
102 return 0; 104 return 0;
103 105
104out: 106out:
105 unlock_kernel(); 107 up_read(&iinfo->i_data_sem);
106 SetPageError(page); 108 SetPageError(page);
107 kunmap(page); 109 kunmap(page);
108 unlock_page(page); 110 unlock_page(page);
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h
index e58d1de41073..d1bd31ea724e 100644
--- a/fs/udf/udf_i.h
+++ b/fs/udf/udf_i.h
@@ -1,6 +1,18 @@
1#ifndef _UDF_I_H 1#ifndef _UDF_I_H
2#define _UDF_I_H 2#define _UDF_I_H
3 3
4/*
5 * The i_data_sem and i_mutex serve for protection of allocation information
6 * of a regular files and symlinks. This includes all extents belonging to
7 * the file/symlink, a fact whether data are in-inode or in external data
8 * blocks, preallocation, goal block information... When extents are read,
9 * i_mutex or i_data_sem must be held (for reading is enough in case of
10 * i_data_sem). When extents are changed, i_data_sem must be held for writing
11 * and also i_mutex must be held.
12 *
13 * For directories i_mutex is used for all the necessary protection.
14 */
15
4struct udf_inode_info { 16struct udf_inode_info {
5 struct timespec i_crtime; 17 struct timespec i_crtime;
6 /* Physical address of inode */ 18 /* Physical address of inode */
@@ -21,6 +33,7 @@ struct udf_inode_info {
21 struct long_ad *i_lad; 33 struct long_ad *i_lad;
22 __u8 *i_data; 34 __u8 *i_data;
23 } i_ext; 35 } i_ext;
36 struct rw_semaphore i_data_sem;
24 struct inode vfs_inode; 37 struct inode vfs_inode;
25}; 38};
26 39