aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdoops.c
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2008-01-29 06:27:11 -0500
committerDavid Woodhouse <dwmw2@infradead.org>2008-02-03 02:14:41 -0500
commit6ce0a856c10c8ab8568764436864616efa88e908 (patch)
tree39c6b56a261c7585357386f42b32d773490bcf84 /drivers/mtd/mtdoops.c
parent2986bd2a330e57af53c016276d63d46ded10e9c5 (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.c82
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
35static struct mtdoops_context { 35struct 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
90static int mtdoops_inc_counter(struct mtdoops_context *cxt) 91static 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
123static void mtdoops_prepare(struct mtdoops_context *cxt) 126/* Scheduled work - when we can't proceed without erasing a block */
127static 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
179static void mtdoops_workfunc(struct work_struct *work) 185static 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
187static int find_next_position(struct mtdoops_context *cxt) 209static 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
235static void mtdoops_notify_add(struct mtd_info *mtd) 257static 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
301static void 302static 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);