[PATCH/RFC] Linux MTD striping middle layer
Vitaly Wool
vwool at ru.mvista.com
Tue Mar 21 09:01:26 EST 2006
Alexander,
1. Looks like it kills XIP.
2. It's pretty funny that you modify only Intel/Sharp command set
implementation, as if the whole MTD exists only for you.
Vitaly
Belyakov, Alexander wrote:
>Hello,
>
>attached diff file is a patch to be applied on MTD snapshot 20060315
>introducing striping feature for Linux MTD. Despite striping
>is well known feature is was not implemented in MTD for some reason.
>We did it and ready to share with community. Hope, striping will find
>its
>place in Linux MTD.
>
>
>1. STRIPING
>(new files here are drivers/mtd/mtdstripe.c and
>include/linux/mtd/stripe.h)
>
>Striping is a MTD middle layer module which allows to join several MTD
>device
>in one by interleaving them. For example, that allows to write to
>different
>physical devices simultaneously significantly increasing overall volume
>performance. It is possible in current solution to stripe NOR, Sibley
>and NAND devices. NOR and Sibley shows up to 85% of performance
>increase if we have just two independent chips in system.
>
>Striping is a MTD middle layer quite similar to concatenation except
>concatenated volume could not show the better performance comparing with
>basic volume.
>
>In the suggested solution it is possible to stripe 2, 4, 8, etc. devices
>of the same type. Note that devices with different sizes are supported.
>
>If the sublayer is build as loadable kernel module (mtdstripe.ko)
>it is possible to pass command line to the module via insmod.
>The format for the command line is as follow:
>
>cmdline_parm="<stripedef>[;<stripedef>]"
><stripedef> := <stripename>(<interleavesize>):<subdevname>.<subdevname>
>
> Example:
> insmod mtdstripe.ko
>cmddline_parm="stripe1(128):vol1.vol3;stripe2(128):vol2.vol4
> Note: you should use '.' as a delimiter for subdevice names here.
>
>If the sub layer is statically linked into kernel it can be configured
>from the
>kernel command line (the same way as for mtdpart module). The format for
>the kernel command line is as follow:
>
> mtdstripe=<stripedef>[;<stripedef>]
> <stripedef> :=
><stripename>(<interleavesize>):<subdevname>,<subdevname>
> Example:
> mtdstripe=stripe1(128):vol1,vol3;stripe2(128):vol2,vol4
>
>In case of static kernel link and kernel configuration string
>parameters set striping is to be initialized by mphysmap module.
>
>Subdevices should belong to different (independent) physical flash
>chips in order to get performance increase. Value "interlavelsize"
>describes striping granularity and it is very important from performance
>point of view. Write operation performance increase should be expected
>only if the amount of data to be written larger than interleave size.
>For example, if we have 512 bytes interleave size, we see no write speed
>boost for files smaller than 512 bytes. File systems have a write buffer
>of
>well known size (let it be 4096 bytes). Thus it is not good idea to set
>interleave size larger than 2048 byte if we are striping two flash chips
>and going to use the file system on it. For NOR devices the bottom
>border
>for interleave size is defined by flash buffer size (64 bytes, 128
>bytes, etc).
>But such a small values affects read speed on striped volumes.
>Read performance decrease on striped volume is due to large number of
>read suboperations. Thus, if you are going to stripe N devices and
>launch a filesystem having write buffer of size B, the better choice
>for interleave size is IS = B / N or somewhat smaller, but not smaller
>than single flash chip buffer size.
>
>Performance increase of this solution is due to simultaneous buffer
>write
>to flash from several threads. On the stage of striped device
>initialization
>several threads created by number of subdevices used. So the main parent
>writing thread splits write operation into parts and pushes these parts
>to
>worker threads queues which write data to subdevices.
>
>In order to provide real simultaneous writes is very important to be
>sure
>that worker thread switches to another while device is flushing data
>from buffer to the chip. For example, having two physical chips we
>should
>observe such a picture. Thread_1 takes data chunk from its queue, put it
>into flash buffer, gives a command to write-buffer-to-flash and after
>that
>switches to Thread_2 which do the same thing but with data chink from
>its
>own queue. After that Thread_2 gave write-buffer-to-flash command it can
>get
>back to Thread_1 or poll his subdevice until write operation completed.
>
>The original MTD code has an issue with such a switching. If we have two
>thread of the same priority, one of them will monopolize CPU until all
>the
>data chunks from its queue are flushed to the chip. Apparently
>such a behavior will not gives any performance increase. Additional
>workaround needed.
>
>Two possible solutions are also presented is the diff file attached.
>First one is more workaround and deals thread priority switching. The
>second one is a solid solution based on CFI common polling thread (CPT)
>creation.
>
>2. Priority switching
>
>The main idea here is to lower priority slightly of the one worker
>thread
>before rescheduling. That gives control to another thread providing
>simultaneous writing. After device has completed write operation thread
>restores its original priority.
>
>Another modification here is concerned with the split udelay time
>in small chunks. Long udelays negatively affects striping
>performance since udelay call is represented by loop and can not be
>interrupted by other thread.
>
>
>3. CPT (Common polling thread)
>(new files here are drivers/mtd/chips/cfi_cpt.c and
>include/linux/mtd/cfi_cpt.h)
>
>Common polling thread is presented as new module in kernel that is being
>used by CFI layer. It creates single polling thread removing
>rescheduling
>problem. Polling for operation completed is being done in one thread
>raising
>semaphores in worker threads. This feature improves performance
>of striped volumes and any operations which used two or more
>physical chips.
>
>The suggested CPT solution can be turned on in kernel configuration
>file.
>
>Please find the complete diff file below.
>
>If you have questions please ask.
>
>Kind Regards,
>Alexander Belyakov
>
>
>
>diff -uNr a/drivers/mtd/chips/cfi_cmdset_0001.c
>b/drivers/mtd/chips/cfi_cmdset_0001.c
>--- a/drivers/mtd/chips/cfi_cmdset_0001.c 2006-03-16
>12:46:25.000000000 +0300
>+++ b/drivers/mtd/chips/cfi_cmdset_0001.c 2006-03-16
>12:35:51.000000000 +0300
>@@ -36,6 +36,10 @@
> #include <linux/mtd/compatmac.h>
> #include <linux/mtd/cfi.h>
>
>+#ifdef CONFIG_MTD_CFI_CPT
>+#include <linux/mtd/cfi_cpt.h>
>+#endif
>+
> /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
> /* #define CMDSET0001_DISABLE_WRITE_SUSPEND */
>
>@@ -1045,19 +1048,62 @@
> #define xip_enable(map, chip, adr)
> #define XIP_INVAL_CACHED_RANGE(x...)
>
>-#define UDELAY(map, chip, adr, usec) \
>-do { \
>- spin_unlock(chip->mutex); \
>- cfi_udelay(usec); \
>- spin_lock(chip->mutex); \
>-} while (0)
>+static void snd_udelay(struct map_info *map, struct flchip *chip,
>+ unsigned long adr, int usec)
>+{
>+ struct cfi_private *cfi = map->fldrv_priv;
>+ map_word status, OK;
>+ int chunk = 10000 / HZ; /* chunk is one percent of HZ
>resolution */
>+ int oldnice = current->static_prio - MAX_RT_PRIO - 20;
>+
>+ /* If we should wait for timeout > than HZ resolution, no need
>+ in resched stuff due to of process sleeping */
>+ if ( 2*usec*HZ >= 1000000) {
>+ msleep((usec+999)/1000);
>+ return;
>+ }
>+
>+ /* Very short time out */
>+ if ( usec == 1 ) {
>+ udelay(usec);
>+ return;
>+ }
>+
>+ /* If we should wait neither too small nor too long */
>+ OK = CMD(0x80);
>+ while ( usec > 0 ) {
>+ spin_unlock(chip->mutex);
>+ /* Lower down thread priority to create concurrency */
>+ if(oldnice > -20)
>+ set_user_nice(current,oldnice - 1);
>+ /* check the status to prevent useless waiting*/
>+ status = map_read(map, adr);
>+ if (map_word_andequal(map, status, OK, OK)) {
>+ /* let recover priority */
>+ set_user_nice(current,oldnice);
>+ break;
>+ }
>+
>+ if (usec < chunk )
>+ udelay(usec);
>+ else
>+ udelay(chunk);
>+
>+ cond_resched();
>+ spin_lock(chip->mutex);
>+
>+ /* let recover priority */
>+ set_user_nice(current,oldnice);
>+ usec -= chunk;
>+ }
>+}
>+
>+#define UDELAY(map, chip, adr, usec) snd_udelay(map, chip, adr, usec)
>
> #define INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr, adr, len, usec) \
> do { \
>- spin_unlock(chip->mutex); \
> INVALIDATE_CACHED_RANGE(map, adr, len); \
>- cfi_udelay(usec); \
>- spin_lock(chip->mutex); \
>+ UDELAY(map, chip, cmd_adr, usec); \
> } while (0)
>
> #endif
>@@ -1452,12 +1498,18 @@
> {
> struct cfi_private *cfi = map->fldrv_priv;
> map_word status, status_OK, write_cmd, datum;
>- unsigned long cmd_adr, timeo;
>+ unsigned long cmd_adr, timeo, prog_timeo;
> int wbufsize, z, ret=0, word_gap, words;
> const struct kvec *vec;
> unsigned long vec_seek;
>+ int datalen = len; /* save it for future use */
>+
>+#ifdef CONFIG_MTD_CFI_CPT
>+ extern struct cpt_thread_info *cpt_info;
>+#endif
>
> wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
>+ prog_timeo = chip->buffer_write_time * len / wbufsize;
> adr += chip->start;
> cmd_adr = adr & ~(wbufsize-1);
>
>@@ -1497,12 +1549,16 @@
> for (;;) {
> map_write(map, write_cmd, cmd_adr);
>
>+#ifndef CONFIG_MTD_CFI_CPT
> status = map_read(map, cmd_adr);
> if (map_word_andequal(map, status, status_OK,
>status_OK))
> break;
>
> UDELAY(map, chip, cmd_adr, 1);
>-
>+#else
>+ if (!cpt_check_wait(cpt_info, chip, map, cmd_adr,
>status_OK, 0))
>+ break;
>+#endif
> if (++z > 20) {
> /* Argh. Not ready for write to buffer */
> map_word Xstatus;
>@@ -1572,9 +1628,11 @@
> map_write(map, CMD(0xd0), cmd_adr);
> chip->state = FL_WRITING;
>
>- INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr,
>- adr, len,
>- chip->buffer_write_time);
>+#ifndef CONFIG_MTD_CFI_CPT
>+ INVALIDATE_CACHE_UDELAY(map, chip,
>+ cmd_adr, adr,
>+ len,
>+ prog_timeo );
>
> timeo = jiffies + (HZ/2);
> z = 0;
>@@ -1610,14 +1668,28 @@
> z++;
> UDELAY(map, chip, cmd_adr, 1);
> }
>- if (!z) {
>+ if (!z && (datalen == wbufsize)) {
> chip->buffer_write_time--;
> if (!chip->buffer_write_time)
> chip->buffer_write_time = 1;
> }
>- if (z > 1)
>+ if ((z > 1) && (datalen == wbufsize))
> chip->buffer_write_time++;
>
>+#else
>+ INVALIDATE_CACHED_RANGE(map, adr, len);
>+ if(cpt_check_wait(cpt_info, chip, map, cmd_adr, status_OK, 1))
>+ {
>+ /* buffer write timeout */
>+ map_write(map, CMD(0x70), cmd_adr);
>+ chip->state = FL_STATUS;
>+ xip_enable(map, chip, cmd_adr);
>+ printk(KERN_ERR "%s: buffer write error (status timeout)\n",
>map->name);
>+ ret = -EIO;
>+ goto out;
>+ }
>+#endif
>+
> /* Done and happy. */
> chip->state = FL_STATUS;
>
>@@ -1693,10 +1765,6 @@
> return 0;
> }
>
>- /* Be nice and reschedule with the chip in a usable
>state for other
>- processes. */
>- cond_resched();
>-
> } while (len);
>
> return 0;
>diff -uNr a/drivers/mtd/chips/cfi_cpt.c b/drivers/mtd/chips/cfi_cpt.c
>--- a/drivers/mtd/chips/cfi_cpt.c 1970-01-01 03:00:00.000000000
>+0300
>+++ b/drivers/mtd/chips/cfi_cpt.c 2006-03-16 12:34:38.000000000
>+0300
>@@ -0,0 +1,344 @@
>+#include <linux/module.h>
>+#include <linux/types.h>
>+#include <linux/kernel.h>
>+#include <linux/sched.h>
>+#include <linux/init.h>
>+#include <asm/io.h>
>+#include <asm/byteorder.h>
>+
>+#include <linux/errno.h>
>+#include <linux/slab.h>
>+#include <linux/delay.h>
>+#include <linux/interrupt.h>
>+#include <linux/reboot.h>
>+#include <linux/mtd/xip.h>
>+#include <linux/mtd/map.h>
>+#include <linux/mtd/mtd.h>
>+#include <linux/mtd/compatmac.h>
>+#include <linux/mtd/cfi.h>
>+
>+#include <linux/mtd/cfi_cpt.h>
>+
>+#define STATIC_PRIO_TO_NICE(a) (((a) - MAX_RT_PRIO - 20))
>+
>+struct cpt_thread_info *cpt_info;
>+
>+static void cpt_set_priority(struct cpt_thread_info* info)
>+{
>+ int oldnice, newnice;
>+
>+ struct list_head *pos, *qos;
>+ struct cpt_chip *chip;
>+ struct cpt_check_desc *desc;
>+
>+ newnice = oldnice = STATIC_PRIO_TO_NICE(info->thread->static_prio);
>+
>+ /* list all chips and check priority */
>+ spin_lock(&info->list_lock);
>+ list_for_each(pos, &info->list)
>+ {
>+ chip = list_entry(pos, struct cpt_chip, list);
>+ spin_lock(&chip->list_lock);
>+ list_for_each(qos, &chip->plist)
>+ {
>+ desc = list_entry(chip->plist.next, struct cpt_check_desc,
>list);
>+ newnice = (desc->task_prio < newnice) ? desc->task_prio :
>newnice;
>+ }
>+ spin_unlock(&chip->list_lock);
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ /* new CPT priority should be less than calling thread one */
>+ newnice = ((newnice + 1) < -20) ? -20 : (newnice + 1);
>+
>+ if(oldnice != newnice)
>+ set_user_nice(info->thread, newnice);
>+}
>+
>+static void cpt_thread(void *arg)
>+{
>+ struct cpt_thread_info* info = (struct cpt_thread_info*)arg;
>+
>+ struct list_head *pos;
>+ struct cpt_chip *chip;
>+ struct cpt_check_desc *desc;
>+
>+ map_word status;
>+
>+ info->thread = current;
>+ up(&info->cpt_startstop);
>+
>+ while(info->cpt_cont)
>+ {
>+ /* wait for check issue */
>+ down(&info->cpt_wait);
>+
>+ /* list all chips and check status */
>+ spin_lock(&info->list_lock);
>+ list_for_each(pos, &info->list)
>+ {
>+ chip = list_entry(pos, struct cpt_chip, list);
>+ spin_lock(&chip->list_lock);
>+ if(!list_empty(&chip->plist))
>+ {
>+ desc = list_entry(chip->plist.next, struct
>cpt_check_desc, list);
>+ if(!desc->timeo)
>+ desc->timeo = jiffies + (HZ/2);
>+
>+#ifndef CONFIG_MTD_XIP
>+ if(chip->chip->state != FL_WRITING && desc->wait)
>+ {
>+ /* Someone's suspended the write. Do not check
>status on this very turn */
>+ desc->timeo = jiffies + (HZ / 2);
>+ up(&info->cpt_wait);
>+ continue;
>+ }
>+#endif
>+
>+ /* check chip status.
>+ * if OK remove item from chip queue and release
>semaphore. */
>+ spin_lock(chip->chip->mutex);
>+ status = map_read(desc->map, desc->cmd_adr);
>+ spin_unlock(chip->chip->mutex);
>+
>+ if(map_word_andequal(desc->map, status, desc->status_OK,
>desc->status_OK))
>+ {
>+ /* chip has status OK */
>+ desc->success = 1;
>+ list_del(&desc->list);
>+ up(&desc->check_semaphore);
>+
>+ cpt_set_priority(info);
>+ }
>+ else if(!desc->wait)
>+ {
>+ /* chip is not ready */
>+ desc->success = 0;
>+ list_del(&desc->list);
>+ up(&desc->check_semaphore);
>+
>+ cpt_set_priority(info);
>+ }
>+ else
>+ {
>+ /* check for timeout */
>+ if(time_after(jiffies, desc->timeo))
>+ {
>+ printk(KERN_ERR "CPT: timeout (%s)\n",
>desc->map->name);
>+
>+ desc->success = 0;
>+ list_del(&desc->list);
>+ up(&desc->check_semaphore);
>+
>+ cpt_set_priority(info);
>+ }
>+ else
>+ {
>+ /* wait one more time */
>+ up(&info->cpt_wait);
>+ }
>+ }
>+ }
>+ spin_unlock(&chip->list_lock);
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ cond_resched();
>+ }
>+
>+ info->thread = NULL;
>+ up(&info->cpt_startstop);
>+}
>+
>+
>+static int cpt_init_thread(struct cpt_thread_info* info)
>+{
>+ pid_t pid;
>+ int ret = 0;
>+
>+ init_MUTEX_LOCKED(&info->cpt_startstop); /* init start/stop
>semaphore */
>+
>+ info->cpt_cont = 1; /* set continue thread
>flag */
>+ init_MUTEX_LOCKED(&info->cpt_wait); /* init "wait
>for data" semaphore */
>+
>+ INIT_LIST_HEAD(&info->list); /* initialize operation
>list head */
>+ spin_lock_init(&info->list_lock); /* init list lock */
>+
>+ pid = kernel_thread((int (*)(void *))cpt_thread, info,
>CLONE_KERNEL); /* flags (3rd arg) TBD */
>+ if (pid < 0)
>+ {
>+ printk(KERN_ERR "fork failed for CFI common polling thread:
>%d\n", -pid);
>+ ret = pid;
>+ }
>+ else
>+ {
>+ /* wait thread started */
>+ DEBUG(MTD_DEBUG_LEVEL1, "CPT: write thread has pid %d\n", pid);
>+ down(&info->cpt_startstop);
>+ }
>+
>+ return ret;
>+}
>+
>+
>+static void cpt_shutdown_thread(struct cpt_thread_info* info)
>+{
>+ struct list_head *pos_chip, *pos_desc, *p, *q;
>+ struct cpt_chip *chip;
>+ struct cpt_check_desc *desc;
>+
>+ if(info->thread)
>+ {
>+ info->cpt_cont = 0; /* drop thread flag */
>+ up(&info->cpt_wait); /* let the thread
>complete */
>+ down(&info->cpt_startstop); /* wait for thread
>completion */
>+ DEBUG(MTD_DEBUG_LEVEL1, "CPT: common polling thread has been
>stopped\n");
>+ }
>+
>+ /* clean queue */
>+ spin_lock(&info->list_lock);
>+ list_for_each_safe(pos_chip, p, &info->list)
>+ {
>+ chip = list_entry(pos_chip, struct cpt_chip, list);
>+ spin_lock(&chip->list_lock);
>+ list_for_each_safe(pos_desc, q, &chip->list)
>+ {
>+ desc = list_entry(pos_desc, struct cpt_check_desc, list);
>+
>+ /* remove polling request from queue */
>+ desc->success = 0;
>+ list_del(&desc->list);
>+ up(&desc->check_semaphore);
>+ }
>+ spin_unlock(&chip->list_lock);
>+
>+ /* remove chip structure from the queue and deallocate memory */
>+ list_del(&chip->list);
>+ kfree(chip);
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ DEBUG(MTD_DEBUG_LEVEL1, "CPT: common polling thread queue has been
>cleaned\n");
>+}
>+
>+
>+/* info - CPT thread structure
>+ * chip - chip structure pointer
>+ * map - map info structure
>+ * cmd_adr - address to write cmd
>+ * status_OK - status to be checked against
>+ * wait - flag defining wait for status or just single check
>+ *
>+ * returns 0 - success or error otherwise
>+ */
>+int cpt_check_wait(struct cpt_thread_info* info, struct flchip *chip,
>struct map_info *map,
>+ unsigned long cmd_adr, map_word status_OK, int wait)
>+{
>+ struct cpt_check_desc desc;
>+ struct list_head *pos_chip;
>+ struct cpt_chip *chip_cpt = NULL;
>+ int chip_found = 0;
>+ int status = 0;
>+
>+ desc.chip = chip;
>+ desc.map = map;
>+ desc.cmd_adr = cmd_adr;
>+ desc.status_OK = status_OK;
>+ desc.timeo = 0;
>+ desc.wait = wait;
>+
>+ /* fill task priority for that task */
>+ desc.task_prio = STATIC_PRIO_TO_NICE(current->static_prio);
>+
>+ init_MUTEX_LOCKED(&desc.check_semaphore);
>+
>+ /* insert element to queue */
>+ spin_lock(&info->list_lock);
>+ list_for_each(pos_chip, &info->list)
>+ {
>+ chip_cpt = list_entry(pos_chip, struct cpt_chip, list);
>+ if(chip_cpt->chip == desc.chip)
>+ {
>+ chip_found = 1;
>+ break;
>+ }
>+ }
>+
>+ if(!chip_found)
>+ {
>+ /* create new chip queue */
>+ chip_cpt = kmalloc(sizeof(struct cpt_chip), GFP_KERNEL);
>+ if(!chip_cpt)
>+ {
>+ printk(KERN_ERR "CPT: memory allocation error\n");
>+ return -ENOMEM;
>+ }
>+ memset(chip_cpt, 0, sizeof(struct cpt_chip));
>+
>+ chip_cpt->chip = desc.chip;
>+ INIT_LIST_HEAD(&chip_cpt->plist);
>+ spin_lock_init(&chip_cpt->list_lock);
>+
>+ /* put chip in queue */
>+ list_add_tail(&chip_cpt->list, &info->list);
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ /* add element to existing chip queue */
>+ spin_lock(&chip_cpt->list_lock);
>+ list_add_tail(&desc.list, &chip_cpt->plist);
>+ spin_unlock(&chip_cpt->list_lock);
>+
>+ /* set new CPT priority if required */
>+ if((desc.task_prio + 1) <
>STATIC_PRIO_TO_NICE(info->thread->static_prio))
>+ cpt_set_priority(info);
>+
>+ /* unlock chip mutex and wait here */
>+ spin_unlock(desc.chip->mutex);
>+ up(&info->cpt_wait); /* let CPT continue */
>+ down(&desc.check_semaphore); /* wait until CPT rise semaphore
>*/
>+ spin_lock(desc.chip->mutex);
>+
>+ status = desc.success ? 0 : -EIO;
>+
>+ return status;
>+}
>+
>+static int __init cfi_cpt_init(void)
>+{
>+ int err;
>+
>+ cpt_info = (struct cpt_thread_info*)kmalloc(sizeof(struct
>cpt_thread_info), GFP_KERNEL);
>+ if (!cpt_info)
>+ {
>+ printk(KERN_ERR "CPT: memory allocation error\n");
>+ return -ENOMEM;
>+ }
>+
>+ err = cpt_init_thread(cpt_info);
>+ if(err)
>+ {
>+ kfree(cpt_info);
>+ cpt_info = NULL;
>+ }
>+
>+ return err;
>+}
>+
>+static void __exit cfi_cpt_exit(void)
>+{
>+ if(cpt_info)
>+ {
>+ cpt_shutdown_thread(cpt_info);
>+ kfree(cpt_info);
>+ }
>+}
>+
>+EXPORT_SYMBOL(cpt_check_wait);
>+
>+module_init(cfi_cpt_init);
>+module_exit(cfi_cpt_exit);
>+
>+MODULE_LICENSE("GPL");
>+MODULE_AUTHOR("Alexander Belyakov <alexander.belyakov at intel.com>, Intel
>Corporation");
>+MODULE_DESCRIPTION("CFI Common Polling Thread");
>diff -uNr a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
>--- a/drivers/mtd/chips/Kconfig 2006-03-16 12:46:25.000000000 +0300
>+++ b/drivers/mtd/chips/Kconfig 2006-03-16 12:34:38.000000000 +0300
>@@ -190,6 +190,13 @@
> provides support for one of those command sets, used on Intel
> StrataFlash and other parts.
>
>+config MTD_CFI_CPT
>+ bool "Common polling thread"
>+ depends on MTD_CFI_INTELEXT
>+ default n
>+ help
>+ Common polling thread for CFI
>+
> config MTD_CFI_AMDSTD
> tristate "Support for AMD/Fujitsu flash chips"
> depends on MTD_GEN_PROBE
>diff -uNr a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile
>--- a/drivers/mtd/chips/Makefile 2006-03-05 22:07:54.000000000
>+0300
>+++ b/drivers/mtd/chips/Makefile 2006-03-16 12:34:38.000000000
>+0300
>@@ -24,3 +24,4 @@
> obj-$(CONFIG_MTD_ROM) += map_rom.o
> obj-$(CONFIG_MTD_SHARP) += sharp.o
> obj-$(CONFIG_MTD_ABSENT) += map_absent.o
>+obj-$(CONFIG_MTD_CFI_CPT) += cfi_cpt.o
>diff -uNr a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
>--- a/drivers/mtd/Kconfig 2006-03-05 22:07:54.000000000 +0300
>+++ b/drivers/mtd/Kconfig 2006-03-16 12:34:38.000000000 +0300
>@@ -36,6 +36,51 @@
> file system spanning multiple physical flash chips. If unsure,
> say 'Y'.
>
>+config MTD_STRIPE
>+ tristate "MTD striping support"
>+ depends on MTD
>+ help
>+ Support for stripinging several MTD devices into a single
>+ (virtual) one. This allows you to have -for example- a JFFS(2)
>+ file system interleaving multiple physical flash chips. If
>unsure,
>+ say 'Y'.
>+
>+ If you build mtdstripe.ko as a module it is possible to pass
>+ command line to the module via insmod
>+
>+ The format for the command line is as follows:
>+
>+ cmdline_parm="<stripedef>[;<stripedef>]"
>+ <stripedef> :=
><stripename>(<interleavesize>):<subdevname>.<subdevname>
>+
>+ Subdevices should belong to different physical flash chips
>+ in order to get performance increase
>+
>+ Example:
>+
>+ insmod mtdstripe.ko
>cmdline_parm="stripe1(128):vol1.vol3;stripe2(128):vol2.vol4"
>+
>+ Note: you should use '.' as a delimeter for subdevice names
>here
>+
>+config MTD_CMDLINE_STRIPE
>+ bool "Command line stripe configuration parsing"
>+ depends on MTD_STRIPE = 'y'
>+ ---help---
>+ Allow generic configuration of the MTD striped volumes via the
>kernel
>+ command line.
>+
>+ The format for the command line is as follows:
>+
>+ mtdstripe=<stripedef>[;<stripedef>]
>+ <stripedef> :=
><stripename>(<interleavesize>):<subdevname>,<subdevname>
>+
>+ Subdevices should belong to different physical flash chips
>+ in order to get performance increase
>+
>+ Example:
>+
>+ mtdstripe=stripe1(128):vol1,vol3;stripe2(128):vol2,vol4
>+
> config MTD_PARTITIONS
> bool "MTD partitioning support"
> depends on MTD
>diff -uNr a/drivers/mtd/Makefile b/drivers/mtd/Makefile
>--- a/drivers/mtd/Makefile 2006-03-05 22:07:54.000000000 +0300
>+++ b/drivers/mtd/Makefile 2006-03-16 12:34:38.000000000 +0300
>@@ -9,6 +9,7 @@
> obj-$(CONFIG_MTD) += $(mtd-y)
>
> obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
>+obj-$(CONFIG_MTD_STRIPE) += mtdstripe.o
> obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
> obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
> obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
>diff -uNr a/drivers/mtd/maps/mphysmap.c b/drivers/mtd/maps/mphysmap.c
>--- a/drivers/mtd/maps/mphysmap.c 2006-03-16 12:46:25.000000000
>+0300
>+++ b/drivers/mtd/maps/mphysmap.c 2006-03-16 12:34:38.000000000
>+0300
>@@ -12,6 +12,9 @@
> #ifdef CONFIG_MTD_PARTITIONS
> #include <linux/mtd/partitions.h>
> #endif
>+#ifdef CONFIG_MTD_CMDLINE_STRIPE
>+#include <linux/mtd/stripe.h>
>+#endif
>
> static struct map_info mphysmap_static_maps[] = {
> #if CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH
>@@ -155,6 +158,15 @@
> };
> };
> up(&map_mutex);
>+
>+#ifdef CONFIG_MTD_CMDLINE_STRIPE
>+#ifndef MODULE
>+ if(mtd_stripe_init()) {
>+ printk(KERN_WARNING "MTD stripe initialization from cmdline
>has failed\n");
>+ }
>+#endif
>+#endif
>+
> return 0;
> }
>
>@@ -162,6 +174,13 @@
> static void __exit mphysmap_exit(void)
> {
> int i;
>+
>+#ifdef CONFIG_MTD_CMDLINE_STRIPE
>+#ifndef MODULE
>+ mtd_stripe_exit();
>+#endif
>+#endif
>+
> down(&map_mutex);
> for (i=0;
>
>i<sizeof(mphysmap_static_maps)/sizeof(mphysmap_static_maps[0]);
>diff -uNr a/drivers/mtd/mtdstripe.c b/drivers/mtd/mtdstripe.c
>--- a/drivers/mtd/mtdstripe.c 1970-01-01 03:00:00.000000000 +0300
>+++ b/drivers/mtd/mtdstripe.c 2006-03-16 12:34:38.000000000 +0300
>@@ -0,0 +1,3542 @@
>+/*
>########################################################################
>#################################
>+ ### This software program is available to you under a choice of one
>of two licenses.
>+ ### You may choose to be licensed under either the GNU General
>Public License (GPL) Version 2,
>+ ### June 1991, available at http://www.fsf.org/copyleft/gpl.html, or
>the Intel BSD + Patent License,
>+ ### the text of which follows:
>+ ###
>+ ### Recipient has requested a license and Intel Corporation
>("Intel") is willing to grant a
>+ ### license for the software entitled MTD stripe middle layer (the
>"Software") being provided by
>+ ### Intel Corporation.
>+ ###
>+ ### The following definitions apply to this License:
>+ ###
>+ ### "Licensed Patents" means patent claims licensable by Intel
>Corporation which are necessarily
>+ ### infringed by the use or sale of the Software alone or when
>combined with the operating system
>+ ### referred to below.
>+ ### "Recipient" means the party to whom Intel delivers this
>Software.
>+ ### "Licensee" means Recipient and those third parties that receive
>a license to any operating system
>+ ### available under the GNU Public License version 2.0 or later.
>+ ###
>+ ### Copyright (c) 1995-2005 Intel Corporation. All rights reserved.
>+ ###
>+ ### The license is provided to Recipient and Recipient's Licensees
>under the following terms.
>+ ###
>+ ### Redistribution and use in source and binary forms of the
>Software, with or without modification,
>+ ### are permitted provided that the following conditions are met:
>+ ### Redistributions of source code of the Software may retain the
>above copyright notice, this list
>+ ### of conditions and the following disclaimer.
>+ ###
>+ ### Redistributions in binary form of the Software may reproduce the
>above copyright notice,
>+ ### this list of conditions and the following disclaimer in the
>documentation and/or other materials
>+ ### provided with the distribution.
>+ ###
>+ ### Neither the name of Intel Corporation nor the names of its
>contributors shall be used to endorse
>+ ### or promote products derived from this Software without specific
>prior written permission.
>+ ###
>+ ### Intel hereby grants Recipient and Licensees a non-exclusive,
>worldwide, royalty-free patent licens
>+ ### e under Licensed Patents to make, use, sell, offer to sell,
>import and otherwise transfer the
>+ ### Software, if any, in source code and object code form. This
>license shall include changes to
>+ ### the Software that are error corrections or other minor changes to
>the Software that do not add
>+ ### functionality or features when the Software is incorporated in
>any version of a operating system
>+ ### that has been distributed under the GNU General Public License
>2.0 or later. This patent license
>+ ### shall apply to the combination of the Software and any operating
>system licensed under the
>+ ### GNU Public License version 2.0 or later if, at the time Intel
>provides the Software to Recipient,
>+ ### such addition of the Software to the then publicly available
>versions of such operating system
>+ ### available under the GNU Public License version 2.0 or later
>(whether in gold, beta or alpha form)
>+ ### causes such combination to be covered by the Licensed Patents.
>The patent license shall not apply
>+ ### to any other combinations which include the Software. No hardware
>per se is licensed hereunder.
>+ ###
>+ ### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
>CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
>+ ### IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
>WARRANTIES OF MERCHANTABILITY AND
>+ ### FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
>SHALL INTEL OR ITS CONTRIBUTORS BE
>+ ### LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
>OR CONSEQUENTIAL DAMAGES
>+ ### (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
>OR SERVICES; LOSS OF USE, DATA,
>+ ### OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
>THEORY OF LIABILITY, WHETHER IN
>+ ### CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
>OTHERWISE) ARISING IN ANY WAY OUT
>+ ### OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
>OF SUCH DAMAGE."
>+ ###
>+
>########################################################################
>################################### */
>+
>+
>+#include <linux/module.h>
>+#include <linux/types.h>
>+#include <linux/kernel.h>
>+#include <linux/string.h>
>+#include <linux/slab.h>
>+
>+#include <linux/mtd/mtd.h>
>+#ifdef STANDALONE
>+#include "stripe.h"
>+#else
>+#include <linux/mtd/stripe.h>
>+#endif
>+
>+#ifdef CONFIG_MTD_CMDLINE_STRIPE
>+#define CMDLINE_PARSER_STRIPE
>+#else
>+#ifdef MODULE
>+#define CMDLINE_PARSER_STRIPE
>+#endif
>+#endif
>+
>+#ifdef MODULE
>+static char *cmdline_parm = NULL;
>+MODULE_PARM(cmdline_parm,"s");
>+MODULE_PARM_DESC(cmdline_parm,"Command line parameters");
>+#endif
>+
>+extern struct semaphore mtd_table_mutex;
>+extern struct mtd_info *mtd_table[];
>+
>+#ifdef CMDLINE_PARSER_STRIPE
>+static char *cmdline;
>+static struct mtd_stripe_info info; /* mtd stripe info head */
>+#endif
>+
>+/*
>+ * Striped device structure:
>+ * Subdev points to an array of pointers to struct mtd_info objects
>+ * which is allocated along with this structure
>+ *
>+ */
>+struct mtd_stripe {
>+ struct mtd_info mtd;
>+ int num_subdev;
>+ u_int32_t erasesize_lcm;
>+ u_int32_t interleave_size;
>+ u_int32_t *subdev_last_offset;
>+ struct mtd_sw_thread_info *sw_threads;
>+ struct mtd_info **subdev;
>+};
>+
>+/* This structure is used for stripe_erase and stripe_lock/unlock
>methods
>+ * and contains erase regions for striped devices
>+ */
>+struct mtd_stripe_erase_bounds {
>+ int need_erase;
>+ u_int32_t addr;
>+ u_int32_t len;
>+};
>+
>+/* Write/erase thread info structure
>+ */
>+struct mtd_sw_thread_info {
>+ struct task_struct *thread;
>+ struct mtd_info *subdev; /* corresponding subdevice pointer */
>+ int sw_thread; /* continue operations flag */
>+
>+ /* wait-for-data semaphore,
>+ * up by stripe_write/erase (stripe_stop_write_thread),
>+ * down by stripe_write_thread
>+ */
>+ struct semaphore sw_thread_wait;
>+
>+ /* start/stop semaphore,
>+ * up by stripe_write_thread,
>+ * down by stripe_start/stop_write_thread
>+ */
>+ struct semaphore sw_thread_startstop;
>+
>+ struct list_head list; /* head of the operation list */
>+ spinlock_t list_lock; /* lock to remove race conditions
>+ * while adding/removing operations
>+ * to/from the list */
>+};
>+
>+/* Single suboperation structure
>+ */
>+struct subop {
>+ u_int32_t ofs; /* offset of write/erase operation */
>+ u_int32_t len; /* length of the data to be
>written/erased */
>+ u_char *buf; /* buffer with data to be written or
>poiner
>+ * to original erase_info structure
>+ * in case of erase operation */
>+ u_char *eccbuf; /* buffer with FS provided oob data.
>+ * used for stripe_write_ecc operation
>+ * NOTE: stripe_write_oob() still uses
>u_char *buf member */
>+};
>+
>+/* Suboperation array structure
>+ */
>+struct subop_struct {
>+ struct list_head list; /* suboperation array queue */
>+
>+ u_int32_t ops_num; /* number of suboperations in the array
>*/
>+ u_int32_t ops_num_max; /* maximum allowed number of
>suboperations */
>+ struct subop *ops_array; /* suboperations array */
>+};
>+
>+/* Operation codes */
>+#define MTD_STRIPE_OPCODE_READ 0x1
>+#define MTD_STRIPE_OPCODE_WRITE 0x2
>+#define MTD_STRIPE_OPCODE_READ_ECC 0x3
>+#define MTD_STRIPE_OPCODE_WRITE_ECC 0x4
>+#define MTD_STRIPE_OPCODE_WRITE_OOB 0x5
>+#define MTD_STRIPE_OPCODE_ERASE 0x6
>+
>+/* Stripe operation structure
>+ */
>+struct mtd_stripe_op {
>+ struct list_head list; /* per thread (device) queue */
>+
>+ char opcode; /* operation code */
>+ int caller_id; /* reserved for thread ID issued this operation */
>+ int op_prio; /* original operation prioriry */
>+
>+ struct semaphore sem; /* operation completed semaphore */
>+ struct subop_struct subops; /* suboperation structure */
>+
>+ int status; /* operation completed status */
>+ u_int32_t fail_addr; /* fail address (for erase operation) */
>+ u_char state; /* state (for erase operation) */
>+};
>+
>+#define SIZEOF_STRUCT_MTD_STRIPE_OP(num_ops) \
>+ ((sizeof(struct mtd_stripe_op) + (num_ops) * sizeof(struct
>subop)))
>+
>+#define SIZEOF_STRUCT_MTD_STRIPE_SUBOP(num_ops) \
>+ ((sizeof(struct subop_struct) + (num_ops) * sizeof(struct
>subop)))
>+
>+/*
>+ * how to calculate the size required for the above structure,
>+ * including the pointer array subdev points to:
>+ */
>+#define SIZEOF_STRUCT_MTD_STRIPE(num_subdev) \
>+ ((sizeof(struct mtd_stripe) + (num_subdev) * sizeof(struct
>mtd_info *) \
>+ + (num_subdev) * sizeof(u_int32_t) \
>+ + (num_subdev) * sizeof(struct mtd_sw_thread_info)))
>+
>+/*
>+ * Given a pointer to the MTD object in the mtd_stripe structure,
>+ * we can retrieve the pointer to that structure with this macro.
>+ */
>+#define STRIPE(x) ((struct mtd_stripe *)(x))
>+
>+/* Forward functions declaration
>+ */
>+static int stripe_dev_erase(struct mtd_info *mtd, struct erase_info
>*erase);
>+
>+/*
>+ * Miscelaneus support routines
>+ */
>+
>+/*
>+ * searches for least common multiple of a and b
>+ * returns: LCM or 0 in case of error
>+ */
>+u_int32_t
>+lcm(u_int32_t a, u_int32_t b)
>+{
>+ u_int32_t lcm;
>+ /* u_int32_t ab = a * b; */
>+ u_int32_t t1 = a;
>+ u_int32_t t2 = b;
>+
>+ if(a <= 0 || b <= 0)
>+ {
>+ lcm = 0;
>+ printk(KERN_ERR "lcm(): wrong arguments\n");
>+ }
>+ else
>+ {
>+ do
>+ {
>+ lcm = a;
>+ a = b;
>+ b = lcm - a*(lcm/a);
>+ }
>+ while(b!=0);
>+
>+ if(t1 % a)
>+ lcm = (t2 / a) * t1;
>+ else
>+ lcm = (t1 / a) * t2;
>+ }
>+
>+ return lcm;
>+} /* int lcm(int a, int b) */
>+
>+u_int32_t last_offset(struct mtd_stripe *stripe, int subdev_num);
>+
>+/*
>+ * Calculates last_offset for specific striped subdevice
>+ * NOTE: subdev array MUST be sorted
>+ * by subdevice size (from the smallest to the largest)
>+ */
>+u_int32_t
>+last_offset(struct mtd_stripe *stripe, int subdev_num)
>+{
>+ u_int32_t offset = 0;
>+
>+ /* Interleave block count for previous subdevice in the array */
>+ u_int32_t prev_dev_size_n = 0;
>+
>+ /* Current subdevice interleaved block count */
>+ u_int32_t curr_size_n = stripe->subdev[subdev_num]->size /
>stripe->interleave_size;
>+
>+ int i;
>+
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ struct mtd_info *subdev = stripe->subdev[i];
>+ /* subdevice interleaved block count */
>+ u_int32_t size_n = subdev->size / stripe->interleave_size;
>+
>+ if(i < subdev_num)
>+ {
>+ if(size_n < curr_size_n)
>+ {
>+ offset += (size_n - prev_dev_size_n) *
>(stripe->num_subdev - i);
>+ prev_dev_size_n = size_n;
>+ }
>+ else
>+ {
>+ offset += (size_n - prev_dev_size_n - 1) *
>(stripe->num_subdev - i) + 1;
>+ prev_dev_size_n = size_n - 1;
>+ }
>+ }
>+ else if (i == subdev_num)
>+ {
>+ offset += (size_n - prev_dev_size_n - 1) *
>(stripe->num_subdev - i) + 1;
>+ break;
>+ }
>+ }
>+
>+ return (offset * stripe->interleave_size);
>+} /* u_int32_t last_offset(struct mtd_stripe *stripe, int subdev_num)
>*/
>+
>+/* this routine returns oobavail size based on oobfree array
>+ * since original mtd_info->oobavail field seems to be zeroed by
>unknown reason
>+ */
>+int stripe_get_oobavail(struct mtd_info *mtd)
>+{
>+ int oobavail = 0;
>+ uint32_t oobfree_max_num = 8; /* array size defined in mtd-abi.h */
>+ int i;
>+
>+ for(i = 0; i < oobfree_max_num; i++)
>+ {
>+ if(mtd->oobinfo.oobfree[i][1])
>+ oobavail += mtd->oobinfo.oobfree[i][1];
>+ }
>+
>+ return oobavail;
>+}
>+
>+/* routine merges subdevs oobinfo into new mtd device oobinfo
>+ * this should be made after subdevices sorting done for proper eccpos
>and oobfree positioning
>+ *
>+ * returns: 0 - success */
>+int stripe_merge_oobinfo(struct mtd_info *mtd, struct mtd_info
>*subdev[], int num_devs)
>+{
>+ int ret = 0;
>+ int i, j;
>+ uint32_t eccpos_max_num = sizeof(mtd->oobinfo.eccpos) /
>sizeof(uint32_t);
>+ uint32_t eccpos_counter = 0;
>+ uint32_t oobfree_max_num = 8; /* array size defined in mtd-abi.h */
>+ uint32_t oobfree_counter = 0;
>+
>+ if(mtd->type != MTD_NANDFLASH)
>+ return 0;
>+
>+ mtd->oobinfo.useecc = subdev[0]->oobinfo.useecc;
>+ mtd->oobinfo.eccbytes = subdev[0]->oobinfo.eccbytes;
>+ for(i = 1; i < num_devs; i++)
>+ {
>+ if(mtd->oobinfo.useecc != subdev[i]->oobinfo.useecc ||
>+ mtd->oobinfo.eccbytes != subdev[i]->oobinfo.eccbytes)
>+ {
>+ printk(KERN_ERR "stripe_merge_oobinfo(): oobinfo parameters
>is not compatible for all subdevices\n");
>+ return -EINVAL;
>+ }
>+ }
>+
>+ mtd->oobinfo.eccbytes *= num_devs;
>+
>+ /* drop old oobavail value */
>+ mtd->oobavail = 0;
>+
>+ /* merge oobfree space positions */
>+ for(i = 0; i < num_devs; i++)
>+ {
>+ for(j = 0; j < oobfree_max_num; j++)
>+ {
>+ if(subdev[i]->oobinfo.oobfree[j][1])
>+ {
>+ if(oobfree_counter >= oobfree_max_num)
>+ break;
>+
>+ mtd->oobinfo.oobfree[oobfree_counter][0] =
>subdev[i]->oobinfo.oobfree[j][0] +
>+ i *
>subdev[i]->oobsize;
>+ mtd->oobinfo.oobfree[oobfree_counter][1] =
>subdev[i]->oobinfo.oobfree[j][1];
>+
>+ mtd->oobavail += subdev[i]->oobinfo.oobfree[j][1];
>+ oobfree_counter++;
>+ }
>+ }
>+ }
>+
>+ /* merge ecc positions */
>+ for(i = 0; i < num_devs; i++)
>+ {
>+ for(j = 0; j < eccpos_max_num; j++)
>+ {
>+ if(subdev[i]->oobinfo.eccpos[j])
>+ {
>+ if(eccpos_counter >= eccpos_max_num)
>+ {
>+ printk(KERN_ERR "stripe_merge_oobinfo(): eccpos
>merge error\n");
>+ return -EINVAL;
>+ }
>+
>mtd->oobinfo.eccpos[eccpos_counter]=subdev[i]->oobinfo.eccpos[j] + i *
>subdev[i]->oobsize;
>+ eccpos_counter++;
>+ }
>+ }
>+ }
>+
>+ return ret;
>+}
>+
>+/* End of support routines */
>+
>+/* Multithreading support routines */
>+
>+/* Write to flash thread */
>+static void
>+stripe_write_thread(void *arg)
>+{
>+ struct mtd_sw_thread_info* info = (struct mtd_sw_thread_info*)arg;
>+ struct mtd_stripe_op* op;
>+ struct subop_struct* subops;
>+ u_int32_t retsize;
>+ int err;
>+
>+ int i;
>+ struct list_head *pos;
>+
>+ /* erase operation stuff */
>+ struct erase_info erase; /* local copy */
>+ struct erase_info *instr; /* pointer to original */
>+
>+ info->thread = current;
>+ up(&info->sw_thread_startstop);
>+
>+ while(info->sw_thread)
>+ {
>+ /* wait for downcoming write/erase operation */
>+ down(&info->sw_thread_wait);
>+
>+ /* issue operation to the device and remove it from the list
>afterwards*/
>+ spin_lock(&info->list_lock);
>+ if(!list_empty(&info->list))
>+ {
>+ op = list_entry(info->list.next,struct mtd_stripe_op, list);
>+ }
>+ else
>+ {
>+ /* no operation in queue but sw_thread_wait has been rised.
>+ * it means stripe_stop_write_thread() has been called
>+ */
>+ op = NULL;
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ /* leave main thread loop if no ops */
>+ if(!op)
>+ break;
>+
>+ err = 0;
>+ op->status = 0;
>+
>+ switch(op->opcode)
>+ {
>+ case MTD_STRIPE_OPCODE_WRITE:
>+ case MTD_STRIPE_OPCODE_WRITE_OOB:
>+ /* proceed with list head first */
>+ subops = &op->subops;
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ if(op->opcode == MTD_STRIPE_OPCODE_WRITE)
>+ err = info->subdev->write(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len, &retsize,
>subops->ops_array[i].buf);
>+ else
>+ err = info->subdev->write_oob(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len, &retsize,
>subops->ops_array[i].buf);
>+
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: write operation
>failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(!op->status)
>+ {
>+ /* now proceed each list element except head */
>+ list_for_each(pos, &op->subops.list)
>+ {
>+ subops = list_entry(pos, struct subop_struct,
>list);
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ if(op->opcode == MTD_STRIPE_OPCODE_WRITE)
>+ err = info->subdev->write(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len, &retsize,
>subops->ops_array[i].buf);
>+ else
>+ err =
>info->subdev->write_oob(info->subdev, subops->ops_array[i].ofs,
>subops->ops_array[i].len, &retsize, subops->ops_array[i].buf);
>+
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: write
>operation failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(op->status)
>+ break;
>+ }
>+ }
>+ break;
>+
>+ case MTD_STRIPE_OPCODE_ERASE:
>+ subops = &op->subops;
>+ instr = (struct erase_info *)subops->ops_array[0].buf;
>+
>+ /* make a local copy of original erase instruction to
>avoid modifying the caller's struct */
>+ erase = *instr;
>+ erase.addr = subops->ops_array[0].ofs;
>+ erase.len = subops->ops_array[0].len;
>+
>+ if ((err = stripe_dev_erase(info->subdev, &erase)))
>+ {
>+ /* sanity check: should never happen since
>+ * block alignment has been checked early in
>stripe_erase() */
>+
>+ if(erase.fail_addr != 0xffffffff)
>+ /* For now this adddres shows address
>+ * at failed subdevice,but not at "super" device
>*/
>+ op->fail_addr = erase.fail_addr;
>+ }
>+
>+ op->status = err;
>+ op->state = erase.state;
>+ break;
>+
>+ case MTD_STRIPE_OPCODE_WRITE_ECC:
>+ /* proceed with list head first */
>+ subops = &op->subops;
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ err = info->subdev->write_ecc(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf,
>+
>subops->ops_array[i].eccbuf, &info->subdev->oobinfo);
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: write operation
>failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(!op->status)
>+ {
>+ /* now proceed each list element except head */
>+ list_for_each(pos, &op->subops.list)
>+ {
>+ subops = list_entry(pos, struct subop_struct,
>list);
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ err = info->subdev->write_ecc(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf,
>+
>subops->ops_array[i].eccbuf, &info->subdev->oobinfo);
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: write
>operation failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(op->status)
>+ break;
>+ }
>+ }
>+ break;
>+
>+ case MTD_STRIPE_OPCODE_READ_ECC:
>+ case MTD_STRIPE_OPCODE_READ:
>+ /* proceed with list head first */
>+ subops = &op->subops;
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ if(op->opcode == MTD_STRIPE_OPCODE_READ_ECC)
>+ {
>+ err = info->subdev->read_ecc(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf,
>+
>subops->ops_array[i].eccbuf, &info->subdev->oobinfo);
>+ }
>+ else
>+ {
>+ err = info->subdev->read(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf);
>+ }
>+
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: read operation
>failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(!op->status)
>+ {
>+ /* now proceed each list element except head */
>+ list_for_each(pos, &op->subops.list)
>+ {
>+ subops = list_entry(pos, struct subop_struct,
>list);
>+
>+ for(i = 0; i < subops->ops_num; i++)
>+ {
>+ if(op->opcode == MTD_STRIPE_OPCODE_READ_ECC)
>+ {
>+ err =
>info->subdev->read_ecc(info->subdev, subops->ops_array[i].ofs,
>subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf,
>+
>subops->ops_array[i].eccbuf, &info->subdev->oobinfo);
>+ }
>+ else
>+ {
>+ err = info->subdev->read(info->subdev,
>subops->ops_array[i].ofs, subops->ops_array[i].len,
>+ &retsize,
>subops->ops_array[i].buf);
>+ }
>+
>+ if(err)
>+ {
>+ op->status = -EINVAL;
>+ printk(KERN_ERR "mtd_stripe: read
>operation failed %d\n",err);
>+ break;
>+ }
>+ }
>+
>+ if(op->status)
>+ break;
>+ }
>+ }
>+
>+ break;
>+
>+ default:
>+ /* unknown operation code */
>+ printk(KERN_ERR "mtd_stripe: invalid operation code %d",
>op->opcode);
>+ op->status = -EINVAL;
>+ break;
>+ };
>+
>+ /* remove issued operation from the list */
>+ spin_lock(&info->list_lock);
>+ list_del(&op->list);
>+ spin_unlock(&info->list_lock);
>+
>+ /* raise semaphore to let stripe_write() or stripe_erase()
>continue */
>+ up(&op->sem);
>+ }
>+
>+ info->thread = NULL;
>+ up(&info->sw_thread_startstop);
>+}
>+
>+/* Launches write to flash thread */
>+int
>+stripe_start_write_thread(struct mtd_sw_thread_info* info, struct
>mtd_info *device)
>+{
>+ pid_t pid;
>+ int ret = 0;
>+
>+ if(info->thread)
>+ BUG();
>+
>+ info->subdev = device; /* set the
>pointer to corresponding device */
>+
>+ init_MUTEX_LOCKED(&info->sw_thread_startstop); /* init
>start/stop semaphore */
>+ info->sw_thread = 1; /* set continue
>thread flag */
>+ init_MUTEX_LOCKED(&info->sw_thread_wait); /* init "wait for data"
>semaphore */
>+
>+ INIT_LIST_HEAD(&info->list); /* initialize
>operation list head */
>+
>+ spin_lock_init(&info->list_lock); /* init list lock */
>+
>+ pid = kernel_thread((int (*)(void *))stripe_write_thread, info,
>CLONE_KERNEL); /* flags (3rd arg) TBD */
>+ if (pid < 0)
>+ {
>+ printk(KERN_ERR "fork failed for MTD stripe thread: %d\n",
>-pid);
>+ ret = pid;
>+ }
>+ else
>+ {
>+ /* wait thread started */
>+ DEBUG(MTD_DEBUG_LEVEL1, "MTD stripe: write thread has pid %d\n",
>pid);
>+ down(&info->sw_thread_startstop);
>+ }
>+
>+ return ret;
>+}
>+
>+/* Complete write to flash thread */
>+void
>+stripe_stop_write_thread(struct mtd_sw_thread_info* info)
>+{
>+ if(info->thread)
>+ {
>+ info->sw_thread = 0; /* drop thread flag */
>+ up(&info->sw_thread_wait); /* let the thread
>complete */
>+ down(&info->sw_thread_startstop); /* wait for thread
>completion */
>+ DEBUG(MTD_DEBUG_LEVEL1, "MTD stripe: writing thread has been
>stopped\n");
>+ }
>+}
>+
>+/* Updates write/erase thread priority to max value
>+ * based on operations in the queue
>+ */
>+void
>+stripe_set_write_thread_prio(struct mtd_sw_thread_info* info)
>+{
>+ struct mtd_stripe_op *op;
>+ int oldnice, newnice;
>+ struct list_head *pos;
>+
>+ newnice = oldnice = info->thread->static_prio - MAX_RT_PRIO - 20;
>+
>+ spin_lock(&info->list_lock);
>+ list_for_each(pos, &info->list)
>+ {
>+ op = list_entry(pos, struct mtd_stripe_op, list);
>+ newnice = (op->op_prio < newnice) ? op->op_prio : newnice;
>+ }
>+ spin_unlock(&info->list_lock);
>+
>+ newnice = (newnice < -20) ? -20 : newnice;
>+
>+ if(oldnice != newnice)
>+ set_user_nice(info->thread, newnice);
>+}
>+
>+/* add sub operation into the array
>+ op - pointer to the operation structure
>+ ofs - operation offset within subdevice
>+ len - data to be written/erased
>+ buf - pointer to the buffer with data to be written (NULL is erase
>operation)
>+
>+ returns: 0 - success
>+*/
>+static inline int
>+stripe_add_subop(struct mtd_stripe_op *op, u_int32_t ofs, u_int32_t
>len, const u_char *buf, const u_char *eccbuf)
>+{
>+ u_int32_t size; /* number of items in
>the new array (if any) */
>+ struct subop_struct *subop;
>+
>+ if(!op)
>+ BUG(); /* error */
>+
>+ /* get tail list element or head */
>+ subop = list_entry(op->subops.list.prev, struct subop_struct,
>list);
>+
>+ /* check if current suboperation array is already filled or not */
>+ if(subop->ops_num >= subop->ops_num_max)
>+ {
>+ /* array is full. allocate new one and add to list */
>+ size = SIZEOF_STRUCT_MTD_STRIPE_SUBOP(op->subops.ops_num_max);
>+ subop = kmalloc(size, GFP_KERNEL);
>+ if(!subop)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(subop, 0, size);
>+ subop->ops_num = 0;
>+ subop->ops_num_max = op->subops.ops_num_max;
>+ subop->ops_array = (struct subop *)(subop + 1);
>+
>+ list_add_tail(&subop->list, &op->subops.list);
>+ }
>+
>+ subop->ops_array[subop->ops_num].ofs = ofs;
>+ subop->ops_array[subop->ops_num].len = len;
>+ subop->ops_array[subop->ops_num].buf = (u_char *)buf;
>+ subop->ops_array[subop->ops_num].eccbuf = (u_char *)eccbuf;
>+
>+ subop->ops_num++; /* increase stored suboperations counter */
>+
>+ return 0;
>+}
>+
>+/* deallocates memory allocated by stripe_add_subop routine */
>+static void
>+stripe_destroy_op(struct mtd_stripe_op *op)
>+{
>+ struct subop_struct *subop;
>+
>+ while(!list_empty(&op->subops.list))
>+ {
>+ subop = list_entry(op->subops.list.next,struct subop_struct,
>list);
>+ list_del(&subop->list);
>+ kfree(subop);
>+ }
>+}
>+
>+/* adds new operation to the thread queue and unlock wait semaphore for
>specific thread */
>+static void
>+stripe_add_op(struct mtd_sw_thread_info* info, struct mtd_stripe_op*
>op)
>+{
>+ if(!info || !op)
>+ BUG();
>+
>+ spin_lock(&info->list_lock);
>+ list_add_tail(&op->list, &info->list);
>+ spin_unlock(&info->list_lock);
>+}
>+
>+/* End of multithreading support routines */
>+
>+
>+/*
>+ * MTD methods which look up the relevant subdevice, translate the
>+ * effective address and pass through to the subdevice.
>+ */
>+
>+
>+/* sychroneous read from striped volume */
>+static int
>+stripe_read_sync(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf)
>+{
>+ u_int32_t from_loc = (u_int32_t)from; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+ size_t retsize; /* data read/written from/to
>subdev (bytes) */
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_sync(): offset = 0x%08x, size
>= %d\n", from_loc, len);
>+
>+ /* Check whole striped device bounds here */
>+ if(from_loc + len > mtd->size)
>+ {
>+ return err;
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_sync(): device = %d, offset =
>0x%08x, len = %d\n", subdev_number, subdev_offset_low, subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read(stripe->subdev[subdev_number],
>subdev_offset_low, subdev_len, &retsize, buf);
>+ if(!err)
>+ {
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_sync(): device = %d, offset
>= 0x%08x, len = %d\n", subdev_number, subdev_offset *
>stripe->interleave_size, subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read(stripe->subdev[subdev_number],
>subdev_offset * stripe->interleave_size, subdev_len, &retsize, buf);
>+ if(err)
>+ break;
>+
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_sync(): read %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+/* asychroneous read from striped volume */
>+static int
>+stripe_read_async(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf)
>+{
>+ u_int32_t from_loc = (u_int32_t)from; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_async(): offset = 0x%08x, size
>= %d\n", from_loc, len);
>+
>+ /* Check whole striped device bounds here */
>+ if(from_loc + len > mtd->size)
>+ {
>+ return err;
>+ }
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = len / stripe->interleave_size / stripe->num_subdev +
>1; /* default queue size. could be set to predefined value */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_READ;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* asynch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_async(): device = %d, offset =
>0x%08x, len = %d\n", subdev_number, subdev_offset_low, subdev_len);
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset_low,
>subdev_len, buf, NULL);
>+ if(!err)
>+ {
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_async(): device = %d,
>offset = 0x%08x, len = %d\n", subdev_number, subdev_offset *
>stripe->interleave_size, subdev_len);
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset *
>stripe->interleave_size, subdev_len, buf, NULL);
>+ if(err)
>+ break;
>+
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ /* Push operation into the corresponding threads queue and rise
>semaphores */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ err = ops[i].status;
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_async(): read %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+static int
>+stripe_read(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf)
>+{
>+ int err;
>+ if(mtd->type == MTD_NANDFLASH)
>+ err = stripe_read_async(mtd, from, len, retlen, buf);
>+ else
>+ err = stripe_read_sync(mtd, from, len, retlen, buf);
>+
>+ return err;
>+}
>+
>+
>+static int
>+stripe_write(struct mtd_info *mtd, loff_t to, size_t len,
>+ size_t * retlen, const u_char * buf)
>+{
>+ u_int32_t to_loc = (u_int32_t)to; /* we can do this since whole
>MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned block */
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write(): offset = 0x%08x, size =
>%d\n", to_loc, len);
>+
>+ /* check if no data is going to be written */
>+ if(!len)
>+ return 0;
>+
>+ /* Check whole striped device bounds here */
>+ if(to_loc + len > mtd->size)
>+ return err;
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = len / stripe->interleave_size / stripe->num_subdev +
>1; /* default queue size. could be set to predefined value */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_WRITE;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(to_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((to_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((to_loc - stripe->subdev_last_offset[i
>- 1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (to_loc / stripe->interleave_size) / dev_count;
>+ subdev_number = (to_loc / stripe->interleave_size) % dev_count;
>+ }
>+
>+ subdev_offset_low = to_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset_low,
>subdev_len, buf, NULL);
>+ if(!err)
>+ {
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(to_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset *
>stripe->interleave_size, subdev_len, buf, NULL);
>+ if(err)
>+ break;
>+
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ if(to_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ /* Push operation into the corresponding threads queue and rise
>semaphores */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ err = ops[i].status;
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write(): written %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+/* synchroneous ecc read from striped volume */
>+static int
>+stripe_read_ecc_sync(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf, u_char * eccbuf,
>+ struct nand_oobinfo *oobsel)
>+{
>+ u_int32_t from_loc = (u_int32_t)from; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+ size_t retsize; /* data read/written from/to
>subdev (bytes) */
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_ecc_sync(): offset = 0x%08x,
>size = %d\n", from_loc, len);
>+
>+ if(oobsel != NULL)
>+ {
>+ /* check if oobinfo is has been chandes by FS */
>+ if(memcmp(oobsel, &mtd->oobinfo, sizeof(struct nand_oobinfo)))
>+ {
>+ printk(KERN_ERR "stripe_read_ecc_sync(): oobinfo has been
>changed by FS (not supported yet)\n");
>+ return err;
>+ }
>+ }
>+
>+ /* Check whole striped device bounds here */
>+ if(from_loc + len > mtd->size)
>+ {
>+ return err;
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_ecc_sync(): device = %d,
>offset = 0x%08x, len = %d\n", subdev_number, subdev_offset_low,
>subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read_ecc(stripe->subdev[subdev_number],
>subdev_offset_low, subdev_len, &retsize, buf, eccbuf,
>&stripe->subdev[subdev_number]->oobinfo);
>+ if(!err)
>+ {
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_ecc_sync(): device = %d,
>offset = 0x%08x, len = %d\n", subdev_number, subdev_offset *
>stripe->interleave_size, subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read_ecc(stripe->subdev[subdev_number],
>subdev_offset * stripe->interleave_size, subdev_len, &retsize, buf,
>eccbuf, &stripe->subdev[subdev_number]->oobinfo);
>+ if(err)
>+ break;
>+
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(from + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_ecc_sync(): read %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+/* asynchroneous ecc read from striped volume */
>+static int
>+stripe_read_ecc_async(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf, u_char * eccbuf,
>+ struct nand_oobinfo *oobsel)
>+{
>+ u_int32_t from_loc = (u_int32_t)from; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_ecc_async(): offset = 0x%08x,
>size = %d\n", from_loc, len);
>+
>+ if(oobsel != NULL)
>+ {
>+ /* check if oobinfo is has been chandes by FS */
>+ if(memcmp(oobsel, &mtd->oobinfo, sizeof(struct nand_oobinfo)))
>+ {
>+ printk(KERN_ERR "stripe_read_ecc_async(): oobinfo has been
>changed by FS (not supported yet)\n");
>+ return err;
>+ }
>+ }
>+
>+ /* Check whole striped device bounds here */
>+ if(from_loc + len > mtd->size)
>+ {
>+ return err;
>+ }
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = len / stripe->interleave_size / stripe->num_subdev +
>1; /* default queue size. could be set to predefined value */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_READ_ECC;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Issue read operation here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_ecc_async(): device = %d,
>offset = 0x%08x, len = %d\n", subdev_number, subdev_offset_low,
>subdev_len);
>+
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset_low,
>subdev_len, buf, eccbuf);
>+ if(!err)
>+ {
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(eccbuf)
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(from_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Issue read operation here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_ecc_async(): device = %d,
>offset = 0x%08x, len = %d\n", subdev_number, subdev_offset *
>stripe->interleave_size, subdev_len);
>+
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset *
>stripe->interleave_size, subdev_len, buf, eccbuf);
>+ if(err)
>+ break;
>+
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(eccbuf)
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(from + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ /* Push operation into the corresponding threads queue and rise
>semaphores */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ err = ops[i].status;
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_ecc_async(): read %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+static int
>+stripe_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf, u_char * eccbuf,
>+ struct nand_oobinfo *oobsel)
>+{
>+ int err;
>+ if(mtd->type == MTD_NANDFLASH)
>+ err = stripe_read_ecc_async(mtd, from, len, retlen, buf, eccbuf,
>oobsel);
>+ else
>+ err = stripe_read_ecc_sync(mtd, from, len, retlen, buf, eccbuf,
>oobsel);
>+
>+ return err;
>+}
>+
>+
>+static int
>+stripe_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
>+ size_t * retlen, const u_char * buf, u_char * eccbuf,
>+ struct nand_oobinfo *oobsel)
>+{
>+ u_int32_t to_loc = (u_int32_t)to; /* we can do this since whole
>MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned block */
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write_ecc(): offset = 0x%08x, size
>= %d\n", to_loc, len);
>+
>+ if(oobsel != NULL)
>+ {
>+ /* check if oobinfo is has been chandes by FS */
>+ if(memcmp(oobsel, &mtd->oobinfo, sizeof(struct nand_oobinfo)))
>+ {
>+ printk(KERN_ERR "stripe_write_ecc(): oobinfo has been
>changed by FS (not supported yet)\n");
>+ return err;
>+ }
>+ }
>+
>+ /* check if no data is going to be written */
>+ if(!len)
>+ return 0;
>+
>+ /* Check whole striped device bounds here */
>+ if(to_loc + len > mtd->size)
>+ return err;
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = len / stripe->interleave_size / stripe->num_subdev +
>1; /* default queue size */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_WRITE_ECC;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(to_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((to_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((to_loc - stripe->subdev_last_offset[i
>- 1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (to_loc / stripe->interleave_size) / dev_count;
>+ subdev_number = (to_loc / stripe->interleave_size) % dev_count;
>+ }
>+
>+ subdev_offset_low = to_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset_low,
>subdev_len, buf, eccbuf);
>+ if(!err)
>+ {
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(eccbuf)
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(to_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset *
>stripe->interleave_size, subdev_len, buf, eccbuf);
>+ if(err)
>+ break;
>+
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+ if(eccbuf)
>+ eccbuf += stripe->subdev[subdev_number]->oobavail;
>+
>+ if(to_loc + *retlen >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ /* Push operation into the corresponding threads queue and rise
>semaphores */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ err = ops[i].status;
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write_ecc(): written %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+
>+static int
>+stripe_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
>+ size_t * retlen, u_char * buf)
>+{
>+ u_int32_t from_loc = (u_int32_t)from; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+ size_t retsize; /* data read/written from/to
>subdev (bytes) */
>+
>+ //u_int32_t subdev_oobavail = stripe->subdev[0]->oobavail;
>+ u_int32_t subdev_oobavail = stripe->subdev[0]->oobsize;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_oob(): offset = 0x%08x, size =
>%d\n", from_loc, len);
>+
>+ /* Check whole striped device bounds here */
>+ if(from_loc + len > mtd->size)
>+ {
>+ return err;
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % subdev_oobavail;
>+ subdev_len = (len_left < (subdev_oobavail - subdev_offset_low)) ?
>len_left : (subdev_oobavail - subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_oob(): device = %d, offset =
>0x%08x, len = %d\n", subdev_number, subdev_offset_low, subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read_oob(stripe->subdev[subdev_number],
>subdev_offset_low, subdev_len, &retsize, buf);
>+ if(!err)
>+ {
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ /* increase flash offset by interleave size since oob blocks
>+ * aligned with page size (i.e. interleave size) */
>+ from_loc += stripe->interleave_size;
>+
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < subdev_oobavail) ? len_left :
>subdev_oobavail;
>+
>+ /* Synch read here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_read_oob(): device = %d, offset
>= 0x%08x, len = %d\n", subdev_number, subdev_offset *
>stripe->interleave_size, subdev_len);
>+ err =
>stripe->subdev[subdev_number]->read_oob(stripe->subdev[subdev_number],
>subdev_offset * stripe->interleave_size, subdev_len, &retsize, buf);
>+ if(err)
>+ break;
>+
>+ *retlen += retsize;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ /* increase flash offset by interleave size since oob blocks
>+ * aligned with page size (i.e. interleave size) */
>+ from_loc += stripe->interleave_size;
>+
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_read_oob(): read %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+static int
>+stripe_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
>+ size_t *retlen, const u_char * buf)
>+{
>+ u_int32_t to_loc = (u_int32_t)to; /* we can do this since whole
>MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned block */
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to read/write
>left (bytes) */
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ //u_int32_t subdev_oobavail = stripe->subdev[0]->oobavail;
>+ u_int32_t subdev_oobavail = stripe->subdev[0]->oobsize;
>+
>+ *retlen = 0;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write_oob(): offset = 0x%08x, size
>= %d\n", to_loc, len);
>+
>+ /* check if no data is going to be written */
>+ if(!len)
>+ return 0;
>+
>+ /* Check whole striped device bounds here */
>+ if(to_loc + len > mtd->size)
>+ return err;
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = len / subdev_oobavail / stripe->num_subdev + 1;
>/* default queue size. could be set to predefined value */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "stripe_write_oob(): memory allocation
>error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_WRITE_OOB;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(to_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((to_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((to_loc - stripe->subdev_last_offset[i
>- 1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (to_loc / stripe->interleave_size) / dev_count;
>+ subdev_number = (to_loc / stripe->interleave_size) % dev_count;
>+ }
>+
>+ subdev_offset_low = to_loc % subdev_oobavail;
>+ subdev_len = (len_left < (subdev_oobavail - subdev_offset_low)) ?
>len_left : (subdev_oobavail - subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset_low,
>subdev_len, buf, NULL);
>+
>
>+ if(!err)
>+ {
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ /* increase flash offset by interleave size since oob blocks
>+ * aligned with page size (i.e. interleave size) */
>+ to_loc += stripe->interleave_size;
>+
>+ if(to_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < subdev_oobavail) ? len_left :
>subdev_oobavail;
>+
>+ /* Add suboperation to queue here */
>+ err = stripe_add_subop(&ops[subdev_number], subdev_offset *
>stripe->interleave_size, subdev_len, buf, NULL);
>+ if(err)
>+ break;
>+
>+ *retlen += subdev_len;
>+ len_left -= subdev_len;
>+ buf += subdev_len;
>+
>+ /* increase flash offset by interleave size since oob blocks
>+ * aligned with page size (i.e. interleave size) */
>+ to_loc += stripe->interleave_size;
>+
>+ if(to_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ /* Push operation into the corresponding threads queue and rise
>semaphores */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ err = ops[i].status;
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_write_oob(): written %d bytes\n",
>*retlen);
>+ return err;
>+}
>+
>+/* this routine aimed to support striping on NOR_ECC
>+ * it has been taken from cfi_cmdset_0001.c
>+ */
>+static int
>+stripe_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned
>long count,
>+ loff_t to, size_t * retlen)
>+{
>+ int i, page, len, total_len, ret = 0, written = 0, cnt = 0,
>towrite;
>+ u_char *bufstart;
>+ char* data_poi;
>+ char* data_buf;
>+ loff_t write_offset;
>+ int rl_wr;
>+
>+ u_int32_t pagesize;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "==> stripe_writev()\n");
>+
>+#ifdef MTD_PROGRAM_REGIONS
>+ /* Montavista patch for Sibley support detected */
>+ if(mtd->flags & MTD_PROGRAM_REGIONS)
>+ {
>+ pagesize = MTD_PROGREGION_SIZE(mtd);
>+ }
>+ else if(mtd->flags & MTD_ECC)
>+ {
>+ pagesize = mtd->eccsize;
>+ }
>+ else
>+ {
>+ printk(KERN_ERR "stripe_writev() has been called for device
>without MTD_PROGRAM_REGIONS or MTD_ECC set\n");
>+ return -EINVAL;
>+ }
>+#else
>+ if(mtd->flags & MTD_ECC)
>+ {
>+ pagesize = mtd->eccsize;
>+ }
>+ else
>+ {
>+ printk(KERN_ERR "stripe_writev() has been called for device
>without MTD_ECC set\n");
>+ return -EINVAL;
>+ }
>+#endif
>+
>+ data_buf = kmalloc(pagesize, GFP_KERNEL);
>+
>+ /* Preset written len for early exit */
>+ *retlen = 0;
>+
>+ /* Calculate total length of data */
>+ total_len = 0;
>+ for (i = 0; i < count; i++)
>+ total_len += (int) vecs[i].iov_len;
>+
>+ /* check if no data is going to be written */
>+ if(!total_len)
>+ {
>+ kfree(data_buf);
>+ return 0;
>+ }
>+
>+ /* Do not allow write past end of page */
>+ if ((to + total_len) > mtd->size) {
>+ DEBUG (MTD_DEBUG_LEVEL0, "stripe_writev(): Attempted write past
>end of device\n");
>+ kfree(data_buf);
>+ return -EINVAL;
>+ }
>+
>+ /* Setup start page */
>+ page = ((int) to) / pagesize;
>+ towrite = (page + 1) * pagesize - to; /* rest of the page */
>+ write_offset = to;
>+ written = 0;
>+ /* Loop until all iovecs' data has been written */
>+ len = 0;
>+ while (len < total_len) {
>+ bufstart = (u_char *)vecs->iov_base;
>+ bufstart += written;
>+ data_poi = bufstart;
>+
>+ /* If the given tuple is >= reet of page then
>+ * write it out from the iov
>+ */
>+ if ( (vecs->iov_len-written) >= towrite) { /* The fastest
>case is to write data by int * blocksize */
>+ ret = mtd->write(mtd, write_offset, towrite, &rl_wr,
>data_poi);
>+ if(ret)
>+ break;
>+ len += towrite;
>+ page ++;
>+ write_offset = page * pagesize;
>+ towrite = pagesize;
>+ written += towrite;
>+ if(vecs->iov_len == written) {
>+ vecs ++;
>+ written = 0;
>+ }
>+ }
>+ else
>+ {
>+ cnt = 0;
>+ while(cnt < towrite ) {
>+ data_buf[cnt++] = ((u_char *)
>vecs->iov_base)[written++];
>+ if(vecs->iov_len == written )
>+ {
>+ if((cnt+len) == total_len )
>+ break;
>+ vecs ++;
>+ written = 0;
>+ }
>+ }
>+ data_poi = data_buf;
>+ ret = mtd->write(mtd, write_offset, cnt, &rl_wr, data_poi);
>+ if (ret)
>+ break;
>+ len += cnt;
>+ page ++;
>+ write_offset = page * pagesize;
>+ towrite = pagesize;
>+ }
>+ }
>+
>+ if(retlen)
>+ *retlen = len;
>+ kfree(data_buf);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "<== stripe_writev()\n");
>+
>+ return ret;
>+}
>+
>+
>+static int
>+stripe_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
>unsigned long count,
>+ loff_t to, size_t * retlen, u_char *eccbuf, struct
>nand_oobinfo *oobsel)
>+{
>+ int i, page, len, total_len, ret = 0, written = 0, cnt = 0,
>towrite;
>+ u_char *bufstart;
>+ char* data_poi;
>+ char* data_buf;
>+ loff_t write_offset;
>+ data_buf = kmalloc(mtd->oobblock, GFP_KERNEL);
>+ int rl_wr;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "==> stripe_writev_ecc()\n");
>+
>+ if(oobsel != NULL)
>+ {
>+ /* check if oobinfo is has been chandes by FS */
>+ if(memcmp(oobsel, &mtd->oobinfo, sizeof(struct nand_oobinfo)))
>+ {
>+ printk(KERN_ERR "stripe_writev_ecc(): oobinfo has been
>changed by FS (not supported yet)\n");
>+ kfree(data_buf);
>+ return -EINVAL;
>+ }
>+ }
>+
>+ if(!(mtd->flags & MTD_ECC))
>+ {
>+ printk(KERN_ERR "stripe_writev_ecc() has been called for device
>without MTD_ECC set\n");
>+ kfree(data_buf);
>+ return -EINVAL;
>+ }
>+
>+ /* Preset written len for early exit */
>+ *retlen = 0;
>+
>+ /* Calculate total length of data */
>+ total_len = 0;
>+ for (i = 0; i < count; i++)
>+ total_len += (int) vecs[i].iov_len;
>+
>+ /* check if no data is going to be written */
>+ if(!total_len)
>+ {
>+ kfree(data_buf);
>+ return 0;
>+ }
>+
>+ /* Do not allow write past end of page */
>+ if ((to + total_len) > mtd->size) {
>+ DEBUG (MTD_DEBUG_LEVEL0, "stripe_writev_ecc(): Attempted write
>past end of device\n");
>+ kfree(data_buf);
>+ return -EINVAL;
>+ }
>+
>+ /* Check "to" and "len" alignment here */
>+ if((to & (mtd->oobblock - 1)) || (total_len & (mtd->oobblock - 1)))
>+ {
>+ printk(KERN_ERR "stripe_writev_ecc(): Attempted write not
>aligned data!\n");
>+ kfree(data_buf);
>+ return -EINVAL;
>+ }
>+
>+ /* Setup start page. Notaligned data is not allowed for write_ecc.
>*/
>+ page = ((int) to) / mtd->oobblock;
>+ towrite = (page + 1) * mtd->oobblock - to; /* aligned with
>oobblock */
>+ write_offset = to;
>+ written = 0;
>+ /* Loop until all iovecs' data has been written */
>+ len = 0;
>+ while (len < total_len) {
>+ bufstart = (u_char *)vecs->iov_base;
>+ bufstart += written;
>+ data_poi = bufstart;
>+
>+ /* If the given tuple is >= reet of page then
>+ * write it out from the iov
>+ */
>+ if ( (vecs->iov_len-written) >= towrite) { /* The fastest
>case is to write data by int * blocksize */
>+ ret = mtd->write_ecc(mtd, write_offset, towrite, &rl_wr,
>data_poi, eccbuf, oobsel);
>+ if(ret)
>+ break;
>+ len += rl_wr;
>+ page ++;
>+ write_offset = page * mtd->oobblock;
>+ towrite = mtd->oobblock;
>+ written += towrite;
>+ if(vecs->iov_len == written) {
>+ vecs ++;
>+ written = 0;
>+ }
>+
>+ if(eccbuf)
>+ eccbuf += mtd->oobavail;
>+ }
>+ else
>+ {
>+ cnt = 0;
>+ while(cnt < towrite ) {
>+ data_buf[cnt++] = ((u_char *)
>vecs->iov_base)[written++];
>+ if(vecs->iov_len == written )
>+ {
>+ if((cnt+len) == total_len )
>+ break;
>+ vecs ++;
>+ written = 0;
>+ }
>+ }
>+ data_poi = data_buf;
>+ ret = mtd->write_ecc(mtd, write_offset, cnt, &rl_wr,
>data_poi, eccbuf, oobsel);
>+ if (ret)
>+ break;
>+ len += rl_wr;
>+ page ++;
>+ write_offset = page * mtd->oobblock;
>+ towrite = mtd->oobblock;
>+
>+ if(eccbuf)
>+ eccbuf += mtd->oobavail;
>+ }
>+ }
>+
>+ if(retlen)
>+ *retlen = len;
>+ kfree(data_buf);
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "<== stripe_writev_ecc()\n");
>+
>+ return ret;
>+}
>+
>+
>+static void
>+stripe_erase_callback(struct erase_info *instr)
>+{
>+ wake_up((wait_queue_head_t *) instr->priv);
>+}
>+
>+static int
>+stripe_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
>+{
>+ int err;
>+ wait_queue_head_t waitq;
>+ DECLARE_WAITQUEUE(wait, current);
>+
>+ init_waitqueue_head(&waitq);
>+
>+ erase->mtd = mtd;
>+ erase->callback = stripe_erase_callback;
>+ erase->priv = (unsigned long) &waitq;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_dev_erase(): addr=0x%08x,
>len=%d\n", erase->addr, erase->len);
>+
>+ /*
>+ * FIXME: Allow INTERRUPTIBLE. Which means
>+ * not having the wait_queue head on the stack.
>+ */
>+ err = mtd->erase(mtd, erase);
>+ if (!err)
>+ {
>+ set_current_state(TASK_UNINTERRUPTIBLE);
>+ add_wait_queue(&waitq, &wait);
>+ if (erase->state != MTD_ERASE_DONE
>+ && erase->state != MTD_ERASE_FAILED)
>+ schedule();
>+ remove_wait_queue(&waitq, &wait);
>+ set_current_state(TASK_RUNNING);
>+
>+ err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
>+ }
>+ return err;
>+}
>+
>+static int
>+stripe_erase(struct mtd_info *mtd, struct erase_info *instr)
>+{
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int i, err;
>+ struct mtd_stripe_erase_bounds *erase_bounds;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to erase
>(bytes) */
>+ size_t subdev_len; /* data size to be erased at
>this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left; /* total data size left to be
>erased (bytes) */
>+ size_t len_done; /* total data size erased */
>+ u_int32_t from;
>+
>+ struct mtd_stripe_op *ops; /* operations array (one per
>thread) */
>+ u_int32_t size; /* amount of memory to be
>allocated for thread operations */
>+ u_int32_t queue_size;
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_earse(): addr=0x%08x, len=%d\n",
>instr->addr, instr->len);
>+
>+ if(!(mtd->flags & MTD_WRITEABLE))
>+ return -EROFS;
>+
>+ if(instr->addr > stripe->mtd.size)
>+ return -EINVAL;
>+
>+ if(instr->len + instr->addr > stripe->mtd.size)
>+ return -EINVAL;
>+
>+ /*
>+ * Check for proper erase block alignment of the to-be-erased area.
>+ */
>+ if(!stripe->mtd.numeraseregions)
>+ {
>+ /* striped device has uniform erase block size */
>+ if(instr->addr & (stripe->mtd.erasesize - 1))
>+ return -EINVAL;
>+ if(instr->len & (stripe->mtd.erasesize - 1))
>+ return -EINVAL;
>+ }
>+ else
>+ {
>+ /* we should not get here */
>+ return -EINVAL;
>+ }
>+
>+ instr->fail_addr = 0xffffffff;
>+
>+ /* allocate memory for multithread operations */
>+ queue_size = 1; /* queue size for erase opration is 1 */
>+ size = stripe->num_subdev *
>SIZEOF_STRUCT_MTD_STRIPE_OP(queue_size);
>+ ops = kmalloc(size, GFP_KERNEL);
>+ if(!ops)
>+ {
>+ printk(KERN_ERR "mtd_stripe: memory allocation error!\n");
>+ return -ENOMEM;
>+ }
>+
>+ memset(ops, 0, size);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ ops[i].opcode = MTD_STRIPE_OPCODE_ERASE;
>+ ops[i].caller_id = 0; /* TBD */
>+ init_MUTEX_LOCKED(&ops[i].sem); /* mutex is locked here.
>to be unlocked by device thread */
>+ //ops[i].status = 0; /* TBD */
>+ ops[i].fail_addr = 0xffffffff;
>+
>+ INIT_LIST_HEAD(&ops[i].subops.list); /* initialize
>suboperation list head */
>+
>+ ops[i].subops.ops_num = 0; /* to be increased later
>here */
>+ ops[i].subops.ops_num_max = queue_size; /* total number of
>suboperations can be stored in the array */
>+ ops[i].subops.ops_array = (struct subop *)((char *)(ops +
>stripe->num_subdev) + i * queue_size * sizeof(struct subop));
>+ }
>+
>+ len_left = instr->len;
>+ len_done = 0;
>+ from = instr->addr;
>+
>+ /* allocate memory for erase boundaries for all subdevices */
>+ erase_bounds = kmalloc(stripe->num_subdev * sizeof(struct
>mtd_stripe_erase_bounds), GFP_KERNEL);
>+ if(!erase_bounds)
>+ {
>+ kfree(ops);
>+ return -ENOMEM;
>+ }
>+ memset(erase_bounds, 0, sizeof(struct mtd_stripe_erase_bounds) *
>stripe->num_subdev);
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from - stripe->subdev_last_offset[i - 1])
>/ stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from / stripe->interleave_size) / dev_count;
>+ subdev_number = (from / stripe->interleave_size) % dev_count;
>+ }
>+
>+ /* Should by optimized for erase op */
>+ subdev_offset_low = from % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add/extend block-to-be erased */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset_low;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+ len_left -= subdev_len;
>+ len_done += subdev_len;
>+
>+ if(from + len_done >= stripe->subdev_last_offset[stripe->num_subdev
>- dev_count])
>+ dev_count--;
>+
>+ while(len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size; /* can by optimized for erase op*/
>+
>+ /* Add/extend block-to-be erased */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset *
>stripe->interleave_size;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+ len_left -= subdev_len;
>+ len_done += subdev_len;
>+
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_erase(): device = %d, addr =
>0x%08x, len = %d\n", subdev_number, erase_bounds[subdev_number].addr,
>erase_bounds[subdev_number].len);
>+
>+ if(from + len_done >=
>stripe->subdev_last_offset[stripe->num_subdev - dev_count])
>+ dev_count--;
>+ }
>+
>+ /* now do the erase: */
>+ err = 0;
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ if(erase_bounds[i].need_erase)
>+ {
>+ if (!(stripe->subdev[i]->flags & MTD_WRITEABLE))
>+ {
>+ err = -EROFS;
>+ break;
>+ }
>+
>+ stripe_add_subop(&ops[i], erase_bounds[i].addr,
>erase_bounds[i].len, (u_char *)instr, NULL);
>+ }
>+ }
>+
>+ /* Push operation queues into the corresponding threads */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ if(erase_bounds[i].need_erase)
>+ {
>+ stripe_add_op(&stripe->sw_threads[i], &ops[i]);
>+
>+ /* set original operation priority */
>+ ops[i].op_prio = current->static_prio - MAX_RT_PRIO - 20;
>+ stripe_set_write_thread_prio(&stripe->sw_threads[i]);
>+
>+ up(&stripe->sw_threads[i].sw_thread_wait);
>+ }
>+ }
>+
>+ /* wait for all suboperations completed and check status */
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ if(erase_bounds[i].need_erase)
>+ {
>+ down(&ops[i].sem);
>+
>+ /* set error if one of operations has failed */
>+ if(ops[i].status)
>+ {
>+ err = ops[i].status;
>+
>+ /* FIX ME: For now this adddres shows address
>+ * at the last failed subdevice,
>+ * but not at the "super" device */
>+ if(ops[i].fail_addr != 0xffffffff)
>+ instr->fail_addr = ops[i].fail_addr;
>+ }
>+
>+ instr->state = ops[i].state;
>+ }
>+ }
>+
>+ /* Deallocate all memory before exit */
>+ kfree(erase_bounds);
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ stripe_destroy_op(&ops[i]);
>+ }
>+ kfree(ops);
>+
>+ if(err)
>+ return err;
>+
>+ if(instr->callback)
>+ instr->callback(instr);
>+ return 0;
>+}
>+
>+static int
>+stripe_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
>+{
>+ u_int32_t ofs_loc = (u_int32_t)ofs; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to lock
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be locked @
>subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to lock left
>(bytes) */
>+
>+ size_t retlen = 0;
>+ struct mtd_stripe_erase_bounds *erase_bounds;
>+
>+ /* Check whole striped device bounds here */
>+ if(ofs_loc + len > mtd->size)
>+ return err;
>+
>+ /* allocate memory for lock boundaries for all subdevices */
>+ erase_bounds = kmalloc(stripe->num_subdev * sizeof(struct
>mtd_stripe_erase_bounds), GFP_KERNEL);
>+ if(!erase_bounds)
>+ return -ENOMEM;
>+ memset(erase_bounds, 0, sizeof(struct mtd_stripe_erase_bounds) *
>stripe->num_subdev);
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(ofs_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((ofs_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((ofs_loc - stripe->subdev_last_offset[i
>- 1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (ofs_loc / stripe->interleave_size) / dev_count;
>+ subdev_number = (ofs_loc / stripe->interleave_size) % dev_count;
>+ }
>+
>+ subdev_offset_low = ofs_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add/extend block-to-be locked */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset_low;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+
>+ retlen += subdev_len;
>+ len_left -= subdev_len;
>+ if(ofs + retlen >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+
>+ while(len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Add/extend block-to-be locked */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset *
>stripe->interleave_size;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+
>+ retlen += subdev_len;
>+ len_left -= subdev_len;
>+
>+ if(ofs + retlen >= stripe->subdev_last_offset[stripe->num_subdev
>- dev_count])
>+ dev_count--;
>+ }
>+
>+ /* now do lock */
>+ err = 0;
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ if(erase_bounds[i].need_erase)
>+ {
>+ if (stripe->subdev[i]->lock)
>+ {
>+ err = stripe->subdev[i]->lock(stripe->subdev[i],
>erase_bounds[i].addr, erase_bounds[i].len);
>+ if(err)
>+ break;
>+ };
>+ }
>+ }
>+
>+ /* Free allocated memory here */
>+ kfree(erase_bounds);
>+
>+ return err;
>+}
>+
>+static int
>+stripe_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
>+{
>+ u_int32_t ofs_loc = (u_int32_t)ofs; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to unlock
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be unlocked @
>subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = len; /* total data size to unlock
>left (bytes) */
>+
>+ size_t retlen = 0;
>+ struct mtd_stripe_erase_bounds *erase_bounds;
>+
>+ /* Check whole striped device bounds here */
>+ if(ofs_loc + len > mtd->size)
>+ return err;
>+
>+ /* allocate memory for unlock boundaries for all subdevices */
>+ erase_bounds = kmalloc(stripe->num_subdev * sizeof(struct
>mtd_stripe_erase_bounds), GFP_KERNEL);
>+ if(!erase_bounds)
>+ return -ENOMEM;
>+ memset(erase_bounds, 0, sizeof(struct mtd_stripe_erase_bounds) *
>stripe->num_subdev);
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(ofs_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((ofs_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((ofs_loc - stripe->subdev_last_offset[i
>- 1]) / stripe->interleave_size) % dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (ofs_loc / stripe->interleave_size) / dev_count;
>+ subdev_number = (ofs_loc / stripe->interleave_size) % dev_count;
>+ }
>+
>+ subdev_offset_low = ofs_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* Add/extend block-to-be unlocked */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset_low;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+
>+ retlen += subdev_len;
>+ len_left -= subdev_len;
>+ if(ofs + retlen >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+
>+ while(len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* Add/extend block-to-be unlocked */
>+ if(!erase_bounds[subdev_number].need_erase)
>+ {
>+ erase_bounds[subdev_number].need_erase = 1;
>+ erase_bounds[subdev_number].addr = subdev_offset *
>stripe->interleave_size;
>+ }
>+ erase_bounds[subdev_number].len += subdev_len;
>+
>+ retlen += subdev_len;
>+ len_left -= subdev_len;
>+
>+ if(ofs + retlen >= stripe->subdev_last_offset[stripe->num_subdev
>- dev_count])
>+ dev_count--;
>+ }
>+
>+ /* now do unlock */
>+ err = 0;
>+ for(i = 0; i < stripe->num_subdev; i++)
>+ {
>+ if(erase_bounds[i].need_erase)
>+ {
>+ if (stripe->subdev[i]->unlock)
>+ {
>+ err = stripe->subdev[i]->unlock(stripe->subdev[i],
>erase_bounds[i].addr, erase_bounds[i].len);
>+ if(err)
>+ break;
>+ };
>+ }
>+ }
>+
>+ /* Free allocated memory here */
>+ kfree(erase_bounds);
>+
>+ return err;
>+}
>+
>+static void
>+stripe_sync(struct mtd_info *mtd)
>+{
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int i;
>+
>+ for (i = 0; i < stripe->num_subdev; i++)
>+ {
>+ struct mtd_info *subdev = stripe->subdev[i];
>+ if (subdev->sync)
>+ subdev->sync(subdev);
>+ }
>+}
>+
>+static int
>+stripe_suspend(struct mtd_info *mtd)
>+{
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int i, rc = 0;
>+
>+ for (i = 0; i < stripe->num_subdev; i++)
>+ {
>+ struct mtd_info *subdev = stripe->subdev[i];
>+ if (subdev->suspend)
>+ {
>+ if ((rc = subdev->suspend(subdev)) < 0)
>+ return rc;
>+ };
>+ }
>+ return rc;
>+}
>+
>+static void
>+stripe_resume(struct mtd_info *mtd)
>+{
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int i;
>+
>+ for (i = 0; i < stripe->num_subdev; i++)
>+ {
>+ struct mtd_info *subdev = stripe->subdev[i];
>+ if (subdev->resume)
>+ subdev->resume(subdev);
>+ }
>+}
>+
>+static int
>+stripe_block_isbad(struct mtd_info *mtd, loff_t ofs)
>+{
>+ u_int32_t from_loc = (u_int32_t)ofs; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int res = 0;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = mtd->oobblock; /* total data size to read/write
>left (bytes) */
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_block_isbad(): offset = 0x%08x\n",
>from_loc);
>+
>+ from_loc = (from_loc / mtd->oobblock) * mtd->oobblock; /* align
>offset here */
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* check block on subdevice is bad here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_block_isbad(): device = %d, offset
>= 0x%08x\n", subdev_number, subdev_offset_low);
>+ res =
>stripe->subdev[subdev_number]->block_isbad(stripe->subdev[subdev_number]
>, subdev_offset_low);
>+ if(!res)
>+ {
>+ len_left -= subdev_len;
>+ from_loc += subdev_len;
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!res && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* check block on subdevice is bad here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_block_isbad(): device = %d,
>offset = 0x%08x\n", subdev_number, subdev_offset *
>stripe->interleave_size);
>+ res =
>stripe->subdev[subdev_number]->block_isbad(stripe->subdev[subdev_number]
>, subdev_offset * stripe->interleave_size);
>+ if(res)
>+ {
>+ break;
>+ }
>+ else
>+ {
>+ len_left -= subdev_len;
>+ from_loc += subdev_len;
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev
>- dev_count])
>+ dev_count--;
>+ }
>+ }
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "<== stripe_block_isbad()\n");
>+ return res;
>+}
>+
>+/* returns 0 - success */
>+static int
>+stripe_block_markbad(struct mtd_info *mtd, loff_t ofs)
>+{
>+ u_int32_t from_loc = (u_int32_t)ofs; /* we can do this since
>whole MTD size in current implementation has u_int32_t type */
>+
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int err = -EINVAL;
>+ int i;
>+
>+ u_int32_t subdev_offset; /* equal size subdevs offset
>(interleaved block size count)*/
>+ u_int32_t subdev_number; /* number of current subdev */
>+ u_int32_t subdev_offset_low; /* subdev offset to read/write
>(bytes). used for "first" probably unaligned with erasesize data block
>*/
>+ size_t subdev_len; /* data size to be read/written
>from/to subdev at this turn (bytes) */
>+ int dev_count; /* equal size subdev count */
>+ size_t len_left = mtd->oobblock; /* total data size to read/write
>left (bytes) */
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "stripe_block_markbad(): offset =
>0x%08x\n", from_loc);
>+
>+ from_loc = (from_loc / mtd->oobblock) * mtd->oobblock; /* align
>offset here */
>+
>+ /* Locate start position and corresponding subdevice number */
>+ subdev_offset = 0;
>+ subdev_number = 0;
>+ dev_count = stripe->num_subdev;
>+ for(i = (stripe->num_subdev - 1); i > 0; i--)
>+ {
>+ if(from_loc >= stripe->subdev_last_offset[i-1])
>+ {
>+ dev_count = stripe->num_subdev - i; /* get "equal size"
>devices count */
>+ subdev_offset = stripe->subdev[i - 1]->size /
>stripe->interleave_size - 1;
>+ subdev_offset += ((from_loc - stripe->subdev_last_offset[i -
>1]) / stripe->interleave_size) / dev_count;
>+ subdev_number = i + ((from_loc -
>stripe->subdev_last_offset[i - 1]) / stripe->interleave_size) %
>dev_count;
>+ break;
>+ }
>+ }
>+
>+ if(subdev_offset == 0)
>+ {
>+ subdev_offset = (from_loc / stripe->interleave_size) /
>dev_count;
>+ subdev_number = (from_loc / stripe->interleave_size) %
>dev_count;
>+ }
>+
>+ subdev_offset_low = from_loc % stripe->interleave_size;
>+ subdev_len = (len_left < (stripe->interleave_size -
>subdev_offset_low)) ? len_left : (stripe->interleave_size -
>subdev_offset_low);
>+ subdev_offset_low += subdev_offset * stripe->interleave_size;
>+
>+ /* check block on subdevice is bad here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_block_markbad(): device = %d,
>offset = 0x%08x\n", subdev_number, subdev_offset_low);
>+ err =
>stripe->subdev[subdev_number]->block_markbad(stripe->subdev[subdev_numbe
>r], subdev_offset_low);
>+ if(!err)
>+ {
>+ len_left -= subdev_len;
>+ from_loc += subdev_len;
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev -
>dev_count])
>+ dev_count--;
>+ }
>+
>+ while(!err && len_left > 0 && dev_count > 0)
>+ {
>+ subdev_number++;
>+ if(subdev_number >= stripe->num_subdev)
>+ {
>+ subdev_number = stripe->num_subdev - dev_count;
>+ subdev_offset++;
>+ }
>+ subdev_len = (len_left < stripe->interleave_size) ? len_left :
>stripe->interleave_size;
>+
>+ /* check block on subdevice is bad here */
>+ DEBUG(MTD_DEBUG_LEVEL3, "stripe_block_markbad(): device = %d,
>offset = 0x%08x\n", subdev_number, subdev_offset *
>stripe->interleave_size);
>+ err =
>stripe->subdev[subdev_number]->block_markbad(stripe->subdev[subdev_numbe
>r], subdev_offset * stripe->interleave_size);
>+ if(err)
>+ {
>+ break;
>+ }
>+ else
>+ {
>+ len_left -= subdev_len;
>+ from_loc += subdev_len;
>+ if(from_loc >= stripe->subdev_last_offset[stripe->num_subdev
>- dev_count])
>+ dev_count--;
>+ }
>+ }
>+
>+ DEBUG(MTD_DEBUG_LEVEL2, "<== stripe_block_markbad()\n");
>+ return err;
>+}
>+
>+/*
>+ * This function constructs a virtual MTD device by interleaving
>(striping)
>+ * num_devs MTD devices. A pointer to the new device object is
>+ * stored to *new_dev upon success. This function does _not_
>+ * register any devices: this is the caller's responsibility.
>+ */
>+struct mtd_info *mtd_stripe_create(struct mtd_info *subdev[], /*
>subdevices to stripe */
>+ int num_devs, /*
>number of subdevices */
>+ char *name, /* name
>for the new device */
>+ int interleave_size) /*
>interleaving size (sanity check is required) */
>+{
>+ int i,j;
>+ size_t size;
>+ struct mtd_stripe *stripe;
>+ u_int32_t curr_erasesize;
>+ int sort_done = 0;
>+
>+ printk(KERN_NOTICE "Striping MTD devices:\n");
>+ for (i = 0; i < num_devs; i++)
>+ printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
>+ printk(KERN_NOTICE "into device \"%s\"\n", name);
>+
>+ /* check if trying to stripe same device */
>+ for(i = 0; i < num_devs; i++)
>+ {
>+ for(j = i; j < num_devs; j++)
>+ {
>+ if(i != j && !(strcmp(subdev[i]->name,subdev[j]->name)))
>+ {
>+ printk(KERN_ERR "MTD Stripe failed. The same subdevice
>names were found.\n");
>+ return NULL;
>+ }
>+ }
>+ }
>+
>+ /* allocate the device structure */
>+ size = SIZEOF_STRUCT_MTD_STRIPE(num_devs);
>+ stripe = kmalloc(size, GFP_KERNEL);
>+ if (!stripe)
>+ {
>+ printk(KERN_ERR "mtd_stripe_create(): memory allocation
>error\n");
>+ return NULL;
>+ }
>+ memset(stripe, 0, size);
>+ stripe->subdev = (struct mtd_info **) (stripe + 1);
>+ stripe->subdev_last_offset = (u_int32_t *) ((char *)(stripe + 1) +
>num_devs * sizeof(struct mtd_info *));
>+ stripe->sw_threads = (struct mtd_sw_thread_info *)((char *)(stripe
>+ 1) + num_devs * sizeof(struct mtd_info *) + num_devs *
>sizeof(u_int32_t));
>+
>+ /*
>+ * Set up the new "super" device's MTD object structure, check for
>+ * incompatibilites between the subdevices.
>+ */
>+ stripe->mtd.type = subdev[0]->type;
>+ stripe->mtd.flags = subdev[0]->flags;
>+ stripe->mtd.size = subdev[0]->size;
>+ stripe->mtd.erasesize = subdev[0]->erasesize;
>+ stripe->mtd.oobblock = subdev[0]->oobblock;
>+ stripe->mtd.oobsize = subdev[0]->oobsize;
>+ stripe->mtd.oobavail = subdev[0]->oobavail;
>+ stripe->mtd.ecctype = subdev[0]->ecctype;
>+ stripe->mtd.eccsize = subdev[0]->eccsize;
>+ if (subdev[0]->read_ecc)
>+ stripe->mtd.read_ecc = stripe_read_ecc;
>+ if (subdev[0]->write_ecc)
>+ stripe->mtd.write_ecc = stripe_write_ecc;
>+ if (subdev[0]->read_oob)
>+ stripe->mtd.read_oob = stripe_read_oob;
>+ if (subdev[0]->write_oob)
>+ stripe->mtd.write_oob = stripe_write_oob;
>+
>+ stripe->subdev[0] = subdev[0];
>+
>+ for(i = 1; i < num_devs; i++)
>+ {
>+ /*
>+ * Check device compatibility,
>+ */
>+ if(stripe->mtd.type != subdev[i]->type)
>+ {
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): incompatible device
>type on \"%s\"\n",
>+ subdev[i]->name);
>+ return NULL;
>+ }
>+
>+ /*
>+ * Check MTD flags
>+ */
>+ if(stripe->mtd.flags != subdev[i]->flags)
>+ {
>+ /*
>+ * Expect all flags to be
>+ * equal on all subdevices.
>+ */
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): incompatible device
>flags on \"%s\"\n",
>+ subdev[i]->name);
>+ return NULL;
>+ }
>+
>+ stripe->mtd.size += subdev[i]->size;
>+
>+ /*
>+ * Check OOB and ECC data
>+ */
>+ if (stripe->mtd.oobblock != subdev[i]->oobblock ||
>+ stripe->mtd.oobsize != subdev[i]->oobsize ||
>+ stripe->mtd.oobavail != subdev[i]->oobavail ||
>+ stripe->mtd.ecctype != subdev[i]->ecctype ||
>+ stripe->mtd.eccsize != subdev[i]->eccsize ||
>+ !stripe->mtd.read_ecc != !subdev[i]->read_ecc ||
>+ !stripe->mtd.write_ecc != !subdev[i]->write_ecc ||
>+ !stripe->mtd.read_oob != !subdev[i]->read_oob ||
>+ !stripe->mtd.write_oob != !subdev[i]->write_oob)
>+ {
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): incompatible OOB or
>ECC data on \"%s\"\n",
>+ subdev[i]->name);
>+ return NULL;
>+ }
>+ stripe->subdev[i] = subdev[i];
>+ }
>+
>+ stripe->num_subdev = num_devs;
>+ stripe->mtd.name = name;
>+
>+ /*
>+ * Main MTD routines
>+ */
>+ stripe->mtd.erase = stripe_erase;
>+ stripe->mtd.read = stripe_read;
>+ stripe->mtd.write = stripe_write;
>+ stripe->mtd.sync = stripe_sync;
>+ stripe->mtd.lock = stripe_lock;
>+ stripe->mtd.unlock = stripe_unlock;
>+ stripe->mtd.suspend = stripe_suspend;
>+ stripe->mtd.resume = stripe_resume;
>+
>+#ifdef MTD_PROGRAM_REGIONS
>+ /* Montavista patch for Sibley support detected */
>+ if((stripe->mtd.flags & MTD_PROGRAM_REGIONS) ||
>(stripe->mtd.flags & MTD_ECC))
>+ stripe->mtd.writev = stripe_writev;
>+#else
>+ if(stripe->mtd.flags & MTD_ECC)
>+ stripe->mtd.writev = stripe_writev;
>+#endif
>+
>+ /* not sure about that case. probably should be used not only for
>NAND */
>+ if(stripe->mtd.type == MTD_NANDFLASH)
>+ stripe->mtd.writev_ecc = stripe_writev_ecc;
>+
>+ if(subdev[0]->block_isbad)
>+ stripe->mtd.block_isbad = stripe_block_isbad;
>+
>+ if(subdev[0]->block_markbad)
>+ stripe->mtd.block_markbad = stripe_block_markbad;
>+
>+ /*
>+ * Create new device with uniform erase size.
>+ */
>+ curr_erasesize = subdev[0]->erasesize;
>+ for (i = 0; i < num_devs; i++)
>+ {
>+ curr_erasesize = lcm(curr_erasesize, subdev[i]->erasesize);
>+ }
>+
>+ /* Check if erase size found is valid */
>+ if(curr_erasesize <= 0)
>+ {
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): Can't find lcm of
>subdevice erase sizes\n");
>+ return NULL;
>+ }
>+
>+ /* store erasesize lcm */
>+ stripe->erasesize_lcm = curr_erasesize;
>+
>+ /* simple erase size estimate. TBD better approach */
>+ curr_erasesize *= num_devs;
>+
>+ /* Check interleave size validity here */
>+ if(curr_erasesize % interleave_size)
>+ {
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): Wrong interleave size\n");
>+ return NULL;
>+ }
>+ stripe->interleave_size = interleave_size;
>+
>+ stripe->mtd.erasesize = curr_erasesize;
>+ stripe->mtd.numeraseregions = 0;
>+
>+ /* NAND specific */
>+ if(stripe->mtd.type == MTD_NANDFLASH)
>+ {
>+ stripe->mtd.oobblock *= num_devs;
>+ stripe->mtd.oobsize *= num_devs;
>+ stripe->mtd.oobavail *= num_devs; /* oobavail is to be changed
>later in stripe_merge_oobinfo() */
>+ stripe->mtd.eccsize *= num_devs;
>+ }
>+
>+#ifdef MTD_PROGRAM_REGIONS
>+ /* Montavista patch for Sibley support detected */
>+ if(stripe->mtd.flags & MTD_PROGRAM_REGIONS)
>+ stripe->mtd.oobblock *= num_devs;
>+ else if(stripe->mtd.flags & MTD_ECC)
>+ stripe->mtd.eccsize *= num_devs;
>+#else
>+ if(stripe->mtd.flags & MTD_ECC)
>+ stripe->mtd.eccsize *= num_devs;
>+#endif
>+
>+ /* update (truncate) super device size in accordance with new
>erasesize */
>+ stripe->mtd.size = (stripe->mtd.size / stripe->mtd.erasesize) *
>stripe->mtd.erasesize;
>+
>+ /* Sort all subdevices by their size */
>+ while(!sort_done)
>+ {
>+ sort_done = 1;
>+ for(i=0; i < num_devs - 1; i++)
>+ {
>+ struct mtd_info *subdev = stripe->subdev[i];
>+ if(subdev->size > stripe->subdev[i+1]->size)
>+ {
>+ stripe->subdev[i] = stripe->subdev[i+1];
>+ stripe->subdev[i+1] = subdev;
>+ sort_done = 0;
>+ }
>+ }
>+ }
>+
>+ /* Calculate last data offset for each striped device */
>+ for (i = 0; i < num_devs; i++)
>+ stripe->subdev_last_offset[i] = last_offset(stripe, i);
>+
>+ /* NAND specific */
>+ if(stripe->mtd.type == MTD_NANDFLASH)
>+ {
>+ /* Fill oobavail with correct values here */
>+ for (i = 0; i < num_devs; i++)
>+ stripe->subdev[i]->oobavail =
>stripe_get_oobavail(stripe->subdev[i]);
>+
>+ /* Sets new device oobinfo
>+ * NAND flash check is performed inside stripe_merge_oobinfo()
>+ * - this should be made after subdevices sorting done for
>proper eccpos and oobfree positioning
>+ * NOTE: there are some limitations with different size NAND
>devices striping. all devices must have
>+ * the same oobfree and eccpos maps */
>+ if(stripe_merge_oobinfo(&stripe->mtd, subdev, num_devs))
>+ {
>+ kfree(stripe);
>+ printk(KERN_ERR "mtd_stripe_create(): oobinfo merge has
>failed\n");
>+ return NULL;
>+ }
>+ }
>+
>+ /* Create write threads */
>+ for (i = 0; i < num_devs; i++)
>+ {
>+ if(stripe_start_write_thread(&stripe->sw_threads[i],
>stripe->subdev[i]) < 0)
>+ {
>+ kfree(stripe);
>+ return NULL;
>+ }
>+ }
>+ return &stripe->mtd;
>+}
>+
>+/*
>+ * This function destroys an Striped MTD object
>+ */
>+void mtd_stripe_destroy(struct mtd_info *mtd)
>+{
>+ struct mtd_stripe *stripe = STRIPE(mtd);
>+ int i;
>+
>+ if (stripe->mtd.numeraseregions)
>+ /* we should not get here. so just in case. */
>+ kfree(stripe->mtd.eraseregions);
>+
>+ /* destroy writing threads */
>+ for (i = 0; i < stripe->num_subdev; i++)
>+ stripe_stop_write_thread(&stripe->sw_threads[i]);
>+
>+ kfree(stripe);
>+}
>+
>+
>+#ifdef CMDLINE_PARSER_STRIPE
>+/*
>+ * MTD stripe init and cmdline parsing routines
>+ */
>+
>+static int
>+parse_cmdline_stripe_part(struct mtd_stripe_info *info, char *s)
>+{
>+ int ret = 0;
>+
>+ struct mtd_stripe_info *new_stripe = NULL;
>+ unsigned int name_size;
>+ char *subdev_name;
>+ char *e;
>+ int j;
>+
>+ DEBUG(MTD_DEBUG_LEVEL1, "parse_cmdline_stripe_part(): arg = %s\n",
>s);
>+
>+ /* parse new striped device name and allocate stripe info structure
>*/
>+ if(!(e = strchr(s,'(')) || (e == s))
>+ return -EINVAL;
>+
>+ name_size = (unsigned int)(e - s);
>+ new_stripe = kmalloc(sizeof(struct mtd_stripe_info) + name_size +
>1, GFP_KERNEL);
>+ if(!new_stripe) {
>+ printk(KERN_ERR "parse_cmdline_stripe_part(): memory allocation
>error!\n");
>+ return -ENOMEM;
>+ }
>+ memset(new_stripe,0,sizeof(struct mtd_stripe_info) + name_size +
>1);
>+ new_stripe->name = (char *)(new_stripe + 1);
>+
>+ INIT_LIST_HEAD(&new_stripe->list);
>+
>+ /* Store new device name */
>+ strncpy(new_stripe->name, s, name_size);
>+ s = e;
>+
>+ while(*s != 0)
>+ {
>+ switch(*s)
>+ {
>+ case '(':
>+ s++;
>+ new_stripe->interleave_size = simple_strtoul(s,&s,10);
>+ if(!new_stripe->interleave_size || *s != ')')
>+ ret = -EINVAL;
>+ else
>+ s++;
>+ break;
>+ case ':':
>+ case ',':
>+ case '.':
>+ // proceed with subdevice names
>+ if((e = strchr(++s,',')))
>+ name_size = (unsigned int)(e - s);
>+ else if((e = strchr(s,'.'))) /* this delimeter is to
>be used for insmod params */
>+ name_size = (unsigned int)(e - s);
>+ else
>+ name_size = strlen(s);
>+
>+ subdev_name = kmalloc(name_size + 1, GFP_KERNEL);
>+ if(!subdev_name)
>+ {
>+ printk(KERN_ERR "parse_cmdline_stripe_part(): memory
>allocation error!\n");
>+ ret = -ENOMEM;
>+ break;
>+ }
>+ strncpy(subdev_name,s,name_size);
>+ *(subdev_name + name_size) = 0;
>+
>+ /* Set up and register striped MTD device */
>+ down(&mtd_table_mutex);
>+ for(j = 0; j < MAX_MTD_DEVICES; j++)
>+ {
>+ if(mtd_table[j] &&
>!strcmp(subdev_name,mtd_table[j]->name))
>+ {
>+ new_stripe->devs[new_stripe->dev_num++] =
>mtd_table[j];
>+ break;
>+ }
>+ }
>+ up(&mtd_table_mutex);
>+
>+ kfree(subdev_name);
>+
>+ if(j == MAX_MTD_DEVICES)
>+ ret = -EINVAL;
>+
>+ s += name_size;
>+
>+ break;
>+ default:
>+ /* should not get here */
>+ printk(KERN_ERR "stripe cmdline parse error\n");
>+ ret = -EINVAL;
>+ break;
>+ };
>+
>+ if(ret)
>+ break;
>+ }
>+
>+ /* Check if all data parsed correctly. Sanity check. */
>+ if(ret)
>+ {
>+ kfree(new_stripe);
>+ }
>+ else
>+ {
>+ list_add_tail(&new_stripe->list,&info->list);
>+ DEBUG(MTD_DEBUG_LEVEL1, "Striped device %s parsed from
>cmdline\n", new_stripe->name);
>+ }
>+
>+ return ret;
>+}
>+
>+/* cmdline format:
>+ * mtdstripe=stripe1(128):vol3,vol5;stripe2(128):vol8,vol9 */
>+static int
>+parse_cmdline_stripes(struct mtd_stripe_info *info, char *s)
>+{
>+ int ret = 0;
>+ char *part;
>+ char *e;
>+ int cmdline_part_size;
>+
>+ struct list_head *pos, *q;
>+ struct mtd_stripe_info *stripe_info;
>+
>+ while(*s)
>+ {
>+ if(!(e = strchr(s,';')))
>+ {
>+ ret = parse_cmdline_stripe_part(info,s);
>+ break;
>+ }
>+ else
>+ {
>+ cmdline_part_size = (int)(e - s);
>+ part = kmalloc(cmdline_part_size + 1, GFP_KERNEL);
>+ if(!part)
>+ {
>+ printk(KERN_ERR "parse_cmdline_stripes(): memory
>allocation error!\n");
>+ ret = -ENOMEM;
>+ break;
>+ }
>+ strncpy(part,s,cmdline_part_size);
>+ *(part + cmdline_part_size) = 0;
>+ ret = parse_cmdline_stripe_part(info,part);
>+ kfree(part);
>+ if(ret)
>+ break;
>+ s = e + 1;
>+ }
>+ }
>+
>+ if(ret)
>+ {
>+ /* free all alocated memory in case of error */
>+ list_for_each_safe(pos, q, &info->list) {
>+ stripe_info = list_entry(pos, struct mtd_stripe_info, list);
>+ list_del(&stripe_info->list);
>+ kfree(stripe_info);
>+ }
>+ }
>+
>+ return ret;
>+}
>+
>+/* initializes striped MTD devices
>+ * to be called from mphysmap.c module or mtdstripe_init()
>+ */
>+int
>+mtd_stripe_init(void)
>+{
>+ static struct mtd_stripe_info *dev_info;
>+ struct list_head *pos, *q;
>+
>+ struct mtd_info* mtdstripe_info;
>+
>+ INIT_LIST_HEAD(&info.list);
>+
>+ /* parse cmdline */
>+ if(!cmdline)
>+ return 0;
>+
>+ if(parse_cmdline_stripes(&info,cmdline))
>+ return -EINVAL;
>+
>+ /* go through the list and create new striped devices */
>+ list_for_each_safe(pos, q, &info.list) {
>+ dev_info = list_entry(pos, struct mtd_stripe_info, list);
>+
>+ mtdstripe_info = mtd_stripe_create(dev_info->devs,
>dev_info->dev_num,
>+ dev_info->name,
>dev_info->interleave_size);
>+ if(!mtdstripe_info)
>+ {
>+ printk(KERN_ERR "mtd_stripe_init: mtd_stripe_create() error
>creating \"%s\"\n", dev_info->name);
>+
>+ /* remove registered striped device info from the list
>+ * free memory allocated by parse_cmdline_stripes()
>+ */
>+ list_del(&dev_info->list);
>+ kfree(dev_info);
>+
>+ return -EINVAL;
>+ }
>+ else
>+ {
>+ if(add_mtd_device(mtdstripe_info))
>+ {
>+ printk(KERN_ERR "mtd_stripe_init: add_mtd_device() error
>creating \"%s\"\n", dev_info->name);
>+ mtd_stripe_destroy(mtdstripe_info);
>+
>+ /* remove registered striped device info from the list
>+ * free memory allocated by parse_cmdline_stripes()
>+ */
>+ list_del(&dev_info->list);
>+ kfree(dev_info);
>+
>+ return -EINVAL;
>+ }
>+ else
>+ printk(KERN_ERR "Striped device \"%s\" has been created
>(interleave size %d bytes)\n",
>+ dev_info->name, dev_info->interleave_size);
>+ }
>+ }
>+
>+ return 0;
>+}
>+
>+/* removes striped devices */
>+int
>+mtd_stripe_exit(void)
>+{
>+ static struct mtd_stripe_info *dev_info;
>+ struct list_head *pos, *q;
>+ struct mtd_info *old_mtd_info;
>+
>+ int j;
>+
>+ /* go through the list and remove striped devices */
>+ list_for_each_safe(pos, q, &info.list) {
>+ dev_info = list_entry(pos, struct mtd_stripe_info, list);
>+
>+ down(&mtd_table_mutex);
>+ for(j = 0; j < MAX_MTD_DEVICES; j++)
>+ {
>+ if(mtd_table[j] &&
>!strcmp(dev_info->name,mtd_table[j]->name))
>+ {
>+ old_mtd_info = mtd_table[j];
>+ up(&mtd_table_mutex); /* up here since del_mtd_device
>down it */
>+ del_mtd_device(mtd_table[j]);
>+ down(&mtd_table_mutex);
>+ mtd_stripe_destroy(old_mtd_info);
>+ break;
>+ }
>+ }
>+ up(&mtd_table_mutex);
>+
>+ /* remove registered striped device info from the list
>+ * free memory allocated by parse_cmdline_stripes()
>+ */
>+ list_del(&dev_info->list);
>+ kfree(dev_info);
>+ }
>+
>+ return 0;
>+}
>+
>+EXPORT_SYMBOL(mtd_stripe_init);
>+EXPORT_SYMBOL(mtd_stripe_exit);
>+#endif
>+
>+#ifdef CONFIG_MTD_CMDLINE_STRIPE
>+#ifndef MODULE
>+/*
>+ * This is the handler for our kernel parameter, called from
>+ * main.c::checksetup(). Note that we can not yet kmalloc() anything,
>+ * so we only save the commandline for later processing.
>+ *
>+ * This function needs to be visible for bootloaders.
>+ */
>+int mtdstripe_setup(char *s)
>+{
>+ cmdline = s;
>+ return 1;
>+}
>+
>+__setup("mtdstripe=", mtdstripe_setup);
>+#endif
>+#endif
>+
>+EXPORT_SYMBOL(mtd_stripe_create);
>+EXPORT_SYMBOL(mtd_stripe_destroy);
>+
>+#ifdef MODULE
>+static int __init init_mtdstripe(void)
>+{
>+ cmdline = cmdline_parm;
>+ if(cmdline)
>+ mtd_stripe_init();
>+
>+ return 0;
>+}
>+
>+static void __exit exit_mtdstripe(void)
>+{
>+ if(cmdline)
>+ mtd_stripe_exit();
>+}
>+
>+module_init(init_mtdstripe);
>+module_exit(exit_mtdstripe);
>+#endif
>+
>+MODULE_LICENSE("GPL");
>+MODULE_AUTHOR("Alexander Belyakov <alexander.belyakov at intel.com>, Intel
>Corporation");
>+MODULE_DESCRIPTION("Generic support for striping of MTD devices");
>diff -uNr a/include/linux/mtd/cfi_cpt.h b/include/linux/mtd/cfi_cpt.h
>--- a/include/linux/mtd/cfi_cpt.h 1970-01-01 03:00:00.000000000
>+0300
>+++ b/include/linux/mtd/cfi_cpt.h 2006-03-16 12:34:38.000000000
>+0300
>@@ -0,0 +1,46 @@
>+
>+#ifndef __MTD_CFI_CPT_H__
>+#define __MTD_CFI_CPT_H__
>+
>+struct cpt_thread_info {
>+ struct task_struct *thread;
>+ int cpt_cont; /* continue flag */
>+
>+ struct semaphore cpt_startstop; /* thread start/stop semaphore */
>+
>+ /* wait-for-operation semaphore,
>+ * up by cpt_check_add,
>+ * down by cpt_thread
>+ */
>+ struct semaphore cpt_wait;
>+
>+ struct list_head list; /* head of chip list */
>+ spinlock_t list_lock; /* lock to remove race conditions
>+ * while adding/removing chips
>+ * to/from the list */
>+};
>+
>+struct cpt_check_desc {
>+ struct list_head list; /* per chip queue */
>+ struct flchip *chip;
>+ struct map_info *map;
>+ map_word status_OK;
>+ unsigned long cmd_adr;
>+ unsigned long timeo; /* timeout */
>+ int task_prio; /* task priority */
>+ int wait; /* if 0 - only one wait loop */
>+ struct semaphore check_semaphore;
>+ int success; /* 1 - success, 0 - timeout, etc. */
>+};
>+
>+struct cpt_chip {
>+ struct list_head list;
>+ struct flchip *chip;
>+ struct list_head plist; /* head of per chip op list */
>+ spinlock_t list_lock;
>+};
>+
>+int cpt_check_wait(struct cpt_thread_info* info, struct flchip *chip,
>struct map_info *map,
>+ unsigned long cmd_adr, map_word status_OK, int
>wait);
>+
>+#endif /* #ifndef __MTD_CFI_CPT_H__ */
>diff -uNr a/include/linux/mtd/stripe.h b/include/linux/mtd/stripe.h
>--- a/include/linux/mtd/stripe.h 1970-01-01 03:00:00.000000000
>+0300
>+++ b/include/linux/mtd/stripe.h 2006-03-16 12:34:38.000000000
>+0300
>@@ -0,0 +1,39 @@
>+/*
>+ * MTD device striping layer definitions
>+ *
>+ * (C) 2005 Intel Corp.
>+ *
>+ * This code is GPL
>+ *
>+ *
>+ */
>+
>+#ifndef MTD_STRIPE_H
>+#define MTD_STRIPE_H
>+
>+struct mtd_stripe_info {
>+ struct list_head list;
>+ char *name; /* new device
>name */
>+ int interleave_size; /* interleave size */
>+ int dev_num; /* number of devices to
>be striped */
>+ struct mtd_info* devs[MAX_MTD_DEVICES]; /* MTD device to be
>striped */
>+};
>+
>+struct mtd_info *mtd_stripe_create(
>+ struct mtd_info *subdev[], /* subdevices to stripe */
>+ int num_devs, /* number of subdevices */
>+ char *name, /* name for the new device */
>+ int inteleave_size); /* interleaving size */
>+
>+
>+struct mtd_info *mtd_stripe_create(struct mtd_info *subdev[], /*
>subdevices to stripe */
>+ int num_devs, /*
>number of subdevices */
>+ char *name, /* name
>for the new device */
>+ int interleave_size); /*
>interleaving size (sanity check is required) */
>+void mtd_stripe_destroy(struct mtd_info *mtd);
>+
>+int mtd_stripe_init(void);
>+int mtd_stripe_exit(void);
>+
>+#endif
>+
>
>______________________________________________________
>Linux MTD discussion mailing list
>http://lists.infradead.org/mailman/listinfo/linux-mtd/
>
>
>
>
More information about the linux-mtd
mailing list