mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-16 09:56:46 +00:00
SUNRPC: Introduce xdr_stream_move_subsegment()
I do this by creating an xdr subsegment for the range we will be operating over. This lets me shift data to the correct place without potentially overwriting anything already there. Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
parent
33ce83ef0b
commit
4f5f3b6028
@ -262,6 +262,8 @@ extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, uns
|
||||
extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length);
|
||||
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
|
||||
unsigned int len);
|
||||
extern unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
|
||||
unsigned int target, unsigned int length);
|
||||
|
||||
/**
|
||||
* xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.
|
||||
|
@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
|
||||
xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift);
|
||||
}
|
||||
|
||||
static void xdr_buf_head_shift_left(const struct xdr_buf *buf,
|
||||
unsigned int base, unsigned int len,
|
||||
unsigned int shift)
|
||||
{
|
||||
const struct kvec *head = buf->head;
|
||||
unsigned int bytes;
|
||||
|
||||
if (!shift || !len)
|
||||
return;
|
||||
|
||||
if (shift > base) {
|
||||
bytes = (shift - base);
|
||||
if (bytes >= len)
|
||||
return;
|
||||
base += bytes;
|
||||
len -= bytes;
|
||||
}
|
||||
|
||||
if (base < head->iov_len) {
|
||||
bytes = min_t(unsigned int, len, head->iov_len - base);
|
||||
memmove(head->iov_base + (base - shift),
|
||||
head->iov_base + base, bytes);
|
||||
base += bytes;
|
||||
len -= bytes;
|
||||
}
|
||||
xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* xdr_shrink_bufhead
|
||||
* @buf: xdr_buf
|
||||
@ -1680,6 +1708,37 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
|
||||
|
||||
/**
|
||||
* xdr_stream_move_subsegment - Move part of a stream to another position
|
||||
* @xdr: the source xdr_stream
|
||||
* @offset: the source offset of the segment
|
||||
* @target: the target offset of the segment
|
||||
* @length: the number of bytes to move
|
||||
*
|
||||
* Moves @length bytes from @offset to @target in the xdr_stream, overwriting
|
||||
* anything in its space. Returns the number of bytes in the segment.
|
||||
*/
|
||||
unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
|
||||
unsigned int target, unsigned int length)
|
||||
{
|
||||
struct xdr_buf buf;
|
||||
unsigned int shift;
|
||||
|
||||
if (offset < target) {
|
||||
shift = target - offset;
|
||||
if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0)
|
||||
return 0;
|
||||
xdr_buf_head_shift_right(&buf, 0, length, shift);
|
||||
} else if (offset > target) {
|
||||
shift = offset - target;
|
||||
if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0)
|
||||
return 0;
|
||||
xdr_buf_head_shift_left(&buf, shift, length, shift);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment);
|
||||
|
||||
/**
|
||||
* xdr_buf_trim - lop at most "len" bytes off the end of "buf"
|
||||
* @buf: buf to be trimmed
|
||||
|
Loading…
x
Reference in New Issue
Block a user