%PDF- %PDF-
Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/ |
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; }