// SPDX-License-Identifier: GPL-2.0 /* * linux/drivers/staging/erofs/unzip_vle_lz4.c * * Copyright (C) 2018 HUAWEI, Inc. * http://www.huawei.com/ * Created by Gao Xiang * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ #include "unzip_vle.h" #include static int z_erofs_unzip_lz4(void *in, void *out, size_t inlen, size_t outlen) { int ret = LZ4_decompress_safe_partial(in, out, inlen, outlen, outlen); if (ret >= 0) return ret; /* * LZ4_decompress_safe_partial will return an error code * (< 0) if decompression failed */ errln("%s, failed to decompress, in[%p, %zu] outlen[%p, %zu]", __func__, in, inlen, out, outlen); WARN_ON(1); print_hex_dump(KERN_DEBUG, "raw data [in]: ", DUMP_PREFIX_OFFSET, 16, 1, in, inlen, true); print_hex_dump(KERN_DEBUG, "raw data [out]: ", DUMP_PREFIX_OFFSET, 16, 1, out, outlen, true); return -EIO; } #if Z_EROFS_CLUSTER_MAX_PAGES > Z_EROFS_VLE_INLINE_PAGEVECS #define EROFS_PERCPU_NR_PAGES Z_EROFS_CLUSTER_MAX_PAGES #else #define EROFS_PERCPU_NR_PAGES Z_EROFS_VLE_INLINE_PAGEVECS #endif static struct { char data[PAGE_SIZE * EROFS_PERCPU_NR_PAGES]; } erofs_pcpubuf[NR_CPUS]; int z_erofs_vle_plain_copy(struct page **compressed_pages, unsigned int clusterpages, struct page **pages, unsigned int nr_pages, unsigned short pageofs) { unsigned int i, j; void *src = NULL; const unsigned int righthalf = PAGE_SIZE - pageofs; char *percpu_data; bool mirrored[Z_EROFS_CLUSTER_MAX_PAGES] = { 0 }; preempt_disable(); percpu_data = erofs_pcpubuf[smp_processor_id()].data; j = 0; for (i = 0; i < nr_pages; j = i++) { struct page *page = pages[i]; void *dst; if (!page) { if (src) { if (!mirrored[j]) kunmap_atomic(src); src = NULL; } continue; } dst = kmap_atomic(page); for (; j < clusterpages; ++j) { if (compressed_pages[j] != page) continue; DBG_BUGON(mirrored[j]); memcpy(percpu_data + j * PAGE_SIZE, dst, PAGE_SIZE); mirrored[j] = true; break; } if (i) { if (!src) src = mirrored[i - 1] ? percpu_data + (i - 1) * PAGE_SIZE : kmap_atomic(compressed_pages[i - 1]); memcpy(dst, src + righthalf, pageofs); if (!mirrored[i - 1]) kunmap_atomic(src); if (unlikely(i >= clusterpages)) { kunmap_atomic(dst); break; } } if (!righthalf) { src = NULL; } else { src = mirrored[i] ? percpu_data + i * PAGE_SIZE : kmap_atomic(compressed_pages[i]); memcpy(dst + pageofs, src, righthalf); } kunmap_atomic(dst); } if (src && !mirrored[j]) kunmap_atomic(src); preempt_enable(); return 0; } int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, unsigned int clusterpages, struct page **pages, unsigned int outlen, unsigned short pageofs) { void *vin, *vout; unsigned int nr_pages, i, j; int ret; if (outlen + pageofs > EROFS_PERCPU_NR_PAGES * PAGE_SIZE) return -ENOTSUPP; nr_pages = DIV_ROUND_UP(outlen + pageofs, PAGE_SIZE); if (clusterpages == 1) vin = kmap_atomic(compressed_pages[0]); else vin = erofs_vmap(compressed_pages, clusterpages); preempt_disable(); vout = erofs_pcpubuf[smp_processor_id()].data; ret = z_erofs_unzip_lz4(vin, vout + pageofs, clusterpages * PAGE_SIZE, outlen); if (ret < 0) goto out; ret = 0; for (i = 0; i < nr_pages; ++i) { j = min((unsigned int)PAGE_SIZE - pageofs, outlen); if (pages[i]) { if (clusterpages == 1 && pages[i] == compressed_pages[0]) { memcpy(vin + pageofs, vout + pageofs, j); } else { void *dst = kmap_atomic(pages[i]); memcpy(dst + pageofs, vout + pageofs, j); kunmap_atomic(dst); } } vout += PAGE_SIZE; outlen -= j; pageofs = 0; } out: preempt_enable(); if (clusterpages == 1) kunmap_atomic(vin); else erofs_vunmap(vin, clusterpages); return ret; } int z_erofs_vle_unzip_vmap(struct page **compressed_pages, unsigned int clusterpages, void *vout, unsigned int llen, unsigned short pageofs, bool overlapped) { void *vin; unsigned int i; int ret; if (overlapped) { preempt_disable(); vin = erofs_pcpubuf[smp_processor_id()].data; for (i = 0; i < clusterpages; ++i) { void *t = kmap_atomic(compressed_pages[i]); memcpy(vin + PAGE_SIZE * i, t, PAGE_SIZE); kunmap_atomic(t); } } else if (clusterpages == 1) { vin = kmap_atomic(compressed_pages[0]); } else { vin = erofs_vmap(compressed_pages, clusterpages); } ret = z_erofs_unzip_lz4(vin, vout + pageofs, clusterpages * PAGE_SIZE, llen); if (ret > 0) ret = 0; if (!overlapped) { if (clusterpages == 1) kunmap_atomic(vin); else erofs_vunmap(vin, clusterpages); } else { preempt_enable(); } return ret; }