18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013
48c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
108c2ecf20Sopenharmony_ci#include "page_actor.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci * This file contains implementations of page_actor for decompressing into
148c2ecf20Sopenharmony_ci * an intermediate buffer, and for decompressing directly into the
158c2ecf20Sopenharmony_ci * page cache.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Calling code should avoid sleeping between calls to squashfs_first_page()
188c2ecf20Sopenharmony_ci * and squashfs_finish_page().
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Implementation of page_actor for decompressing into intermediate buffer */
228c2ecf20Sopenharmony_cistatic void *cache_first_page(struct squashfs_page_actor *actor)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	actor->next_page = 1;
258c2ecf20Sopenharmony_ci	return actor->buffer[0];
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void *cache_next_page(struct squashfs_page_actor *actor)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	if (actor->next_page == actor->pages)
318c2ecf20Sopenharmony_ci		return NULL;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return actor->buffer[actor->next_page++];
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void cache_finish_page(struct squashfs_page_actor *actor)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	/* empty */
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
428c2ecf20Sopenharmony_ci	int pages, int length)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (actor == NULL)
478c2ecf20Sopenharmony_ci		return NULL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	actor->length = length ? : pages * PAGE_SIZE;
508c2ecf20Sopenharmony_ci	actor->buffer = buffer;
518c2ecf20Sopenharmony_ci	actor->pages = pages;
528c2ecf20Sopenharmony_ci	actor->next_page = 0;
538c2ecf20Sopenharmony_ci	actor->squashfs_first_page = cache_first_page;
548c2ecf20Sopenharmony_ci	actor->squashfs_next_page = cache_next_page;
558c2ecf20Sopenharmony_ci	actor->squashfs_finish_page = cache_finish_page;
568c2ecf20Sopenharmony_ci	return actor;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* Implementation of page_actor for decompressing directly into page cache. */
608c2ecf20Sopenharmony_cistatic void *direct_first_page(struct squashfs_page_actor *actor)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	actor->next_page = 1;
638c2ecf20Sopenharmony_ci	return actor->pageaddr = kmap_atomic(actor->page[0]);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void *direct_next_page(struct squashfs_page_actor *actor)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	if (actor->pageaddr)
698c2ecf20Sopenharmony_ci		kunmap_atomic(actor->pageaddr);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return actor->pageaddr = actor->next_page == actor->pages ? NULL :
728c2ecf20Sopenharmony_ci		kmap_atomic(actor->page[actor->next_page++]);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void direct_finish_page(struct squashfs_page_actor *actor)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	if (actor->pageaddr)
788c2ecf20Sopenharmony_ci		kunmap_atomic(actor->pageaddr);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
828c2ecf20Sopenharmony_ci	int pages, int length)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (actor == NULL)
878c2ecf20Sopenharmony_ci		return NULL;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	actor->length = length ? : pages * PAGE_SIZE;
908c2ecf20Sopenharmony_ci	actor->page = page;
918c2ecf20Sopenharmony_ci	actor->pages = pages;
928c2ecf20Sopenharmony_ci	actor->next_page = 0;
938c2ecf20Sopenharmony_ci	actor->pageaddr = NULL;
948c2ecf20Sopenharmony_ci	actor->squashfs_first_page = direct_first_page;
958c2ecf20Sopenharmony_ci	actor->squashfs_next_page = direct_next_page;
968c2ecf20Sopenharmony_ci	actor->squashfs_finish_page = direct_finish_page;
978c2ecf20Sopenharmony_ci	return actor;
988c2ecf20Sopenharmony_ci}
99