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

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Interface to Linux block layer for MTD 'translation layers'.
       3                 :            :  *
       4                 :            :  * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org>
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU General Public License as published by
       8                 :            :  * the Free Software Foundation; either version 2 of the License, or
       9                 :            :  * (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19                 :            :  *
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <linux/kernel.h>
      23                 :            : #include <linux/slab.h>
      24                 :            : #include <linux/module.h>
      25                 :            : #include <linux/list.h>
      26                 :            : #include <linux/fs.h>
      27                 :            : #include <linux/mtd/blktrans.h>
      28                 :            : #include <linux/mtd/mtd.h>
      29                 :            : #include <linux/blkdev.h>
      30                 :            : #include <linux/blkpg.h>
      31                 :            : #include <linux/spinlock.h>
      32                 :            : #include <linux/hdreg.h>
      33                 :            : #include <linux/init.h>
      34                 :            : #include <linux/mutex.h>
      35                 :            : #include <asm/uaccess.h>
      36                 :            : 
      37                 :            : #include "mtdcore.h"
      38                 :            : 
      39                 :            : static LIST_HEAD(blktrans_majors);
      40                 :            : static DEFINE_MUTEX(blktrans_ref_mutex);
      41                 :            : 
      42                 :          0 : static void blktrans_dev_release(struct kref *kref)
      43                 :            : {
      44                 :          0 :         struct mtd_blktrans_dev *dev =
      45                 :            :                 container_of(kref, struct mtd_blktrans_dev, ref);
      46                 :            : 
      47                 :          0 :         dev->disk->private_data = NULL;
      48                 :          0 :         blk_cleanup_queue(dev->rq);
      49                 :          0 :         put_disk(dev->disk);
      50                 :            :         list_del(&dev->list);
      51                 :          0 :         kfree(dev);
      52                 :          0 : }
      53                 :            : 
      54                 :          0 : static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
      55                 :            : {
      56                 :            :         struct mtd_blktrans_dev *dev;
      57                 :            : 
      58                 :          0 :         mutex_lock(&blktrans_ref_mutex);
      59                 :          0 :         dev = disk->private_data;
      60                 :            : 
      61         [ #  # ]:          0 :         if (!dev)
      62                 :            :                 goto unlock;
      63                 :            :         kref_get(&dev->ref);
      64                 :            : unlock:
      65                 :          0 :         mutex_unlock(&blktrans_ref_mutex);
      66                 :          0 :         return dev;
      67                 :            : }
      68                 :            : 
      69                 :          0 : static void blktrans_dev_put(struct mtd_blktrans_dev *dev)
      70                 :            : {
      71                 :          0 :         mutex_lock(&blktrans_ref_mutex);
      72                 :          0 :         kref_put(&dev->ref, blktrans_dev_release);
      73                 :          0 :         mutex_unlock(&blktrans_ref_mutex);
      74                 :          0 : }
      75                 :            : 
      76                 :            : 
      77                 :          0 : static int do_blktrans_request(struct mtd_blktrans_ops *tr,
      78                 :            :                                struct mtd_blktrans_dev *dev,
      79                 :          0 :                                struct request *req)
      80                 :            : {
      81                 :            :         unsigned long block, nsect;
      82                 :            :         char *buf;
      83                 :            : 
      84                 :          0 :         block = blk_rq_pos(req) << 9 >> tr->blkshift;
      85                 :          0 :         nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
      86                 :            : 
      87                 :          0 :         buf = req->buffer;
      88                 :            : 
      89         [ #  # ]:          0 :         if (req->cmd_type != REQ_TYPE_FS)
      90                 :            :                 return -EIO;
      91                 :            : 
      92         [ #  # ]:          0 :         if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
      93                 :          0 :             get_capacity(req->rq_disk))
      94                 :            :                 return -EIO;
      95                 :            : 
      96         [ #  # ]:          0 :         if (req->cmd_flags & REQ_DISCARD)
      97                 :          0 :                 return tr->discard(dev, block, nsect);
      98                 :            : 
      99      [ #  #  # ]:          0 :         switch(rq_data_dir(req)) {
     100                 :            :         case READ:
     101         [ #  # ]:          0 :                 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
     102         [ #  # ]:          0 :                         if (tr->readsect(dev, block, buf))
     103                 :            :                                 return -EIO;
     104                 :          0 :                 rq_flush_dcache_pages(req);
     105                 :          0 :                 return 0;
     106                 :            :         case WRITE:
     107         [ #  # ]:          0 :                 if (!tr->writesect)
     108                 :            :                         return -EIO;
     109                 :            : 
     110                 :          0 :                 rq_flush_dcache_pages(req);
     111         [ #  # ]:          0 :                 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
     112         [ #  # ]:          0 :                         if (tr->writesect(dev, block, buf))
     113                 :            :                                 return -EIO;
     114                 :            :                 return 0;
     115                 :            :         default:
     116                 :          0 :                 printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
     117                 :          0 :                 return -EIO;
     118                 :            :         }
     119                 :            : }
     120                 :            : 
     121                 :          0 : int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev)
     122                 :            : {
     123                 :          0 :         return dev->bg_stop;
     124                 :            : }
     125                 :            : EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
     126                 :            : 
     127                 :          0 : static void mtd_blktrans_work(struct work_struct *work)
     128                 :            : {
     129                 :          0 :         struct mtd_blktrans_dev *dev =
     130                 :            :                 container_of(work, struct mtd_blktrans_dev, work);
     131                 :          0 :         struct mtd_blktrans_ops *tr = dev->tr;
     132                 :          0 :         struct request_queue *rq = dev->rq;
     133                 :            :         struct request *req = NULL;
     134                 :            :         int background_done = 0;
     135                 :            : 
     136                 :          0 :         spin_lock_irq(rq->queue_lock);
     137                 :            : 
     138                 :            :         while (1) {
     139                 :            :                 int res;
     140                 :            : 
     141                 :          0 :                 dev->bg_stop = false;
     142 [ #  # ][ #  # ]:          0 :                 if (!req && !(req = blk_fetch_request(rq))) {
     143 [ #  # ][ #  # ]:          0 :                         if (tr->background && !background_done) {
     144                 :          0 :                                 spin_unlock_irq(rq->queue_lock);
     145                 :          0 :                                 mutex_lock(&dev->lock);
     146                 :          0 :                                 tr->background(dev);
     147                 :          0 :                                 mutex_unlock(&dev->lock);
     148                 :          0 :                                 spin_lock_irq(rq->queue_lock);
     149                 :            :                                 /*
     150                 :            :                                  * Do background processing just once per idle
     151                 :            :                                  * period.
     152                 :            :                                  */
     153                 :          0 :                                 background_done = !dev->bg_stop;
     154                 :          0 :                                 continue;
     155                 :            :                         }
     156                 :            :                         break;
     157                 :            :                 }
     158                 :            : 
     159                 :          0 :                 spin_unlock_irq(rq->queue_lock);
     160                 :            : 
     161                 :          0 :                 mutex_lock(&dev->lock);
     162                 :          0 :                 res = do_blktrans_request(dev->tr, dev, req);
     163                 :          0 :                 mutex_unlock(&dev->lock);
     164                 :            : 
     165                 :          0 :                 spin_lock_irq(rq->queue_lock);
     166                 :            : 
     167         [ #  # ]:          0 :                 if (!__blk_end_request_cur(req, res))
     168                 :            :                         req = NULL;
     169                 :            : 
     170                 :            :                 background_done = 0;
     171                 :            :         }
     172                 :            : 
     173         [ #  # ]:          0 :         if (req)
     174                 :          0 :                 __blk_end_request_all(req, -EIO);
     175                 :            : 
     176                 :          0 :         spin_unlock_irq(rq->queue_lock);
     177                 :          0 : }
     178                 :            : 
     179                 :          0 : static void mtd_blktrans_request(struct request_queue *rq)
     180                 :            : {
     181                 :            :         struct mtd_blktrans_dev *dev;
     182                 :            :         struct request *req = NULL;
     183                 :            : 
     184                 :          0 :         dev = rq->queuedata;
     185                 :            : 
     186         [ #  # ]:          0 :         if (!dev)
     187         [ #  # ]:          0 :                 while ((req = blk_fetch_request(rq)) != NULL)
     188                 :          0 :                         __blk_end_request_all(req, -ENODEV);
     189                 :            :         else
     190                 :          0 :                 queue_work(dev->wq, &dev->work);
     191                 :          0 : }
     192                 :            : 
     193                 :          0 : static int blktrans_open(struct block_device *bdev, fmode_t mode)
     194                 :            : {
     195                 :          0 :         struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
     196                 :            :         int ret = 0;
     197                 :            : 
     198         [ #  # ]:          0 :         if (!dev)
     199                 :            :                 return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
     200                 :            : 
     201                 :          0 :         mutex_lock(&dev->lock);
     202                 :            : 
     203         [ #  # ]:          0 :         if (dev->open)
     204                 :            :                 goto unlock;
     205                 :            : 
     206                 :            :         kref_get(&dev->ref);
     207                 :          0 :         __module_get(dev->tr->owner);
     208                 :            : 
     209         [ #  # ]:          0 :         if (!dev->mtd)
     210                 :            :                 goto unlock;
     211                 :            : 
     212         [ #  # ]:          0 :         if (dev->tr->open) {
     213                 :          0 :                 ret = dev->tr->open(dev);
     214         [ #  # ]:          0 :                 if (ret)
     215                 :            :                         goto error_put;
     216                 :            :         }
     217                 :            : 
     218                 :          0 :         ret = __get_mtd_device(dev->mtd);
     219         [ #  # ]:          0 :         if (ret)
     220                 :            :                 goto error_release;
     221                 :          0 :         dev->file_mode = mode;
     222                 :            : 
     223                 :            : unlock:
     224                 :          0 :         dev->open++;
     225                 :          0 :         mutex_unlock(&dev->lock);
     226                 :          0 :         blktrans_dev_put(dev);
     227                 :          0 :         return ret;
     228                 :            : 
     229                 :            : error_release:
     230         [ #  # ]:          0 :         if (dev->tr->release)
     231                 :          0 :                 dev->tr->release(dev);
     232                 :            : error_put:
     233                 :          0 :         module_put(dev->tr->owner);
     234                 :            :         kref_put(&dev->ref, blktrans_dev_release);
     235                 :          0 :         mutex_unlock(&dev->lock);
     236                 :          0 :         blktrans_dev_put(dev);
     237                 :          0 :         return ret;
     238                 :            : }
     239                 :            : 
     240                 :          0 : static void blktrans_release(struct gendisk *disk, fmode_t mode)
     241                 :            : {
     242                 :          0 :         struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
     243                 :            : 
     244         [ #  # ]:          0 :         if (!dev)
     245                 :          0 :                 return;
     246                 :            : 
     247                 :          0 :         mutex_lock(&dev->lock);
     248                 :            : 
     249         [ #  # ]:          0 :         if (--dev->open)
     250                 :            :                 goto unlock;
     251                 :            : 
     252                 :          0 :         kref_put(&dev->ref, blktrans_dev_release);
     253                 :          0 :         module_put(dev->tr->owner);
     254                 :            : 
     255         [ #  # ]:          0 :         if (dev->mtd) {
     256         [ #  # ]:          0 :                 if (dev->tr->release)
     257                 :          0 :                         dev->tr->release(dev);
     258                 :          0 :                 __put_mtd_device(dev->mtd);
     259                 :            :         }
     260                 :            : unlock:
     261                 :          0 :         mutex_unlock(&dev->lock);
     262                 :          0 :         blktrans_dev_put(dev);
     263                 :            : }
     264                 :            : 
     265                 :          0 : static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
     266                 :            : {
     267                 :          0 :         struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
     268                 :            :         int ret = -ENXIO;
     269                 :            : 
     270         [ #  # ]:          0 :         if (!dev)
     271                 :            :                 return ret;
     272                 :            : 
     273                 :          0 :         mutex_lock(&dev->lock);
     274                 :            : 
     275         [ #  # ]:          0 :         if (!dev->mtd)
     276                 :            :                 goto unlock;
     277                 :            : 
     278         [ #  # ]:          0 :         ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
     279                 :            : unlock:
     280                 :          0 :         mutex_unlock(&dev->lock);
     281                 :          0 :         blktrans_dev_put(dev);
     282                 :          0 :         return ret;
     283                 :            : }
     284                 :            : 
     285                 :          0 : static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
     286                 :            :                               unsigned int cmd, unsigned long arg)
     287                 :            : {
     288                 :          0 :         struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
     289                 :            :         int ret = -ENXIO;
     290                 :            : 
     291         [ #  # ]:          0 :         if (!dev)
     292                 :            :                 return ret;
     293                 :            : 
     294                 :          0 :         mutex_lock(&dev->lock);
     295                 :            : 
     296         [ #  # ]:          0 :         if (!dev->mtd)
     297                 :            :                 goto unlock;
     298                 :            : 
     299         [ #  # ]:          0 :         switch (cmd) {
     300                 :            :         case BLKFLSBUF:
     301         [ #  # ]:          0 :                 ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
     302                 :          0 :                 break;
     303                 :            :         default:
     304                 :            :                 ret = -ENOTTY;
     305                 :            :         }
     306                 :            : unlock:
     307                 :          0 :         mutex_unlock(&dev->lock);
     308                 :          0 :         blktrans_dev_put(dev);
     309                 :          0 :         return ret;
     310                 :            : }
     311                 :            : 
     312                 :            : static const struct block_device_operations mtd_block_ops = {
     313                 :            :         .owner          = THIS_MODULE,
     314                 :            :         .open           = blktrans_open,
     315                 :            :         .release        = blktrans_release,
     316                 :            :         .ioctl          = blktrans_ioctl,
     317                 :            :         .getgeo         = blktrans_getgeo,
     318                 :            : };
     319                 :            : 
     320                 :          0 : int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
     321                 :            : {
     322                 :          0 :         struct mtd_blktrans_ops *tr = new->tr;
     323                 :            :         struct mtd_blktrans_dev *d;
     324                 :            :         int last_devnum = -1;
     325                 :            :         struct gendisk *gd;
     326                 :            :         int ret;
     327                 :            : 
     328         [ #  # ]:          0 :         if (mutex_trylock(&mtd_table_mutex)) {
     329                 :          0 :                 mutex_unlock(&mtd_table_mutex);
     330                 :          0 :                 BUG();
     331                 :            :         }
     332                 :            : 
     333                 :          0 :         mutex_lock(&blktrans_ref_mutex);
     334         [ #  # ]:          0 :         list_for_each_entry(d, &tr->devs, list) {
     335         [ #  # ]:          0 :                 if (new->devnum == -1) {
     336                 :            :                         /* Use first free number */
     337         [ #  # ]:          0 :                         if (d->devnum != last_devnum+1) {
     338                 :            :                                 /* Found a free devnum. Plug it in here */
     339                 :          0 :                                 new->devnum = last_devnum+1;
     340                 :          0 :                                 list_add_tail(&new->list, &d->list);
     341                 :            :                                 goto added;
     342                 :            :                         }
     343         [ #  # ]:          0 :                 } else if (d->devnum == new->devnum) {
     344                 :            :                         /* Required number taken */
     345                 :          0 :                         mutex_unlock(&blktrans_ref_mutex);
     346                 :          0 :                         return -EBUSY;
     347         [ #  # ]:          0 :                 } else if (d->devnum > new->devnum) {
     348                 :            :                         /* Required number was free */
     349                 :          0 :                         list_add_tail(&new->list, &d->list);
     350                 :            :                         goto added;
     351                 :            :                 }
     352                 :          0 :                 last_devnum = d->devnum;
     353                 :            :         }
     354                 :            : 
     355                 :            :         ret = -EBUSY;
     356         [ #  # ]:          0 :         if (new->devnum == -1)
     357                 :          0 :                 new->devnum = last_devnum+1;
     358                 :            : 
     359                 :            :         /* Check that the device and any partitions will get valid
     360                 :            :          * minor numbers and that the disk naming code below can cope
     361                 :            :          * with this number. */
     362 [ #  # ][ #  # ]:          0 :         if (new->devnum > (MINORMASK >> tr->part_bits) ||
     363         [ #  # ]:          0 :             (tr->part_bits && new->devnum >= 27 * 26)) {
     364                 :          0 :                 mutex_unlock(&blktrans_ref_mutex);
     365                 :          0 :                 goto error1;
     366                 :            :         }
     367                 :            : 
     368                 :          0 :         list_add_tail(&new->list, &tr->devs);
     369                 :            :  added:
     370                 :          0 :         mutex_unlock(&blktrans_ref_mutex);
     371                 :            : 
     372                 :          0 :         mutex_init(&new->lock);
     373                 :            :         kref_init(&new->ref);
     374         [ #  # ]:          0 :         if (!tr->writesect)
     375                 :          0 :                 new->readonly = 1;
     376                 :            : 
     377                 :            :         /* Create gendisk */
     378                 :            :         ret = -ENOMEM;
     379                 :          0 :         gd = alloc_disk(1 << tr->part_bits);
     380                 :            : 
     381         [ #  # ]:          0 :         if (!gd)
     382                 :            :                 goto error2;
     383                 :            : 
     384                 :          0 :         new->disk = gd;
     385                 :          0 :         gd->private_data = new;
     386                 :          0 :         gd->major = tr->major;
     387                 :          0 :         gd->first_minor = (new->devnum) << tr->part_bits;
     388                 :          0 :         gd->fops = &mtd_block_ops;
     389                 :            : 
     390         [ #  # ]:          0 :         if (tr->part_bits)
     391         [ #  # ]:          0 :                 if (new->devnum < 26)
     392                 :          0 :                         snprintf(gd->disk_name, sizeof(gd->disk_name),
     393                 :            :                                  "%s%c", tr->name, 'a' + new->devnum);
     394                 :            :                 else
     395                 :          0 :                         snprintf(gd->disk_name, sizeof(gd->disk_name),
     396                 :            :                                  "%s%c%c", tr->name,
     397                 :          0 :                                  'a' - 1 + new->devnum / 26,
     398                 :          0 :                                  'a' + new->devnum % 26);
     399                 :            :         else
     400                 :          0 :                 snprintf(gd->disk_name, sizeof(gd->disk_name),
     401                 :            :                          "%s%d", tr->name, new->devnum);
     402                 :            : 
     403                 :          0 :         set_capacity(gd, (new->size * tr->blksize) >> 9);
     404                 :            : 
     405                 :            :         /* Create the request queue */
     406                 :          0 :         spin_lock_init(&new->queue_lock);
     407                 :          0 :         new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);
     408                 :            : 
     409         [ #  # ]:          0 :         if (!new->rq)
     410                 :            :                 goto error3;
     411                 :            : 
     412                 :          0 :         new->rq->queuedata = new;
     413                 :          0 :         blk_queue_logical_block_size(new->rq, tr->blksize);
     414                 :            : 
     415                 :          0 :         queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq);
     416                 :            : 
     417         [ #  # ]:          0 :         if (tr->discard) {
     418                 :          0 :                 queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
     419                 :          0 :                 new->rq->limits.max_discard_sectors = UINT_MAX;
     420                 :            :         }
     421                 :            : 
     422                 :          0 :         gd->queue = new->rq;
     423                 :            : 
     424                 :            :         /* Create processing workqueue */
     425                 :          0 :         new->wq = alloc_workqueue("%s%d", 0, 0,
     426                 :            :                                   tr->name, new->mtd->index);
     427         [ #  # ]:          0 :         if (!new->wq)
     428                 :            :                 goto error4;
     429                 :          0 :         INIT_WORK(&new->work, mtd_blktrans_work);
     430                 :            : 
     431                 :          0 :         gd->driverfs_dev = &new->mtd->dev;
     432                 :            : 
     433         [ #  # ]:          0 :         if (new->readonly)
     434                 :          0 :                 set_disk_ro(gd, 1);
     435                 :            : 
     436                 :          0 :         add_disk(gd);
     437                 :            : 
     438         [ #  # ]:          0 :         if (new->disk_attributes) {
     439                 :          0 :                 ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
     440                 :            :                                         new->disk_attributes);
     441         [ #  # ]:          0 :                 WARN_ON(ret);
     442                 :            :         }
     443                 :            :         return 0;
     444                 :            : error4:
     445                 :          0 :         blk_cleanup_queue(new->rq);
     446                 :            : error3:
     447                 :          0 :         put_disk(new->disk);
     448                 :            : error2:
     449                 :            :         list_del(&new->list);
     450                 :            : error1:
     451                 :          0 :         return ret;
     452                 :            : }
     453                 :            : 
     454                 :          0 : int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
     455                 :            : {
     456                 :            :         unsigned long flags;
     457                 :            : 
     458         [ #  # ]:          0 :         if (mutex_trylock(&mtd_table_mutex)) {
     459                 :          0 :                 mutex_unlock(&mtd_table_mutex);
     460                 :          0 :                 BUG();
     461                 :            :         }
     462                 :            : 
     463         [ #  # ]:          0 :         if (old->disk_attributes)
     464                 :          0 :                 sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
     465                 :            :                                                 old->disk_attributes);
     466                 :            : 
     467                 :            :         /* Stop new requests to arrive */
     468                 :          0 :         del_gendisk(old->disk);
     469                 :            : 
     470                 :            :         /* Stop workqueue. This will perform any pending request. */
     471                 :          0 :         destroy_workqueue(old->wq);
     472                 :            : 
     473                 :            :         /* Kill current requests */
     474                 :          0 :         spin_lock_irqsave(&old->queue_lock, flags);
     475                 :          0 :         old->rq->queuedata = NULL;
     476                 :          0 :         blk_start_queue(old->rq);
     477                 :            :         spin_unlock_irqrestore(&old->queue_lock, flags);
     478                 :            : 
     479                 :            :         /* If the device is currently open, tell trans driver to close it,
     480                 :            :                 then put mtd device, and don't touch it again */
     481                 :          0 :         mutex_lock(&old->lock);
     482         [ #  # ]:          0 :         if (old->open) {
     483         [ #  # ]:          0 :                 if (old->tr->release)
     484                 :          0 :                         old->tr->release(old);
     485                 :          0 :                 __put_mtd_device(old->mtd);
     486                 :            :         }
     487                 :            : 
     488                 :          0 :         old->mtd = NULL;
     489                 :            : 
     490                 :          0 :         mutex_unlock(&old->lock);
     491                 :          0 :         blktrans_dev_put(old);
     492                 :          0 :         return 0;
     493                 :            : }
     494                 :            : 
     495                 :          0 : static void blktrans_notify_remove(struct mtd_info *mtd)
     496                 :            : {
     497                 :            :         struct mtd_blktrans_ops *tr;
     498                 :            :         struct mtd_blktrans_dev *dev, *next;
     499                 :            : 
     500         [ #  # ]:          0 :         list_for_each_entry(tr, &blktrans_majors, list)
     501         [ #  # ]:          0 :                 list_for_each_entry_safe(dev, next, &tr->devs, list)
     502         [ #  # ]:          0 :                         if (dev->mtd == mtd)
     503                 :          0 :                                 tr->remove_dev(dev);
     504                 :          0 : }
     505                 :            : 
     506                 :          0 : static void blktrans_notify_add(struct mtd_info *mtd)
     507                 :            : {
     508                 :            :         struct mtd_blktrans_ops *tr;
     509                 :            : 
     510         [ #  # ]:          0 :         if (mtd->type == MTD_ABSENT)
     511                 :          0 :                 return;
     512                 :            : 
     513         [ #  # ]:          0 :         list_for_each_entry(tr, &blktrans_majors, list)
     514                 :          0 :                 tr->add_mtd(tr, mtd);
     515                 :            : }
     516                 :            : 
     517                 :            : static struct mtd_notifier blktrans_notifier = {
     518                 :            :         .add = blktrans_notify_add,
     519                 :            :         .remove = blktrans_notify_remove,
     520                 :            : };
     521                 :            : 
     522                 :          0 : int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
     523                 :            : {
     524                 :            :         struct mtd_info *mtd;
     525                 :            :         int ret;
     526                 :            : 
     527                 :            :         /* Register the notifier if/when the first device type is
     528                 :            :            registered, to prevent the link/init ordering from fucking
     529                 :            :            us over. */
     530         [ #  # ]:          0 :         if (!blktrans_notifier.list.next)
     531                 :          0 :                 register_mtd_user(&blktrans_notifier);
     532                 :            : 
     533                 :            : 
     534                 :          0 :         mutex_lock(&mtd_table_mutex);
     535                 :            : 
     536                 :          0 :         ret = register_blkdev(tr->major, tr->name);
     537         [ #  # ]:          0 :         if (ret < 0) {
     538                 :          0 :                 printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
     539                 :            :                        tr->name, tr->major, ret);
     540                 :          0 :                 mutex_unlock(&mtd_table_mutex);
     541                 :          0 :                 return ret;
     542                 :            :         }
     543                 :            : 
     544         [ #  # ]:          0 :         if (ret)
     545                 :          0 :                 tr->major = ret;
     546                 :            : 
     547                 :          0 :         tr->blkshift = ffs(tr->blksize) - 1;
     548                 :            : 
     549                 :          0 :         INIT_LIST_HEAD(&tr->devs);
     550                 :          0 :         list_add(&tr->list, &blktrans_majors);
     551                 :            : 
     552         [ #  # ]:          0 :         mtd_for_each_device(mtd)
     553         [ #  # ]:          0 :                 if (mtd->type != MTD_ABSENT)
     554                 :          0 :                         tr->add_mtd(tr, mtd);
     555                 :            : 
     556                 :          0 :         mutex_unlock(&mtd_table_mutex);
     557                 :          0 :         return 0;
     558                 :            : }
     559                 :            : 
     560                 :          0 : int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
     561                 :            : {
     562                 :            :         struct mtd_blktrans_dev *dev, *next;
     563                 :            : 
     564                 :          0 :         mutex_lock(&mtd_table_mutex);
     565                 :            : 
     566                 :            :         /* Remove it from the list of active majors */
     567                 :            :         list_del(&tr->list);
     568                 :            : 
     569         [ #  # ]:          0 :         list_for_each_entry_safe(dev, next, &tr->devs, list)
     570                 :          0 :                 tr->remove_dev(dev);
     571                 :            : 
     572                 :          0 :         unregister_blkdev(tr->major, tr->name);
     573                 :          0 :         mutex_unlock(&mtd_table_mutex);
     574                 :            : 
     575         [ #  # ]:          0 :         BUG_ON(!list_empty(&tr->devs));
     576                 :          0 :         return 0;
     577                 :            : }
     578                 :            : 
     579                 :          0 : static void __exit mtd_blktrans_exit(void)
     580                 :            : {
     581                 :            :         /* No race here -- if someone's currently in register_mtd_blktrans
     582                 :            :            we're screwed anyway. */
     583         [ #  # ]:          0 :         if (blktrans_notifier.list.next)
     584                 :          0 :                 unregister_mtd_user(&blktrans_notifier);
     585                 :          0 : }
     586                 :            : 
     587                 :            : module_exit(mtd_blktrans_exit);
     588                 :            : 
     589                 :            : EXPORT_SYMBOL_GPL(register_mtd_blktrans);
     590                 :            : EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
     591                 :            : EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
     592                 :            : EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
     593                 :            : 
     594                 :            : MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
     595                 :            : MODULE_LICENSE("GPL");
     596                 :            : MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");

Generated by: LCOV version 1.9