/* * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> * Copyright (C) 2004-2006 Joern Engel <joern@wh.fh-wedel.de> * * Licence: GPL
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* * When the first attempt at device initialization fails, we may need to * wait a little bit and retry. This timeout, by default 3 seconds, gives * device time to start up. Required on BCM2708 and a few other chipsets.
*/ #define MTD_DEFAULT_TIMEOUT 3
/* erase a specified part of the device */ staticint _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
{ struct address_space *mapping = dev->bdev_file->f_mapping; struct page *page;
pgoff_t index = to >> PAGE_SHIFT; // page index int pages = len >> PAGE_SHIFT;
u_long *p;
u_long *max;
while (pages) {
page = page_read(mapping, index); if (IS_ERR(page)) return PTR_ERR(page);
max = page_address(page) + PAGE_SIZE; for (p=page_address(page); p<max; p++) if (*p != -1UL) {
lock_page(page);
memset(page_address(page), 0xff, PAGE_SIZE);
set_page_dirty(page);
unlock_page(page);
balance_dirty_pages_ratelimited(mapping); break;
}
put_page(page);
pages--;
index++;
} return 0;
} staticint block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{ struct block2mtd_dev *dev = mtd->priv;
size_t from = instr->addr;
size_t len = instr->len; int err;
/* write data to the underlying device */ staticint _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
loff_t to, size_t len, size_t *retlen)
{ struct page *page; struct address_space *mapping = dev->bdev_file->f_mapping;
pgoff_t index = to >> PAGE_SHIFT; // page index int offset = to & ~PAGE_MASK; // page offset int cpylen;
while (len) { if ((offset+len) > PAGE_SIZE)
cpylen = PAGE_SIZE - offset; // multiple pages else
cpylen = len; // this page
len = len - cpylen;
page = page_read(mapping, index); if (IS_ERR(page)) return PTR_ERR(page);
mutex_lock(&dev->write_mutex);
err = _block2mtd_write(dev, buf, to, len, retlen);
mutex_unlock(&dev->write_mutex); if (err > 0)
err = 0; return err;
}
/* sync the device - wait until the write queue is empty */ staticvoid block2mtd_sync(struct mtd_info *mtd)
{ struct block2mtd_dev *dev = mtd->priv;
sync_blockdev(file_bdev(dev->bdev_file)); return;
}
staticvoid block2mtd_free_device(struct block2mtd_dev *dev)
{ if (!dev) return;
kfree(dev->mtd.name);
if (dev->bdev_file) {
invalidate_mapping_pages(dev->bdev_file->f_mapping, 0, -1);
bdev_fput(dev->bdev_file);
}
kfree(dev);
}
/* * This function is marked __ref because it calls the __init marked * early_lookup_bdev when called from the early boot code.
*/ staticstruct file __ref *mdtblock_early_get_bdev(constchar *devname,
blk_mode_t mode, int timeout, struct block2mtd_dev *dev)
{ struct file *bdev_file = ERR_PTR(-ENODEV); #ifndef MODULE int i;
/* * We can't use early_lookup_bdev from a running system.
*/ if (system_state >= SYSTEM_RUNNING) return bdev_file;
/* * We might not have the root device mounted at this point. * Try to resolve the device name by other means.
*/ for (i = 0; i <= timeout; i++) {
dev_t devt;
if (i) /* * Calling wait_for_device_probe in the first loop * was not enough, sleep for a bit in subsequent * go-arounds.
*/
msleep(1000);
wait_for_device_probe();
if (!early_lookup_bdev(devname, &devt)) {
bdev_file = bdev_file_open_by_dev(devt, mode, dev, NULL); if (!IS_ERR(bdev_file)) break;
}
} #endif return bdev_file;
}
dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); if (!dev) return NULL;
/* Get a handle on the device */
bdev_file = bdev_file_open_by_path(devname, mode, dev, NULL); if (IS_ERR(bdev_file))
bdev_file = mdtblock_early_get_bdev(devname, mode, timeout,
dev); if (IS_ERR(bdev_file)) {
pr_err("error: cannot open device %s\n", devname); goto err_free_block2mtd;
}
dev->bdev_file = bdev_file;
bdev = file_bdev(bdev_file);
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
pr_err("attempting to use an MTD device as a block device\n"); goto err_free_block2mtd;
}
size = bdev_nr_bytes(bdev); if ((long)size % erase_size) {
pr_err("erasesize must be a divisor of device size\n"); goto err_free_block2mtd;
}
mutex_init(&dev->write_mutex);
/* Setup the MTD structure */ /* make the name contain the block device in */ if (!label)
name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); else
name = kstrdup(label, GFP_KERNEL); if (!name) goto err_destroy_mutex;
/* This function works similar to reguler strtoul. In addition, it * allows some suffixes for a more human-readable number format: * ki, Ki, kiB, KiB - multiply result with 1024 * Mi, MiB - multiply result with 1024^2 * Gi, GiB - multiply result with 1024^3
*/ staticint ustrtoul(constchar *cp, char **endp, unsignedint base)
{ unsignedlong result = simple_strtoul(cp, endp, base); switch (**endp) { case'G' :
result *= 1024;
fallthrough; case'M':
result *= 1024;
fallthrough; case'K': case'k':
result *= 1024; /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ if ((*endp)[1] == 'i') { if ((*endp)[2] == 'B')
(*endp) += 3; else
(*endp) += 2;
}
} return result;
}
#ifndef MODULE staticint block2mtd_init_called = 0; /* 80 for device, 12 for erase size */ staticchar block2mtd_paramline[80 + 12]; #endif
staticint block2mtd_setup2(constchar *val)
{ /* 80 for device, 12 for erase size, 80 for name, 8 for timeout */ char buf[80 + 12 + 80 + 8]; char *str = buf; char *token[BLOCK2MTD_PARAM_MAX_COUNT]; char *name; char *label = NULL;
size_t erase_size = PAGE_SIZE; unsignedlong timeout = MTD_DEFAULT_TIMEOUT; int i, ret;
if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
pr_err("parameter too long\n"); return 0;
}
strcpy(str, val);
kill_final_newline(str);
for (i = 0; i < BLOCK2MTD_PARAM_MAX_COUNT; i++)
token[i] = strsep(&str, ",");
if (str) {
pr_err("too many arguments\n"); return 0;
}
if (!token[0]) {
pr_err("no argument\n"); return 0;
}
name = token[0]; if (strlen(name) + 1 > 80) {
pr_err("device name too long\n"); return 0;
}
/* Optional argument when custom label is used */ if (token[1] && strlen(token[1])) {
ret = parse_num(&erase_size, token[1]); if (ret) {
pr_err("illegal erase size\n"); return 0;
}
}
if (token[2]) {
label = token[2];
pr_info("Using custom MTD label '%s' for dev %s\n", label, name);
}
add_device(name, erase_size, label, timeout);
return 0;
}
staticint block2mtd_setup(constchar *val, conststruct kernel_param *kp)
{ #ifdef MODULE return block2mtd_setup2(val); #else /* If more parameters are later passed in via /sys/module/block2mtd/parameters/block2mtd and block2mtd_init() has already been called,
we can parse the argument now. */
if (block2mtd_init_called) return block2mtd_setup2(val);
/* During early boot stage, we only save the parameters here. We must parse them later: if the param passed from kernel boot command line, block2mtd_setup() is called so early that it is not possible to resolve the device (even kmalloc() fails). Deter that work to
block2mtd_setup2(). */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.