Whamcloud - gitweb
b=3244
[fs/lustre-release.git] / lustre / kernel_patches / patches / quota-deadlock-on-pagelock-ext3.patch
1 Index: linux-2.6.9/fs/ext3/inode.c
2 ===================================================================
3 --- linux-2.6.9.orig/fs/ext3/inode.c    2006-08-25 16:39:10.000000000 +0800
4 +++ linux-2.6.9/fs/ext3/inode.c 2006-09-14 11:44:29.000000000 +0800
5 @@ -1028,7 +1028,7 @@
6         return ret;
7  }
8  
9 -static int
10 +int
11  ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh)
12  {
13         int err = journal_dirty_data(handle, bh);
14 Index: linux-2.6.9/fs/ext3/super.c
15 ===================================================================
16 --- linux-2.6.9.orig/fs/ext3/super.c    2006-08-25 16:39:48.000000000 +0800
17 +++ linux-2.6.9/fs/ext3/super.c 2006-09-14 11:51:48.000000000 +0800
18 @@ -529,7 +529,10 @@
19  static int ext3_write_info(struct super_block *sb, int type);
20  static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path);
21  static int ext3_quota_on_mount(struct super_block *sb, int type);
22 -static int ext3_quota_off_mount(struct super_block *sb, int type);
23 +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
24 +                              size_t len, loff_t off);
25 +static ssize_t ext3_quota_write(struct super_block *sb, int type,
26 +                               const char *data, size_t len, loff_t off);
27  
28  static struct dquot_operations ext3_quota_operations = {
29         .initialize     = ext3_dquot_initialize,
30 @@ -572,6 +575,10 @@
31         .statfs         = ext3_statfs,
32         .remount_fs     = ext3_remount,
33         .clear_inode    = ext3_clear_inode,
34 +#ifdef CONFIG_QUOTA
35 +       .quota_read     = ext3_quota_read,
36 +       .quota_write    = ext3_quota_write,
37 +#endif
38  };
39  
40  static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp)
41 @@ -713,6 +720,7 @@
42         int option;
43  #ifdef CONFIG_QUOTA
44         int qtype;
45 +       char *qname;
46  #endif
47  
48         if (!options)
49 @@ -891,19 +899,22 @@
50                                         "quota options when quota turned on.\n");
51                                 return 0;
52                         }
53 -                       if (sbi->s_qf_names[qtype]) {
54 +                       qname = match_strdup(&args[0]);
55 +                       if (!qname) {
56                                 printk(KERN_ERR
57 -                                       "EXT3-fs: %s quota file already "
58 -                                       "specified.\n", QTYPE2NAME(qtype));
59 +                                       "EXT3-fs: not enough memory for "
60 +                                       "storing quotafile name.\n");
61                                 return 0;
62                         }
63 -                       sbi->s_qf_names[qtype] = match_strdup(&args[0]);
64 -                       if (!sbi->s_qf_names[qtype]) {
65 +                       if (sbi->s_qf_names[qtype] &&
66 +                           strcmp(sbi->s_qf_names[qtype], qname)) {
67                                 printk(KERN_ERR
68 -                                       "EXT3-fs: not enough memory for "
69 -                                       "storing quotafile name.\n");
70 +                                       "EXT3-fs: %s quota file already "
71 +                                       "specified.\n", QTYPE2NAME(qtype));
72 +                               kfree(qname);
73                                 return 0;
74                         }
75 +                       sbi->s_qf_names[qtype] = qname;
76                         if (strchr(sbi->s_qf_names[qtype], '/')) {
77                                 printk(KERN_ERR
78                                         "EXT3-fs: quotafile must be on "
79 @@ -1223,7 +1234,7 @@
80         /* Turn quotas off */
81         for (i = 0; i < MAXQUOTAS; i++) {
82                 if (sb_dqopt(sb)->files[i])
83 -                       ext3_quota_off_mount(sb, i);
84 +                       vfs_quota_off(sb, i);
85         }
86  #endif
87         sb->s_flags = s_flags; /* Restore MS_RDONLY status */
88 @@ -2240,7 +2251,7 @@
89  
90  static inline struct inode *dquot_to_inode(struct dquot *dquot)
91  {
92 -       return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]->f_dentry->d_inode;
93 +       return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
94  }
95  
96  static int ext3_dquot_initialize(struct inode *inode, int type)
97 @@ -2279,8 +2290,10 @@
98  {
99         int ret, err;
100         handle_t *handle;
101 +       struct inode *inode;
102  
103 -       handle = ext3_journal_start(dquot_to_inode(dquot),
104 +       inode = dquot_to_inode(dquot);
105 +       handle = ext3_journal_start(inode,
106                                         EXT3_QUOTA_TRANS_BLOCKS);
107         if (IS_ERR(handle))
108                 return PTR_ERR(handle);
109 @@ -2367,22 +2380,9 @@
110         if (IS_ERR(dentry))
111                 return PTR_ERR(dentry);
112         err = vfs_quota_on_mount(type, EXT3_SB(sb)->s_jquota_fmt, dentry);
113 -       if (err)
114 -               dput(dentry);
115 -       /* We keep the dentry reference if everything went ok - we drop it
116 -        * on quota_off time */
117 -       return err;
118 -}
119 -
120 -/* Turn quotas off during mount time */
121 -static int ext3_quota_off_mount(struct super_block *sb, int type)
122 -{
123 -       int err;
124 -       struct dentry *dentry;
125 -
126 -       dentry = sb_dqopt(sb)->files[type]->f_dentry;
127 -       err = vfs_quota_off_mount(sb, type);
128 -       /* We invalidate dentry - it has at least wrong hash... */
129 +       /* Now invalidate and put the dentry - quota got its own reference
130 +        * to inode and dentry has at least wrong hash so we had better
131 +        * throw it away */
132         d_invalidate(dentry);
133         dput(dentry);
134         return err;
135 @@ -2405,20 +2405,121 @@
136         if (err)
137                 return err;
138         /* Quotafile not on the same filesystem? */
139 -       if (nd.mnt->mnt_sb != sb)
140 +       if (nd.mnt->mnt_sb != sb) {
141 +               path_release(&nd);
142                 return -EXDEV;
143 +       }
144         /* Quotafile not of fs root? */
145         if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode)
146                 printk(KERN_WARNING
147                         "EXT3-fs: Quota file not on filesystem root. "
148                         "Journalled quota will not work.\n");
149 -       if (!ext3_should_journal_data(nd.dentry->d_inode))
150 -               printk(KERN_WARNING "EXT3-fs: Quota file does not have "
151 -                       "data-journalling. Journalled quota will not work.\n");
152         path_release(&nd);
153         return vfs_quota_on(sb, type, format_id, path);
154  }
155  
156 +/* Read data from quotafile - avoid pagecache and such because we cannot afford
157 + * acquiring the locks... As quota files are never truncated and quota code
158 + * itself serializes the operations (and noone else should touch the files)
159 + * we don't have to be afraid of races */
160 +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
161 +                              size_t len, loff_t off)
162 +{
163 +       struct inode *inode = sb_dqopt(sb)->files[type];
164 +       sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
165 +       int err = 0;
166 +       int offset = off & (sb->s_blocksize - 1);
167 +       int tocopy;
168 +       size_t toread;
169 +       struct buffer_head *bh;
170 +       loff_t i_size = i_size_read(inode);
171 +
172 +       if (off > i_size)
173 +               return 0;
174 +       if (off+len > i_size)
175 +               len = i_size-off;
176 +       toread = len;
177 +       while (toread > 0) {
178 +               tocopy = sb->s_blocksize - offset < toread ?
179 +                               sb->s_blocksize - offset : toread;
180 +               bh = ext3_bread(NULL, inode, blk, 0, &err);
181 +               if (err)
182 +                       return err;
183 +               if (!bh)        /* A hole? */
184 +                       memset(data, 0, tocopy);
185 +               else
186 +                       memcpy(data, bh->b_data+offset, tocopy);
187 +               brelse(bh);
188 +               offset = 0;
189 +               toread -= tocopy;
190 +               data += tocopy;
191 +               blk++;
192 +       }
193 +       return len;
194 +}
195 +
196 +/* Write to quotafile (we know the transaction is already started and has
197 + * enough credits) */
198 +static ssize_t ext3_quota_write(struct super_block *sb, int type,
199 +                               const char *data, size_t len, loff_t off)
200 +{
201 +       struct inode *inode = sb_dqopt(sb)->files[type];
202 +       sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
203 +       int err = 0;
204 +       int offset = off & (sb->s_blocksize - 1);
205 +       int tocopy;
206 +       int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL;
207 +       size_t towrite = len;
208 +       struct buffer_head *bh;
209 +       handle_t *handle = journal_current_handle();
210 +
211 +       down(&inode->i_sem);
212 +       while (towrite > 0) {
213 +               tocopy = sb->s_blocksize - offset < towrite ?
214 +                               sb->s_blocksize - offset : towrite;
215 +               bh = ext3_bread(handle, inode, blk, 1, &err);
216 +               if (!bh)
217 +                       goto out;
218 +               if (journal_quota) {
219 +                       err = ext3_journal_get_write_access(handle, bh);
220 +                       if (err) {
221 +                               brelse(bh);
222 +                               goto out;
223 +                       }
224 +               }
225 +               lock_buffer(bh);
226 +               memcpy(bh->b_data+offset, data, tocopy);
227 +               flush_dcache_page(bh->b_page);
228 +               unlock_buffer(bh);
229 +               if (journal_quota)
230 +                       err = ext3_journal_dirty_metadata(handle, bh);
231 +               else {
232 +                       /* Always do at least ordered writes for quotas */
233 +                       err = ext3_journal_dirty_data(handle, bh);
234 +                       mark_buffer_dirty(bh);
235 +               }
236 +               brelse(bh);
237 +               if (err)
238 +                       goto out;
239 +               offset = 0;
240 +               towrite -= tocopy;
241 +               data += tocopy;
242 +               blk++;
243 +       }
244 +out:
245 +       if (len == towrite)
246 +               return err;
247 +       if (inode->i_size < off+len-towrite) {
248 +               i_size_write(inode, off+len-towrite);
249 +               EXT3_I(inode)->i_disksize = inode->i_size;
250 +       }
251 +       inode->i_version++;
252 +       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
253 +       ext3_mark_inode_dirty(handle, inode);
254 +       up(&inode->i_sem);
255 +       return len - towrite;
256 +}
257 +
258  #endif
259  
260  static struct super_block *ext3_get_sb(struct file_system_type *fs_type,
261 Index: linux-2.6.9/include/linux/ext3_jbd.h
262 ===================================================================
263 --- linux-2.6.9.orig/include/linux/ext3_jbd.h   2006-08-25 16:39:09.000000000 +0800
264 +++ linux-2.6.9/include/linux/ext3_jbd.h        2006-09-14 11:44:29.000000000 +0800
265 @@ -193,6 +193,8 @@
266  #define ext3_journal_forget(handle, bh) \
267         __ext3_journal_forget(__FUNCTION__, (handle), (bh))
268  
269 +int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh);
270 +
271  handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks);
272  int __ext3_journal_stop(const char *where, handle_t *handle);
273