📄 blkmtd-24.c
字号:
pagenr++; pages--; unlock_page(page); if(!PageDirty(page)) page_cache_release(page); } readerr: kfree(pagelst); kfree(pagenrs); DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", *retlen, err); return err;}/* write data to the underlying device */static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ struct blkmtd_dev *dev = mtd->priv; int err; *retlen = 0; if(!len) return 0; DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", bdevname(dev->binding->bd_dev), to, len, buf); /* handle readonly and out of range numbers */ if(!dev->wr_buf) { err("error: trying to write to a readonly device %s", mtd->name); return -EROFS; } if(to >= mtd->size) { return -ENOSPC; } if(to + len > mtd->size) { len = (mtd->size - to); } err = write_pages(dev, buf, to, len, retlen); if(err < 0) *retlen = 0; else err = 0; DEBUG(2, "blkmtd: write: end, err = %d\n", err); return err;}/* sync the device - wait until the write queue is empty */static void blkmtd_sync(struct mtd_info *mtd){ struct blkmtd_dev *dev = mtd->priv; struct kiobuf *iobuf = dev->wr_buf; DEBUG(2, "blkmtd: sync: called\n"); if(iobuf == NULL) return; DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", iobuf->length, iobuf->nr_pages); down(&dev->wrbuf_mutex); if(iobuf->nr_pages) commit_pages(dev); up(&dev->wrbuf_mutex);}#ifdef BLKMTD_PROC_DEBUG/* procfs stuff */static int blkmtd_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){ int len; struct list_head *temp1, *temp2; MOD_INC_USE_COUNT; /* Count the size of the page lists */ len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); list_for_each_safe(temp1, temp2, &blkmtd_device_list) { struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, list); struct list_head *temp; struct page *pagei; int clean = 0, dirty = 0, locked = 0, lru = 0; /* Count the size of the page lists */ list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { pagei = list_entry(temp, struct page, list); clean++; if(PageLocked(pagei)) locked++; if(PageDirty(pagei)) dirty++; if(PageLRU(pagei)) lru++; } list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { pagei = list_entry(temp, struct page, list); if(PageLocked(pagei)) locked++; if(PageDirty(pagei)) dirty++; if(PageLRU(pagei)) lru++; } list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { pagei = list_entry(temp, struct page, list); if(PageLocked(pagei)) locked++; if(PageDirty(pagei)) dirty++; if(PageLRU(pagei)) lru++; } len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", dev->mtd_info.index, (dev->wr_buf && dev->wr_buf->nr_pages) ? dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, dev->binding->bd_inode->i_mapping->nrpages, clean, dirty, locked, lru); } if(len <= count) *eof = 1; MOD_DEC_USE_COUNT; return len;}#endifstatic void free_device(struct blkmtd_dev *dev){ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); if(dev) { del_mtd_device(&dev->mtd_info); info("mtd%d: [%s] removed", dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: ")); if(dev->mtd_info.eraseregions) kfree(dev->mtd_info.eraseregions); if(dev->mtd_info.name) kfree(dev->mtd_info.name); if(dev->rd_buf) { dev->rd_buf->locked = 0;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) if(dev->rd_buf->blocks) kfree(dev->rd_buf->blocks);#endif free_kiovec(1, &dev->rd_buf); } if(dev->wr_buf) { dev->wr_buf->locked = 0;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) if(dev->wr_buf->blocks) kfree(dev->rw_buf->blocks);#endif free_kiovec(1, &dev->wr_buf); } if(dev->binding) { kdev_t kdev = to_kdev_t(dev->binding->bd_dev); invalidate_inode_pages(dev->binding->bd_inode); set_blocksize(kdev, 1 << 10); blkdev_put(dev->binding, BDEV_RAW); } kfree(dev); }}/* For a given size and initial erase size, calculate the number * and size of each erase region. Goes round the loop twice, * once to find out how many regions, then allocates space, * then round the loop again to fill it in. */static struct mtd_erase_region_info *calc_erase_regions( size_t erase_size, size_t total_size, int *regions){ struct mtd_erase_region_info *info = NULL; DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); /* Make any user specified erasesize be a power of 2 and at least PAGE_SIZE */ if(erase_size) { int es = erase_size; erase_size = 1; while(es != 1) { es >>= 1; erase_size <<= 1; } if(erase_size < PAGE_SIZE) erase_size = PAGE_SIZE; } else { erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; } *regions = 0; do { int tot_size = total_size; int er_size = erase_size; int count = 0, offset = 0, regcnt = 0; while(tot_size) { count = tot_size / er_size; if(count) { tot_size = tot_size % er_size; if(info) { DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", offset, er_size, count); (info+regcnt)->offset = offset; (info+regcnt)->erasesize = er_size; (info+regcnt)->numblocks = count; (*regions)++; } regcnt++; offset += (count * er_size); } while(er_size > tot_size) er_size >>= 1; } if(info == NULL) { info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); if(!info) break; } } while(!(*regions)); DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); return info;}extern kdev_t name_to_kdev_t(char *line) __init;static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size){ int maj, min; kdev_t kdev; int mode; struct blkmtd_dev *dev;#ifdef MODULE struct file *file = NULL; struct inode *inode;#endif if(!devname) return NULL; /* Get a handle on the device */ mode = (readonly) ? O_RDONLY : O_RDWR;#ifdef MODULE file = filp_open(devname, mode, 0); if(IS_ERR(file)) { err("error: cant open device %s", devname); DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); return NULL; } /* determine is this is a block device and * if so get its major and minor numbers */ inode = file->f_dentry->d_inode; if(!S_ISBLK(inode->i_mode)) { err("%s not a block device", devname); filp_close(file, NULL); return NULL; } kdev = inode->i_rdev; filp_close(file, NULL);#else kdev = name_to_kdev_t(devname);#endif /* MODULE */ if(!kdev) { err("bad block device: `%s'", devname); return NULL; } maj = MAJOR(kdev); min = MINOR(kdev); DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", maj, min); if(maj == MTD_BLOCK_MAJOR) { err("attempting to use an MTD device as a block device"); return NULL; } DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); if(dev == NULL) return NULL; memset(dev, 0, sizeof(struct blkmtd_dev)); if(alloc_kiovec(1, &dev->rd_buf)) { err("cant allocate read iobuf"); goto devinit_err; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); if(dev->rd_buf->blocks == NULL) { crit("cant allocate rd_buf blocks"); goto devinit_err; }#endif if(!readonly) { if(alloc_kiovec(1, &dev->wr_buf)) { err("cant allocate kiobuf - readonly enabled");#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) } else { dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); if(dev->wr_buf->blocks == NULL) { crit("cant allocate wr_buf blocks - readonly enabled"); free_kiovec(1, &iobuf); }#endif } if(dev->wr_buf) init_MUTEX(&dev->wrbuf_mutex); } /* get the block device */ dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) goto devinit_err; if(set_blocksize(kdev, PAGE_SIZE)) { err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); goto devinit_err; } dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; /* Setup the MTD structure */ /* make the name contain the block device in */ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); if(dev->mtd_info.name == NULL) goto devinit_err; sprintf(dev->mtd_info.name, "blkmtd: %s", devname); dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, &dev->mtd_info.numeraseregions); if(dev->mtd_info.eraseregions == NULL) goto devinit_err; dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; DEBUG(1, "blkmtd: init: found %d erase regions\n", dev->mtd_info.numeraseregions); if(readonly) { dev->mtd_info.type = MTD_ROM; dev->mtd_info.flags = MTD_CAP_ROM; } else { dev->mtd_info.type = MTD_RAM; dev->mtd_info.flags = MTD_CAP_RAM; } dev->mtd_info.erase = blkmtd_erase; dev->mtd_info.read = blkmtd_read; dev->mtd_info.write = blkmtd_write; dev->mtd_info.sync = blkmtd_sync; dev->mtd_info.point = 0; dev->mtd_info.unpoint = 0; dev->mtd_info.priv = dev; dev->mtd_info.owner = THIS_MODULE; list_add(&dev->list, &blkmtd_device_list); if (add_mtd_device(&dev->mtd_info)) { /* Device didnt get added, so free the entry */ list_del(&dev->list); free_device(dev); return NULL; } else { info("mtd%d: [%s] erase_size = %dKiB %s", dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), dev->mtd_info.erasesize >> 10, (dev->wr_buf) ? "" : "(read-only)"); } return dev; devinit_err: free_device(dev); return NULL;}/* Cleanup and exit - sync the device and kill of the kernel thread */static void __devexit cleanup_blkmtd(void){ struct list_head *temp1, *temp2;#ifdef BLKMTD_PROC_DEBUG if(blkmtd_proc) { remove_proc_entry("blkmtd_debug", NULL); }#endif /* Remove the MTD devices */ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, list); blkmtd_sync(&dev->mtd_info); free_device(dev); }}#ifndef MODULE/* Handle kernel boot params */static int __init param_blkmtd_device(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { device[i] = str; DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); strsep(&str, ","); } return 1;}static int __init param_blkmtd_erasesz(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { char *val = strsep(&str, ","); if(val) erasesz[i] = simple_strtoul(val, NULL, 0); DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); } return 1;}static int __init param_blkmtd_ro(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { char *val = strsep(&str, ","); if(val) ro[i] = simple_strtoul(val, NULL, 0); DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); } return 1;}static int __init param_blkmtd_sync(char *str){ if(str[0] == '1') sync = 1; return 1;}__setup("blkmtd_device=", param_blkmtd_device);__setup("blkmtd_erasesz=", param_blkmtd_erasesz);__setup("blkmtd_ro=", param_blkmtd_ro);__setup("blkmtd_sync=", param_blkmtd_sync);#endif/* Startup */static int __init init_blkmtd(void){ int i; /* Check args - device[0] is the bare minimum*/ if(!device[0]) { err("error: missing `device' name\n"); return -EINVAL; } for(i = 0; i < MAX_DEVICES; i++) add_device(device[i], ro[i], erasesz[i] << 10); if(list_empty(&blkmtd_device_list)) goto init_err; info("version " VERSION);#ifdef BLKMTD_PROC_DEBUG /* create proc entry */ DEBUG(2, "Creating /proc/blkmtd_debug\n"); blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, NULL, blkmtd_proc_read, NULL); if(blkmtd_proc == NULL) { err("Cant create /proc/blkmtd_debug"); } else { blkmtd_proc->owner = THIS_MODULE; }#endif if(!list_empty(&blkmtd_device_list)) /* Everything is ok if we got here */ return 0; init_err: return -EINVAL;}module_init(init_blkmtd);module_exit(cleanup_blkmtd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -