Skip to content

Commit 4f5f3b6

Browse files
amschuma-ntapTrond Myklebust
authored andcommitted
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>
1 parent 33ce83e commit 4f5f3b6

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

include/linux/sunrpc/xdr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ extern unsigned int xdr_align_data(struct xdr_stream *, unsigned int offset, uns
262262
extern unsigned int xdr_expand_hole(struct xdr_stream *, unsigned int offset, unsigned int length);
263263
extern bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
264264
unsigned int len);
265+
extern unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
266+
unsigned int target, unsigned int length);
265267

266268
/**
267269
* xdr_set_scratch_buffer - Attach a scratch buffer for decoding data.

net/sunrpc/xdr.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,34 @@ static void xdr_buf_pages_shift_left(const struct xdr_buf *buf,
775775
xdr_buf_tail_copy_left(buf, 0, len - buf->page_len, shift);
776776
}
777777

778+
static void xdr_buf_head_shift_left(const struct xdr_buf *buf,
779+
unsigned int base, unsigned int len,
780+
unsigned int shift)
781+
{
782+
const struct kvec *head = buf->head;
783+
unsigned int bytes;
784+
785+
if (!shift || !len)
786+
return;
787+
788+
if (shift > base) {
789+
bytes = (shift - base);
790+
if (bytes >= len)
791+
return;
792+
base += bytes;
793+
len -= bytes;
794+
}
795+
796+
if (base < head->iov_len) {
797+
bytes = min_t(unsigned int, len, head->iov_len - base);
798+
memmove(head->iov_base + (base - shift),
799+
head->iov_base + base, bytes);
800+
base += bytes;
801+
len -= bytes;
802+
}
803+
xdr_buf_pages_shift_left(buf, base - head->iov_len, len, shift);
804+
}
805+
778806
/**
779807
* xdr_shrink_bufhead
780808
* @buf: xdr_buf
@@ -1680,6 +1708,37 @@ bool xdr_stream_subsegment(struct xdr_stream *xdr, struct xdr_buf *subbuf,
16801708
}
16811709
EXPORT_SYMBOL_GPL(xdr_stream_subsegment);
16821710

1711+
/**
1712+
* xdr_stream_move_subsegment - Move part of a stream to another position
1713+
* @xdr: the source xdr_stream
1714+
* @offset: the source offset of the segment
1715+
* @target: the target offset of the segment
1716+
* @length: the number of bytes to move
1717+
*
1718+
* Moves @length bytes from @offset to @target in the xdr_stream, overwriting
1719+
* anything in its space. Returns the number of bytes in the segment.
1720+
*/
1721+
unsigned int xdr_stream_move_subsegment(struct xdr_stream *xdr, unsigned int offset,
1722+
unsigned int target, unsigned int length)
1723+
{
1724+
struct xdr_buf buf;
1725+
unsigned int shift;
1726+
1727+
if (offset < target) {
1728+
shift = target - offset;
1729+
if (xdr_buf_subsegment(xdr->buf, &buf, offset, shift + length) < 0)
1730+
return 0;
1731+
xdr_buf_head_shift_right(&buf, 0, length, shift);
1732+
} else if (offset > target) {
1733+
shift = offset - target;
1734+
if (xdr_buf_subsegment(xdr->buf, &buf, target, shift + length) < 0)
1735+
return 0;
1736+
xdr_buf_head_shift_left(&buf, shift, length, shift);
1737+
}
1738+
return length;
1739+
}
1740+
EXPORT_SYMBOL_GPL(xdr_stream_move_subsegment);
1741+
16831742
/**
16841743
* xdr_buf_trim - lop at most "len" bytes off the end of "buf"
16851744
* @buf: buf to be trimmed

0 commit comments

Comments
 (0)