From: Sergei Shtepa <sergei.shtepa@veeam.com>
To: <axboe@kernel.dk>, <linux-block@vger.kernel.org>,
<linux-kernel@vger.kernel.org>
Cc: Sergei Shtepa <sergei.shtepa@veeam.com>
Subject: [PATCH 08/20] block, blksnap: big buffer in the form of an array of pages
Date: Mon, 13 Jun 2022 18:53:01 +0300 [thread overview]
Message-ID: <1655135593-1900-9-git-send-email-sergei.shtepa@veeam.com> (raw)
In-Reply-To: <1655135593-1900-1-git-send-email-sergei.shtepa@veeam.com>
Description of the struct big_buffer, which is used to store
sufficiently large amounts of data, such as a CBT map, and functions for
working with it. There are systems on which quite often the kmalloc()
memory allocation function of several hundred kilobytes ends with an
error code, and it is impossible to use virtual memory when handling an
I/O unit, since a page fault situation is possible. A simple array of
pages solves this problem.
Signed-off-by: Sergei Shtepa <sergei.shtepa@veeam.com>
---
drivers/block/blksnap/big_buffer.c | 218 +++++++++++++++++++++++++++++
drivers/block/blksnap/big_buffer.h | 27 ++++
2 files changed, 245 insertions(+)
create mode 100644 drivers/block/blksnap/big_buffer.c
create mode 100644 drivers/block/blksnap/big_buffer.h
diff --git a/drivers/block/blksnap/big_buffer.c b/drivers/block/blksnap/big_buffer.c
new file mode 100644
index 000000000000..bcefc09653e4
--- /dev/null
+++ b/drivers/block/blksnap/big_buffer.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
+#include "memory_checker.h"
+#endif
+#include "big_buffer.h"
+
+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;
+#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
+ memory_object_inc(memory_object_big_buffer);
+#endif
+ bbuff->pg_cnt = count;
+ for (inx = 0; inx < bbuff->pg_cnt; ++inx) {
+ struct page *pg = alloc_page(gfp_opt);
+
+ if (!pg) {
+ res = -ENOMEM;
+ break;
+ }
+#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
+ memory_object_inc(memory_object_page);
+#endif
+ 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]);
+#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
+ memory_object_dec(memory_object_page);
+#endif
+ }
+
+ kfree(bbuff);
+#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
+ memory_object_dec(memory_object_big_buffer);
+#endif
+}
+
+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) { //first
+ 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) { //first
+ 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;
+}
diff --git a/drivers/block/blksnap/big_buffer.h b/drivers/block/blksnap/big_buffer.h
new file mode 100644
index 000000000000..4519d5f11c8e
--- /dev/null
+++ b/drivers/block/blksnap/big_buffer.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#pragma once
+#include <linux/mm.h>
+
+struct big_buffer {
+ size_t pg_cnt;
+ u8 *pg[0];
+};
+
+struct big_buffer *big_buffer_alloc(size_t count, int gfp_opt);
+void big_buffer_free(struct big_buffer *bbuff);
+
+size_t big_buffer_copy_to_user(char __user *dst_user_buffer, size_t offset,
+ struct big_buffer *bbuff, size_t length);
+size_t big_buffer_copy_from_user(const char __user *src_user_buffer,
+ size_t offset, struct big_buffer *bbuff,
+ size_t length);
+
+void *big_buffer_get_element(struct big_buffer *bbuff, size_t index,
+ size_t sizeof_element);
+
+void big_buffer_memset(struct big_buffer *bbuff, int value);
+void big_buffer_memcpy(struct big_buffer *dst, struct big_buffer *src);
+
+//byte access
+int big_buffer_byte_get(struct big_buffer *bbuff, size_t inx, u8 *value);
+int big_buffer_byte_set(struct big_buffer *bbuff, size_t inx, u8 value);
--
2.20.1
next prev parent reply other threads:[~2022-06-13 18:56 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-13 15:52 [PATCH 00/20] blksnap - creating non-persistent snapshots for backup Sergei Shtepa
2022-06-13 15:52 ` [PATCH 01/20] block, blk_filter: enable block device filters Sergei Shtepa
2022-06-13 21:50 ` Randy Dunlap
2022-06-14 9:19 ` Sergei Shtepa
2022-06-14 9:21 ` Sergei Shtepa
2022-06-14 9:01 ` kernel test robot
2022-07-06 12:59 ` Christoph Hellwig
2022-07-07 8:26 ` Sergei Shtepa
2022-07-07 17:26 ` Christoph Hellwig
2022-07-08 10:45 ` Sergei Shtepa
2022-07-13 11:56 ` Christoph Hellwig
2022-07-13 13:47 ` Sergei Shtepa
2022-07-14 5:12 ` Christoph Hellwig
2022-07-14 9:22 ` Sergei Shtepa
2022-06-13 15:52 ` [PATCH 02/20] block, blksnap: header file of the module interface Sergei Shtepa
2022-07-06 13:03 ` Christoph Hellwig
2022-06-13 15:52 ` [PATCH 03/20] block, blksnap: module management interface functions Sergei Shtepa
2022-06-13 22:44 ` Chaitanya Kulkarni
2022-06-13 15:52 ` [PATCH 04/20] block, blksnap: init() and exit() functions Sergei Shtepa
2022-06-13 15:52 ` [PATCH 05/20] block, blksnap: interaction with sysfs Sergei Shtepa
2022-06-13 15:52 ` [PATCH 06/20] block, blksnap: attaching and detaching the filter and handling a bios Sergei Shtepa
2022-06-13 15:53 ` [PATCH 07/20] block, blksnap: map of change block tracking Sergei Shtepa
2022-06-13 15:53 ` Sergei Shtepa [this message]
2022-07-06 13:09 ` [PATCH 08/20] block, blksnap: big buffer in the form of an array of pages Christoph Hellwig
2022-06-13 15:53 ` [PATCH 09/20] block, blksnap: minimum data storage unit of the original block device Sergei Shtepa
2022-06-13 15:53 ` [PATCH 10/20] block, blksnap: buffer in memory for the minimum data storage unit Sergei Shtepa
2022-06-13 15:53 ` [PATCH 11/20] block, blksnap: functions and structures for performing block I/O operations Sergei Shtepa
2022-07-07 17:33 ` Christoph Hellwig
2022-06-13 15:53 ` [PATCH 12/20] block, blksnap: storage for storing difference blocks Sergei Shtepa
2022-06-13 15:53 ` [PATCH 13/20] block, blksnap: event queue from the difference storage Sergei Shtepa
2022-06-13 15:53 ` [PATCH 14/20] block, blksnap: owner of information about overwritten blocks of the original block device Sergei Shtepa
2022-06-13 15:53 ` [PATCH 15/20] block, blksnap: snapshot image " Sergei Shtepa
2022-07-06 13:13 ` Christoph Hellwig
2022-07-07 9:16 ` Sergei Shtepa
2022-07-07 17:24 ` Christoph Hellwig
2022-07-08 7:58 ` Sergei Shtepa
2022-07-08 8:04 ` Christoph Hellwig
2022-06-13 15:53 ` [PATCH 16/20] block, blksnap: snapshot Sergei Shtepa
2022-06-13 15:53 ` [PATCH 17/20] block, blksnap: debugging mechanism for monitoring memory consumption Sergei Shtepa
2022-06-13 15:53 ` [PATCH 18/20] block, blksnap: Kconfig Sergei Shtepa
2022-06-13 21:38 ` Randy Dunlap
2022-07-06 13:06 ` Christoph Hellwig
2022-06-13 15:53 ` [PATCH 19/20] block, blksnap: Makefile Sergei Shtepa
2022-07-06 13:06 ` Christoph Hellwig
2022-06-13 15:53 ` [PATCH 20/20] block, blksnap: adds a blksnap to the kernel tree Sergei Shtepa
2022-06-18 2:11 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1655135593-1900-9-git-send-email-sergei.shtepa@veeam.com \
--to=sergei.shtepa@veeam.com \
--cc=axboe@kernel.dk \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®