Commit 356bbbb6 authored by Omar Sandoval's avatar Omar Sandoval Committed by David Sterba

btrfs: send: write larger chunks when using stream v2

The length field of the send stream TLV header is 16 bits. This means
that the maximum amount of data that can be sent for one write is 64K
minus one. However, encoded writes must be able to send the maximum
compressed extent (128K) in one command, or more. To support this, send
stream version 2 encodes the DATA attribute differently: it has no
length field, and the length is implicitly up to the end of containing
command (which has a 32bit length field). Although this is necessary
for encoded writes, normal writes can benefit from it, too.

Also add a check to enforce that the DATA attribute is last. It is only
strictly necessary for v2, but we might as well make v1 consistent with
it.

For v2, let's bump up the send buffer to the maximum compressed extent
size plus 16K for the other metadata (144K total). Since this will most
likely be vmalloc'd (and always will be after the next commit), we round
it up to the next page since we might as well use the rest of the page
on systems with >16K pages.
Reviewed-by: default avatarNikolay Borisov <nborisov@suse.com>
Signed-off-by: default avatarOmar Sandoval <osandov@fb.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent b7c14f23
...@@ -82,6 +82,11 @@ struct send_ctx { ...@@ -82,6 +82,11 @@ struct send_ctx {
char *send_buf; char *send_buf;
u32 send_size; u32 send_size;
u32 send_max_size; u32 send_max_size;
/*
* Whether BTRFS_SEND_A_DATA attribute was already added to current
* command (since protocol v2, data must be the last attribute).
*/
bool put_data;
u64 flags; /* 'flags' member of btrfs_ioctl_send_args is u64 */ u64 flags; /* 'flags' member of btrfs_ioctl_send_args is u64 */
/* Protocol version compatibility requested */ /* Protocol version compatibility requested */
u32 proto; u32 proto;
...@@ -596,6 +601,9 @@ static int tlv_put(struct send_ctx *sctx, u16 attr, const void *data, int len) ...@@ -596,6 +601,9 @@ static int tlv_put(struct send_ctx *sctx, u16 attr, const void *data, int len)
int total_len = sizeof(*hdr) + len; int total_len = sizeof(*hdr) + len;
int left = sctx->send_max_size - sctx->send_size; int left = sctx->send_max_size - sctx->send_size;
if (WARN_ON_ONCE(sctx->put_data))
return -EINVAL;
if (unlikely(left < total_len)) if (unlikely(left < total_len))
return -EOVERFLOW; return -EOVERFLOW;
...@@ -733,6 +741,7 @@ static int send_cmd(struct send_ctx *sctx) ...@@ -733,6 +741,7 @@ static int send_cmd(struct send_ctx *sctx)
&sctx->send_off); &sctx->send_off);
sctx->send_size = 0; sctx->send_size = 0;
sctx->put_data = false;
return ret; return ret;
} }
...@@ -4860,6 +4869,19 @@ static inline u64 max_send_read_size(const struct send_ctx *sctx) ...@@ -4860,6 +4869,19 @@ static inline u64 max_send_read_size(const struct send_ctx *sctx)
static int put_data_header(struct send_ctx *sctx, u32 len) static int put_data_header(struct send_ctx *sctx, u32 len)
{ {
if (WARN_ON_ONCE(sctx->put_data))
return -EINVAL;
sctx->put_data = true;
if (sctx->proto >= 2) {
/*
* Since v2, the data attribute header doesn't include a length,
* it is implicitly to the end of the command.
*/
if (sctx->send_max_size - sctx->send_size < sizeof(__le16) + len)
return -EOVERFLOW;
put_unaligned_le16(BTRFS_SEND_A_DATA, sctx->send_buf + sctx->send_size);
sctx->send_size += sizeof(__le16);
} else {
struct btrfs_tlv_header *hdr; struct btrfs_tlv_header *hdr;
if (sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len) if (sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len)
...@@ -4868,6 +4890,7 @@ static int put_data_header(struct send_ctx *sctx, u32 len) ...@@ -4868,6 +4890,7 @@ static int put_data_header(struct send_ctx *sctx, u32 len)
put_unaligned_le16(BTRFS_SEND_A_DATA, &hdr->tlv_type); put_unaligned_le16(BTRFS_SEND_A_DATA, &hdr->tlv_type);
put_unaligned_le16(len, &hdr->tlv_len); put_unaligned_le16(len, &hdr->tlv_len);
sctx->send_size += sizeof(*hdr); sctx->send_size += sizeof(*hdr);
}
return 0; return 0;
} }
...@@ -7552,7 +7575,11 @@ long btrfs_ioctl_send(struct inode *inode, struct btrfs_ioctl_send_args *arg) ...@@ -7552,7 +7575,11 @@ long btrfs_ioctl_send(struct inode *inode, struct btrfs_ioctl_send_args *arg)
sctx->clone_roots_cnt = arg->clone_sources_count; sctx->clone_roots_cnt = arg->clone_sources_count;
if (sctx->proto >= 2)
sctx->send_max_size = ALIGN(SZ_16K + BTRFS_MAX_COMPRESSED, PAGE_SIZE);
else
sctx->send_max_size = BTRFS_SEND_BUF_SIZE_V1; sctx->send_max_size = BTRFS_SEND_BUF_SIZE_V1;
sctx->send_buf = kvmalloc(sctx->send_max_size, GFP_KERNEL); sctx->send_buf = kvmalloc(sctx->send_max_size, GFP_KERNEL);
if (!sctx->send_buf) { if (!sctx->send_buf) {
ret = -ENOMEM; ret = -ENOMEM;
......
Markdown is supported
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