LCOV - code coverage report
Current view: top level - drivers/mtd - mtdoops.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 194 0.0 %
Date: 2014-02-18 Functions: 0 12 0.0 %
Branches: 0 118 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * MTD Oops/Panic logger
       3                 :            :  *
       4                 :            :  * Copyright © 2007 Nokia Corporation. All rights reserved.
       5                 :            :  *
       6                 :            :  * Author: Richard Purdie <rpurdie@openedhand.com>
       7                 :            :  *
       8                 :            :  * This program is free software; you can redistribute it and/or
       9                 :            :  * modify it under the terms of the GNU General Public License
      10                 :            :  * version 2 as published by the Free Software Foundation.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful, but
      13                 :            :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :            :  * General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
      20                 :            :  * 02110-1301 USA
      21                 :            :  *
      22                 :            :  */
      23                 :            : 
      24                 :            : #include <linux/kernel.h>
      25                 :            : #include <linux/module.h>
      26                 :            : #include <linux/console.h>
      27                 :            : #include <linux/vmalloc.h>
      28                 :            : #include <linux/workqueue.h>
      29                 :            : #include <linux/sched.h>
      30                 :            : #include <linux/wait.h>
      31                 :            : #include <linux/delay.h>
      32                 :            : #include <linux/interrupt.h>
      33                 :            : #include <linux/mtd/mtd.h>
      34                 :            : #include <linux/kmsg_dump.h>
      35                 :            : 
      36                 :            : /* Maximum MTD partition size */
      37                 :            : #define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
      38                 :            : 
      39                 :            : #define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
      40                 :            : #define MTDOOPS_HEADER_SIZE   8
      41                 :            : 
      42                 :            : static unsigned long record_size = 4096;
      43                 :            : module_param(record_size, ulong, 0400);
      44                 :            : MODULE_PARM_DESC(record_size,
      45                 :            :                 "record size for MTD OOPS pages in bytes (default 4096)");
      46                 :            : 
      47                 :            : static char mtddev[80];
      48                 :            : module_param_string(mtddev, mtddev, 80, 0400);
      49                 :            : MODULE_PARM_DESC(mtddev,
      50                 :            :                 "name or index number of the MTD device to use");
      51                 :            : 
      52                 :            : static int dump_oops = 1;
      53                 :            : module_param(dump_oops, int, 0600);
      54                 :            : MODULE_PARM_DESC(dump_oops,
      55                 :            :                 "set to 1 to dump oopses, 0 to only dump panics (default 1)");
      56                 :            : 
      57                 :            : static struct mtdoops_context {
      58                 :            :         struct kmsg_dumper dump;
      59                 :            : 
      60                 :            :         int mtd_index;
      61                 :            :         struct work_struct work_erase;
      62                 :            :         struct work_struct work_write;
      63                 :            :         struct mtd_info *mtd;
      64                 :            :         int oops_pages;
      65                 :            :         int nextpage;
      66                 :            :         int nextcount;
      67                 :            :         unsigned long *oops_page_used;
      68                 :            : 
      69                 :            :         void *oops_buf;
      70                 :            : } oops_cxt;
      71                 :            : 
      72                 :            : static void mark_page_used(struct mtdoops_context *cxt, int page)
      73                 :            : {
      74                 :          0 :         set_bit(page, cxt->oops_page_used);
      75                 :            : }
      76                 :            : 
      77                 :            : static void mark_page_unused(struct mtdoops_context *cxt, int page)
      78                 :            : {
      79                 :          0 :         clear_bit(page, cxt->oops_page_used);
      80                 :            : }
      81                 :            : 
      82                 :            : static int page_is_used(struct mtdoops_context *cxt, int page)
      83                 :            : {
      84                 :            :         return test_bit(page, cxt->oops_page_used);
      85                 :            : }
      86                 :            : 
      87                 :          0 : static void mtdoops_erase_callback(struct erase_info *done)
      88                 :            : {
      89                 :          0 :         wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
      90                 :          0 :         wake_up(wait_q);
      91                 :          0 : }
      92                 :            : 
      93                 :          0 : static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
      94                 :            : {
      95                 :          0 :         struct mtd_info *mtd = cxt->mtd;
      96                 :          0 :         u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize;
      97                 :          0 :         u32 start_page = start_page_offset / record_size;
      98                 :          0 :         u32 erase_pages = mtd->erasesize / record_size;
      99                 :            :         struct erase_info erase;
     100                 :          0 :         DECLARE_WAITQUEUE(wait, current);
     101                 :            :         wait_queue_head_t wait_q;
     102                 :            :         int ret;
     103                 :            :         int page;
     104                 :            : 
     105                 :          0 :         init_waitqueue_head(&wait_q);
     106                 :          0 :         erase.mtd = mtd;
     107                 :          0 :         erase.callback = mtdoops_erase_callback;
     108                 :          0 :         erase.addr = offset;
     109                 :          0 :         erase.len = mtd->erasesize;
     110                 :          0 :         erase.priv = (u_long)&wait_q;
     111                 :            : 
     112                 :          0 :         set_current_state(TASK_INTERRUPTIBLE);
     113                 :          0 :         add_wait_queue(&wait_q, &wait);
     114                 :            : 
     115                 :          0 :         ret = mtd_erase(mtd, &erase);
     116         [ #  # ]:          0 :         if (ret) {
     117                 :          0 :                 set_current_state(TASK_RUNNING);
     118                 :          0 :                 remove_wait_queue(&wait_q, &wait);
     119                 :          0 :                 printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
     120                 :            :                        (unsigned long long)erase.addr,
     121                 :            :                        (unsigned long long)erase.len, mtddev);
     122                 :          0 :                 return ret;
     123                 :            :         }
     124                 :            : 
     125                 :          0 :         schedule();  /* Wait for erase to finish. */
     126                 :          0 :         remove_wait_queue(&wait_q, &wait);
     127                 :            : 
     128                 :            :         /* Mark pages as unused */
     129         [ #  # ]:          0 :         for (page = start_page; page < start_page + erase_pages; page++)
     130                 :            :                 mark_page_unused(cxt, page);
     131                 :            : 
     132                 :            :         return 0;
     133                 :            : }
     134                 :            : 
     135                 :          0 : static void mtdoops_inc_counter(struct mtdoops_context *cxt)
     136                 :            : {
     137                 :          0 :         cxt->nextpage++;
     138         [ #  # ]:          0 :         if (cxt->nextpage >= cxt->oops_pages)
     139                 :          0 :                 cxt->nextpage = 0;
     140                 :          0 :         cxt->nextcount++;
     141         [ #  # ]:          0 :         if (cxt->nextcount == 0xffffffff)
     142                 :          0 :                 cxt->nextcount = 0;
     143                 :            : 
     144         [ #  # ]:          0 :         if (page_is_used(cxt, cxt->nextpage)) {
     145                 :          0 :                 schedule_work(&cxt->work_erase);
     146                 :          0 :                 return;
     147                 :            :         }
     148                 :            : 
     149                 :          0 :         printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
     150                 :            :                cxt->nextpage, cxt->nextcount);
     151                 :            : }
     152                 :            : 
     153                 :            : /* Scheduled work - when we can't proceed without erasing a block */
     154                 :          0 : static void mtdoops_workfunc_erase(struct work_struct *work)
     155                 :            : {
     156                 :          0 :         struct mtdoops_context *cxt =
     157                 :            :                         container_of(work, struct mtdoops_context, work_erase);
     158                 :          0 :         struct mtd_info *mtd = cxt->mtd;
     159                 :            :         int i = 0, j, ret, mod;
     160                 :            : 
     161                 :            :         /* We were unregistered */
     162         [ #  # ]:          0 :         if (!mtd)
     163                 :            :                 return;
     164                 :            : 
     165                 :          0 :         mod = (cxt->nextpage * record_size) % mtd->erasesize;
     166         [ #  # ]:          0 :         if (mod != 0) {
     167                 :          0 :                 cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / record_size);
     168         [ #  # ]:          0 :                 if (cxt->nextpage >= cxt->oops_pages)
     169                 :          0 :                         cxt->nextpage = 0;
     170                 :            :         }
     171                 :            : 
     172         [ #  # ]:          0 :         while ((ret = mtd_block_isbad(mtd, cxt->nextpage * record_size)) > 0) {
     173                 :            : badblock:
     174                 :          0 :                 printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
     175                 :          0 :                        cxt->nextpage * record_size);
     176                 :          0 :                 i++;
     177                 :          0 :                 cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
     178         [ #  # ]:          0 :                 if (cxt->nextpage >= cxt->oops_pages)
     179                 :          0 :                         cxt->nextpage = 0;
     180         [ #  # ]:          0 :                 if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
     181                 :          0 :                         printk(KERN_ERR "mtdoops: all blocks bad!\n");
     182                 :          0 :                         return;
     183                 :            :                 }
     184                 :            :         }
     185                 :            : 
     186         [ #  # ]:          0 :         if (ret < 0) {
     187                 :          0 :                 printk(KERN_ERR "mtdoops: mtd_block_isbad failed, aborting\n");
     188                 :          0 :                 return;
     189                 :            :         }
     190                 :            : 
     191         [ #  # ]:          0 :         for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
     192                 :          0 :                 ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
     193                 :            : 
     194         [ #  # ]:          0 :         if (ret >= 0) {
     195                 :          0 :                 printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
     196                 :            :                        cxt->nextpage, cxt->nextcount);
     197                 :          0 :                 return;
     198                 :            :         }
     199                 :            : 
     200         [ #  # ]:          0 :         if (ret == -EIO) {
     201                 :          0 :                 ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
     202         [ #  # ]:          0 :                 if (ret < 0 && ret != -EOPNOTSUPP) {
     203                 :          0 :                         printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
     204                 :          0 :                         return;
     205                 :            :                 }
     206                 :            :         }
     207                 :            :         goto badblock;
     208                 :            : }
     209                 :            : 
     210                 :          0 : static void mtdoops_write(struct mtdoops_context *cxt, int panic)
     211                 :            : {
     212                 :          0 :         struct mtd_info *mtd = cxt->mtd;
     213                 :            :         size_t retlen;
     214                 :            :         u32 *hdr;
     215                 :            :         int ret;
     216                 :            : 
     217                 :            :         /* Add mtdoops header to the buffer */
     218                 :          0 :         hdr = cxt->oops_buf;
     219                 :          0 :         hdr[0] = cxt->nextcount;
     220                 :          0 :         hdr[1] = MTDOOPS_KERNMSG_MAGIC;
     221                 :            : 
     222         [ #  # ]:          0 :         if (panic) {
     223                 :          0 :                 ret = mtd_panic_write(mtd, cxt->nextpage * record_size,
     224                 :          0 :                                       record_size, &retlen, cxt->oops_buf);
     225         [ #  # ]:          0 :                 if (ret == -EOPNOTSUPP) {
     226                 :          0 :                         printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
     227                 :          0 :                         return;
     228                 :            :                 }
     229                 :            :         } else
     230                 :          0 :                 ret = mtd_write(mtd, cxt->nextpage * record_size,
     231                 :          0 :                                 record_size, &retlen, cxt->oops_buf);
     232                 :            : 
     233 [ #  # ][ #  # ]:          0 :         if (retlen != record_size || ret < 0)
     234                 :          0 :                 printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
     235                 :          0 :                        cxt->nextpage * record_size, retlen, record_size, ret);
     236                 :          0 :         mark_page_used(cxt, cxt->nextpage);
     237         [ #  # ]:          0 :         memset(cxt->oops_buf, 0xff, record_size);
     238                 :            : 
     239                 :          0 :         mtdoops_inc_counter(cxt);
     240                 :            : }
     241                 :            : 
     242                 :          0 : static void mtdoops_workfunc_write(struct work_struct *work)
     243                 :            : {
     244                 :          0 :         struct mtdoops_context *cxt =
     245                 :            :                         container_of(work, struct mtdoops_context, work_write);
     246                 :            : 
     247                 :          0 :         mtdoops_write(cxt, 0);
     248                 :          0 : }
     249                 :            : 
     250                 :          0 : static void find_next_position(struct mtdoops_context *cxt)
     251                 :            : {
     252                 :          0 :         struct mtd_info *mtd = cxt->mtd;
     253                 :            :         int ret, page, maxpos = 0;
     254                 :            :         u32 count[2], maxcount = 0xffffffff;
     255                 :            :         size_t retlen;
     256                 :            : 
     257         [ #  # ]:          0 :         for (page = 0; page < cxt->oops_pages; page++) {
     258         [ #  # ]:          0 :                 if (mtd_block_isbad(mtd, page * record_size))
     259                 :          0 :                         continue;
     260                 :            :                 /* Assume the page is used */
     261                 :            :                 mark_page_used(cxt, page);
     262                 :          0 :                 ret = mtd_read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
     263                 :            :                                &retlen, (u_char *)&count[0]);
     264 [ #  # ][ #  # ]:          0 :                 if (retlen != MTDOOPS_HEADER_SIZE ||
     265         [ #  # ]:          0 :                                 (ret < 0 && !mtd_is_bitflip(ret))) {
     266                 :          0 :                         printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n",
     267                 :            :                                page * record_size, retlen,
     268                 :            :                                MTDOOPS_HEADER_SIZE, ret);
     269                 :          0 :                         continue;
     270                 :            :                 }
     271                 :            : 
     272 [ #  # ][ #  # ]:          0 :                 if (count[0] == 0xffffffff && count[1] == 0xffffffff)
     273                 :            :                         mark_page_unused(cxt, page);
     274 [ #  # ][ #  # ]:          0 :                 if (count[0] == 0xffffffff || count[1] != MTDOOPS_KERNMSG_MAGIC)
     275                 :          0 :                         continue;
     276         [ #  # ]:          0 :                 if (maxcount == 0xffffffff) {
     277                 :            :                         maxcount = count[0];
     278                 :            :                         maxpos = page;
     279 [ #  # ][ #  # ]:          0 :                 } else if (count[0] < 0x40000000 && maxcount > 0xc0000000) {
     280                 :            :                         maxcount = count[0];
     281                 :            :                         maxpos = page;
     282 [ #  # ][ #  # ]:          0 :                 } else if (count[0] > maxcount && count[0] < 0xc0000000) {
     283                 :            :                         maxcount = count[0];
     284                 :            :                         maxpos = page;
     285 [ #  # ][ #  # ]:          0 :                 } else if (count[0] > maxcount && count[0] > 0xc0000000
     286         [ #  # ]:          0 :                                         && maxcount > 0x80000000) {
     287                 :            :                         maxcount = count[0];
     288                 :            :                         maxpos = page;
     289                 :            :                 }
     290                 :            :         }
     291         [ #  # ]:          0 :         if (maxcount == 0xffffffff) {
     292                 :          0 :                 cxt->nextpage = cxt->oops_pages - 1;
     293                 :          0 :                 cxt->nextcount = 0;
     294                 :            :         }
     295                 :            :         else {
     296                 :          0 :                 cxt->nextpage = maxpos;
     297                 :          0 :                 cxt->nextcount = maxcount;
     298                 :            :         }
     299                 :            : 
     300                 :          0 :         mtdoops_inc_counter(cxt);
     301                 :          0 : }
     302                 :            : 
     303                 :          0 : static void mtdoops_do_dump(struct kmsg_dumper *dumper,
     304                 :            :                             enum kmsg_dump_reason reason)
     305                 :            : {
     306                 :            :         struct mtdoops_context *cxt = container_of(dumper,
     307                 :            :                         struct mtdoops_context, dump);
     308                 :            : 
     309                 :            :         /* Only dump oopses if dump_oops is set */
     310 [ #  # ][ #  # ]:          0 :         if (reason == KMSG_DUMP_OOPS && !dump_oops)
     311                 :          0 :                 return;
     312                 :            : 
     313                 :          0 :         kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
     314                 :          0 :                              record_size - MTDOOPS_HEADER_SIZE, NULL);
     315                 :            : 
     316                 :            :         /* Panics must be written immediately */
     317         [ #  # ]:          0 :         if (reason != KMSG_DUMP_OOPS)
     318                 :          0 :                 mtdoops_write(cxt, 1);
     319                 :            : 
     320                 :            :         /* For other cases, schedule work to write it "nicely" */
     321                 :          0 :         schedule_work(&cxt->work_write);
     322                 :            : }
     323                 :            : 
     324                 :          0 : static void mtdoops_notify_add(struct mtd_info *mtd)
     325                 :            : {
     326                 :            :         struct mtdoops_context *cxt = &oops_cxt;
     327                 :          0 :         u64 mtdoops_pages = div_u64(mtd->size, record_size);
     328                 :            :         int err;
     329                 :            : 
     330         [ #  # ]:          0 :         if (!strcmp(mtd->name, mtddev))
     331                 :          0 :                 cxt->mtd_index = mtd->index;
     332                 :            : 
     333 [ #  # ][ #  # ]:          0 :         if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
     334                 :            :                 return;
     335                 :            : 
     336         [ #  # ]:          0 :         if (mtd->size < mtd->erasesize * 2) {
     337                 :          0 :                 printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n",
     338                 :            :                        mtd->index);
     339                 :          0 :                 return;
     340                 :            :         }
     341         [ #  # ]:          0 :         if (mtd->erasesize < record_size) {
     342                 :          0 :                 printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n",
     343                 :            :                        mtd->index);
     344                 :          0 :                 return;
     345                 :            :         }
     346         [ #  # ]:          0 :         if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
     347                 :          0 :                 printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n",
     348                 :            :                        mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
     349                 :          0 :                 return;
     350                 :            :         }
     351                 :            : 
     352                 :            :         /* oops_page_used is a bit field */
     353                 :          0 :         cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages,
     354                 :            :                         BITS_PER_LONG) * sizeof(unsigned long));
     355         [ #  # ]:          0 :         if (!cxt->oops_page_used) {
     356                 :          0 :                 printk(KERN_ERR "mtdoops: could not allocate page array\n");
     357                 :          0 :                 return;
     358                 :            :         }
     359                 :            : 
     360                 :          0 :         cxt->dump.max_reason = KMSG_DUMP_OOPS;
     361                 :          0 :         cxt->dump.dump = mtdoops_do_dump;
     362                 :          0 :         err = kmsg_dump_register(&cxt->dump);
     363         [ #  # ]:          0 :         if (err) {
     364                 :          0 :                 printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
     365                 :          0 :                 vfree(cxt->oops_page_used);
     366                 :          0 :                 cxt->oops_page_used = NULL;
     367                 :          0 :                 return;
     368                 :            :         }
     369                 :            : 
     370                 :          0 :         cxt->mtd = mtd;
     371                 :          0 :         cxt->oops_pages = (int)mtd->size / record_size;
     372                 :          0 :         find_next_position(cxt);
     373                 :          0 :         printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
     374                 :            : }
     375                 :            : 
     376                 :          0 : static void mtdoops_notify_remove(struct mtd_info *mtd)
     377                 :            : {
     378                 :            :         struct mtdoops_context *cxt = &oops_cxt;
     379                 :            : 
     380 [ #  # ][ #  # ]:          0 :         if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
     381                 :          0 :                 return;
     382                 :            : 
     383         [ #  # ]:          0 :         if (kmsg_dump_unregister(&cxt->dump) < 0)
     384                 :          0 :                 printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
     385                 :            : 
     386                 :          0 :         cxt->mtd = NULL;
     387                 :          0 :         flush_work(&cxt->work_erase);
     388                 :          0 :         flush_work(&cxt->work_write);
     389                 :            : }
     390                 :            : 
     391                 :            : 
     392                 :            : static struct mtd_notifier mtdoops_notifier = {
     393                 :            :         .add    = mtdoops_notify_add,
     394                 :            :         .remove = mtdoops_notify_remove,
     395                 :            : };
     396                 :            : 
     397                 :          0 : static int __init mtdoops_init(void)
     398                 :            : {
     399                 :            :         struct mtdoops_context *cxt = &oops_cxt;
     400                 :            :         int mtd_index;
     401                 :            :         char *endp;
     402                 :            : 
     403         [ #  # ]:          0 :         if (strlen(mtddev) == 0) {
     404                 :          0 :                 printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
     405                 :          0 :                 return -EINVAL;
     406                 :            :         }
     407         [ #  # ]:          0 :         if ((record_size & 4095) != 0) {
     408                 :          0 :                 printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n");
     409                 :          0 :                 return -EINVAL;
     410                 :            :         }
     411         [ #  # ]:          0 :         if (record_size < 4096) {
     412                 :          0 :                 printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n");
     413                 :          0 :                 return -EINVAL;
     414                 :            :         }
     415                 :            : 
     416                 :            :         /* Setup the MTD device to use */
     417                 :          0 :         cxt->mtd_index = -1;
     418                 :          0 :         mtd_index = simple_strtoul(mtddev, &endp, 0);
     419         [ #  # ]:          0 :         if (*endp == '\0')
     420                 :          0 :                 cxt->mtd_index = mtd_index;
     421                 :            : 
     422                 :          0 :         cxt->oops_buf = vmalloc(record_size);
     423         [ #  # ]:          0 :         if (!cxt->oops_buf) {
     424                 :          0 :                 printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n");
     425                 :          0 :                 return -ENOMEM;
     426                 :            :         }
     427         [ #  # ]:          0 :         memset(cxt->oops_buf, 0xff, record_size);
     428                 :            : 
     429                 :          0 :         INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
     430                 :          0 :         INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
     431                 :            : 
     432                 :          0 :         register_mtd_user(&mtdoops_notifier);
     433                 :          0 :         return 0;
     434                 :            : }
     435                 :            : 
     436                 :          0 : static void __exit mtdoops_exit(void)
     437                 :            : {
     438                 :            :         struct mtdoops_context *cxt = &oops_cxt;
     439                 :            : 
     440                 :          0 :         unregister_mtd_user(&mtdoops_notifier);
     441                 :          0 :         vfree(cxt->oops_buf);
     442                 :          0 :         vfree(cxt->oops_page_used);
     443                 :          0 : }
     444                 :            : 
     445                 :            : 
     446                 :            : module_init(mtdoops_init);
     447                 :            : module_exit(mtdoops_exit);
     448                 :            : 
     449                 :            : MODULE_LICENSE("GPL");
     450                 :            : MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
     451                 :            : MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");

Generated by: LCOV version 1.9