diff options
Diffstat (limited to 'fs/btrfs/compression.c')
-rw-r--r-- | fs/btrfs/compression.c | 236 |
1 files changed, 231 insertions, 5 deletions
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index b50bc4bd5c56..6638c9877720 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c | |||
@@ -62,6 +62,9 @@ struct compressed_bio { | |||
62 | /* number of bytes on disk */ | 62 | /* number of bytes on disk */ |
63 | unsigned long compressed_len; | 63 | unsigned long compressed_len; |
64 | 64 | ||
65 | /* the compression algorithm for this bio */ | ||
66 | int compress_type; | ||
67 | |||
65 | /* number of compressed pages in the array */ | 68 | /* number of compressed pages in the array */ |
66 | unsigned long nr_pages; | 69 | unsigned long nr_pages; |
67 | 70 | ||
@@ -173,11 +176,12 @@ static void end_compressed_bio_read(struct bio *bio, int err) | |||
173 | /* ok, we're the last bio for this extent, lets start | 176 | /* ok, we're the last bio for this extent, lets start |
174 | * the decompression. | 177 | * the decompression. |
175 | */ | 178 | */ |
176 | ret = btrfs_zlib_decompress_biovec(cb->compressed_pages, | 179 | ret = btrfs_decompress_biovec(cb->compress_type, |
177 | cb->start, | 180 | cb->compressed_pages, |
178 | cb->orig_bio->bi_io_vec, | 181 | cb->start, |
179 | cb->orig_bio->bi_vcnt, | 182 | cb->orig_bio->bi_io_vec, |
180 | cb->compressed_len); | 183 | cb->orig_bio->bi_vcnt, |
184 | cb->compressed_len); | ||
181 | csum_failed: | 185 | csum_failed: |
182 | if (ret) | 186 | if (ret) |
183 | cb->errors = 1; | 187 | cb->errors = 1; |
@@ -588,6 +592,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, | |||
588 | 592 | ||
589 | cb->len = uncompressed_len; | 593 | cb->len = uncompressed_len; |
590 | cb->compressed_len = compressed_len; | 594 | cb->compressed_len = compressed_len; |
595 | cb->compress_type = extent_compress_type(bio_flags); | ||
591 | cb->orig_bio = bio; | 596 | cb->orig_bio = bio; |
592 | 597 | ||
593 | nr_pages = (compressed_len + PAGE_CACHE_SIZE - 1) / | 598 | nr_pages = (compressed_len + PAGE_CACHE_SIZE - 1) / |
@@ -677,3 +682,224 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, | |||
677 | bio_put(comp_bio); | 682 | bio_put(comp_bio); |
678 | return 0; | 683 | return 0; |
679 | } | 684 | } |
685 | |||
686 | static struct list_head comp_idle_workspace[BTRFS_COMPRESS_TYPES]; | ||
687 | static spinlock_t comp_workspace_lock[BTRFS_COMPRESS_TYPES]; | ||
688 | static int comp_num_workspace[BTRFS_COMPRESS_TYPES]; | ||
689 | static atomic_t comp_alloc_workspace[BTRFS_COMPRESS_TYPES]; | ||
690 | static wait_queue_head_t comp_workspace_wait[BTRFS_COMPRESS_TYPES]; | ||
691 | |||
692 | struct btrfs_compress_op *btrfs_compress_op[] = { | ||
693 | &btrfs_zlib_compress, | ||
694 | }; | ||
695 | |||
696 | int __init btrfs_init_compress(void) | ||
697 | { | ||
698 | int i; | ||
699 | |||
700 | for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) { | ||
701 | INIT_LIST_HEAD(&comp_idle_workspace[i]); | ||
702 | spin_lock_init(&comp_workspace_lock[i]); | ||
703 | atomic_set(&comp_alloc_workspace[i], 0); | ||
704 | init_waitqueue_head(&comp_workspace_wait[i]); | ||
705 | } | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | /* | ||
710 | * this finds an available workspace or allocates a new one | ||
711 | * ERR_PTR is returned if things go bad. | ||
712 | */ | ||
713 | static struct list_head *find_workspace(int type) | ||
714 | { | ||
715 | struct list_head *workspace; | ||
716 | int cpus = num_online_cpus(); | ||
717 | int idx = type - 1; | ||
718 | |||
719 | struct list_head *idle_workspace = &comp_idle_workspace[idx]; | ||
720 | spinlock_t *workspace_lock = &comp_workspace_lock[idx]; | ||
721 | atomic_t *alloc_workspace = &comp_alloc_workspace[idx]; | ||
722 | wait_queue_head_t *workspace_wait = &comp_workspace_wait[idx]; | ||
723 | int *num_workspace = &comp_num_workspace[idx]; | ||
724 | again: | ||
725 | spin_lock(workspace_lock); | ||
726 | if (!list_empty(idle_workspace)) { | ||
727 | workspace = idle_workspace->next; | ||
728 | list_del(workspace); | ||
729 | (*num_workspace)--; | ||
730 | spin_unlock(workspace_lock); | ||
731 | return workspace; | ||
732 | |||
733 | } | ||
734 | if (atomic_read(alloc_workspace) > cpus) { | ||
735 | DEFINE_WAIT(wait); | ||
736 | |||
737 | spin_unlock(workspace_lock); | ||
738 | prepare_to_wait(workspace_wait, &wait, TASK_UNINTERRUPTIBLE); | ||
739 | if (atomic_read(alloc_workspace) > cpus && !*num_workspace) | ||
740 | schedule(); | ||
741 | finish_wait(workspace_wait, &wait); | ||
742 | goto again; | ||
743 | } | ||
744 | atomic_inc(alloc_workspace); | ||
745 | spin_unlock(workspace_lock); | ||
746 | |||
747 | workspace = btrfs_compress_op[idx]->alloc_workspace(); | ||
748 | if (IS_ERR(workspace)) { | ||
749 | atomic_dec(alloc_workspace); | ||
750 | wake_up(workspace_wait); | ||
751 | } | ||
752 | return workspace; | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * put a workspace struct back on the list or free it if we have enough | ||
757 | * idle ones sitting around | ||
758 | */ | ||
759 | static void free_workspace(int type, struct list_head *workspace) | ||
760 | { | ||
761 | int idx = type - 1; | ||
762 | struct list_head *idle_workspace = &comp_idle_workspace[idx]; | ||
763 | spinlock_t *workspace_lock = &comp_workspace_lock[idx]; | ||
764 | atomic_t *alloc_workspace = &comp_alloc_workspace[idx]; | ||
765 | wait_queue_head_t *workspace_wait = &comp_workspace_wait[idx]; | ||
766 | int *num_workspace = &comp_num_workspace[idx]; | ||
767 | |||
768 | spin_lock(workspace_lock); | ||
769 | if (*num_workspace < num_online_cpus()) { | ||
770 | list_add_tail(workspace, idle_workspace); | ||
771 | (*num_workspace)++; | ||
772 | spin_unlock(workspace_lock); | ||
773 | goto wake; | ||
774 | } | ||
775 | spin_unlock(workspace_lock); | ||
776 | |||
777 | btrfs_compress_op[idx]->free_workspace(workspace); | ||
778 | atomic_dec(alloc_workspace); | ||
779 | wake: | ||
780 | if (waitqueue_active(workspace_wait)) | ||
781 | wake_up(workspace_wait); | ||
782 | } | ||
783 | |||
784 | /* | ||
785 | * cleanup function for module exit | ||
786 | */ | ||
787 | static void free_workspaces(void) | ||
788 | { | ||
789 | struct list_head *workspace; | ||
790 | int i; | ||
791 | |||
792 | for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) { | ||
793 | while (!list_empty(&comp_idle_workspace[i])) { | ||
794 | workspace = comp_idle_workspace[i].next; | ||
795 | list_del(workspace); | ||
796 | btrfs_compress_op[i]->free_workspace(workspace); | ||
797 | atomic_dec(&comp_alloc_workspace[i]); | ||
798 | } | ||
799 | } | ||
800 | } | ||
801 | |||
802 | /* | ||
803 | * given an address space and start/len, compress the bytes. | ||
804 | * | ||
805 | * pages are allocated to hold the compressed result and stored | ||
806 | * in 'pages' | ||
807 | * | ||
808 | * out_pages is used to return the number of pages allocated. There | ||
809 | * may be pages allocated even if we return an error | ||
810 | * | ||
811 | * total_in is used to return the number of bytes actually read. It | ||
812 | * may be smaller then len if we had to exit early because we | ||
813 | * ran out of room in the pages array or because we cross the | ||
814 | * max_out threshold. | ||
815 | * | ||
816 | * total_out is used to return the total number of compressed bytes | ||
817 | * | ||
818 | * max_out tells us the max number of bytes that we're allowed to | ||
819 | * stuff into pages | ||
820 | */ | ||
821 | int btrfs_compress_pages(int type, struct address_space *mapping, | ||
822 | u64 start, unsigned long len, | ||
823 | struct page **pages, | ||
824 | unsigned long nr_dest_pages, | ||
825 | unsigned long *out_pages, | ||
826 | unsigned long *total_in, | ||
827 | unsigned long *total_out, | ||
828 | unsigned long max_out) | ||
829 | { | ||
830 | struct list_head *workspace; | ||
831 | int ret; | ||
832 | |||
833 | workspace = find_workspace(type); | ||
834 | if (IS_ERR(workspace)) | ||
835 | return -1; | ||
836 | |||
837 | ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping, | ||
838 | start, len, pages, | ||
839 | nr_dest_pages, out_pages, | ||
840 | total_in, total_out, | ||
841 | max_out); | ||
842 | free_workspace(type, workspace); | ||
843 | return ret; | ||
844 | } | ||
845 | |||
846 | /* | ||
847 | * pages_in is an array of pages with compressed data. | ||
848 | * | ||
849 | * disk_start is the starting logical offset of this array in the file | ||
850 | * | ||
851 | * bvec is a bio_vec of pages from the file that we want to decompress into | ||
852 | * | ||
853 | * vcnt is the count of pages in the biovec | ||
854 | * | ||
855 | * srclen is the number of bytes in pages_in | ||
856 | * | ||
857 | * The basic idea is that we have a bio that was created by readpages. | ||
858 | * The pages in the bio are for the uncompressed data, and they may not | ||
859 | * be contiguous. They all correspond to the range of bytes covered by | ||
860 | * the compressed extent. | ||
861 | */ | ||
862 | int btrfs_decompress_biovec(int type, struct page **pages_in, u64 disk_start, | ||
863 | struct bio_vec *bvec, int vcnt, size_t srclen) | ||
864 | { | ||
865 | struct list_head *workspace; | ||
866 | int ret; | ||
867 | |||
868 | workspace = find_workspace(type); | ||
869 | if (IS_ERR(workspace)) | ||
870 | return -ENOMEM; | ||
871 | |||
872 | ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in, | ||
873 | disk_start, | ||
874 | bvec, vcnt, srclen); | ||
875 | free_workspace(type, workspace); | ||
876 | return ret; | ||
877 | } | ||
878 | |||
879 | /* | ||
880 | * a less complex decompression routine. Our compressed data fits in a | ||
881 | * single page, and we want to read a single page out of it. | ||
882 | * start_byte tells us the offset into the compressed data we're interested in | ||
883 | */ | ||
884 | int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page, | ||
885 | unsigned long start_byte, size_t srclen, size_t destlen) | ||
886 | { | ||
887 | struct list_head *workspace; | ||
888 | int ret; | ||
889 | |||
890 | workspace = find_workspace(type); | ||
891 | if (IS_ERR(workspace)) | ||
892 | return -ENOMEM; | ||
893 | |||
894 | ret = btrfs_compress_op[type-1]->decompress(workspace, data_in, | ||
895 | dest_page, start_byte, | ||
896 | srclen, destlen); | ||
897 | |||
898 | free_workspace(type, workspace); | ||
899 | return ret; | ||
900 | } | ||
901 | |||
902 | void __exit btrfs_exit_compress(void) | ||
903 | { | ||
904 | free_workspaces(); | ||
905 | } | ||