// 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" #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 clusterpages, struct page **pages, unsigned nr_pages, unsigned short pageofs) { unsigned i, j; void *src = NULL; const unsigned 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 == NULL) { if (src != NULL) { 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 == NULL) 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 != NULL && !mirrored[j]) kunmap_atomic(src); preempt_enable(); return 0; } extern int z_erofs_unzip_lz4(void *in, void *out, size_t inlen, size_t outlen); int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, unsigned clusterpages, struct page **pages, unsigned outlen, unsigned short pageofs) { void *vin, *vout; unsigned 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); if (!vin) return -ENOMEM; } 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)PAGE_SIZE - pageofs, outlen); if (pages[i] != NULL) { 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 clusterpages, void *vout, unsigned llen, unsigned short pageofs, bool overlapped) { void *vin; unsigned 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; }