cregit-Linux how code gets into the kernel

Release 4.7 drivers/mtd/mtd_blkdevs.c

Directory: drivers/mtd
/*
 * Interface to Linux block layer for MTD 'translation layers'.
 *
 * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/mtd/blktrans.h>
#include <linux/mtd/mtd.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>

#include "mtdcore.h"

static LIST_HEAD(blktrans_majors);
static DEFINE_MUTEX(blktrans_ref_mutex);


static void blktrans_dev_release(struct kref *kref) { struct mtd_blktrans_dev *dev = container_of(kref, struct mtd_blktrans_dev, ref); dev->disk->private_data = NULL; blk_cleanup_queue(dev->rq); put_disk(dev->disk); list_del(&dev->list); kfree(dev); }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky6098.36%266.67%
h hartley sweetenh hartley sweeten11.64%133.33%
Total61100.00%3100.00%


static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) { struct mtd_blktrans_dev *dev; mutex_lock(&blktrans_ref_mutex); dev = disk->private_data; if (!dev) goto unlock; kref_get(&dev->ref); unlock: mutex_unlock(&blktrans_ref_mutex); return dev; }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky57100.00%1100.00%
Total57100.00%1100.00%


static void blktrans_dev_put(struct mtd_blktrans_dev *dev) { mutex_lock(&blktrans_ref_mutex); kref_put(&dev->ref, blktrans_dev_release); mutex_unlock(&blktrans_ref_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky3296.97%150.00%
h hartley sweetenh hartley sweeten13.03%150.00%
Total33100.00%2100.00%


static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, struct request *req) { unsigned long block, nsect; char *buf; block = blk_rq_pos(req) << 9 >> tr->blkshift; nsect = blk_rq_cur_bytes(req) >> tr->blkshift; buf = bio_data(req->bio); if (req->cmd_type != REQ_TYPE_FS) return -EIO; if (req->cmd_flags & REQ_FLUSH) return tr->flush(dev); if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) return -EIO; if (req->cmd_flags & REQ_DISCARD) return tr->discard(dev, block, nsect); if (rq_data_dir(req) == READ) { for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->readsect(dev, block, buf)) return -EIO; rq_flush_dcache_pages(req); return 0; } else { if (!tr->writesect) return -EIO; rq_flush_dcache_pages(req); for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->writesect(dev, block, buf)) return -EIO; return 0; } }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse14658.87%19.09%
tejun heotejun heo249.68%327.27%
christoph hellwigchristoph hellwig249.68%218.18%
richard purdierichard purdie187.26%19.09%
roman peniaevroman peniaev166.45%19.09%
ilya loginovilya loginov104.03%19.09%
tomer barletztomer barletz62.42%19.09%
jens axboejens axboe41.61%19.09%
Total248100.00%11100.00%


int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) { return dev->bg_stop; }

Contributors

PersonTokensPropCommitsCommitProp
jarkko lavinenjarkko lavinen1493.33%150.00%
artem bityutskiyartem bityutskiy16.67%150.00%
Total15100.00%2100.00%

EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
static void mtd_blktrans_work(struct work_struct *work) { struct mtd_blktrans_dev *dev = container_of(work, struct mtd_blktrans_dev, work); struct mtd_blktrans_ops *tr = dev->tr; struct request_queue *rq = dev->rq; struct request *req = NULL; int background_done = 0; spin_lock_irq(rq->queue_lock); while (1) { int res; dev->bg_stop = false; if (!req && !(req = blk_fetch_request(rq))) { if (tr->background && !background_done) { spin_unlock_irq(rq->queue_lock); mutex_lock(&dev->lock); tr->background(dev); mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); /* * Do background processing just once per idle * period. */ background_done = !dev->bg_stop; continue; } break; } spin_unlock_irq(rq->queue_lock); mutex_lock(&dev->lock); res = do_blktrans_request(dev->tr, dev, req); mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); if (!__blk_end_request_cur(req, res)) req = NULL; background_done = 0; } spin_unlock_irq(rq->queue_lock); }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse8438.01%218.18%
jarkko lavinenjarkko lavinen7132.13%19.09%
tejun heotejun heo2410.86%327.27%
ezequiel garciaezequiel garcia167.24%19.09%
artem bityutskiyartem bityutskiy104.52%19.09%
andrew mortonandrew morton73.17%19.09%
maxim levitskymaxim levitsky52.26%19.09%
ingo molnaringo molnar41.81%19.09%
Total221100.00%11100.00%


static void mtd_blktrans_request(struct request_queue *rq) { struct mtd_blktrans_dev *dev; struct request *req = NULL; dev = rq->queuedata; if (!dev) while ((req = blk_fetch_request(rq)) != NULL) __blk_end_request_all(req, -ENODEV); else queue_work(dev->wq, &dev->work); }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky4058.82%240.00%
david woodhousedavid woodhouse2029.41%120.00%
ezequiel garciaezequiel garcia68.82%120.00%
artem bityutskiyartem bityutskiy22.94%120.00%
Total68100.00%5100.00%


static int blktrans_open(struct block_device *bdev, fmode_t mode) { struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); int ret = 0; if (!dev) return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ mutex_lock(&mtd_table_mutex); mutex_lock(&dev->lock); if (dev->open) goto unlock; kref_get(&dev->ref); __module_get(dev->tr->owner); if (!dev->mtd) goto unlock; if (dev->tr->open) { ret = dev->tr->open(dev); if (ret) goto error_put; } ret = __get_mtd_device(dev->mtd); if (ret) goto error_release; dev->file_mode = mode; unlock: dev->open++; mutex_unlock(&dev->lock); mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); return ret; error_release: if (dev->tr->release) dev->tr->release(dev); error_put: module_put(dev->tr->owner); kref_put(&dev->ref, blktrans_dev_release); mutex_unlock(&dev->lock); mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
artem bityutskiyartem bityutskiy8034.48%19.09%
maxim levitskymaxim levitsky5322.84%218.18%
david woodhousedavid woodhouse5222.41%218.18%
brian norrisbrian norris3213.79%327.27%
al viroal viro83.45%19.09%
alexander steinalexander stein62.59%19.09%
arnd bergmannarnd bergmann10.43%19.09%
Total232100.00%11100.00%


static void blktrans_release(struct gendisk *disk, fmode_t mode) { struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); if (!dev) return; mutex_lock(&mtd_table_mutex); mutex_lock(&dev->lock); if (--dev->open) goto unlock; kref_put(&dev->ref, blktrans_dev_release); module_put(dev->tr->owner); if (dev->mtd) { if (dev->tr->release) dev->tr->release(dev); __put_mtd_device(dev->mtd); } unlock: mutex_unlock(&dev->lock); mutex_unlock(&mtd_table_mutex); blktrans_dev_put(dev); }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky6450.79%222.22%
david woodhousedavid woodhouse3326.19%222.22%
brian norrisbrian norris1814.29%222.22%
al viroal viro118.73%333.33%
Total126100.00%9100.00%


static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) { struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); int ret = -ENXIO; if (!dev) return ret; mutex_lock(&dev->lock); if (!dev->mtd) goto unlock; ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY; unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky5454.00%125.00%
christoph hellwigchristoph hellwig4444.00%125.00%
brian norrisbrian norris11.00%125.00%
wenlin kangwenlin kang11.00%125.00%
Total100100.00%4100.00%


static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); int ret = -ENXIO; if (!dev) return ret; mutex_lock(&dev->lock); if (!dev->mtd) goto unlock; switch (cmd) { case BLKFLSBUF: ret = dev->tr->flush ? dev->tr->flush(dev) : 0; break; default: ret = -ENOTTY; } unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky6252.10%120.00%
david woodhousedavid woodhouse5142.86%240.00%
al viroal viro54.20%120.00%
dan carpenterdan carpenter10.84%120.00%
Total119100.00%5100.00%

static const struct block_device_operations mtd_block_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, .ioctl = blktrans_ioctl, .getgeo = blktrans_getgeo, };
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) { struct mtd_blktrans_ops *tr = new->tr; struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; int ret; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } mutex_lock(&blktrans_ref_mutex); list_for_each_entry(d, &tr->devs, list) { if (new->devnum == -1) { /* Use first free number */ if (d->devnum != last_devnum+1) { /* Found a free devnum. Plug it in here */ new->devnum = last_devnum+1; list_add_tail(&new->list, &d->list); goto added; } } else if (d->devnum == new->devnum) { /* Required number taken */ mutex_unlock(&blktrans_ref_mutex); return -EBUSY; } else if (d->devnum > new->devnum) { /* Required number was free */ list_add_tail(&new->list, &d->list); goto added; } last_devnum = d->devnum; } ret = -EBUSY; if (new->devnum == -1) new->devnum = last_devnum+1; /* Check that the device and any partitions will get valid * minor numbers and that the disk naming code below can cope * with this number. */ if (new->devnum > (MINORMASK >> tr->part_bits) || (tr->part_bits && new->devnum >= 27 * 26)) { mutex_unlock(&blktrans_ref_mutex); goto error1; } list_add_tail(&new->list, &tr->devs); added: mutex_unlock(&blktrans_ref_mutex); mutex_init(&new->lock); kref_init(&new->ref); if (!tr->writesect) new->readonly = 1; /* Create gendisk */ ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); if (!gd) goto error2; new->disk = gd; gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_block_ops; if (tr->part_bits) if (new->devnum < 26) snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%c", tr->name, 'a' + new->devnum); else snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%c%c", tr->name, 'a' - 1 + new->devnum / 26, 'a' + new->devnum % 26); else snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", tr->name, new->devnum); set_capacity(gd, ((u64)new->size * tr->blksize) >> 9); /* Create the request queue */ spin_lock_init(&new->queue_lock); new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); if (!new->rq) goto error3; if (tr->flush) blk_queue_write_cache(new->rq, true, false); new->rq->queuedata = new; blk_queue_logical_block_size(new->rq, tr->blksize); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq); queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, new->rq); if (tr->discard) { queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq); blk_queue_max_discard_sectors(new->rq, UINT_MAX); } gd->queue = new->rq; /* Create processing workqueue */ new->wq = alloc_workqueue("%s%d", 0, 0, tr->name, new->mtd->index); if (!new->wq) goto error4; INIT_WORK(&new->work, mtd_blktrans_work); gd->driverfs_dev = &new->mtd->dev; if (new->readonly) set_disk_ro(gd, 1); add_disk(gd); if (new->disk_attributes) { ret = sysfs_create_group(&disk_to_dev(gd)->kobj, new->disk_attributes); WARN_ON(ret); } return 0; error4: blk_cleanup_queue(new->rq); error3: put_disk(new->disk); error2: list_del(&new->list); error1: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse35147.18%417.39%
maxim levitskymaxim levitsky21328.63%417.39%
todd poynortodd poynor7610.22%14.35%
ben hutchingsben hutchings192.55%14.35%
ezequiel garciaezequiel garcia172.28%28.70%
roman peniaevroman peniaev131.75%14.35%
david brownelldavid brownell101.34%14.35%
mike snitzermike snitzer91.21%14.35%
dan mcgeedan mcgee91.21%14.35%
jens axboejens axboe81.08%28.70%
jarkko lavinenjarkko lavinen70.94%14.35%
chris malleychris malley60.81%14.35%
peng fanpeng fan30.40%14.35%
ingo molnaringo molnar20.27%14.35%
richard purdierichard purdie10.13%14.35%
Total744100.00%23100.00%


int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) { unsigned long flags; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } if (old->disk_attributes) sysfs_remove_group(&disk_to_dev(old->disk)->kobj, old->disk_attributes); /* Stop new requests to arrive */ del_gendisk(old->disk); /* Stop workqueue. This will perform any pending request. */ destroy_workqueue(old->wq); /* Kill current requests */ spin_lock_irqsave(&old->queue_lock, flags); old->rq->queuedata = NULL; blk_start_queue(old->rq); spin_unlock_irqrestore(&old->queue_lock, flags); /* If the device is currently open, tell trans driver to close it, then put mtd device, and don't touch it again */ mutex_lock(&old->lock); if (old->open) { if (old->tr->release) old->tr->release(old); __put_mtd_device(old->mtd); } old->mtd = NULL; mutex_unlock(&old->lock); blktrans_dev_put(old); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
maxim levitskymaxim levitsky12271.35%562.50%
david woodhousedavid woodhouse4425.73%112.50%
ezequiel garciaezequiel garcia31.75%112.50%
ingo molnaringo molnar21.17%112.50%
Total171100.00%8100.00%


static void blktrans_notify_remove(struct mtd_info *mtd) { struct mtd_blktrans_ops *tr; struct mtd_blktrans_dev *dev, *next; list_for_each_entry(tr, &blktrans_majors, list) list_for_each_entry_safe(dev, next, &tr->devs, list) if (dev->mtd == mtd) tr->remove_dev(dev); }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse4578.95%266.67%
chris malleychris malley1221.05%133.33%
Total57100.00%3100.00%


static void blktrans_notify_add(struct mtd_info *mtd) { struct mtd_blktrans_ops *tr; if (mtd->type == MTD_ABSENT) return; list_for_each_entry(tr, &blktrans_majors, list) tr->add_mtd(tr, mtd); }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse3788.10%266.67%
chris malleychris malley511.90%133.33%
Total42100.00%3100.00%

static struct mtd_notifier blktrans_notifier = { .add = blktrans_notify_add, .remove = blktrans_notify_remove, };
int register_mtd_blktrans(struct mtd_blktrans_ops *tr) { struct mtd_info *mtd; int ret; /* Register the notifier if/when the first device type is registered, to prevent the link/init ordering from fucking us over. */ if (!blktrans_notifier.list.next) register_mtd_user(&blktrans_notifier); mutex_lock(&mtd_table_mutex); ret = register_blkdev(tr->major, tr->name); if (ret < 0) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); mutex_unlock(&mtd_table_mutex); return ret; } if (ret) tr->major = ret; tr->blkshift = ffs(tr->blksize) - 1; INIT_LIST_HEAD(&tr->devs); list_add(&tr->list, &blktrans_majors); mtd_for_each_device(mtd) if (mtd->type != MTD_ABSENT) tr->add_mtd(tr, mtd); mutex_unlock(&mtd_table_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse11874.68%116.67%
richard purdierichard purdie138.23%116.67%
frank lifrank li127.59%116.67%
ben hutchingsben hutchings116.96%116.67%
ingo molnaringo molnar31.90%116.67%
thomas gleixnerthomas gleixner10.63%116.67%
Total158100.00%6100.00%


int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) { struct mtd_blktrans_dev *dev, *next; mutex_lock(&mtd_table_mutex); /* Remove it from the list of active majors */ list_del(&tr->list); list_for_each_entry_safe(dev, next, &tr->devs, list) tr->remove_dev(dev); unregister_blkdev(tr->major, tr->name); mutex_unlock(&mtd_table_mutex); BUG_ON(!list_empty(&tr->devs)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse7287.80%125.00%
chris malleychris malley56.10%125.00%
eric sesterhenneric sesterhenn33.66%125.00%
ingo molnaringo molnar22.44%125.00%
Total82100.00%4100.00%


static void __exit mtd_blktrans_exit(void) { /* No race here -- if someone's currently in register_mtd_blktrans we're screwed anyway. */ if (blktrans_notifier.list.next) unregister_mtd_user(&blktrans_notifier); }

Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse24100.00%1100.00%
Total24100.00%1100.00%

module_exit(mtd_blktrans_exit); EXPORT_SYMBOL_GPL(register_mtd_blktrans); EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");

Overall Contributors

PersonTokensPropCommitsCommitProp
david woodhousedavid woodhouse120144.35%710.94%
maxim levitskymaxim levitsky76828.36%710.94%
jarkko lavinenjarkko lavinen973.58%23.12%
artem bityutskiyartem bityutskiy933.43%23.12%
todd poynortodd poynor762.81%11.56%
christoph hellwigchristoph hellwig732.70%34.69%
brian norrisbrian norris511.88%46.25%
tejun heotejun heo481.77%57.81%
ezequiel garciaezequiel garcia431.59%23.12%
richard purdierichard purdie321.18%11.56%
ben hutchingsben hutchings301.11%23.12%
roman peniaevroman peniaev291.07%11.56%
chris malleychris malley281.03%11.56%
al viroal viro260.96%34.69%
ingo molnaringo molnar140.52%11.56%
frank lifrank li120.44%11.56%
jens axboejens axboe120.44%34.69%
david brownelldavid brownell100.37%11.56%
ilya loginovilya loginov100.37%11.56%
dan mcgeedan mcgee90.33%11.56%
mike snitzermike snitzer90.33%11.56%
andrew mortonandrew morton70.26%11.56%
tomer barletztomer barletz60.22%11.56%
alexander steinalexander stein60.22%11.56%
ben dooksben dooks40.15%23.12%
eric sesterhenneric sesterhenn30.11%11.56%
peng fanpeng fan30.11%11.56%
arnd bergmannarnd bergmann20.07%23.12%
h hartley sweetenh hartley sweeten20.07%11.56%
thomas gleixnerthomas gleixner10.04%11.56%
alexey dobriyanalexey dobriyan10.04%11.56%
wenlin kangwenlin kang10.04%11.56%
dan carpenterdan carpenter10.04%11.56%
Total2708100.00%64100.00%
Directory: drivers/mtd
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}