diff options
Diffstat (limited to 'fs/jffs2/wbuf.c')
-rw-r--r-- | fs/jffs2/wbuf.c | 264 |
1 files changed, 97 insertions, 167 deletions
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index c7e3040240b2..916c87d3393b 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -613,20 +613,30 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) | |||
613 | 613 | ||
614 | return ret; | 614 | return ret; |
615 | } | 615 | } |
616 | int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) | 616 | |
617 | static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf, | ||
618 | size_t len) | ||
619 | { | ||
620 | if (len && !c->wbuf_len && (len >= c->wbuf_pagesize)) | ||
621 | return 0; | ||
622 | |||
623 | if (len > (c->wbuf_pagesize - c->wbuf_len)) | ||
624 | len = c->wbuf_pagesize - c->wbuf_len; | ||
625 | memcpy(c->wbuf + c->wbuf_len, buf, len); | ||
626 | c->wbuf_len += (uint32_t) len; | ||
627 | return len; | ||
628 | } | ||
629 | |||
630 | int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, | ||
631 | unsigned long count, loff_t to, size_t *retlen, | ||
632 | uint32_t ino) | ||
617 | { | 633 | { |
618 | struct kvec outvecs[3]; | 634 | struct jffs2_eraseblock *jeb; |
619 | uint32_t totlen = 0; | 635 | size_t wbuf_retlen, donelen = 0; |
620 | uint32_t split_ofs = 0; | ||
621 | uint32_t old_totlen; | ||
622 | int ret, splitvec = -1; | ||
623 | int invec, outvec; | ||
624 | size_t wbuf_retlen; | ||
625 | unsigned char *wbuf_ptr; | ||
626 | size_t donelen = 0; | ||
627 | uint32_t outvec_to = to; | 636 | uint32_t outvec_to = to; |
637 | int ret, invec; | ||
628 | 638 | ||
629 | /* If not NAND flash, don't bother */ | 639 | /* If not writebuffered flash, don't bother */ |
630 | if (!jffs2_is_writebuffered(c)) | 640 | if (!jffs2_is_writebuffered(c)) |
631 | return jffs2_flash_direct_writev(c, invecs, count, to, retlen); | 641 | return jffs2_flash_direct_writev(c, invecs, count, to, retlen); |
632 | 642 | ||
@@ -639,23 +649,22 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig | |||
639 | memset(c->wbuf,0xff,c->wbuf_pagesize); | 649 | memset(c->wbuf,0xff,c->wbuf_pagesize); |
640 | } | 650 | } |
641 | 651 | ||
642 | /* Sanity checks on target address. | 652 | /* |
643 | It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), | 653 | * Sanity checks on target address. It's permitted to write |
644 | and it's permitted to write at the beginning of a new | 654 | * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to |
645 | erase block. Anything else, and you die. | 655 | * write at the beginning of a new erase block. Anything else, |
646 | New block starts at xxx000c (0-b = block header) | 656 | * and you die. New block starts at xxx000c (0-b = block |
647 | */ | 657 | * header) |
658 | */ | ||
648 | if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { | 659 | if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { |
649 | /* It's a write to a new block */ | 660 | /* It's a write to a new block */ |
650 | if (c->wbuf_len) { | 661 | if (c->wbuf_len) { |
651 | D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); | 662 | D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx " |
663 | "causes flush of wbuf at 0x%08x\n", | ||
664 | (unsigned long)to, c->wbuf_ofs)); | ||
652 | ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); | 665 | ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); |
653 | if (ret) { | 666 | if (ret) |
654 | /* the underlying layer has to check wbuf_len to do the cleanup */ | 667 | goto outerr; |
655 | D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); | ||
656 | *retlen = 0; | ||
657 | goto exit; | ||
658 | } | ||
659 | } | 668 | } |
660 | /* set pointer to new block */ | 669 | /* set pointer to new block */ |
661 | c->wbuf_ofs = PAGE_DIV(to); | 670 | c->wbuf_ofs = PAGE_DIV(to); |
@@ -664,165 +673,70 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig | |||
664 | 673 | ||
665 | if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { | 674 | if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { |
666 | /* We're not writing immediately after the writebuffer. Bad. */ | 675 | /* We're not writing immediately after the writebuffer. Bad. */ |
667 | printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); | 676 | printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write " |
677 | "to %08lx\n", (unsigned long)to); | ||
668 | if (c->wbuf_len) | 678 | if (c->wbuf_len) |
669 | printk(KERN_CRIT "wbuf was previously %08x-%08x\n", | 679 | printk(KERN_CRIT "wbuf was previously %08x-%08x\n", |
670 | c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); | 680 | c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); |
671 | BUG(); | ||
672 | } | ||
673 | |||
674 | /* Note outvecs[3] above. We know count is never greater than 2 */ | ||
675 | if (count > 2) { | ||
676 | printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); | ||
677 | BUG(); | 681 | BUG(); |
678 | } | 682 | } |
679 | 683 | ||
680 | invec = 0; | 684 | /* adjust alignment offset */ |
681 | outvec = 0; | 685 | if (c->wbuf_len != PAGE_MOD(to)) { |
682 | 686 | c->wbuf_len = PAGE_MOD(to); | |
683 | /* Fill writebuffer first, if already in use */ | 687 | /* take care of alignment to next page */ |
684 | if (c->wbuf_len) { | 688 | if (!c->wbuf_len) { |
685 | uint32_t invec_ofs = 0; | 689 | c->wbuf_len = c->wbuf_pagesize; |
686 | 690 | ret = __jffs2_flush_wbuf(c, NOPAD); | |
687 | /* adjust alignment offset */ | 691 | if (ret) |
688 | if (c->wbuf_len != PAGE_MOD(to)) { | 692 | goto outerr; |
689 | c->wbuf_len = PAGE_MOD(to); | ||
690 | /* take care of alignment to next page */ | ||
691 | if (!c->wbuf_len) | ||
692 | c->wbuf_len = c->wbuf_pagesize; | ||
693 | } | ||
694 | |||
695 | while(c->wbuf_len < c->wbuf_pagesize) { | ||
696 | uint32_t thislen; | ||
697 | |||
698 | if (invec == count) | ||
699 | goto alldone; | ||
700 | |||
701 | thislen = c->wbuf_pagesize - c->wbuf_len; | ||
702 | |||
703 | if (thislen >= invecs[invec].iov_len) | ||
704 | thislen = invecs[invec].iov_len; | ||
705 | |||
706 | invec_ofs = thislen; | ||
707 | |||
708 | memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); | ||
709 | c->wbuf_len += thislen; | ||
710 | donelen += thislen; | ||
711 | /* Get next invec, if actual did not fill the buffer */ | ||
712 | if (c->wbuf_len < c->wbuf_pagesize) | ||
713 | invec++; | ||
714 | } | ||
715 | |||
716 | /* write buffer is full, flush buffer */ | ||
717 | ret = __jffs2_flush_wbuf(c, NOPAD); | ||
718 | if (ret) { | ||
719 | /* the underlying layer has to check wbuf_len to do the cleanup */ | ||
720 | D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); | ||
721 | /* Retlen zero to make sure our caller doesn't mark the space dirty. | ||
722 | We've already done everything that's necessary */ | ||
723 | *retlen = 0; | ||
724 | goto exit; | ||
725 | } | ||
726 | outvec_to += donelen; | ||
727 | c->wbuf_ofs = outvec_to; | ||
728 | |||
729 | /* All invecs done ? */ | ||
730 | if (invec == count) | ||
731 | goto alldone; | ||
732 | |||
733 | /* Set up the first outvec, containing the remainder of the | ||
734 | invec we partially used */ | ||
735 | if (invecs[invec].iov_len > invec_ofs) { | ||
736 | outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; | ||
737 | totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; | ||
738 | if (totlen > c->wbuf_pagesize) { | ||
739 | splitvec = outvec; | ||
740 | split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); | ||
741 | } | ||
742 | outvec++; | ||
743 | } | ||
744 | invec++; | ||
745 | } | ||
746 | |||
747 | /* OK, now we've flushed the wbuf and the start of the bits | ||
748 | we have been asked to write, now to write the rest.... */ | ||
749 | |||
750 | /* totlen holds the amount of data still to be written */ | ||
751 | old_totlen = totlen; | ||
752 | for ( ; invec < count; invec++,outvec++ ) { | ||
753 | outvecs[outvec].iov_base = invecs[invec].iov_base; | ||
754 | totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; | ||
755 | if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { | ||
756 | splitvec = outvec; | ||
757 | split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); | ||
758 | old_totlen = totlen; | ||
759 | } | 693 | } |
760 | } | 694 | } |
761 | 695 | ||
762 | /* Now the outvecs array holds all the remaining data to write */ | 696 | for (invec = 0; invec < count; invec++) { |
763 | /* Up to splitvec,split_ofs is to be written immediately. The rest | 697 | int vlen = invecs[invec].iov_len; |
764 | goes into the (now-empty) wbuf */ | 698 | uint8_t *v = invecs[invec].iov_base; |
765 | |||
766 | if (splitvec != -1) { | ||
767 | uint32_t remainder; | ||
768 | |||
769 | remainder = outvecs[splitvec].iov_len - split_ofs; | ||
770 | outvecs[splitvec].iov_len = split_ofs; | ||
771 | |||
772 | /* We did cross a page boundary, so we write some now */ | ||
773 | if (jffs2_cleanmarker_oob(c)) | ||
774 | ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); | ||
775 | else | ||
776 | ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); | ||
777 | |||
778 | if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { | ||
779 | /* At this point we have no problem, | ||
780 | c->wbuf is empty. However refile nextblock to avoid | ||
781 | writing again to same address. | ||
782 | */ | ||
783 | struct jffs2_eraseblock *jeb; | ||
784 | |||
785 | spin_lock(&c->erase_completion_lock); | ||
786 | 699 | ||
787 | jeb = &c->blocks[outvec_to / c->sector_size]; | 700 | wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); |
788 | jffs2_block_refile(c, jeb, REFILE_ANYWAY); | ||
789 | 701 | ||
790 | *retlen = 0; | 702 | if (c->wbuf_len == c->wbuf_pagesize) { |
791 | spin_unlock(&c->erase_completion_lock); | 703 | ret = __jffs2_flush_wbuf(c, NOPAD); |
792 | goto exit; | 704 | if (ret) |
705 | goto outerr; | ||
793 | } | 706 | } |
794 | 707 | vlen -= wbuf_retlen; | |
708 | outvec_to += wbuf_retlen; | ||
795 | donelen += wbuf_retlen; | 709 | donelen += wbuf_retlen; |
796 | c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); | 710 | v += wbuf_retlen; |
797 | 711 | ||
798 | if (remainder) { | 712 | if (vlen >= c->wbuf_pagesize) { |
799 | outvecs[splitvec].iov_base += split_ofs; | 713 | ret = c->mtd->write(c->mtd, outvec_to, PAGE_DIV(vlen), |
800 | outvecs[splitvec].iov_len = remainder; | 714 | &wbuf_retlen, v); |
801 | } else { | 715 | if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen)) |
802 | splitvec++; | 716 | goto outfile; |
717 | |||
718 | vlen -= wbuf_retlen; | ||
719 | outvec_to += wbuf_retlen; | ||
720 | c->wbuf_ofs = outvec_to; | ||
721 | donelen += wbuf_retlen; | ||
722 | v += wbuf_retlen; | ||
803 | } | 723 | } |
804 | 724 | ||
805 | } else { | 725 | wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); |
806 | splitvec = 0; | 726 | if (c->wbuf_len == c->wbuf_pagesize) { |
807 | } | 727 | ret = __jffs2_flush_wbuf(c, NOPAD); |
808 | 728 | if (ret) | |
809 | /* Now splitvec points to the start of the bits we have to copy | 729 | goto outerr; |
810 | into the wbuf */ | 730 | } |
811 | wbuf_ptr = c->wbuf; | ||
812 | 731 | ||
813 | for ( ; splitvec < outvec; splitvec++) { | 732 | outvec_to += wbuf_retlen; |
814 | /* Don't copy the wbuf into itself */ | 733 | donelen += wbuf_retlen; |
815 | if (outvecs[splitvec].iov_base == c->wbuf) | ||
816 | continue; | ||
817 | memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); | ||
818 | wbuf_ptr += outvecs[splitvec].iov_len; | ||
819 | donelen += outvecs[splitvec].iov_len; | ||
820 | } | 734 | } |
821 | c->wbuf_len = wbuf_ptr - c->wbuf; | ||
822 | 735 | ||
823 | /* If there's a remainder in the wbuf and it's a non-GC write, | 736 | /* |
824 | remember that the wbuf affects this ino */ | 737 | * If there's a remainder in the wbuf and it's a non-GC write, |
825 | alldone: | 738 | * remember that the wbuf affects this ino |
739 | */ | ||
826 | *retlen = donelen; | 740 | *retlen = donelen; |
827 | 741 | ||
828 | if (jffs2_sum_active()) { | 742 | if (jffs2_sum_active()) { |
@@ -835,8 +749,24 @@ alldone: | |||
835 | jffs2_wbuf_dirties_inode(c, ino); | 749 | jffs2_wbuf_dirties_inode(c, ino); |
836 | 750 | ||
837 | ret = 0; | 751 | ret = 0; |
752 | up_write(&c->wbuf_sem); | ||
753 | return ret; | ||
838 | 754 | ||
839 | exit: | 755 | outfile: |
756 | /* | ||
757 | * At this point we have no problem, c->wbuf is empty. However | ||
758 | * refile nextblock to avoid writing again to same address. | ||
759 | */ | ||
760 | |||
761 | spin_lock(&c->erase_completion_lock); | ||
762 | |||
763 | jeb = &c->blocks[outvec_to / c->sector_size]; | ||
764 | jffs2_block_refile(c, jeb, REFILE_ANYWAY); | ||
765 | |||
766 | spin_unlock(&c->erase_completion_lock); | ||
767 | |||
768 | outerr: | ||
769 | *retlen = 0; | ||
840 | up_write(&c->wbuf_sem); | 770 | up_write(&c->wbuf_sem); |
841 | return ret; | 771 | return ret; |
842 | } | 772 | } |