diff options
author | Richard Purdie <rpurdie@rpsys.net> | 2008-01-29 06:27:11 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2008-02-03 02:14:41 -0500 |
commit | 6ce0a856c10c8ab8568764436864616efa88e908 (patch) | |
tree | 39c6b56a261c7585357386f42b32d773490bcf84 /drivers/mtd/mtdoops.c | |
parent | 2986bd2a330e57af53c016276d63d46ded10e9c5 (diff) |
[MTD] mtdoops: Perform write operations in a workqueue
Writing to the flash needs to be done in a workqueue. The console
write functions may be called in any context which can lead to
lockups otherwise.
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/mtdoops.c')
-rw-r--r-- | drivers/mtd/mtdoops.c | 82 |
1 files changed, 42 insertions, 40 deletions
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 723ca76d6b55..72c434c61b0a 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c | |||
@@ -32,9 +32,10 @@ | |||
32 | 32 | ||
33 | #define OOPS_PAGE_SIZE 4096 | 33 | #define OOPS_PAGE_SIZE 4096 |
34 | 34 | ||
35 | static struct mtdoops_context { | 35 | struct mtdoops_context { |
36 | int mtd_index; | 36 | int mtd_index; |
37 | struct work_struct work; | 37 | struct work_struct work_erase; |
38 | struct work_struct work_write; | ||
38 | struct mtd_info *mtd; | 39 | struct mtd_info *mtd; |
39 | int oops_pages; | 40 | int oops_pages; |
40 | int nextpage; | 41 | int nextpage; |
@@ -87,7 +88,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) | |||
87 | return 0; | 88 | return 0; |
88 | } | 89 | } |
89 | 90 | ||
90 | static int mtdoops_inc_counter(struct mtdoops_context *cxt) | 91 | static void mtdoops_inc_counter(struct mtdoops_context *cxt) |
91 | { | 92 | { |
92 | struct mtd_info *mtd = cxt->mtd; | 93 | struct mtd_info *mtd = cxt->mtd; |
93 | size_t retlen; | 94 | size_t retlen; |
@@ -107,21 +108,26 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt) | |||
107 | printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" | 108 | printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" |
108 | ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, | 109 | ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, |
109 | retlen, ret); | 110 | retlen, ret); |
110 | return 1; | 111 | schedule_work(&cxt->work_erase); |
112 | return; | ||
111 | } | 113 | } |
112 | 114 | ||
113 | /* See if we need to erase the next block */ | 115 | /* See if we need to erase the next block */ |
114 | if (count != 0xffffffff) | 116 | if (count != 0xffffffff) { |
115 | return 1; | 117 | schedule_work(&cxt->work_erase); |
118 | return; | ||
119 | } | ||
116 | 120 | ||
117 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", | 121 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", |
118 | cxt->nextpage, cxt->nextcount); | 122 | cxt->nextpage, cxt->nextcount); |
119 | cxt->ready = 1; | 123 | cxt->ready = 1; |
120 | return 0; | ||
121 | } | 124 | } |
122 | 125 | ||
123 | static void mtdoops_prepare(struct mtdoops_context *cxt) | 126 | /* Scheduled work - when we can't proceed without erasing a block */ |
127 | static void mtdoops_workfunc_erase(struct work_struct *work) | ||
124 | { | 128 | { |
129 | struct mtdoops_context *cxt = | ||
130 | container_of(work, struct mtdoops_context, work_erase); | ||
125 | struct mtd_info *mtd = cxt->mtd; | 131 | struct mtd_info *mtd = cxt->mtd; |
126 | int i = 0, j, ret, mod; | 132 | int i = 0, j, ret, mod; |
127 | 133 | ||
@@ -176,15 +182,31 @@ badblock: | |||
176 | goto badblock; | 182 | goto badblock; |
177 | } | 183 | } |
178 | 184 | ||
179 | static void mtdoops_workfunc(struct work_struct *work) | 185 | static void mtdoops_workfunc_write(struct work_struct *work) |
180 | { | 186 | { |
181 | struct mtdoops_context *cxt = | 187 | struct mtdoops_context *cxt = |
182 | container_of(work, struct mtdoops_context, work); | 188 | container_of(work, struct mtdoops_context, work_write); |
189 | struct mtd_info *mtd = cxt->mtd; | ||
190 | size_t retlen; | ||
191 | int ret; | ||
183 | 192 | ||
184 | mtdoops_prepare(cxt); | 193 | if (cxt->writecount < OOPS_PAGE_SIZE) |
185 | } | 194 | memset(cxt->oops_buf + cxt->writecount, 0xff, |
195 | OOPS_PAGE_SIZE - cxt->writecount); | ||
196 | |||
197 | ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, | ||
198 | OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); | ||
199 | |||
200 | cxt->writecount = 0; | ||
201 | |||
202 | if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) | ||
203 | printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", | ||
204 | cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); | ||
205 | |||
206 | mtdoops_inc_counter(cxt); | ||
207 | } | ||
186 | 208 | ||
187 | static int find_next_position(struct mtdoops_context *cxt) | 209 | static void find_next_position(struct mtdoops_context *cxt) |
188 | { | 210 | { |
189 | struct mtd_info *mtd = cxt->mtd; | 211 | struct mtd_info *mtd = cxt->mtd; |
190 | int ret, page, maxpos = 0; | 212 | int ret, page, maxpos = 0; |
@@ -222,20 +244,19 @@ static int find_next_position(struct mtdoops_context *cxt) | |||
222 | cxt->ready = 1; | 244 | cxt->ready = 1; |
223 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", | 245 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", |
224 | cxt->nextpage, cxt->nextcount); | 246 | cxt->nextpage, cxt->nextcount); |
225 | return 0; | 247 | return; |
226 | } | 248 | } |
227 | 249 | ||
228 | cxt->nextpage = maxpos; | 250 | cxt->nextpage = maxpos; |
229 | cxt->nextcount = maxcount; | 251 | cxt->nextcount = maxcount; |
230 | 252 | ||
231 | return mtdoops_inc_counter(cxt); | 253 | mtdoops_inc_counter(cxt); |
232 | } | 254 | } |
233 | 255 | ||
234 | 256 | ||
235 | static void mtdoops_notify_add(struct mtd_info *mtd) | 257 | static void mtdoops_notify_add(struct mtd_info *mtd) |
236 | { | 258 | { |
237 | struct mtdoops_context *cxt = &oops_cxt; | 259 | struct mtdoops_context *cxt = &oops_cxt; |
238 | int ret; | ||
239 | 260 | ||
240 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) | 261 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) |
241 | return; | 262 | return; |
@@ -249,9 +270,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) | |||
249 | cxt->mtd = mtd; | 270 | cxt->mtd = mtd; |
250 | cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; | 271 | cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; |
251 | 272 | ||
252 | ret = find_next_position(cxt); | 273 | find_next_position(cxt); |
253 | if (ret == 1) | ||
254 | mtdoops_prepare(cxt); | ||
255 | 274 | ||
256 | printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); | 275 | printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); |
257 | } | 276 | } |
@@ -271,31 +290,13 @@ static void mtdoops_console_sync(void) | |||
271 | { | 290 | { |
272 | struct mtdoops_context *cxt = &oops_cxt; | 291 | struct mtdoops_context *cxt = &oops_cxt; |
273 | struct mtd_info *mtd = cxt->mtd; | 292 | struct mtd_info *mtd = cxt->mtd; |
274 | size_t retlen; | ||
275 | int ret; | ||
276 | 293 | ||
277 | if (!cxt->ready || !mtd) | 294 | if (!cxt->ready || !mtd || cxt->writecount == 0) |
278 | return; | 295 | return; |
279 | 296 | ||
280 | if (cxt->writecount == 0) | ||
281 | return; | ||
282 | |||
283 | if (cxt->writecount < OOPS_PAGE_SIZE) | ||
284 | memset(cxt->oops_buf + cxt->writecount, 0xff, | ||
285 | OOPS_PAGE_SIZE - cxt->writecount); | ||
286 | |||
287 | ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, | ||
288 | OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); | ||
289 | cxt->ready = 0; | 297 | cxt->ready = 0; |
290 | cxt->writecount = 0; | ||
291 | |||
292 | if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) | ||
293 | printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", | ||
294 | cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); | ||
295 | 298 | ||
296 | ret = mtdoops_inc_counter(cxt); | 299 | schedule_work(&cxt->work_write); |
297 | if (ret == 1) | ||
298 | schedule_work(&cxt->work); | ||
299 | } | 300 | } |
300 | 301 | ||
301 | static void | 302 | static void |
@@ -365,7 +366,8 @@ static int __init mtdoops_console_init(void) | |||
365 | return -ENOMEM; | 366 | return -ENOMEM; |
366 | } | 367 | } |
367 | 368 | ||
368 | INIT_WORK(&cxt->work, mtdoops_workfunc); | 369 | INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); |
370 | INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); | ||
369 | 371 | ||
370 | register_console(&mtdoops_console); | 372 | register_console(&mtdoops_console); |
371 | register_mtd_user(&mtdoops_notifier); | 373 | register_mtd_user(&mtdoops_notifier); |