cregit-Linux how code gets into the kernel

Release 4.11 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 <linux/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 Levitsky6098.36%266.67%
H 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 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 Levitsky3296.97%150.00%
H 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_op(req) == REQ_OP_FLUSH) return tr->flush(dev); if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) return -EIO; switch (req_op(req)) { case REQ_OP_DISCARD: return tr->discard(dev, block, nsect); case REQ_OP_READ: for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->readsect(dev, block, buf)) return -EIO; rq_flush_dcache_pages(req); return 0; case REQ_OP_WRITE: 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; default: return -EIO; } }

Contributors

PersonTokensPropCommitsCommitProp
David Woodhouse13355.88%18.33%
Christoph Hellwig3113.03%216.67%
Tejun Heo229.24%325.00%
Richard Purdie187.56%18.33%
Roman Peniaev125.04%18.33%
Ilya Loginov104.20%18.33%
Michael Christie83.36%216.67%
Jens Axboe41.68%18.33%
Total238100.00%12100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
Jarkko Lavinen1493.33%150.00%
Artem B. 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 Woodhouse8438.01%218.18%
Jarkko Lavinen7132.13%19.09%
Tejun Heo2410.86%327.27%
Ezequiel García167.24%19.09%
Artem B. Bityutskiy104.52%19.09%
Andrew Morton73.17%19.09%
Maxim Levitsky52.26%19.09%
Ingo 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 Levitsky4058.82%240.00%
David Woodhouse2029.41%120.00%
Ezequiel García68.82%120.00%
Artem B. 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 B. Bityutskiy8034.48%19.09%
Maxim Levitsky5322.84%218.18%
David Woodhouse5222.41%218.18%
Brian Norris3213.79%327.27%
Al Viro83.45%19.09%
Alexander Stein62.59%19.09%
Arnd 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 Levitsky6450.79%222.22%
David Woodhouse3326.19%222.22%
Brian Norris1814.29%222.22%
Al 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 Levitsky5454.00%125.00%
Christoph Hellwig4444.00%125.00%
Wenlin Kang11.00%125.00%
Brian Norris11.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 Levitsky6252.10%120.00%
David Woodhouse5142.86%240.00%
Al Viro54.20%120.00%
Dan 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); if (new->readonly) set_disk_ro(gd, 1); device_add_disk(&new->mtd->dev, 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 Woodhouse34947.16%313.64%
Maxim Levitsky21328.78%418.18%
Todd Android Poynor7610.27%14.55%
Ben Hutchings192.57%14.55%
Ezequiel García172.30%29.09%
Roman Peniaev131.76%14.55%
Mike Snitzer91.22%14.55%
Dan McGee91.22%14.55%
Dan J Williams81.08%14.55%
Jens Axboe81.08%29.09%
Jarkko Lavinen70.95%14.55%
Chris Malley60.81%14.55%
Peng Fan30.41%14.55%
Ingo Molnar20.27%14.55%
Richard Purdie10.14%14.55%
Total740100.00%22100.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 Levitsky12271.35%562.50%
David Woodhouse4425.73%112.50%
Ezequiel García31.75%112.50%
Ingo 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 Woodhouse4578.95%266.67%
Chris 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 Woodhouse3788.10%266.67%
Chris 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 Woodhouse11874.68%116.67%
Richard Purdie138.23%116.67%
Frank Li127.59%116.67%
Ben Hutchings116.96%116.67%
Ingo Molnar31.90%116.67%
Thomas 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 Woodhouse7287.80%125.00%
Chris Malley56.10%125.00%
Eric Sesterhenn / Snakebyte33.66%125.00%
Ingo 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 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 Woodhouse118543.99%69.23%
Maxim Levitsky76828.51%710.77%
Jarkko Lavinen973.60%23.08%
Artem B. Bityutskiy933.45%23.08%
Christoph Hellwig802.97%34.62%
Todd Android Poynor762.82%11.54%
Brian Norris511.89%46.15%
Tejun Heo461.71%57.69%
Ezequiel García431.60%23.08%
Richard Purdie321.19%11.54%
Ben Hutchings301.11%23.08%
Chris Malley281.04%11.54%
Al Viro260.97%34.62%
Roman Peniaev250.93%11.54%
Ingo Molnar140.52%11.54%
Frank Li120.45%11.54%
Jens Axboe120.45%34.62%
Ilya Loginov100.37%11.54%
Mike Snitzer90.33%11.54%
Dan McGee90.33%11.54%
Michael Christie80.30%23.08%
Dan J Williams80.30%11.54%
Andrew Morton70.26%11.54%
Alexander Stein60.22%11.54%
Ben Dooks40.15%23.08%
Eric Sesterhenn / Snakebyte30.11%11.54%
Peng Fan30.11%11.54%
Arnd Bergmann20.07%23.08%
H Hartley Sweeten20.07%11.54%
Wenlin Kang10.04%11.54%
Thomas Gleixner10.04%11.54%
Linus Torvalds10.04%11.54%
Dan Carpenter10.04%11.54%
Alexey Dobriyan10.04%11.54%
Total2694100.00%65100.00%
Directory: drivers/mtd
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.