Root/drivers/mtd/mtdblock.c

1/*
2 * Direct MTD block device access
3 *
4 * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
5 * Copyright © 2000-2003 Nicolas Pitre <nico@fluxnic.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23#include <linux/fs.h>
24#include <linux/init.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/sched.h>
28#include <linux/slab.h>
29#include <linux/types.h>
30#include <linux/vmalloc.h>
31
32#include <linux/mtd/mtd.h>
33#include <linux/mtd/blktrans.h>
34#include <linux/mutex.h>
35
36
37struct mtdblk_dev {
38    struct mtd_blktrans_dev mbd;
39    int count;
40    struct mutex cache_mutex;
41    unsigned char *cache_data;
42    unsigned long cache_offset;
43    unsigned int cache_size;
44    enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
45};
46
47static DEFINE_MUTEX(mtdblks_lock);
48
49/*
50 * Cache stuff...
51 *
52 * Since typical flash erasable sectors are much larger than what Linux's
53 * buffer cache can handle, we must implement read-modify-write on flash
54 * sectors for each block write requests. To avoid over-erasing flash sectors
55 * and to speed things up, we locally cache a whole flash sector while it is
56 * being written to until a different sector is required.
57 */
58
59static void erase_callback(struct erase_info *done)
60{
61    wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
62    wake_up(wait_q);
63}
64
65static int erase_write (struct mtd_info *mtd, unsigned long pos,
66            int len, const char *buf)
67{
68    struct erase_info erase;
69    DECLARE_WAITQUEUE(wait, current);
70    wait_queue_head_t wait_q;
71    size_t retlen;
72    int ret;
73
74    /*
75     * First, let's erase the flash block.
76     */
77
78    init_waitqueue_head(&wait_q);
79    erase.mtd = mtd;
80    erase.callback = erase_callback;
81    erase.addr = pos;
82    erase.len = len;
83    erase.priv = (u_long)&wait_q;
84
85    set_current_state(TASK_INTERRUPTIBLE);
86    add_wait_queue(&wait_q, &wait);
87
88    ret = mtd_erase(mtd, &erase);
89    if (ret) {
90        set_current_state(TASK_RUNNING);
91        remove_wait_queue(&wait_q, &wait);
92        printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
93                     "on \"%s\" failed\n",
94            pos, len, mtd->name);
95        return ret;
96    }
97
98    schedule(); /* Wait for erase to finish. */
99    remove_wait_queue(&wait_q, &wait);
100
101    /*
102     * Next, write the data to flash.
103     */
104
105    ret = mtd_write(mtd, pos, len, &retlen, buf);
106    if (ret)
107        return ret;
108    if (retlen != len)
109        return -EIO;
110    return 0;
111}
112
113
114static int write_cached_data (struct mtdblk_dev *mtdblk)
115{
116    struct mtd_info *mtd = mtdblk->mbd.mtd;
117    int ret;
118
119    if (mtdblk->cache_state != STATE_DIRTY)
120        return 0;
121
122    pr_debug("mtdblock: writing cached data for \"%s\" "
123            "at 0x%lx, size 0x%x\n", mtd->name,
124            mtdblk->cache_offset, mtdblk->cache_size);
125
126    ret = erase_write (mtd, mtdblk->cache_offset,
127               mtdblk->cache_size, mtdblk->cache_data);
128    if (ret)
129        return ret;
130
131    /*
132     * Here we could arguably set the cache state to STATE_CLEAN.
133     * However this could lead to inconsistency since we will not
134     * be notified if this content is altered on the flash by other
135     * means. Let's declare it empty and leave buffering tasks to
136     * the buffer cache instead.
137     */
138    mtdblk->cache_state = STATE_EMPTY;
139    return 0;
140}
141
142
143static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
144                int len, const char *buf)
145{
146    struct mtd_info *mtd = mtdblk->mbd.mtd;
147    unsigned int sect_size = mtdblk->cache_size;
148    size_t retlen;
149    int ret;
150
151    pr_debug("mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
152        mtd->name, pos, len);
153
154    if (!sect_size)
155        return mtd_write(mtd, pos, len, &retlen, buf);
156
157    while (len > 0) {
158        unsigned long sect_start = (pos/sect_size)*sect_size;
159        unsigned int offset = pos - sect_start;
160        unsigned int size = sect_size - offset;
161        if( size > len )
162            size = len;
163
164        if (size == sect_size) {
165            /*
166             * We are covering a whole sector. Thus there is no
167             * need to bother with the cache while it may still be
168             * useful for other partial writes.
169             */
170            ret = erase_write (mtd, pos, size, buf);
171            if (ret)
172                return ret;
173        } else {
174            /* Partial sector: need to use the cache */
175
176            if (mtdblk->cache_state == STATE_DIRTY &&
177                mtdblk->cache_offset != sect_start) {
178                ret = write_cached_data(mtdblk);
179                if (ret)
180                    return ret;
181            }
182
183            if (mtdblk->cache_state == STATE_EMPTY ||
184                mtdblk->cache_offset != sect_start) {
185                /* fill the cache with the current sector */
186                mtdblk->cache_state = STATE_EMPTY;
187                ret = mtd_read(mtd, sect_start, sect_size,
188                           &retlen, mtdblk->cache_data);
189                if (ret)
190                    return ret;
191                if (retlen != sect_size)
192                    return -EIO;
193
194                mtdblk->cache_offset = sect_start;
195                mtdblk->cache_size = sect_size;
196                mtdblk->cache_state = STATE_CLEAN;
197            }
198
199            /* write data to our local cache */
200            memcpy (mtdblk->cache_data + offset, buf, size);
201            mtdblk->cache_state = STATE_DIRTY;
202        }
203
204        buf += size;
205        pos += size;
206        len -= size;
207    }
208
209    return 0;
210}
211
212
213static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
214               int len, char *buf)
215{
216    struct mtd_info *mtd = mtdblk->mbd.mtd;
217    unsigned int sect_size = mtdblk->cache_size;
218    size_t retlen;
219    int ret;
220
221    pr_debug("mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
222            mtd->name, pos, len);
223
224    if (!sect_size)
225        return mtd_read(mtd, pos, len, &retlen, buf);
226
227    while (len > 0) {
228        unsigned long sect_start = (pos/sect_size)*sect_size;
229        unsigned int offset = pos - sect_start;
230        unsigned int size = sect_size - offset;
231        if (size > len)
232            size = len;
233
234        /*
235         * Check if the requested data is already cached
236         * Read the requested amount of data from our internal cache if it
237         * contains what we want, otherwise we read the data directly
238         * from flash.
239         */
240        if (mtdblk->cache_state != STATE_EMPTY &&
241            mtdblk->cache_offset == sect_start) {
242            memcpy (buf, mtdblk->cache_data + offset, size);
243        } else {
244            ret = mtd_read(mtd, pos, size, &retlen, buf);
245            if (ret)
246                return ret;
247            if (retlen != size)
248                return -EIO;
249        }
250
251        buf += size;
252        pos += size;
253        len -= size;
254    }
255
256    return 0;
257}
258
259static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
260                  unsigned long block, char *buf)
261{
262    struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
263    return do_cached_read(mtdblk, block<<9, 512, buf);
264}
265
266static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
267                  unsigned long block, char *buf)
268{
269    struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
270    if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
271        mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize);
272        if (!mtdblk->cache_data)
273            return -EINTR;
274        /* -EINTR is not really correct, but it is the best match
275         * documented in man 2 write for all cases. We could also
276         * return -EAGAIN sometimes, but why bother?
277         */
278    }
279    return do_cached_write(mtdblk, block<<9, 512, buf);
280}
281
282static int mtdblock_open(struct mtd_blktrans_dev *mbd)
283{
284    struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
285
286    pr_debug("mtdblock_open\n");
287
288    mutex_lock(&mtdblks_lock);
289    if (mtdblk->count) {
290        mtdblk->count++;
291        mutex_unlock(&mtdblks_lock);
292        return 0;
293    }
294
295    /* OK, it's not open. Create cache info for it */
296    mtdblk->count = 1;
297    mutex_init(&mtdblk->cache_mutex);
298    mtdblk->cache_state = STATE_EMPTY;
299    if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) {
300        mtdblk->cache_size = mbd->mtd->erasesize;
301        mtdblk->cache_data = NULL;
302    }
303
304    mutex_unlock(&mtdblks_lock);
305
306    pr_debug("ok\n");
307
308    return 0;
309}
310
311static int mtdblock_release(struct mtd_blktrans_dev *mbd)
312{
313    struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
314
315    pr_debug("mtdblock_release\n");
316
317    mutex_lock(&mtdblks_lock);
318
319    mutex_lock(&mtdblk->cache_mutex);
320    write_cached_data(mtdblk);
321    mutex_unlock(&mtdblk->cache_mutex);
322
323    if (!--mtdblk->count) {
324        /*
325         * It was the last usage. Free the cache, but only sync if
326         * opened for writing.
327         */
328        if (mbd->file_mode & FMODE_WRITE)
329            mtd_sync(mbd->mtd);
330        vfree(mtdblk->cache_data);
331    }
332
333    mutex_unlock(&mtdblks_lock);
334
335    pr_debug("ok\n");
336
337    return 0;
338}
339
340static int mtdblock_flush(struct mtd_blktrans_dev *dev)
341{
342    struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
343
344    mutex_lock(&mtdblk->cache_mutex);
345    write_cached_data(mtdblk);
346    mutex_unlock(&mtdblk->cache_mutex);
347    mtd_sync(dev->mtd);
348    return 0;
349}
350
351static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
352{
353    struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
354
355    if (!dev)
356        return;
357
358    dev->mbd.mtd = mtd;
359    dev->mbd.devnum = mtd->index;
360
361    dev->mbd.size = mtd->size >> 9;
362    dev->mbd.tr = tr;
363
364    if (!(mtd->flags & MTD_WRITEABLE))
365        dev->mbd.readonly = 1;
366
367    if (add_mtd_blktrans_dev(&dev->mbd))
368        kfree(dev);
369}
370
371static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
372{
373    del_mtd_blktrans_dev(dev);
374}
375
376static struct mtd_blktrans_ops mtdblock_tr = {
377    .name = "mtdblock",
378    .major = 31,
379    .part_bits = 0,
380    .blksize = 512,
381    .open = mtdblock_open,
382    .flush = mtdblock_flush,
383    .release = mtdblock_release,
384    .readsect = mtdblock_readsect,
385    .writesect = mtdblock_writesect,
386    .add_mtd = mtdblock_add_mtd,
387    .remove_dev = mtdblock_remove_dev,
388    .owner = THIS_MODULE,
389};
390
391static int __init init_mtdblock(void)
392{
393    return register_mtd_blktrans(&mtdblock_tr);
394}
395
396static void __exit cleanup_mtdblock(void)
397{
398    deregister_mtd_blktrans(&mtdblock_tr);
399}
400
401module_init(init_mtdblock);
402module_exit(cleanup_mtdblock);
403
404
405MODULE_LICENSE("GPL");
406MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net> et al.");
407MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices");
408

Archive Download this file



interactive