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

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Direct MTD block device access
       3                 :            :  *
       4                 :            :  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
       5                 :            :  * Copyright © 2000-2003 Nicolas Pitre <nico@fluxnic.net>
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or modify
       8                 :            :  * it under the terms of the GNU General Public License as published by
       9                 :            :  * the Free Software Foundation; either version 2 of the License, or
      10                 :            :  * (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :            :  * GNU 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  02110-1301  USA
      20                 :            :  *
      21                 :            :  */
      22                 :            : 
      23                 :            : #include <linux/fs.h>
      24                 :            : #include <linux/init.h>
      25                 :            : #include <linux/kernel.h>
      26                 :            : #include <linux/module.h>
      27                 :            : #include <linux/sched.h>
      28                 :            : #include <linux/slab.h>
      29                 :            : #include <linux/types.h>
      30                 :            : #include <linux/vmalloc.h>
      31                 :            : 
      32                 :            : #include <linux/mtd/mtd.h>
      33                 :            : #include <linux/mtd/blktrans.h>
      34                 :            : #include <linux/mutex.h>
      35                 :            : #include <linux/major.h>
      36                 :            : 
      37                 :            : 
      38                 :            : struct mtdblk_dev {
      39                 :            :         struct mtd_blktrans_dev mbd;
      40                 :            :         int count;
      41                 :            :         struct mutex cache_mutex;
      42                 :            :         unsigned char *cache_data;
      43                 :            :         unsigned long cache_offset;
      44                 :            :         unsigned int cache_size;
      45                 :            :         enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
      46                 :            : };
      47                 :            : 
      48                 :            : static DEFINE_MUTEX(mtdblks_lock);
      49                 :            : 
      50                 :            : /*
      51                 :            :  * Cache stuff...
      52                 :            :  *
      53                 :            :  * Since typical flash erasable sectors are much larger than what Linux's
      54                 :            :  * buffer cache can handle, we must implement read-modify-write on flash
      55                 :            :  * sectors for each block write requests.  To avoid over-erasing flash sectors
      56                 :            :  * and to speed things up, we locally cache a whole flash sector while it is
      57                 :            :  * being written to until a different sector is required.
      58                 :            :  */
      59                 :            : 
      60                 :          0 : static void erase_callback(struct erase_info *done)
      61                 :            : {
      62                 :          0 :         wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
      63                 :          0 :         wake_up(wait_q);
      64                 :          0 : }
      65                 :            : 
      66                 :          0 : static int erase_write (struct mtd_info *mtd, unsigned long pos,
      67                 :            :                         int len, const char *buf)
      68                 :            : {
      69                 :            :         struct erase_info erase;
      70                 :          0 :         DECLARE_WAITQUEUE(wait, current);
      71                 :            :         wait_queue_head_t wait_q;
      72                 :            :         size_t retlen;
      73                 :            :         int ret;
      74                 :            : 
      75                 :            :         /*
      76                 :            :          * First, let's erase the flash block.
      77                 :            :          */
      78                 :            : 
      79                 :          0 :         init_waitqueue_head(&wait_q);
      80                 :          0 :         erase.mtd = mtd;
      81                 :          0 :         erase.callback = erase_callback;
      82                 :          0 :         erase.addr = pos;
      83                 :          0 :         erase.len = len;
      84                 :          0 :         erase.priv = (u_long)&wait_q;
      85                 :            : 
      86                 :          0 :         set_current_state(TASK_INTERRUPTIBLE);
      87                 :          0 :         add_wait_queue(&wait_q, &wait);
      88                 :            : 
      89                 :          0 :         ret = mtd_erase(mtd, &erase);
      90         [ #  # ]:          0 :         if (ret) {
      91                 :          0 :                 set_current_state(TASK_RUNNING);
      92                 :          0 :                 remove_wait_queue(&wait_q, &wait);
      93                 :          0 :                 printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
      94                 :            :                                      "on \"%s\" failed\n",
      95                 :            :                         pos, len, mtd->name);
      96                 :          0 :                 return ret;
      97                 :            :         }
      98                 :            : 
      99                 :          0 :         schedule();  /* Wait for erase to finish. */
     100                 :          0 :         remove_wait_queue(&wait_q, &wait);
     101                 :            : 
     102                 :            :         /*
     103                 :            :          * Next, write the data to flash.
     104                 :            :          */
     105                 :            : 
     106                 :          0 :         ret = mtd_write(mtd, pos, len, &retlen, buf);
     107         [ #  # ]:          0 :         if (ret)
     108                 :            :                 return ret;
     109         [ #  # ]:          0 :         if (retlen != len)
     110                 :            :                 return -EIO;
     111                 :          0 :         return 0;
     112                 :            : }
     113                 :            : 
     114                 :            : 
     115                 :          0 : static int write_cached_data (struct mtdblk_dev *mtdblk)
     116                 :            : {
     117                 :          0 :         struct mtd_info *mtd = mtdblk->mbd.mtd;
     118                 :            :         int ret;
     119                 :            : 
     120         [ #  # ]:          0 :         if (mtdblk->cache_state != STATE_DIRTY)
     121                 :            :                 return 0;
     122                 :            : 
     123                 :            :         pr_debug("mtdblock: writing cached data for \"%s\" "
     124                 :            :                         "at 0x%lx, size 0x%x\n", mtd->name,
     125                 :            :                         mtdblk->cache_offset, mtdblk->cache_size);
     126                 :            : 
     127                 :          0 :         ret = erase_write (mtd, mtdblk->cache_offset,
     128                 :          0 :                            mtdblk->cache_size, mtdblk->cache_data);
     129         [ #  # ]:          0 :         if (ret)
     130                 :            :                 return ret;
     131                 :            : 
     132                 :            :         /*
     133                 :            :          * Here we could arguably set the cache state to STATE_CLEAN.
     134                 :            :          * However this could lead to inconsistency since we will not
     135                 :            :          * be notified if this content is altered on the flash by other
     136                 :            :          * means.  Let's declare it empty and leave buffering tasks to
     137                 :            :          * the buffer cache instead.
     138                 :            :          */
     139                 :          0 :         mtdblk->cache_state = STATE_EMPTY;
     140                 :          0 :         return 0;
     141                 :            : }
     142                 :            : 
     143                 :            : 
     144                 :          0 : static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
     145                 :            :                             int len, const char *buf)
     146                 :            : {
     147                 :          0 :         struct mtd_info *mtd = mtdblk->mbd.mtd;
     148                 :          0 :         unsigned int sect_size = mtdblk->cache_size;
     149                 :            :         size_t retlen;
     150                 :            :         int ret;
     151                 :            : 
     152                 :            :         pr_debug("mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
     153                 :            :                 mtd->name, pos, len);
     154                 :            : 
     155         [ #  # ]:          0 :         if (!sect_size)
     156                 :          0 :                 return mtd_write(mtd, pos, len, &retlen, buf);
     157                 :            : 
     158         [ #  # ]:          0 :         while (len > 0) {
     159                 :          0 :                 unsigned long sect_start = (pos/sect_size)*sect_size;
     160                 :          0 :                 unsigned int offset = pos - sect_start;
     161                 :          0 :                 unsigned int size = sect_size - offset;
     162         [ #  # ]:          0 :                 if( size > len )
     163                 :            :                         size = len;
     164                 :            : 
     165         [ #  # ]:          0 :                 if (size == sect_size) {
     166                 :            :                         /*
     167                 :            :                          * We are covering a whole sector.  Thus there is no
     168                 :            :                          * need to bother with the cache while it may still be
     169                 :            :                          * useful for other partial writes.
     170                 :            :                          */
     171                 :          0 :                         ret = erase_write (mtd, pos, size, buf);
     172         [ #  # ]:          0 :                         if (ret)
     173                 :            :                                 return ret;
     174                 :            :                 } else {
     175                 :            :                         /* Partial sector: need to use the cache */
     176                 :            : 
     177 [ #  # ][ #  # ]:          0 :                         if (mtdblk->cache_state == STATE_DIRTY &&
     178                 :          0 :                             mtdblk->cache_offset != sect_start) {
     179                 :          0 :                                 ret = write_cached_data(mtdblk);
     180         [ #  # ]:          0 :                                 if (ret)
     181                 :            :                                         return ret;
     182                 :            :                         }
     183                 :            : 
     184 [ #  # ][ #  # ]:          0 :                         if (mtdblk->cache_state == STATE_EMPTY ||
     185                 :          0 :                             mtdblk->cache_offset != sect_start) {
     186                 :            :                                 /* fill the cache with the current sector */
     187                 :          0 :                                 mtdblk->cache_state = STATE_EMPTY;
     188                 :          0 :                                 ret = mtd_read(mtd, sect_start, sect_size,
     189                 :          0 :                                                &retlen, mtdblk->cache_data);
     190         [ #  # ]:          0 :                                 if (ret)
     191                 :            :                                         return ret;
     192         [ #  # ]:          0 :                                 if (retlen != sect_size)
     193                 :            :                                         return -EIO;
     194                 :            : 
     195                 :          0 :                                 mtdblk->cache_offset = sect_start;
     196                 :          0 :                                 mtdblk->cache_size = sect_size;
     197                 :          0 :                                 mtdblk->cache_state = STATE_CLEAN;
     198                 :            :                         }
     199                 :            : 
     200                 :            :                         /* write data to our local cache */
     201                 :          0 :                         memcpy (mtdblk->cache_data + offset, buf, size);
     202                 :          0 :                         mtdblk->cache_state = STATE_DIRTY;
     203                 :            :                 }
     204                 :            : 
     205                 :          0 :                 buf += size;
     206                 :          0 :                 pos += size;
     207                 :          0 :                 len -= size;
     208                 :            :         }
     209                 :            : 
     210                 :            :         return 0;
     211                 :            : }
     212                 :            : 
     213                 :            : 
     214                 :          0 : static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
     215                 :            :                            int len, char *buf)
     216                 :            : {
     217                 :          0 :         struct mtd_info *mtd = mtdblk->mbd.mtd;
     218                 :          0 :         unsigned int sect_size = mtdblk->cache_size;
     219                 :            :         size_t retlen;
     220                 :            :         int ret;
     221                 :            : 
     222                 :            :         pr_debug("mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
     223                 :            :                         mtd->name, pos, len);
     224                 :            : 
     225         [ #  # ]:          0 :         if (!sect_size)
     226                 :          0 :                 return mtd_read(mtd, pos, len, &retlen, buf);
     227                 :            : 
     228         [ #  # ]:          0 :         while (len > 0) {
     229                 :          0 :                 unsigned long sect_start = (pos/sect_size)*sect_size;
     230                 :          0 :                 unsigned int offset = pos - sect_start;
     231                 :          0 :                 unsigned int size = sect_size - offset;
     232         [ #  # ]:          0 :                 if (size > len)
     233                 :            :                         size = len;
     234                 :            : 
     235                 :            :                 /*
     236                 :            :                  * Check if the requested data is already cached
     237                 :            :                  * Read the requested amount of data from our internal cache if it
     238                 :            :                  * contains what we want, otherwise we read the data directly
     239                 :            :                  * from flash.
     240                 :            :                  */
     241 [ #  # ][ #  # ]:          0 :                 if (mtdblk->cache_state != STATE_EMPTY &&
     242                 :          0 :                     mtdblk->cache_offset == sect_start) {
     243                 :          0 :                         memcpy (buf, mtdblk->cache_data + offset, size);
     244                 :            :                 } else {
     245                 :          0 :                         ret = mtd_read(mtd, pos, size, &retlen, buf);
     246         [ #  # ]:          0 :                         if (ret)
     247                 :            :                                 return ret;
     248         [ #  # ]:          0 :                         if (retlen != size)
     249                 :            :                                 return -EIO;
     250                 :            :                 }
     251                 :            : 
     252                 :          0 :                 buf += size;
     253                 :          0 :                 pos += size;
     254                 :          0 :                 len -= size;
     255                 :            :         }
     256                 :            : 
     257                 :            :         return 0;
     258                 :            : }
     259                 :            : 
     260                 :          0 : static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
     261                 :            :                               unsigned long block, char *buf)
     262                 :            : {
     263                 :            :         struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
     264                 :          0 :         return do_cached_read(mtdblk, block<<9, 512, buf);
     265                 :            : }
     266                 :            : 
     267                 :          0 : static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
     268                 :            :                               unsigned long block, char *buf)
     269                 :            : {
     270                 :            :         struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
     271 [ #  # ][ #  # ]:          0 :         if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
     272                 :          0 :                 mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize);
     273         [ #  # ]:          0 :                 if (!mtdblk->cache_data)
     274                 :            :                         return -EINTR;
     275                 :            :                 /* -EINTR is not really correct, but it is the best match
     276                 :            :                  * documented in man 2 write for all cases.  We could also
     277                 :            :                  * return -EAGAIN sometimes, but why bother?
     278                 :            :                  */
     279                 :            :         }
     280                 :          0 :         return do_cached_write(mtdblk, block<<9, 512, buf);
     281                 :            : }
     282                 :            : 
     283                 :          0 : static int mtdblock_open(struct mtd_blktrans_dev *mbd)
     284                 :            : {
     285                 :            :         struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
     286                 :            : 
     287                 :            :         pr_debug("mtdblock_open\n");
     288                 :            : 
     289                 :          0 :         mutex_lock(&mtdblks_lock);
     290         [ #  # ]:          0 :         if (mtdblk->count) {
     291                 :          0 :                 mtdblk->count++;
     292                 :          0 :                 mutex_unlock(&mtdblks_lock);
     293                 :          0 :                 return 0;
     294                 :            :         }
     295                 :            : 
     296                 :            :         /* OK, it's not open. Create cache info for it */
     297                 :          0 :         mtdblk->count = 1;
     298                 :          0 :         mutex_init(&mtdblk->cache_mutex);
     299                 :          0 :         mtdblk->cache_state = STATE_EMPTY;
     300 [ #  # ][ #  # ]:          0 :         if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) {
     301                 :          0 :                 mtdblk->cache_size = mbd->mtd->erasesize;
     302                 :          0 :                 mtdblk->cache_data = NULL;
     303                 :            :         }
     304                 :            : 
     305                 :          0 :         mutex_unlock(&mtdblks_lock);
     306                 :            : 
     307                 :            :         pr_debug("ok\n");
     308                 :            : 
     309                 :          0 :         return 0;
     310                 :            : }
     311                 :            : 
     312                 :          0 : static void mtdblock_release(struct mtd_blktrans_dev *mbd)
     313                 :            : {
     314                 :            :         struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
     315                 :            : 
     316                 :            :         pr_debug("mtdblock_release\n");
     317                 :            : 
     318                 :          0 :         mutex_lock(&mtdblks_lock);
     319                 :            : 
     320                 :          0 :         mutex_lock(&mtdblk->cache_mutex);
     321                 :          0 :         write_cached_data(mtdblk);
     322                 :          0 :         mutex_unlock(&mtdblk->cache_mutex);
     323                 :            : 
     324         [ #  # ]:          0 :         if (!--mtdblk->count) {
     325                 :            :                 /*
     326                 :            :                  * It was the last usage. Free the cache, but only sync if
     327                 :            :                  * opened for writing.
     328                 :            :                  */
     329         [ #  # ]:          0 :                 if (mbd->file_mode & FMODE_WRITE)
     330                 :          0 :                         mtd_sync(mbd->mtd);
     331                 :          0 :                 vfree(mtdblk->cache_data);
     332                 :            :         }
     333                 :            : 
     334                 :          0 :         mutex_unlock(&mtdblks_lock);
     335                 :            : 
     336                 :            :         pr_debug("ok\n");
     337                 :          0 : }
     338                 :            : 
     339                 :          0 : static int mtdblock_flush(struct mtd_blktrans_dev *dev)
     340                 :            : {
     341                 :            :         struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
     342                 :            : 
     343                 :          0 :         mutex_lock(&mtdblk->cache_mutex);
     344                 :          0 :         write_cached_data(mtdblk);
     345                 :          0 :         mutex_unlock(&mtdblk->cache_mutex);
     346                 :          0 :         mtd_sync(dev->mtd);
     347                 :          0 :         return 0;
     348                 :            : }
     349                 :            : 
     350                 :          0 : static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
     351                 :            : {
     352                 :            :         struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
     353                 :            : 
     354         [ #  # ]:          0 :         if (!dev)
     355                 :          0 :                 return;
     356                 :            : 
     357                 :          0 :         dev->mbd.mtd = mtd;
     358                 :          0 :         dev->mbd.devnum = mtd->index;
     359                 :            : 
     360                 :          0 :         dev->mbd.size = mtd->size >> 9;
     361                 :          0 :         dev->mbd.tr = tr;
     362                 :            : 
     363         [ #  # ]:          0 :         if (!(mtd->flags & MTD_WRITEABLE))
     364                 :          0 :                 dev->mbd.readonly = 1;
     365                 :            : 
     366         [ #  # ]:          0 :         if (add_mtd_blktrans_dev(&dev->mbd))
     367                 :          0 :                 kfree(dev);
     368                 :            : }
     369                 :            : 
     370                 :          0 : static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
     371                 :            : {
     372                 :          0 :         del_mtd_blktrans_dev(dev);
     373                 :          0 : }
     374                 :            : 
     375                 :            : static struct mtd_blktrans_ops mtdblock_tr = {
     376                 :            :         .name           = "mtdblock",
     377                 :            :         .major          = MTD_BLOCK_MAJOR,
     378                 :            :         .part_bits      = 0,
     379                 :            :         .blksize        = 512,
     380                 :            :         .open           = mtdblock_open,
     381                 :            :         .flush          = mtdblock_flush,
     382                 :            :         .release        = mtdblock_release,
     383                 :            :         .readsect       = mtdblock_readsect,
     384                 :            :         .writesect      = mtdblock_writesect,
     385                 :            :         .add_mtd        = mtdblock_add_mtd,
     386                 :            :         .remove_dev     = mtdblock_remove_dev,
     387                 :            :         .owner          = THIS_MODULE,
     388                 :            : };
     389                 :            : 
     390                 :          0 : static int __init init_mtdblock(void)
     391                 :            : {
     392                 :          0 :         return register_mtd_blktrans(&mtdblock_tr);
     393                 :            : }
     394                 :            : 
     395                 :          0 : static void __exit cleanup_mtdblock(void)
     396                 :            : {
     397                 :          0 :         deregister_mtd_blktrans(&mtdblock_tr);
     398                 :          0 : }
     399                 :            : 
     400                 :            : module_init(init_mtdblock);
     401                 :            : module_exit(cleanup_mtdblock);
     402                 :            : 
     403                 :            : 
     404                 :            : MODULE_LICENSE("GPL");
     405                 :            : MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net> et al.");
     406                 :            : MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices");

Generated by: LCOV version 1.9