Forum | Documentation | Website | Blog

Skip to content
Snippets Groups Projects
Commit 13840f3f authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba
Browse files

btrfs: refactor main loop in memcpy_extent_buffer()


[BACKGROUND]
Currently memcpy_extent_buffer() does a loop where it would stop at
any page boundary inside [dst_offset, dst_offset + len) or [src_offset,
src_offset + len).

This is mostly allowing us to do copy_pages(), but if we're going to use
folios we will need to handle multi-page (the old behavior) or single
folio (the new optimization).

The current code would be a burden for future changes.

[ENHANCEMENT]
There is a hidden pitfall of the naming memcpy_extent_buffer(), unlike
regular memcpy(), this function can handle overlapping ranges.

So here we extract write_extent_buffer() into a new internal helper,
__write_extent_buffer(), and add a new parameter @use_memmove, to
indicate whether we should use memmove() or regular memcpy().

Now we can go __write_extent_buffer() to handle writing into the dst
range, with proper overlapping detection.

This has a tiny change to the chance of calling memmove().
As the split only happens at the source range page boundaries, the
memcpy/memmove() range would be slightly larger than the old code,
thus slightly increase the chance we call memmove() other than memcopy().

Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 682a0bc5
No related merge requests found
...@@ -4129,8 +4129,9 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb, ...@@ -4129,8 +4129,9 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb,
} }
} }
void write_extent_buffer(const struct extent_buffer *eb, const void *srcv, static void __write_extent_buffer(const struct extent_buffer *eb,
unsigned long start, unsigned long len) const void *srcv, unsigned long start,
unsigned long len, bool use_memmove)
{ {
size_t cur; size_t cur;
size_t offset; size_t offset;
...@@ -4138,6 +4139,8 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv, ...@@ -4138,6 +4139,8 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
char *kaddr; char *kaddr;
char *src = (char *)srcv; char *src = (char *)srcv;
unsigned long i = get_eb_page_index(start); unsigned long i = get_eb_page_index(start);
/* For unmapped (dummy) ebs, no need to check their uptodate status. */
const bool check_uptodate = !test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags);
WARN_ON(test_bit(EXTENT_BUFFER_NO_CHECK, &eb->bflags)); WARN_ON(test_bit(EXTENT_BUFFER_NO_CHECK, &eb->bflags));
...@@ -4148,11 +4151,15 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv, ...@@ -4148,11 +4151,15 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
while (len > 0) { while (len > 0) {
page = eb->pages[i]; page = eb->pages[i];
assert_eb_page_uptodate(eb, page); if (check_uptodate)
assert_eb_page_uptodate(eb, page);
cur = min(len, PAGE_SIZE - offset); cur = min(len, PAGE_SIZE - offset);
kaddr = page_address(page); kaddr = page_address(page);
memcpy(kaddr + offset, src, cur); if (use_memmove)
memmove(kaddr + offset, src, cur);
else
memcpy(kaddr + offset, src, cur);
src += cur; src += cur;
len -= cur; len -= cur;
...@@ -4161,6 +4168,12 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv, ...@@ -4161,6 +4168,12 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
} }
} }
void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
unsigned long start, unsigned long len)
{
return __write_extent_buffer(eb, srcv, start, len, false);
}
static void memset_extent_buffer(const struct extent_buffer *eb, int c, static void memset_extent_buffer(const struct extent_buffer *eb, int c,
unsigned long start, unsigned long len) unsigned long start, unsigned long len)
{ {
...@@ -4409,34 +4422,25 @@ void memcpy_extent_buffer(const struct extent_buffer *dst, ...@@ -4409,34 +4422,25 @@ void memcpy_extent_buffer(const struct extent_buffer *dst,
unsigned long dst_offset, unsigned long src_offset, unsigned long dst_offset, unsigned long src_offset,
unsigned long len) unsigned long len)
{ {
size_t cur; unsigned long cur_off = 0;
size_t dst_off_in_page;
size_t src_off_in_page;
unsigned long dst_i;
unsigned long src_i;
if (check_eb_range(dst, dst_offset, len) || if (check_eb_range(dst, dst_offset, len) ||
check_eb_range(dst, src_offset, len)) check_eb_range(dst, src_offset, len))
return; return;
while (len > 0) { while (cur_off < len) {
dst_off_in_page = get_eb_offset_in_page(dst, dst_offset); unsigned long cur_src = cur_off + src_offset;
src_off_in_page = get_eb_offset_in_page(dst, src_offset); unsigned long pg_index = get_eb_page_index(cur_src);
unsigned long pg_off = get_eb_offset_in_page(dst, cur_src);
dst_i = get_eb_page_index(dst_offset); unsigned long cur_len = min(src_offset + len - cur_src,
src_i = get_eb_page_index(src_offset); PAGE_SIZE - pg_off);
void *src_addr = page_address(dst->pages[pg_index]) + pg_off;
cur = min(len, (unsigned long)(PAGE_SIZE - const bool use_memmove = areas_overlap(src_offset + cur_off,
src_off_in_page)); dst_offset + cur_off, cur_len);
cur = min_t(unsigned long, cur,
(unsigned long)(PAGE_SIZE - dst_off_in_page)); __write_extent_buffer(dst, src_addr, dst_offset + cur_off, cur_len,
use_memmove);
copy_pages(dst->pages[dst_i], dst->pages[src_i], cur_off += cur_len;
dst_off_in_page, src_off_in_page, cur);
src_offset += cur;
dst_offset += cur;
len -= cur;
} }
} }
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment