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 | |
| 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>
| -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); |
