Skip to content

Commit d3b00a8

Browse files
amschuma-ntapTrond Myklebust
authored andcommitted
NFS: Replace the READ_PLUS decoding code
We now take a 2-step process that allows us to place data and hole segments directly at their final position in the xdr_stream without needing to do a bunch of redundant copies to expand holes. Due to the variable lengths of each segment, the xdr metadata might cross page boundaries which I account for by setting a small scratch buffer so xdr_inline_decode() won't fail. Signed-off-by: Anna Schumaker <[email protected]> Signed-off-by: Trond Myklebust <[email protected]>
1 parent e1bd876 commit d3b00a8

File tree

1 file changed

+88
-82
lines changed

1 file changed

+88
-82
lines changed

fs/nfs/nfs42xdr.c

Lines changed: 88 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,82 +1025,95 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re
10251025
return decode_op_hdr(xdr, OP_DEALLOCATE);
10261026
}
10271027

1028-
static int decode_read_plus_data(struct xdr_stream *xdr,
1029-
struct nfs_pgio_args *args,
1030-
struct nfs_pgio_res *res)
1031-
{
1032-
uint32_t count, recvd;
1028+
struct read_plus_segment {
1029+
enum data_content4 type;
10331030
uint64_t offset;
1034-
__be32 *p;
1035-
1036-
p = xdr_inline_decode(xdr, 8 + 4);
1037-
if (!p)
1038-
return 1;
1031+
union {
1032+
struct {
1033+
uint64_t length;
1034+
} hole;
1035+
1036+
struct {
1037+
uint32_t length;
1038+
unsigned int from;
1039+
} data;
1040+
};
1041+
};
10391042

1040-
p = xdr_decode_hyper(p, &offset);
1041-
count = be32_to_cpup(p);
1042-
recvd = xdr_align_data(xdr, res->count, xdr_align_size(count));
1043-
if (recvd > count)
1044-
recvd = count;
1045-
if (res->count + recvd > args->count) {
1046-
if (args->count > res->count)
1047-
res->count += args->count - res->count;
1048-
return 1;
1049-
}
1050-
res->count += recvd;
1051-
if (count > recvd)
1052-
return 1;
1053-
return 0;
1043+
static inline uint64_t read_plus_segment_length(struct read_plus_segment *seg)
1044+
{
1045+
return seg->type == NFS4_CONTENT_DATA ? seg->data.length : seg->hole.length;
10541046
}
10551047

1056-
static int decode_read_plus_hole(struct xdr_stream *xdr,
1057-
struct nfs_pgio_args *args,
1058-
struct nfs_pgio_res *res, uint32_t *eof)
1048+
static int decode_read_plus_segment(struct xdr_stream *xdr,
1049+
struct read_plus_segment *seg)
10591050
{
1060-
uint64_t offset, length, recvd;
10611051
__be32 *p;
10621052

1063-
p = xdr_inline_decode(xdr, 8 + 8);
1053+
p = xdr_inline_decode(xdr, 4);
10641054
if (!p)
1065-
return 1;
1066-
1067-
p = xdr_decode_hyper(p, &offset);
1068-
p = xdr_decode_hyper(p, &length);
1069-
if (offset != args->offset + res->count) {
1070-
/* Server returned an out-of-sequence extent */
1071-
if (offset > args->offset + res->count ||
1072-
offset + length < args->offset + res->count) {
1073-
dprintk("NFS: server returned out of sequence extent: "
1074-
"offset/size = %llu/%llu != expected %llu\n",
1075-
(unsigned long long)offset,
1076-
(unsigned long long)length,
1077-
(unsigned long long)(args->offset +
1078-
res->count));
1079-
return 1;
1080-
}
1081-
length -= args->offset + res->count - offset;
1082-
}
1083-
if (length + res->count > args->count) {
1084-
*eof = 0;
1085-
if (unlikely(res->count >= args->count))
1086-
return 1;
1087-
length = args->count - res->count;
1088-
}
1089-
recvd = xdr_expand_hole(xdr, res->count, length);
1090-
res->count += recvd;
1055+
return -EIO;
1056+
seg->type = be32_to_cpup(p++);
1057+
1058+
p = xdr_inline_decode(xdr, seg->type == NFS4_CONTENT_DATA ? 12 : 16);
1059+
if (!p)
1060+
return -EIO;
1061+
p = xdr_decode_hyper(p, &seg->offset);
1062+
1063+
if (seg->type == NFS4_CONTENT_DATA) {
1064+
struct xdr_buf buf;
1065+
uint32_t len = be32_to_cpup(p);
1066+
1067+
seg->data.length = len;
1068+
seg->data.from = xdr_stream_pos(xdr);
10911069

1092-
if (recvd < length)
1093-
return 1;
1070+
if (!xdr_stream_subsegment(xdr, &buf, xdr_align_size(len)))
1071+
return -EIO;
1072+
} else if (seg->type == NFS4_CONTENT_HOLE) {
1073+
xdr_decode_hyper(p, &seg->hole.length);
1074+
} else
1075+
return -EINVAL;
10941076
return 0;
10951077
}
10961078

1079+
static int process_read_plus_segment(struct xdr_stream *xdr,
1080+
struct nfs_pgio_args *args,
1081+
struct nfs_pgio_res *res,
1082+
struct read_plus_segment *seg)
1083+
{
1084+
unsigned long offset = seg->offset;
1085+
unsigned long length = read_plus_segment_length(seg);
1086+
unsigned int bufpos;
1087+
1088+
if (offset + length < args->offset)
1089+
return 0;
1090+
else if (offset > args->offset + args->count) {
1091+
res->eof = 0;
1092+
return 0;
1093+
} else if (offset < args->offset) {
1094+
length -= (args->offset - offset);
1095+
offset = args->offset;
1096+
} else if (offset + length > args->offset + args->count) {
1097+
length = (args->offset + args->count) - offset;
1098+
res->eof = 0;
1099+
}
1100+
1101+
bufpos = xdr->buf->head[0].iov_len + (offset - args->offset);
1102+
if (seg->type == NFS4_CONTENT_HOLE)
1103+
return xdr_stream_zero(xdr, bufpos, length);
1104+
else
1105+
return xdr_stream_move_subsegment(xdr, seg->data.from, bufpos, length);
1106+
}
1107+
10971108
static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
10981109
{
10991110
struct nfs_pgio_header *hdr =
11001111
container_of(res, struct nfs_pgio_header, res);
11011112
struct nfs_pgio_args *args = &hdr->args;
1102-
uint32_t eof, segments, type;
1113+
uint32_t segments;
1114+
struct read_plus_segment *segs;
11031115
int status, i;
1116+
char scratch_buf[16];
11041117
__be32 *p;
11051118

11061119
status = decode_op_hdr(xdr, OP_READ_PLUS);
@@ -1112,38 +1125,31 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
11121125
return -EIO;
11131126

11141127
res->count = 0;
1115-
eof = be32_to_cpup(p++);
1128+
res->eof = be32_to_cpup(p++);
11161129
segments = be32_to_cpup(p++);
11171130
if (segments == 0)
1118-
goto out;
1119-
1120-
for (i = 0; i < segments; i++) {
1121-
p = xdr_inline_decode(xdr, 4);
1122-
if (!p)
1123-
goto early_out;
1131+
return status;
11241132

1125-
type = be32_to_cpup(p++);
1126-
if (type == NFS4_CONTENT_DATA)
1127-
status = decode_read_plus_data(xdr, args, res);
1128-
else if (type == NFS4_CONTENT_HOLE)
1129-
status = decode_read_plus_hole(xdr, args, res, &eof);
1130-
else
1131-
return -EINVAL;
1133+
segs = kmalloc_array(segments, sizeof(*segs), GFP_KERNEL);
1134+
if (!segs)
1135+
return -ENOMEM;
11321136

1137+
xdr_set_scratch_buffer(xdr, &scratch_buf, 32);
1138+
status = -EIO;
1139+
for (i = 0; i < segments; i++) {
1140+
status = decode_read_plus_segment(xdr, &segs[i]);
11331141
if (status < 0)
1134-
return status;
1135-
if (status > 0)
1136-
goto early_out;
1142+
goto out;
11371143
}
11381144

1145+
xdr_set_pagelen(xdr, xdr_align_size(args->count));
1146+
for (i = segments; i > 0; i--)
1147+
res->count += process_read_plus_segment(xdr, args, res, &segs[i-1]);
1148+
status = 0;
1149+
11391150
out:
1140-
res->eof = eof;
1141-
return 0;
1142-
early_out:
1143-
if (unlikely(!i))
1144-
return -EIO;
1145-
res->eof = 0;
1146-
return 0;
1151+
kfree(segs);
1152+
return status;
11471153
}
11481154

11491155
static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)

0 commit comments

Comments
 (0)