diff options
author | Jan Kara <jack@suse.cz> | 2008-10-20 17:50:38 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2009-01-05 11:40:24 -0500 |
commit | 2205363dce7447b8e85f1ead14387664c1a98753 (patch) | |
tree | 729b2716f2e31bda2e035a11cc39aa5472dff2c4 /fs | |
parent | 171bf93ce11f4c9929fdce6ce63df8da2f3c4475 (diff) |
ocfs2: Implement quota recovery
Implement functions for recovery after a crash. Functions just
read local quota file and sync info to global quota file.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ocfs2/journal.c | 108 | ||||
-rw-r--r-- | fs/ocfs2/journal.h | 1 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2.h | 4 | ||||
-rw-r--r-- | fs/ocfs2/quota.h | 21 | ||||
-rw-r--r-- | fs/ocfs2/quota_global.c | 1 | ||||
-rw-r--r-- | fs/ocfs2/quota_local.c | 425 |
6 files changed, 528 insertions, 32 deletions
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 11a1178d5ee8..c60242018d9a 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include "slot_map.h" | 45 | #include "slot_map.h" |
46 | #include "super.h" | 46 | #include "super.h" |
47 | #include "sysfile.h" | 47 | #include "sysfile.h" |
48 | #include "quota.h" | ||
48 | 49 | ||
49 | #include "buffer_head_io.h" | 50 | #include "buffer_head_io.h" |
50 | 51 | ||
@@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock); | |||
52 | 53 | ||
53 | static int ocfs2_force_read_journal(struct inode *inode); | 54 | static int ocfs2_force_read_journal(struct inode *inode); |
54 | static int ocfs2_recover_node(struct ocfs2_super *osb, | 55 | static int ocfs2_recover_node(struct ocfs2_super *osb, |
55 | int node_num); | 56 | int node_num, int slot_num); |
56 | static int __ocfs2_recovery_thread(void *arg); | 57 | static int __ocfs2_recovery_thread(void *arg); |
57 | static int ocfs2_commit_cache(struct ocfs2_super *osb); | 58 | static int ocfs2_commit_cache(struct ocfs2_super *osb); |
58 | static int ocfs2_wait_on_mount(struct ocfs2_super *osb); | 59 | static int ocfs2_wait_on_mount(struct ocfs2_super *osb); |
@@ -857,6 +858,7 @@ struct ocfs2_la_recovery_item { | |||
857 | int lri_slot; | 858 | int lri_slot; |
858 | struct ocfs2_dinode *lri_la_dinode; | 859 | struct ocfs2_dinode *lri_la_dinode; |
859 | struct ocfs2_dinode *lri_tl_dinode; | 860 | struct ocfs2_dinode *lri_tl_dinode; |
861 | struct ocfs2_quota_recovery *lri_qrec; | ||
860 | }; | 862 | }; |
861 | 863 | ||
862 | /* Does the second half of the recovery process. By this point, the | 864 | /* Does the second half of the recovery process. By this point, the |
@@ -877,6 +879,7 @@ void ocfs2_complete_recovery(struct work_struct *work) | |||
877 | struct ocfs2_super *osb = journal->j_osb; | 879 | struct ocfs2_super *osb = journal->j_osb; |
878 | struct ocfs2_dinode *la_dinode, *tl_dinode; | 880 | struct ocfs2_dinode *la_dinode, *tl_dinode; |
879 | struct ocfs2_la_recovery_item *item, *n; | 881 | struct ocfs2_la_recovery_item *item, *n; |
882 | struct ocfs2_quota_recovery *qrec; | ||
880 | LIST_HEAD(tmp_la_list); | 883 | LIST_HEAD(tmp_la_list); |
881 | 884 | ||
882 | mlog_entry_void(); | 885 | mlog_entry_void(); |
@@ -922,6 +925,16 @@ void ocfs2_complete_recovery(struct work_struct *work) | |||
922 | if (ret < 0) | 925 | if (ret < 0) |
923 | mlog_errno(ret); | 926 | mlog_errno(ret); |
924 | 927 | ||
928 | qrec = item->lri_qrec; | ||
929 | if (qrec) { | ||
930 | mlog(0, "Recovering quota files"); | ||
931 | ret = ocfs2_finish_quota_recovery(osb, qrec, | ||
932 | item->lri_slot); | ||
933 | if (ret < 0) | ||
934 | mlog_errno(ret); | ||
935 | /* Recovery info is already freed now */ | ||
936 | } | ||
937 | |||
925 | kfree(item); | 938 | kfree(item); |
926 | } | 939 | } |
927 | 940 | ||
@@ -935,7 +948,8 @@ void ocfs2_complete_recovery(struct work_struct *work) | |||
935 | static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, | 948 | static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, |
936 | int slot_num, | 949 | int slot_num, |
937 | struct ocfs2_dinode *la_dinode, | 950 | struct ocfs2_dinode *la_dinode, |
938 | struct ocfs2_dinode *tl_dinode) | 951 | struct ocfs2_dinode *tl_dinode, |
952 | struct ocfs2_quota_recovery *qrec) | ||
939 | { | 953 | { |
940 | struct ocfs2_la_recovery_item *item; | 954 | struct ocfs2_la_recovery_item *item; |
941 | 955 | ||
@@ -950,6 +964,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, | |||
950 | if (tl_dinode) | 964 | if (tl_dinode) |
951 | kfree(tl_dinode); | 965 | kfree(tl_dinode); |
952 | 966 | ||
967 | if (qrec) | ||
968 | ocfs2_free_quota_recovery(qrec); | ||
969 | |||
953 | mlog_errno(-ENOMEM); | 970 | mlog_errno(-ENOMEM); |
954 | return; | 971 | return; |
955 | } | 972 | } |
@@ -958,6 +975,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, | |||
958 | item->lri_la_dinode = la_dinode; | 975 | item->lri_la_dinode = la_dinode; |
959 | item->lri_slot = slot_num; | 976 | item->lri_slot = slot_num; |
960 | item->lri_tl_dinode = tl_dinode; | 977 | item->lri_tl_dinode = tl_dinode; |
978 | item->lri_qrec = qrec; | ||
961 | 979 | ||
962 | spin_lock(&journal->j_lock); | 980 | spin_lock(&journal->j_lock); |
963 | list_add_tail(&item->lri_list, &journal->j_la_cleanups); | 981 | list_add_tail(&item->lri_list, &journal->j_la_cleanups); |
@@ -977,6 +995,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb) | |||
977 | ocfs2_queue_recovery_completion(journal, | 995 | ocfs2_queue_recovery_completion(journal, |
978 | osb->slot_num, | 996 | osb->slot_num, |
979 | osb->local_alloc_copy, | 997 | osb->local_alloc_copy, |
998 | NULL, | ||
980 | NULL); | 999 | NULL); |
981 | ocfs2_schedule_truncate_log_flush(osb, 0); | 1000 | ocfs2_schedule_truncate_log_flush(osb, 0); |
982 | 1001 | ||
@@ -985,11 +1004,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb) | |||
985 | } | 1004 | } |
986 | } | 1005 | } |
987 | 1006 | ||
1007 | void ocfs2_complete_quota_recovery(struct ocfs2_super *osb) | ||
1008 | { | ||
1009 | if (osb->quota_rec) { | ||
1010 | ocfs2_queue_recovery_completion(osb->journal, | ||
1011 | osb->slot_num, | ||
1012 | NULL, | ||
1013 | NULL, | ||
1014 | osb->quota_rec); | ||
1015 | osb->quota_rec = NULL; | ||
1016 | } | ||
1017 | } | ||
1018 | |||
988 | static int __ocfs2_recovery_thread(void *arg) | 1019 | static int __ocfs2_recovery_thread(void *arg) |
989 | { | 1020 | { |
990 | int status, node_num; | 1021 | int status, node_num, slot_num; |
991 | struct ocfs2_super *osb = arg; | 1022 | struct ocfs2_super *osb = arg; |
992 | struct ocfs2_recovery_map *rm = osb->recovery_map; | 1023 | struct ocfs2_recovery_map *rm = osb->recovery_map; |
1024 | int *rm_quota = NULL; | ||
1025 | int rm_quota_used = 0, i; | ||
1026 | struct ocfs2_quota_recovery *qrec; | ||
993 | 1027 | ||
994 | mlog_entry_void(); | 1028 | mlog_entry_void(); |
995 | 1029 | ||
@@ -998,6 +1032,11 @@ static int __ocfs2_recovery_thread(void *arg) | |||
998 | goto bail; | 1032 | goto bail; |
999 | } | 1033 | } |
1000 | 1034 | ||
1035 | rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS); | ||
1036 | if (!rm_quota) { | ||
1037 | status = -ENOMEM; | ||
1038 | goto bail; | ||
1039 | } | ||
1001 | restart: | 1040 | restart: |
1002 | status = ocfs2_super_lock(osb, 1); | 1041 | status = ocfs2_super_lock(osb, 1); |
1003 | if (status < 0) { | 1042 | if (status < 0) { |
@@ -1011,8 +1050,28 @@ restart: | |||
1011 | * clear it until ocfs2_recover_node() has succeeded. */ | 1050 | * clear it until ocfs2_recover_node() has succeeded. */ |
1012 | node_num = rm->rm_entries[0]; | 1051 | node_num = rm->rm_entries[0]; |
1013 | spin_unlock(&osb->osb_lock); | 1052 | spin_unlock(&osb->osb_lock); |
1014 | 1053 | mlog(0, "checking node %d\n", node_num); | |
1015 | status = ocfs2_recover_node(osb, node_num); | 1054 | slot_num = ocfs2_node_num_to_slot(osb, node_num); |
1055 | if (slot_num == -ENOENT) { | ||
1056 | status = 0; | ||
1057 | mlog(0, "no slot for this node, so no recovery" | ||
1058 | "required.\n"); | ||
1059 | goto skip_recovery; | ||
1060 | } | ||
1061 | mlog(0, "node %d was using slot %d\n", node_num, slot_num); | ||
1062 | |||
1063 | /* It is a bit subtle with quota recovery. We cannot do it | ||
1064 | * immediately because we have to obtain cluster locks from | ||
1065 | * quota files and we also don't want to just skip it because | ||
1066 | * then quota usage would be out of sync until some node takes | ||
1067 | * the slot. So we remember which nodes need quota recovery | ||
1068 | * and when everything else is done, we recover quotas. */ | ||
1069 | for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++); | ||
1070 | if (i == rm_quota_used) | ||
1071 | rm_quota[rm_quota_used++] = slot_num; | ||
1072 | |||
1073 | status = ocfs2_recover_node(osb, node_num, slot_num); | ||
1074 | skip_recovery: | ||
1016 | if (!status) { | 1075 | if (!status) { |
1017 | ocfs2_recovery_map_clear(osb, node_num); | 1076 | ocfs2_recovery_map_clear(osb, node_num); |
1018 | } else { | 1077 | } else { |
@@ -1034,13 +1093,27 @@ restart: | |||
1034 | if (status < 0) | 1093 | if (status < 0) |
1035 | mlog_errno(status); | 1094 | mlog_errno(status); |
1036 | 1095 | ||
1096 | /* Now it is right time to recover quotas... We have to do this under | ||
1097 | * superblock lock so that noone can start using the slot (and crash) | ||
1098 | * before we recover it */ | ||
1099 | for (i = 0; i < rm_quota_used; i++) { | ||
1100 | qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]); | ||
1101 | if (IS_ERR(qrec)) { | ||
1102 | status = PTR_ERR(qrec); | ||
1103 | mlog_errno(status); | ||
1104 | continue; | ||
1105 | } | ||
1106 | ocfs2_queue_recovery_completion(osb->journal, rm_quota[i], | ||
1107 | NULL, NULL, qrec); | ||
1108 | } | ||
1109 | |||
1037 | ocfs2_super_unlock(osb, 1); | 1110 | ocfs2_super_unlock(osb, 1); |
1038 | 1111 | ||
1039 | /* We always run recovery on our own orphan dir - the dead | 1112 | /* We always run recovery on our own orphan dir - the dead |
1040 | * node(s) may have disallowd a previos inode delete. Re-processing | 1113 | * node(s) may have disallowd a previos inode delete. Re-processing |
1041 | * is therefore required. */ | 1114 | * is therefore required. */ |
1042 | ocfs2_queue_recovery_completion(osb->journal, osb->slot_num, NULL, | 1115 | ocfs2_queue_recovery_completion(osb->journal, osb->slot_num, NULL, |
1043 | NULL); | 1116 | NULL, NULL); |
1044 | 1117 | ||
1045 | bail: | 1118 | bail: |
1046 | mutex_lock(&osb->recovery_lock); | 1119 | mutex_lock(&osb->recovery_lock); |
@@ -1055,6 +1128,9 @@ bail: | |||
1055 | 1128 | ||
1056 | mutex_unlock(&osb->recovery_lock); | 1129 | mutex_unlock(&osb->recovery_lock); |
1057 | 1130 | ||
1131 | if (rm_quota) | ||
1132 | kfree(rm_quota); | ||
1133 | |||
1058 | mlog_exit(status); | 1134 | mlog_exit(status); |
1059 | /* no one is callint kthread_stop() for us so the kthread() api | 1135 | /* no one is callint kthread_stop() for us so the kthread() api |
1060 | * requires that we call do_exit(). And it isn't exported, but | 1136 | * requires that we call do_exit(). And it isn't exported, but |
@@ -1282,31 +1358,19 @@ done: | |||
1282 | * far less concerning. | 1358 | * far less concerning. |
1283 | */ | 1359 | */ |
1284 | static int ocfs2_recover_node(struct ocfs2_super *osb, | 1360 | static int ocfs2_recover_node(struct ocfs2_super *osb, |
1285 | int node_num) | 1361 | int node_num, int slot_num) |
1286 | { | 1362 | { |
1287 | int status = 0; | 1363 | int status = 0; |
1288 | int slot_num; | ||
1289 | struct ocfs2_dinode *la_copy = NULL; | 1364 | struct ocfs2_dinode *la_copy = NULL; |
1290 | struct ocfs2_dinode *tl_copy = NULL; | 1365 | struct ocfs2_dinode *tl_copy = NULL; |
1291 | 1366 | ||
1292 | mlog_entry("(node_num=%d, osb->node_num = %d)\n", | 1367 | mlog_entry("(node_num=%d, slot_num=%d, osb->node_num = %d)\n", |
1293 | node_num, osb->node_num); | 1368 | node_num, slot_num, osb->node_num); |
1294 | |||
1295 | mlog(0, "checking node %d\n", node_num); | ||
1296 | 1369 | ||
1297 | /* Should not ever be called to recover ourselves -- in that | 1370 | /* Should not ever be called to recover ourselves -- in that |
1298 | * case we should've called ocfs2_journal_load instead. */ | 1371 | * case we should've called ocfs2_journal_load instead. */ |
1299 | BUG_ON(osb->node_num == node_num); | 1372 | BUG_ON(osb->node_num == node_num); |
1300 | 1373 | ||
1301 | slot_num = ocfs2_node_num_to_slot(osb, node_num); | ||
1302 | if (slot_num == -ENOENT) { | ||
1303 | status = 0; | ||
1304 | mlog(0, "no slot for this node, so no recovery required.\n"); | ||
1305 | goto done; | ||
1306 | } | ||
1307 | |||
1308 | mlog(0, "node %d was using slot %d\n", node_num, slot_num); | ||
1309 | |||
1310 | status = ocfs2_replay_journal(osb, node_num, slot_num); | 1374 | status = ocfs2_replay_journal(osb, node_num, slot_num); |
1311 | if (status < 0) { | 1375 | if (status < 0) { |
1312 | if (status == -EBUSY) { | 1376 | if (status == -EBUSY) { |
@@ -1342,7 +1406,7 @@ static int ocfs2_recover_node(struct ocfs2_super *osb, | |||
1342 | 1406 | ||
1343 | /* This will kfree the memory pointed to by la_copy and tl_copy */ | 1407 | /* This will kfree the memory pointed to by la_copy and tl_copy */ |
1344 | ocfs2_queue_recovery_completion(osb->journal, slot_num, la_copy, | 1408 | ocfs2_queue_recovery_completion(osb->journal, slot_num, la_copy, |
1345 | tl_copy); | 1409 | tl_copy, NULL); |
1346 | 1410 | ||
1347 | status = 0; | 1411 | status = 0; |
1348 | done: | 1412 | done: |
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index ee08e9c1fc12..37013bf9ce28 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h | |||
@@ -168,6 +168,7 @@ void ocfs2_recovery_thread(struct ocfs2_super *osb, | |||
168 | int node_num); | 168 | int node_num); |
169 | int ocfs2_mark_dead_nodes(struct ocfs2_super *osb); | 169 | int ocfs2_mark_dead_nodes(struct ocfs2_super *osb); |
170 | void ocfs2_complete_mount_recovery(struct ocfs2_super *osb); | 170 | void ocfs2_complete_mount_recovery(struct ocfs2_super *osb); |
171 | void ocfs2_complete_quota_recovery(struct ocfs2_super *osb); | ||
171 | 172 | ||
172 | static inline void ocfs2_start_checkpoint(struct ocfs2_super *osb) | 173 | static inline void ocfs2_start_checkpoint(struct ocfs2_super *osb) |
173 | { | 174 | { |
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index f04b229fc757..6b25b4aa7205 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h | |||
@@ -206,6 +206,7 @@ enum ocfs2_mount_options | |||
206 | struct ocfs2_journal; | 206 | struct ocfs2_journal; |
207 | struct ocfs2_slot_info; | 207 | struct ocfs2_slot_info; |
208 | struct ocfs2_recovery_map; | 208 | struct ocfs2_recovery_map; |
209 | struct ocfs2_quota_recovery; | ||
209 | struct ocfs2_super | 210 | struct ocfs2_super |
210 | { | 211 | { |
211 | struct task_struct *commit_task; | 212 | struct task_struct *commit_task; |
@@ -287,10 +288,11 @@ struct ocfs2_super | |||
287 | char *local_alloc_debug_buf; | 288 | char *local_alloc_debug_buf; |
288 | #endif | 289 | #endif |
289 | 290 | ||
290 | /* Next two fields are for local node slot recovery during | 291 | /* Next three fields are for local node slot recovery during |
291 | * mount. */ | 292 | * mount. */ |
292 | int dirty; | 293 | int dirty; |
293 | struct ocfs2_dinode *local_alloc_copy; | 294 | struct ocfs2_dinode *local_alloc_copy; |
295 | struct ocfs2_quota_recovery *quota_rec; | ||
294 | 296 | ||
295 | struct ocfs2_alloc_stats alloc_stats; | 297 | struct ocfs2_alloc_stats alloc_stats; |
296 | char dev_str[20]; /* "major,minor" of the device */ | 298 | char dev_str[20]; /* "major,minor" of the device */ |
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h index e2233d51507f..04872b45b990 100644 --- a/fs/ocfs2/quota.h +++ b/fs/ocfs2/quota.h | |||
@@ -33,6 +33,17 @@ struct ocfs2_dquot { | |||
33 | s64 dq_originodes; /* Last globally synced inode usage */ | 33 | s64 dq_originodes; /* Last globally synced inode usage */ |
34 | }; | 34 | }; |
35 | 35 | ||
36 | /* Description of one chunk to recover in memory */ | ||
37 | struct ocfs2_recovery_chunk { | ||
38 | struct list_head rc_list; /* List of chunks */ | ||
39 | int rc_chunk; /* Chunk number */ | ||
40 | unsigned long *rc_bitmap; /* Bitmap of entries to recover */ | ||
41 | }; | ||
42 | |||
43 | struct ocfs2_quota_recovery { | ||
44 | struct list_head r_list[MAXQUOTAS]; /* List of chunks to recover */ | ||
45 | }; | ||
46 | |||
36 | /* In-memory structure with quota header information */ | 47 | /* In-memory structure with quota header information */ |
37 | struct ocfs2_mem_dqinfo { | 48 | struct ocfs2_mem_dqinfo { |
38 | unsigned int dqi_type; /* Quota type this structure describes */ | 49 | unsigned int dqi_type; /* Quota type this structure describes */ |
@@ -49,6 +60,10 @@ struct ocfs2_mem_dqinfo { | |||
49 | struct buffer_head *dqi_ibh; /* Buffer with information header */ | 60 | struct buffer_head *dqi_ibh; /* Buffer with information header */ |
50 | struct qtree_mem_dqinfo dqi_gi; /* Info about global file */ | 61 | struct qtree_mem_dqinfo dqi_gi; /* Info about global file */ |
51 | struct delayed_work dqi_sync_work; /* Work for syncing dquots */ | 62 | struct delayed_work dqi_sync_work; /* Work for syncing dquots */ |
63 | struct ocfs2_quota_recovery *dqi_rec; /* Pointer to recovery | ||
64 | * information, in case we | ||
65 | * enable quotas on file | ||
66 | * needing it */ | ||
52 | }; | 67 | }; |
53 | 68 | ||
54 | static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot) | 69 | static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot) |
@@ -67,6 +82,12 @@ extern struct kmem_cache *ocfs2_qf_chunk_cachep; | |||
67 | 82 | ||
68 | extern struct qtree_fmt_operations ocfs2_global_ops; | 83 | extern struct qtree_fmt_operations ocfs2_global_ops; |
69 | 84 | ||
85 | struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery( | ||
86 | struct ocfs2_super *osb, int slot_num); | ||
87 | int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, | ||
88 | struct ocfs2_quota_recovery *rec, | ||
89 | int slot_num); | ||
90 | void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec); | ||
70 | ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data, | 91 | ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data, |
71 | size_t len, loff_t off); | 92 | size_t len, loff_t off); |
72 | ssize_t ocfs2_quota_write(struct super_block *sb, int type, | 93 | ssize_t ocfs2_quota_write(struct super_block *sb, int type, |
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index adf53508bdb8..49b536a2190d 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c | |||
@@ -87,7 +87,6 @@ struct qtree_fmt_operations ocfs2_global_ops = { | |||
87 | .is_id = ocfs2_global_is_id, | 87 | .is_id = ocfs2_global_is_id, |
88 | }; | 88 | }; |
89 | 89 | ||
90 | |||
91 | struct buffer_head *ocfs2_read_quota_block(struct inode *inode, | 90 | struct buffer_head *ocfs2_read_quota_block(struct inode *inode, |
92 | int block, int *err) | 91 | int block, int *err) |
93 | { | 92 | { |
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 40e82b483136..b98562174cd0 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c | |||
@@ -49,14 +49,25 @@ static unsigned int ol_quota_chunk_block(struct super_block *sb, int c) | |||
49 | return 1 + (ol_chunk_blocks(sb) + 1) * c; | 49 | return 1 + (ol_chunk_blocks(sb) + 1) * c; |
50 | } | 50 | } |
51 | 51 | ||
52 | /* Offset of the dquot structure in the quota file */ | 52 | static unsigned int ol_dqblk_block(struct super_block *sb, int c, int off) |
53 | static loff_t ol_dqblk_off(struct super_block *sb, int c, int off) | 53 | { |
54 | int epb = ol_quota_entries_per_block(sb); | ||
55 | |||
56 | return ol_quota_chunk_block(sb, c) + 1 + off / epb; | ||
57 | } | ||
58 | |||
59 | static unsigned int ol_dqblk_block_off(struct super_block *sb, int c, int off) | ||
54 | { | 60 | { |
55 | int epb = ol_quota_entries_per_block(sb); | 61 | int epb = ol_quota_entries_per_block(sb); |
56 | 62 | ||
57 | return ((ol_quota_chunk_block(sb, c) + 1 + off / epb) | 63 | return (off % epb) * sizeof(struct ocfs2_local_disk_dqblk); |
58 | << sb->s_blocksize_bits) + | 64 | } |
59 | (off % epb) * sizeof(struct ocfs2_local_disk_dqblk); | 65 | |
66 | /* Offset of the dquot structure in the quota file */ | ||
67 | static loff_t ol_dqblk_off(struct super_block *sb, int c, int off) | ||
68 | { | ||
69 | return (ol_dqblk_block(sb, c, off) << sb->s_blocksize_bits) + | ||
70 | ol_dqblk_block_off(sb, c, off); | ||
60 | } | 71 | } |
61 | 72 | ||
62 | /* Compute block number from given offset */ | 73 | /* Compute block number from given offset */ |
@@ -253,6 +264,379 @@ static void olq_update_info(struct buffer_head *bh, void *private) | |||
253 | spin_unlock(&dq_data_lock); | 264 | spin_unlock(&dq_data_lock); |
254 | } | 265 | } |
255 | 266 | ||
267 | static int ocfs2_add_recovery_chunk(struct super_block *sb, | ||
268 | struct ocfs2_local_disk_chunk *dchunk, | ||
269 | int chunk, | ||
270 | struct list_head *head) | ||
271 | { | ||
272 | struct ocfs2_recovery_chunk *rc; | ||
273 | |||
274 | rc = kmalloc(sizeof(struct ocfs2_recovery_chunk), GFP_NOFS); | ||
275 | if (!rc) | ||
276 | return -ENOMEM; | ||
277 | rc->rc_chunk = chunk; | ||
278 | rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); | ||
279 | if (!rc->rc_bitmap) { | ||
280 | kfree(rc); | ||
281 | return -ENOMEM; | ||
282 | } | ||
283 | memcpy(rc->rc_bitmap, dchunk->dqc_bitmap, | ||
284 | (ol_chunk_entries(sb) + 7) >> 3); | ||
285 | list_add_tail(&rc->rc_list, head); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static void free_recovery_list(struct list_head *head) | ||
290 | { | ||
291 | struct ocfs2_recovery_chunk *next; | ||
292 | struct ocfs2_recovery_chunk *rchunk; | ||
293 | |||
294 | list_for_each_entry_safe(rchunk, next, head, rc_list) { | ||
295 | list_del(&rchunk->rc_list); | ||
296 | kfree(rchunk->rc_bitmap); | ||
297 | kfree(rchunk); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec) | ||
302 | { | ||
303 | int type; | ||
304 | |||
305 | for (type = 0; type < MAXQUOTAS; type++) | ||
306 | free_recovery_list(&(rec->r_list[type])); | ||
307 | kfree(rec); | ||
308 | } | ||
309 | |||
310 | /* Load entries in our quota file we have to recover*/ | ||
311 | static int ocfs2_recovery_load_quota(struct inode *lqinode, | ||
312 | struct ocfs2_local_disk_dqinfo *ldinfo, | ||
313 | int type, | ||
314 | struct list_head *head) | ||
315 | { | ||
316 | struct super_block *sb = lqinode->i_sb; | ||
317 | struct buffer_head *hbh; | ||
318 | struct ocfs2_local_disk_chunk *dchunk; | ||
319 | int i, chunks = le32_to_cpu(ldinfo->dqi_chunks); | ||
320 | int status = 0; | ||
321 | |||
322 | for (i = 0; i < chunks; i++) { | ||
323 | hbh = ocfs2_read_quota_block(lqinode, | ||
324 | ol_quota_chunk_block(sb, i), | ||
325 | &status); | ||
326 | if (!hbh) { | ||
327 | mlog_errno(status); | ||
328 | break; | ||
329 | } | ||
330 | dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; | ||
331 | if (le32_to_cpu(dchunk->dqc_free) < ol_chunk_entries(sb)) | ||
332 | status = ocfs2_add_recovery_chunk(sb, dchunk, i, head); | ||
333 | brelse(hbh); | ||
334 | if (status < 0) | ||
335 | break; | ||
336 | } | ||
337 | if (status < 0) | ||
338 | free_recovery_list(head); | ||
339 | return status; | ||
340 | } | ||
341 | |||
342 | static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void) | ||
343 | { | ||
344 | int type; | ||
345 | struct ocfs2_quota_recovery *rec; | ||
346 | |||
347 | rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS); | ||
348 | if (!rec) | ||
349 | return NULL; | ||
350 | for (type = 0; type < MAXQUOTAS; type++) | ||
351 | INIT_LIST_HEAD(&(rec->r_list[type])); | ||
352 | return rec; | ||
353 | } | ||
354 | |||
355 | /* Load information we need for quota recovery into memory */ | ||
356 | struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery( | ||
357 | struct ocfs2_super *osb, | ||
358 | int slot_num) | ||
359 | { | ||
360 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
361 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
362 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
363 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
364 | struct super_block *sb = osb->sb; | ||
365 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
366 | struct inode *lqinode; | ||
367 | struct buffer_head *bh; | ||
368 | int type; | ||
369 | int status = 0; | ||
370 | struct ocfs2_quota_recovery *rec; | ||
371 | |||
372 | mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num); | ||
373 | rec = ocfs2_alloc_quota_recovery(); | ||
374 | if (!rec) | ||
375 | return ERR_PTR(-ENOMEM); | ||
376 | /* First init... */ | ||
377 | |||
378 | for (type = 0; type < MAXQUOTAS; type++) { | ||
379 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
380 | continue; | ||
381 | /* At this point, journal of the slot is already replayed so | ||
382 | * we can trust metadata and data of the quota file */ | ||
383 | lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num); | ||
384 | if (!lqinode) { | ||
385 | status = -ENOENT; | ||
386 | goto out; | ||
387 | } | ||
388 | status = ocfs2_inode_lock_full(lqinode, NULL, 1, | ||
389 | OCFS2_META_LOCK_RECOVERY); | ||
390 | if (status < 0) { | ||
391 | mlog_errno(status); | ||
392 | goto out_put; | ||
393 | } | ||
394 | /* Now read local header */ | ||
395 | bh = ocfs2_read_quota_block(lqinode, 0, &status); | ||
396 | if (!bh) { | ||
397 | mlog_errno(status); | ||
398 | mlog(ML_ERROR, "failed to read quota file info header " | ||
399 | "(slot=%d type=%d)\n", slot_num, type); | ||
400 | goto out_lock; | ||
401 | } | ||
402 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
403 | OCFS2_LOCAL_INFO_OFF); | ||
404 | status = ocfs2_recovery_load_quota(lqinode, ldinfo, type, | ||
405 | &rec->r_list[type]); | ||
406 | brelse(bh); | ||
407 | out_lock: | ||
408 | ocfs2_inode_unlock(lqinode, 1); | ||
409 | out_put: | ||
410 | iput(lqinode); | ||
411 | if (status < 0) | ||
412 | break; | ||
413 | } | ||
414 | out: | ||
415 | if (status < 0) { | ||
416 | ocfs2_free_quota_recovery(rec); | ||
417 | rec = ERR_PTR(status); | ||
418 | } | ||
419 | return rec; | ||
420 | } | ||
421 | |||
422 | /* Sync changes in local quota file into global quota file and | ||
423 | * reinitialize local quota file. | ||
424 | * The function expects local quota file to be already locked and | ||
425 | * dqonoff_mutex locked. */ | ||
426 | static int ocfs2_recover_local_quota_file(struct inode *lqinode, | ||
427 | int type, | ||
428 | struct ocfs2_quota_recovery *rec) | ||
429 | { | ||
430 | struct super_block *sb = lqinode->i_sb; | ||
431 | struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
432 | struct ocfs2_local_disk_chunk *dchunk; | ||
433 | struct ocfs2_local_disk_dqblk *dqblk; | ||
434 | struct dquot *dquot; | ||
435 | handle_t *handle; | ||
436 | struct buffer_head *hbh = NULL, *qbh = NULL; | ||
437 | int status = 0; | ||
438 | int bit, chunk; | ||
439 | struct ocfs2_recovery_chunk *rchunk, *next; | ||
440 | qsize_t spacechange, inodechange; | ||
441 | |||
442 | mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type); | ||
443 | |||
444 | status = ocfs2_lock_global_qf(oinfo, 1); | ||
445 | if (status < 0) | ||
446 | goto out; | ||
447 | |||
448 | list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) { | ||
449 | chunk = rchunk->rc_chunk; | ||
450 | hbh = ocfs2_read_quota_block(lqinode, | ||
451 | ol_quota_chunk_block(sb, chunk), | ||
452 | &status); | ||
453 | if (!hbh) { | ||
454 | mlog_errno(status); | ||
455 | break; | ||
456 | } | ||
457 | dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; | ||
458 | for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) { | ||
459 | qbh = ocfs2_read_quota_block(lqinode, | ||
460 | ol_dqblk_block(sb, chunk, bit), | ||
461 | &status); | ||
462 | if (!qbh) { | ||
463 | mlog_errno(status); | ||
464 | break; | ||
465 | } | ||
466 | dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data + | ||
467 | ol_dqblk_block_off(sb, chunk, bit)); | ||
468 | dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type); | ||
469 | if (!dquot) { | ||
470 | status = -EIO; | ||
471 | mlog(ML_ERROR, "Failed to get quota structure " | ||
472 | "for id %u, type %d. Cannot finish quota " | ||
473 | "file recovery.\n", | ||
474 | (unsigned)le64_to_cpu(dqblk->dqb_id), | ||
475 | type); | ||
476 | goto out_put_bh; | ||
477 | } | ||
478 | handle = ocfs2_start_trans(OCFS2_SB(sb), | ||
479 | OCFS2_QSYNC_CREDITS); | ||
480 | if (IS_ERR(handle)) { | ||
481 | status = PTR_ERR(handle); | ||
482 | mlog_errno(status); | ||
483 | goto out_put_dquot; | ||
484 | } | ||
485 | mutex_lock(&sb_dqopt(sb)->dqio_mutex); | ||
486 | spin_lock(&dq_data_lock); | ||
487 | /* Add usage from quota entry into quota changes | ||
488 | * of our node. Auxiliary variables are important | ||
489 | * due to signedness */ | ||
490 | spacechange = le64_to_cpu(dqblk->dqb_spacemod); | ||
491 | inodechange = le64_to_cpu(dqblk->dqb_inodemod); | ||
492 | dquot->dq_dqb.dqb_curspace += spacechange; | ||
493 | dquot->dq_dqb.dqb_curinodes += inodechange; | ||
494 | spin_unlock(&dq_data_lock); | ||
495 | /* We want to drop reference held by the crashed | ||
496 | * node. Since we have our own reference we know | ||
497 | * global structure actually won't be freed. */ | ||
498 | status = ocfs2_global_release_dquot(dquot); | ||
499 | if (status < 0) { | ||
500 | mlog_errno(status); | ||
501 | goto out_commit; | ||
502 | } | ||
503 | /* Release local quota file entry */ | ||
504 | status = ocfs2_journal_access(handle, lqinode, | ||
505 | qbh, OCFS2_JOURNAL_ACCESS_WRITE); | ||
506 | if (status < 0) { | ||
507 | mlog_errno(status); | ||
508 | goto out_commit; | ||
509 | } | ||
510 | lock_buffer(qbh); | ||
511 | WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap)); | ||
512 | ocfs2_clear_bit(bit, dchunk->dqc_bitmap); | ||
513 | le32_add_cpu(&dchunk->dqc_free, 1); | ||
514 | unlock_buffer(qbh); | ||
515 | status = ocfs2_journal_dirty(handle, qbh); | ||
516 | if (status < 0) | ||
517 | mlog_errno(status); | ||
518 | out_commit: | ||
519 | mutex_unlock(&sb_dqopt(sb)->dqio_mutex); | ||
520 | ocfs2_commit_trans(OCFS2_SB(sb), handle); | ||
521 | out_put_dquot: | ||
522 | dqput(dquot); | ||
523 | out_put_bh: | ||
524 | brelse(qbh); | ||
525 | if (status < 0) | ||
526 | break; | ||
527 | } | ||
528 | brelse(hbh); | ||
529 | list_del(&rchunk->rc_list); | ||
530 | kfree(rchunk->rc_bitmap); | ||
531 | kfree(rchunk); | ||
532 | if (status < 0) | ||
533 | break; | ||
534 | } | ||
535 | ocfs2_unlock_global_qf(oinfo, 1); | ||
536 | out: | ||
537 | if (status < 0) | ||
538 | free_recovery_list(&(rec->r_list[type])); | ||
539 | mlog_exit(status); | ||
540 | return status; | ||
541 | } | ||
542 | |||
543 | /* Recover local quota files for given node different from us */ | ||
544 | int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, | ||
545 | struct ocfs2_quota_recovery *rec, | ||
546 | int slot_num) | ||
547 | { | ||
548 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
549 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
550 | struct super_block *sb = osb->sb; | ||
551 | struct ocfs2_local_disk_dqinfo *ldinfo; | ||
552 | struct buffer_head *bh; | ||
553 | handle_t *handle; | ||
554 | int type; | ||
555 | int status = 0; | ||
556 | struct inode *lqinode; | ||
557 | unsigned int flags; | ||
558 | |||
559 | mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num); | ||
560 | mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); | ||
561 | for (type = 0; type < MAXQUOTAS; type++) { | ||
562 | if (list_empty(&(rec->r_list[type]))) | ||
563 | continue; | ||
564 | mlog(0, "Recovering quota in slot %d\n", slot_num); | ||
565 | lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num); | ||
566 | if (!lqinode) { | ||
567 | status = -ENOENT; | ||
568 | goto out; | ||
569 | } | ||
570 | status = ocfs2_inode_lock_full(lqinode, NULL, 1, | ||
571 | OCFS2_META_LOCK_NOQUEUE); | ||
572 | /* Someone else is holding the lock? Then he must be | ||
573 | * doing the recovery. Just skip the file... */ | ||
574 | if (status == -EAGAIN) { | ||
575 | mlog(ML_NOTICE, "skipping quota recovery for slot %d " | ||
576 | "because quota file is locked.\n", slot_num); | ||
577 | status = 0; | ||
578 | goto out_put; | ||
579 | } else if (status < 0) { | ||
580 | mlog_errno(status); | ||
581 | goto out_put; | ||
582 | } | ||
583 | /* Now read local header */ | ||
584 | bh = ocfs2_read_quota_block(lqinode, 0, &status); | ||
585 | if (!bh) { | ||
586 | mlog_errno(status); | ||
587 | mlog(ML_ERROR, "failed to read quota file info header " | ||
588 | "(slot=%d type=%d)\n", slot_num, type); | ||
589 | goto out_lock; | ||
590 | } | ||
591 | ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + | ||
592 | OCFS2_LOCAL_INFO_OFF); | ||
593 | /* Is recovery still needed? */ | ||
594 | flags = le32_to_cpu(ldinfo->dqi_flags); | ||
595 | if (!(flags & OLQF_CLEAN)) | ||
596 | status = ocfs2_recover_local_quota_file(lqinode, | ||
597 | type, | ||
598 | rec); | ||
599 | /* We don't want to mark file as clean when it is actually | ||
600 | * active */ | ||
601 | if (slot_num == osb->slot_num) | ||
602 | goto out_bh; | ||
603 | /* Mark quota file as clean if we are recovering quota file of | ||
604 | * some other node. */ | ||
605 | handle = ocfs2_start_trans(osb, 1); | ||
606 | if (IS_ERR(handle)) { | ||
607 | status = PTR_ERR(handle); | ||
608 | mlog_errno(status); | ||
609 | goto out_bh; | ||
610 | } | ||
611 | status = ocfs2_journal_access(handle, lqinode, bh, | ||
612 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
613 | if (status < 0) { | ||
614 | mlog_errno(status); | ||
615 | goto out_trans; | ||
616 | } | ||
617 | lock_buffer(bh); | ||
618 | ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN); | ||
619 | unlock_buffer(bh); | ||
620 | status = ocfs2_journal_dirty(handle, bh); | ||
621 | if (status < 0) | ||
622 | mlog_errno(status); | ||
623 | out_trans: | ||
624 | ocfs2_commit_trans(osb, handle); | ||
625 | out_bh: | ||
626 | brelse(bh); | ||
627 | out_lock: | ||
628 | ocfs2_inode_unlock(lqinode, 1); | ||
629 | out_put: | ||
630 | iput(lqinode); | ||
631 | if (status < 0) | ||
632 | break; | ||
633 | } | ||
634 | out: | ||
635 | mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); | ||
636 | kfree(rec); | ||
637 | return status; | ||
638 | } | ||
639 | |||
256 | /* Read information header from quota file */ | 640 | /* Read information header from quota file */ |
257 | static int ocfs2_local_read_info(struct super_block *sb, int type) | 641 | static int ocfs2_local_read_info(struct super_block *sb, int type) |
258 | { | 642 | { |
@@ -262,6 +646,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) | |||
262 | struct inode *lqinode = sb_dqopt(sb)->files[type]; | 646 | struct inode *lqinode = sb_dqopt(sb)->files[type]; |
263 | int status; | 647 | int status; |
264 | struct buffer_head *bh = NULL; | 648 | struct buffer_head *bh = NULL; |
649 | struct ocfs2_quota_recovery *rec; | ||
265 | int locked = 0; | 650 | int locked = 0; |
266 | 651 | ||
267 | info->dqi_maxblimit = 0x7fffffffffffffffLL; | 652 | info->dqi_maxblimit = 0x7fffffffffffffffLL; |
@@ -275,6 +660,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) | |||
275 | info->dqi_priv = oinfo; | 660 | info->dqi_priv = oinfo; |
276 | oinfo->dqi_type = type; | 661 | oinfo->dqi_type = type; |
277 | INIT_LIST_HEAD(&oinfo->dqi_chunk); | 662 | INIT_LIST_HEAD(&oinfo->dqi_chunk); |
663 | oinfo->dqi_rec = NULL; | ||
278 | oinfo->dqi_lqi_bh = NULL; | 664 | oinfo->dqi_lqi_bh = NULL; |
279 | oinfo->dqi_ibh = NULL; | 665 | oinfo->dqi_ibh = NULL; |
280 | 666 | ||
@@ -305,10 +691,27 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) | |||
305 | oinfo->dqi_ibh = bh; | 691 | oinfo->dqi_ibh = bh; |
306 | 692 | ||
307 | /* We crashed when using local quota file? */ | 693 | /* We crashed when using local quota file? */ |
308 | if (!(info->dqi_flags & OLQF_CLEAN)) | 694 | if (!(info->dqi_flags & OLQF_CLEAN)) { |
309 | goto out_err; /* So far we just bail out. Later we should resync here */ | 695 | rec = OCFS2_SB(sb)->quota_rec; |
696 | if (!rec) { | ||
697 | rec = ocfs2_alloc_quota_recovery(); | ||
698 | if (!rec) { | ||
699 | status = -ENOMEM; | ||
700 | mlog_errno(status); | ||
701 | goto out_err; | ||
702 | } | ||
703 | OCFS2_SB(sb)->quota_rec = rec; | ||
704 | } | ||
310 | 705 | ||
311 | status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type], | 706 | status = ocfs2_recovery_load_quota(lqinode, ldinfo, type, |
707 | &rec->r_list[type]); | ||
708 | if (status < 0) { | ||
709 | mlog_errno(status); | ||
710 | goto out_err; | ||
711 | } | ||
712 | } | ||
713 | |||
714 | status = ocfs2_load_local_quota_bitmaps(lqinode, | ||
312 | ldinfo, | 715 | ldinfo, |
313 | &oinfo->dqi_chunk); | 716 | &oinfo->dqi_chunk); |
314 | if (status < 0) { | 717 | if (status < 0) { |
@@ -394,6 +797,12 @@ static int ocfs2_local_free_info(struct super_block *sb, int type) | |||
394 | } | 797 | } |
395 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); | 798 | ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); |
396 | 799 | ||
800 | /* dqonoff_mutex protects us against racing with recovery thread... */ | ||
801 | if (oinfo->dqi_rec) { | ||
802 | ocfs2_free_quota_recovery(oinfo->dqi_rec); | ||
803 | mark_clean = 0; | ||
804 | } | ||
805 | |||
397 | if (!mark_clean) | 806 | if (!mark_clean) |
398 | goto out; | 807 | goto out; |
399 | 808 | ||