162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Oracle. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/sunrpc/svc_rdma.h> 762306a36Sopenharmony_ci#include <linux/sunrpc/rpc_rdma.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "xprt_rdma.h" 1062306a36Sopenharmony_ci#include <trace/events/rpcrdma.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * pcl_free - Release all memory associated with a parsed chunk list 1462306a36Sopenharmony_ci * @pcl: parsed chunk list 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_civoid pcl_free(struct svc_rdma_pcl *pcl) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci while (!list_empty(&pcl->cl_chunks)) { 2062306a36Sopenharmony_ci struct svc_rdma_chunk *chunk; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci chunk = pcl_first_chunk(pcl); 2362306a36Sopenharmony_ci list_del(&chunk->ch_list); 2462306a36Sopenharmony_ci kfree(chunk); 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct svc_rdma_chunk *pcl_alloc_chunk(u32 segcount, u32 position) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct svc_rdma_chunk *chunk; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci chunk = kmalloc(struct_size(chunk, ch_segments, segcount), GFP_KERNEL); 3362306a36Sopenharmony_ci if (!chunk) 3462306a36Sopenharmony_ci return NULL; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci chunk->ch_position = position; 3762306a36Sopenharmony_ci chunk->ch_length = 0; 3862306a36Sopenharmony_ci chunk->ch_payload_length = 0; 3962306a36Sopenharmony_ci chunk->ch_segcount = 0; 4062306a36Sopenharmony_ci return chunk; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct svc_rdma_chunk * 4462306a36Sopenharmony_cipcl_lookup_position(struct svc_rdma_pcl *pcl, u32 position) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct svc_rdma_chunk *pos; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci pcl_for_each_chunk(pos, pcl) { 4962306a36Sopenharmony_ci if (pos->ch_position == position) 5062306a36Sopenharmony_ci return pos; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci return NULL; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void pcl_insert_position(struct svc_rdma_pcl *pcl, 5662306a36Sopenharmony_ci struct svc_rdma_chunk *chunk) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct svc_rdma_chunk *pos; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci pcl_for_each_chunk(pos, pcl) { 6162306a36Sopenharmony_ci if (pos->ch_position > chunk->ch_position) 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci __list_add(&chunk->ch_list, pos->ch_list.prev, &pos->ch_list); 6562306a36Sopenharmony_ci pcl->cl_count++; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void pcl_set_read_segment(const struct svc_rdma_recv_ctxt *rctxt, 6962306a36Sopenharmony_ci struct svc_rdma_chunk *chunk, 7062306a36Sopenharmony_ci u32 handle, u32 length, u64 offset) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct svc_rdma_segment *segment; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci segment = &chunk->ch_segments[chunk->ch_segcount]; 7562306a36Sopenharmony_ci segment->rs_handle = handle; 7662306a36Sopenharmony_ci segment->rs_length = length; 7762306a36Sopenharmony_ci segment->rs_offset = offset; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci trace_svcrdma_decode_rseg(&rctxt->rc_cid, chunk, segment); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci chunk->ch_length += length; 8262306a36Sopenharmony_ci chunk->ch_segcount++; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * pcl_alloc_call - Construct a parsed chunk list for the Call body 8762306a36Sopenharmony_ci * @rctxt: Ingress receive context 8862306a36Sopenharmony_ci * @p: Start of an un-decoded Read list 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Assumptions: 9162306a36Sopenharmony_ci * - The incoming Read list has already been sanity checked. 9262306a36Sopenharmony_ci * - cl_count is already set to the number of segments in 9362306a36Sopenharmony_ci * the un-decoded list. 9462306a36Sopenharmony_ci * - The list might not be in order by position. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Return values: 9762306a36Sopenharmony_ci * %true: Parsed chunk list was successfully constructed, and 9862306a36Sopenharmony_ci * cl_count is updated to be the number of chunks (ie. 9962306a36Sopenharmony_ci * unique positions) in the Read list. 10062306a36Sopenharmony_ci * %false: Memory allocation failed. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cibool pcl_alloc_call(struct svc_rdma_recv_ctxt *rctxt, __be32 *p) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct svc_rdma_pcl *pcl = &rctxt->rc_call_pcl; 10562306a36Sopenharmony_ci unsigned int i, segcount = pcl->cl_count; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci pcl->cl_count = 0; 10862306a36Sopenharmony_ci for (i = 0; i < segcount; i++) { 10962306a36Sopenharmony_ci struct svc_rdma_chunk *chunk; 11062306a36Sopenharmony_ci u32 position, handle, length; 11162306a36Sopenharmony_ci u64 offset; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci p++; /* skip the list discriminator */ 11462306a36Sopenharmony_ci p = xdr_decode_read_segment(p, &position, &handle, 11562306a36Sopenharmony_ci &length, &offset); 11662306a36Sopenharmony_ci if (position != 0) 11762306a36Sopenharmony_ci continue; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (pcl_is_empty(pcl)) { 12062306a36Sopenharmony_ci chunk = pcl_alloc_chunk(segcount, position); 12162306a36Sopenharmony_ci if (!chunk) 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci pcl_insert_position(pcl, chunk); 12462306a36Sopenharmony_ci } else { 12562306a36Sopenharmony_ci chunk = list_first_entry(&pcl->cl_chunks, 12662306a36Sopenharmony_ci struct svc_rdma_chunk, 12762306a36Sopenharmony_ci ch_list); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pcl_set_read_segment(rctxt, chunk, handle, length, offset); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return true; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * pcl_alloc_read - Construct a parsed chunk list for normal Read chunks 13862306a36Sopenharmony_ci * @rctxt: Ingress receive context 13962306a36Sopenharmony_ci * @p: Start of an un-decoded Read list 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Assumptions: 14262306a36Sopenharmony_ci * - The incoming Read list has already been sanity checked. 14362306a36Sopenharmony_ci * - cl_count is already set to the number of segments in 14462306a36Sopenharmony_ci * the un-decoded list. 14562306a36Sopenharmony_ci * - The list might not be in order by position. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Return values: 14862306a36Sopenharmony_ci * %true: Parsed chunk list was successfully constructed, and 14962306a36Sopenharmony_ci * cl_count is updated to be the number of chunks (ie. 15062306a36Sopenharmony_ci * unique position values) in the Read list. 15162306a36Sopenharmony_ci * %false: Memory allocation failed. 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * TODO: 15462306a36Sopenharmony_ci * - Check for chunk range overlaps 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cibool pcl_alloc_read(struct svc_rdma_recv_ctxt *rctxt, __be32 *p) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct svc_rdma_pcl *pcl = &rctxt->rc_read_pcl; 15962306a36Sopenharmony_ci unsigned int i, segcount = pcl->cl_count; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci pcl->cl_count = 0; 16262306a36Sopenharmony_ci for (i = 0; i < segcount; i++) { 16362306a36Sopenharmony_ci struct svc_rdma_chunk *chunk; 16462306a36Sopenharmony_ci u32 position, handle, length; 16562306a36Sopenharmony_ci u64 offset; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci p++; /* skip the list discriminator */ 16862306a36Sopenharmony_ci p = xdr_decode_read_segment(p, &position, &handle, 16962306a36Sopenharmony_ci &length, &offset); 17062306a36Sopenharmony_ci if (position == 0) 17162306a36Sopenharmony_ci continue; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci chunk = pcl_lookup_position(pcl, position); 17462306a36Sopenharmony_ci if (!chunk) { 17562306a36Sopenharmony_ci chunk = pcl_alloc_chunk(segcount, position); 17662306a36Sopenharmony_ci if (!chunk) 17762306a36Sopenharmony_ci return false; 17862306a36Sopenharmony_ci pcl_insert_position(pcl, chunk); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pcl_set_read_segment(rctxt, chunk, handle, length, offset); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return true; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/** 18862306a36Sopenharmony_ci * pcl_alloc_write - Construct a parsed chunk list from a Write list 18962306a36Sopenharmony_ci * @rctxt: Ingress receive context 19062306a36Sopenharmony_ci * @pcl: Parsed chunk list to populate 19162306a36Sopenharmony_ci * @p: Start of an un-decoded Write list 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Assumptions: 19462306a36Sopenharmony_ci * - The incoming Write list has already been sanity checked, and 19562306a36Sopenharmony_ci * - cl_count is set to the number of chunks in the un-decoded list. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Return values: 19862306a36Sopenharmony_ci * %true: Parsed chunk list was successfully constructed. 19962306a36Sopenharmony_ci * %false: Memory allocation failed. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cibool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt, 20262306a36Sopenharmony_ci struct svc_rdma_pcl *pcl, __be32 *p) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct svc_rdma_segment *segment; 20562306a36Sopenharmony_ci struct svc_rdma_chunk *chunk; 20662306a36Sopenharmony_ci unsigned int i, j; 20762306a36Sopenharmony_ci u32 segcount; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = 0; i < pcl->cl_count; i++) { 21062306a36Sopenharmony_ci p++; /* skip the list discriminator */ 21162306a36Sopenharmony_ci segcount = be32_to_cpup(p++); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci chunk = pcl_alloc_chunk(segcount, 0); 21462306a36Sopenharmony_ci if (!chunk) 21562306a36Sopenharmony_ci return false; 21662306a36Sopenharmony_ci list_add_tail(&chunk->ch_list, &pcl->cl_chunks); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (j = 0; j < segcount; j++) { 21962306a36Sopenharmony_ci segment = &chunk->ch_segments[j]; 22062306a36Sopenharmony_ci p = xdr_decode_rdma_segment(p, &segment->rs_handle, 22162306a36Sopenharmony_ci &segment->rs_length, 22262306a36Sopenharmony_ci &segment->rs_offset); 22362306a36Sopenharmony_ci trace_svcrdma_decode_wseg(&rctxt->rc_cid, chunk, j); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci chunk->ch_length += segment->rs_length; 22662306a36Sopenharmony_ci chunk->ch_segcount++; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci return true; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int pcl_process_region(const struct xdr_buf *xdr, 23362306a36Sopenharmony_ci unsigned int offset, unsigned int length, 23462306a36Sopenharmony_ci int (*actor)(const struct xdr_buf *, void *), 23562306a36Sopenharmony_ci void *data) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct xdr_buf subbuf; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!length) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci if (xdr_buf_subsegment(xdr, &subbuf, offset, length)) 24262306a36Sopenharmony_ci return -EMSGSIZE; 24362306a36Sopenharmony_ci return actor(&subbuf, data); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/** 24762306a36Sopenharmony_ci * pcl_process_nonpayloads - Process non-payload regions inside @xdr 24862306a36Sopenharmony_ci * @pcl: Chunk list to process 24962306a36Sopenharmony_ci * @xdr: xdr_buf to process 25062306a36Sopenharmony_ci * @actor: Function to invoke on each non-payload region 25162306a36Sopenharmony_ci * @data: Arguments for @actor 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * This mechanism must ignore not only result payloads that were already 25462306a36Sopenharmony_ci * sent via RDMA Write, but also XDR padding for those payloads that 25562306a36Sopenharmony_ci * the upper layer has added. 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Assumptions: 25862306a36Sopenharmony_ci * The xdr->len and ch_position fields are aligned to 4-byte multiples. 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Returns: 26162306a36Sopenharmony_ci * On success, zero, 26262306a36Sopenharmony_ci * %-EMSGSIZE on XDR buffer overflow, or 26362306a36Sopenharmony_ci * The return value of @actor 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ciint pcl_process_nonpayloads(const struct svc_rdma_pcl *pcl, 26662306a36Sopenharmony_ci const struct xdr_buf *xdr, 26762306a36Sopenharmony_ci int (*actor)(const struct xdr_buf *, void *), 26862306a36Sopenharmony_ci void *data) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct svc_rdma_chunk *chunk, *next; 27162306a36Sopenharmony_ci unsigned int start; 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci chunk = pcl_first_chunk(pcl); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* No result payloads were generated */ 27762306a36Sopenharmony_ci if (!chunk || !chunk->ch_payload_length) 27862306a36Sopenharmony_ci return actor(xdr, data); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Process the region before the first result payload */ 28162306a36Sopenharmony_ci ret = pcl_process_region(xdr, 0, chunk->ch_position, actor, data); 28262306a36Sopenharmony_ci if (ret < 0) 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Process the regions between each middle result payload */ 28662306a36Sopenharmony_ci while ((next = pcl_next_chunk(pcl, chunk))) { 28762306a36Sopenharmony_ci if (!next->ch_payload_length) 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci start = pcl_chunk_end_offset(chunk); 29162306a36Sopenharmony_ci ret = pcl_process_region(xdr, start, next->ch_position - start, 29262306a36Sopenharmony_ci actor, data); 29362306a36Sopenharmony_ci if (ret < 0) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci chunk = next; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Process the region after the last result payload */ 30062306a36Sopenharmony_ci start = pcl_chunk_end_offset(chunk); 30162306a36Sopenharmony_ci ret = pcl_process_region(xdr, start, xdr->len - start, actor, data); 30262306a36Sopenharmony_ci if (ret < 0) 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 307