%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/
Upload File :
Create Path :
Current File : //var/lib/dkms/blksnap/6.3.0.73/source/big_buffer.c

// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "memory_checker.h"
#include "big_buffer.h"
#ifdef STANDALONE_BDEVFILTER
#include "log.h"
#endif

static inline size_t page_count_calc(size_t buffer_size)
{
	size_t page_count = buffer_size / PAGE_SIZE;

	if (buffer_size & (PAGE_SIZE - 1))
		page_count += 1;
	return page_count;
}

struct big_buffer *big_buffer_alloc(size_t buffer_size, int gfp_opt)
{
	int res = 0;
	struct big_buffer *bbuff;
	size_t count;
	size_t inx;

	count = page_count_calc(buffer_size);

	bbuff = kzalloc(sizeof(struct big_buffer) + count * sizeof(void *),
			gfp_opt);
	if (bbuff == NULL)
		return NULL;
	memory_object_inc(memory_object_big_buffer);

	bbuff->pg_cnt = count;
	for (inx = 0; inx < bbuff->pg_cnt; ++inx) {
		struct page *pg = alloc_page(gfp_opt);

		if (!pg) {
			res = -ENOMEM;
			break;
		}
		memory_object_inc(memory_object_page);
		bbuff->pg[inx] = page_address(pg);
	}

	if (res) {
		big_buffer_free(bbuff);
		return NULL;
	}

	return bbuff;
}

void big_buffer_free(struct big_buffer *bbuff)
{
	size_t inx;
	size_t count = bbuff->pg_cnt;

	if (bbuff == NULL)
		return;

	for (inx = 0; inx < count; ++inx) {
		if (bbuff->pg[inx] != NULL) {
			free_page((unsigned long)bbuff->pg[inx]);
			memory_object_dec(memory_object_page);
		}
	}

	kfree(bbuff);
	memory_object_dec(memory_object_big_buffer);
}

size_t big_buffer_copy_to_user(char __user *dst_user, size_t offset,
			       struct big_buffer *bbuff, size_t length)
{
	size_t left_data_length;
	int page_inx = offset / PAGE_SIZE;
	size_t processed_len = 0;
	size_t unordered = offset & (PAGE_SIZE - 1);

	if (unordered) {
		size_t page_len =
			min_t(size_t, (PAGE_SIZE - unordered), length);

		left_data_length =
			copy_to_user(dst_user + processed_len,
				     bbuff->pg[page_inx] + unordered, page_len);
		if (left_data_length) {
			pr_err("Failed to copy data from big_buffer to user buffer\n");
			return processed_len;
		}

		++page_inx;
		processed_len += page_len;
	}

	while ((processed_len < length) && (page_inx < bbuff->pg_cnt)) {
		size_t page_len =
			min_t(size_t, PAGE_SIZE, (length - processed_len));

		left_data_length = copy_to_user(dst_user + processed_len,
						bbuff->pg[page_inx], page_len);
		if (left_data_length) {
			pr_err("Failed to copy data from big_buffer to user buffer\n");
			break;
		}

		++page_inx;
		processed_len += page_len;
	}

	return processed_len;
}

size_t big_buffer_copy_from_user(const char __user *src_user, size_t offset,
				 struct big_buffer *bbuff, size_t length)
{
	size_t left_data_length;
	int page_inx = offset / PAGE_SIZE;
	size_t processed_len = 0;
	size_t unordered = offset & (PAGE_SIZE - 1);

	if (unordered) {
		size_t page_len =
			min_t(size_t, (PAGE_SIZE - unordered), length);

		left_data_length =
			copy_from_user(bbuff->pg[page_inx] + unordered,
				       src_user + processed_len, page_len);
		if (left_data_length) {
			pr_err("Failed to copy data from user buffer to big_buffer\n");
			return processed_len;
		}

		++page_inx;
		processed_len += page_len;
	}

	while ((processed_len < length) && (page_inx < bbuff->pg_cnt)) {
		size_t page_len =
			min_t(size_t, PAGE_SIZE, (length - processed_len));

		left_data_length =
			copy_from_user(bbuff->pg[page_inx],
				       src_user + processed_len, page_len);
		if (left_data_length) {
			pr_err("Failed to copy data from user buffer to big_buffer\n");
			break;
		}

		++page_inx;
		processed_len += page_len;
	}

	return processed_len;
}

void *big_buffer_get_element(struct big_buffer *bbuff, size_t index,
			     size_t sizeof_element)
{
	size_t elements_in_page = PAGE_SIZE / sizeof_element;
	size_t pg_inx = index / elements_in_page;
	size_t pg_ofs = (index - (pg_inx * elements_in_page)) * sizeof_element;

	if (pg_inx >= bbuff->pg_cnt)
		return NULL;

	return bbuff->pg[pg_inx] + pg_ofs;
}

void big_buffer_memset(struct big_buffer *bbuff, int value)
{
	size_t inx;

	for (inx = 0; inx < bbuff->pg_cnt; ++inx)
		memset(bbuff->pg[inx], value, PAGE_SIZE);
}

void big_buffer_memcpy(struct big_buffer *dst, struct big_buffer *src)
{
	size_t inx;
	size_t count = min_t(size_t, dst->pg_cnt, src->pg_cnt);

	for (inx = 0; inx < count; ++inx)
		memcpy(dst->pg[inx], src->pg[inx], PAGE_SIZE);
}

int big_buffer_byte_get(struct big_buffer *bbuff, size_t inx, u8 *value)
{
	size_t page_inx = inx >> PAGE_SHIFT;
	size_t byte_pos = inx & (PAGE_SIZE - 1);

	if (page_inx >= bbuff->pg_cnt)
		return -ENODATA;

	*value = bbuff->pg[page_inx][byte_pos];

	return 0;
}

int big_buffer_byte_set(struct big_buffer *bbuff, size_t inx, u8 value)
{
	size_t page_inx = inx >> PAGE_SHIFT;
	size_t byte_pos = inx & (PAGE_SIZE - 1);

	if (page_inx >= bbuff->pg_cnt)
		return -ENODATA;

	bbuff->pg[page_inx][byte_pos] = value;

	return 0;
}

Zerion Mini Shell 1.0