aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2011-09-04 10:18:14 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-09-04 10:18:14 -0400
commit9ea7a0df63630ad8197716cd313ea66e28906fc0 (patch)
tree2f9cb8bb3f4c709aa666c7863b496f2757b261fb
parent56889787cfa77dfd96f0b3a3e6a4f26c2e4a5134 (diff)
jbd2: add debugging information to jbd2_journal_dirty_metadata()
Add debugging information in case jbd2_journal_dirty_metadata() is called with a buffer_head which didn't have jbd2_journal_get_write_access() called on it, or if the journal_head has the wrong transaction in it. In addition, return an error code. This won't change anything for ocfs2, which will BUG_ON() the non-zero exit code. For ext4, the caller of this function is ext4_handle_dirty_metadata(), and on seeing a non-zero return code, will call __ext4_journal_stop(), which will print the function and line number of the (buggy) calling function and abort the journal. This will allow us to recover instead of bug halting, which is better from a robustness and reliability point of view. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/ext4_jbd2.c8
-rw-r--r--fs/ext4/extents.c10
-rw-r--r--fs/jbd2/transaction.c58
3 files changed, 64 insertions, 12 deletions
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index f5240aa15601..aca179017582 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -109,9 +109,11 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
109 109
110 if (ext4_handle_valid(handle)) { 110 if (ext4_handle_valid(handle)) {
111 err = jbd2_journal_dirty_metadata(handle, bh); 111 err = jbd2_journal_dirty_metadata(handle, bh);
112 if (err) 112 if (err) {
113 ext4_journal_abort_handle(where, line, __func__, 113 /* Errors can only happen if there is a bug */
114 bh, handle, err); 114 handle->h_err = err;
115 __ext4_journal_stop(where, line, handle);
116 }
115 } else { 117 } else {
116 if (inode) 118 if (inode)
117 mark_buffer_dirty_inode(bh, inode); 119 mark_buffer_dirty_inode(bh, inode);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 9124cd24e093..2c5216a8d03b 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -96,13 +96,17 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
96 * - ENOMEM 96 * - ENOMEM
97 * - EIO 97 * - EIO
98 */ 98 */
99static int ext4_ext_dirty(handle_t *handle, struct inode *inode, 99#define ext4_ext_dirty(handle, inode, path) \
100 struct ext4_ext_path *path) 100 __ext4_ext_dirty(__func__, __LINE__, (handle), (inode), (path))
101static int __ext4_ext_dirty(const char *where, unsigned int line,
102 handle_t *handle, struct inode *inode,
103 struct ext4_ext_path *path)
101{ 104{
102 int err; 105 int err;
103 if (path->p_bh) { 106 if (path->p_bh) {
104 /* path points to block */ 107 /* path points to block */
105 err = ext4_handle_dirty_metadata(handle, inode, path->p_bh); 108 err = __ext4_handle_dirty_metadata(where, line, handle,
109 inode, path->p_bh);
106 } else { 110 } else {
107 /* path points to leaf/index in inode body */ 111 /* path points to leaf/index in inode body */
108 err = ext4_mark_inode_dirty(handle, inode); 112 err = ext4_mark_inode_dirty(handle, inode);
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 2d7109414cdd..cb56fe9aaabb 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1049,6 +1049,10 @@ void jbd2_buffer_abort_trigger(struct journal_head *jh,
1049 * mark dirty metadata which needs to be journaled as part of the current 1049 * mark dirty metadata which needs to be journaled as part of the current
1050 * transaction. 1050 * transaction.
1051 * 1051 *
1052 * The buffer must have previously had jbd2_journal_get_write_access()
1053 * called so that it has a valid journal_head attached to the buffer
1054 * head.
1055 *
1052 * The buffer is placed on the transaction's metadata list and is marked 1056 * The buffer is placed on the transaction's metadata list and is marked
1053 * as belonging to the transaction. 1057 * as belonging to the transaction.
1054 * 1058 *
@@ -1065,11 +1069,16 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
1065 transaction_t *transaction = handle->h_transaction; 1069 transaction_t *transaction = handle->h_transaction;
1066 journal_t *journal = transaction->t_journal; 1070 journal_t *journal = transaction->t_journal;
1067 struct journal_head *jh = bh2jh(bh); 1071 struct journal_head *jh = bh2jh(bh);
1072 int ret = 0;
1068 1073
1069 jbd_debug(5, "journal_head %p\n", jh); 1074 jbd_debug(5, "journal_head %p\n", jh);
1070 JBUFFER_TRACE(jh, "entry"); 1075 JBUFFER_TRACE(jh, "entry");
1071 if (is_handle_aborted(handle)) 1076 if (is_handle_aborted(handle))
1072 goto out; 1077 goto out;
1078 if (!buffer_jbd(bh)) {
1079 ret = -EUCLEAN;
1080 goto out;
1081 }
1073 1082
1074 jbd_lock_bh_state(bh); 1083 jbd_lock_bh_state(bh);
1075 1084
@@ -1093,8 +1102,20 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
1093 */ 1102 */
1094 if (jh->b_transaction == transaction && jh->b_jlist == BJ_Metadata) { 1103 if (jh->b_transaction == transaction && jh->b_jlist == BJ_Metadata) {
1095 JBUFFER_TRACE(jh, "fastpath"); 1104 JBUFFER_TRACE(jh, "fastpath");
1096 J_ASSERT_JH(jh, jh->b_transaction == 1105 if (unlikely(jh->b_transaction !=
1097 journal->j_running_transaction); 1106 journal->j_running_transaction)) {
1107 printk(KERN_EMERG "JBD: %s: "
1108 "jh->b_transaction (%llu, %p, %u) != "
1109 "journal->j_running_transaction (%p, %u)",
1110 journal->j_devname,
1111 (unsigned long long) bh->b_blocknr,
1112 jh->b_transaction,
1113 jh->b_transaction ? jh->b_transaction->t_tid : 0,
1114 journal->j_running_transaction,
1115 journal->j_running_transaction ?
1116 journal->j_running_transaction->t_tid : 0);
1117 ret = -EINVAL;
1118 }
1098 goto out_unlock_bh; 1119 goto out_unlock_bh;
1099 } 1120 }
1100 1121
@@ -1108,9 +1129,32 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
1108 */ 1129 */
1109 if (jh->b_transaction != transaction) { 1130 if (jh->b_transaction != transaction) {
1110 JBUFFER_TRACE(jh, "already on other transaction"); 1131 JBUFFER_TRACE(jh, "already on other transaction");
1111 J_ASSERT_JH(jh, jh->b_transaction == 1132 if (unlikely(jh->b_transaction !=
1112 journal->j_committing_transaction); 1133 journal->j_committing_transaction)) {
1113 J_ASSERT_JH(jh, jh->b_next_transaction == transaction); 1134 printk(KERN_EMERG "JBD: %s: "
1135 "jh->b_transaction (%llu, %p, %u) != "
1136 "journal->j_committing_transaction (%p, %u)",
1137 journal->j_devname,
1138 (unsigned long long) bh->b_blocknr,
1139 jh->b_transaction,
1140 jh->b_transaction ? jh->b_transaction->t_tid : 0,
1141 journal->j_committing_transaction,
1142 journal->j_committing_transaction ?
1143 journal->j_committing_transaction->t_tid : 0);
1144 ret = -EINVAL;
1145 }
1146 if (unlikely(jh->b_next_transaction != transaction)) {
1147 printk(KERN_EMERG "JBD: %s: "
1148 "jh->b_next_transaction (%llu, %p, %u) != "
1149 "transaction (%p, %u)",
1150 journal->j_devname,
1151 (unsigned long long) bh->b_blocknr,
1152 jh->b_next_transaction,
1153 jh->b_next_transaction ?
1154 jh->b_next_transaction->t_tid : 0,
1155 transaction, transaction->t_tid);
1156 ret = -EINVAL;
1157 }
1114 /* And this case is illegal: we can't reuse another 1158 /* And this case is illegal: we can't reuse another
1115 * transaction's data buffer, ever. */ 1159 * transaction's data buffer, ever. */
1116 goto out_unlock_bh; 1160 goto out_unlock_bh;
@@ -1127,7 +1171,9 @@ out_unlock_bh:
1127 jbd_unlock_bh_state(bh); 1171 jbd_unlock_bh_state(bh);
1128out: 1172out:
1129 JBUFFER_TRACE(jh, "exit"); 1173 JBUFFER_TRACE(jh, "exit");
1130 return 0; 1174 if (ret)
1175 __WARN(); /* All errors are bugs, so dump the stack */
1176 return ret;
1131} 1177}
1132 1178
1133/* 1179/*