<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:DengXian;
panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:"\@DengXian";
panose-1:2 1 6 0 3 1 1 1 1 1;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-family:"Calibri",sans-serif;
mso-ligatures:none;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
{page:WordSection1;}
--></style>
</head>
<body lang="EN-SG" link="#0563C1" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">In our userspace RDMA implementation, it is required that every<o:p></o:p></p>
<p class="MsoNormal">RIO_MSG_RDMA_READ_REQ msg must be acked strictly in order. However<o:p></o:p></p>
<p class="MsoNormal">this rule can be broken due to a bug in kio, which though is<o:p></o:p></p>
<p class="MsoNormal">triggered by very abnormal hardware behaviour that it can take<o:p></o:p></p>
<p class="MsoNormal">very long time(>10s) for a WR to complete.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This happens in the read workload with large block size that the<o:p></o:p></p>
<p class="MsoNormal">the client needs to issue RDMA READ wr to pull the data portion<o:p></o:p></p>
<p class="MsoNormal">of a response msg returned by csd. When this operation takes very<o:p></o:p></p>
<p class="MsoNormal">long time to complete for a msg, it will block responses to requests<o:p></o:p></p>
<p class="MsoNormal">after it from being sent out by csd for as long as it can take.<o:p></o:p></p>
<p class="MsoNormal">As a result, these requests will be killed due to calendar timout.<o:p></o:p></p>
<p class="MsoNormal">However when these responses arrives later in form of RIO_MSG_RDMA_READ_REQ<o:p></o:p></p>
<p class="MsoNormal">msg, they will be ignored silently due to missing reqeust msg without<o:p></o:p></p>
<p class="MsoNormal">returning corresponding RIO_MSG_RDMA_RAD_ACK back, therefore breaks the<o:p></o:p></p>
<p class="MsoNormal">expectation of ordered ack on the side of csd. Since the rio connection<o:p></o:p></p>
<p class="MsoNormal">is still in working state, a later valid msg exchange will trigger<o:p></o:p></p>
<p class="MsoNormal">the BUGON check of rb->xid in csd, causing it to crash.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This patch makes sure client will always ack every RIO_MSG_RDMA_READ_REQ<o:p></o:p></p>
<p class="MsoNormal">recevied and in order to avoid crashing csd. However it can't address<o:p></o:p></p>
<p class="MsoNormal">any performance impact due to the strange hardware behaviour that it<o:p></o:p></p>
<p class="MsoNormal">takes abnorma long time for a WR to complete.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">https://pmc.acronis.work/browse/VSTOR-76834<o:p></o:p></p>
<p class="MsoNormal">https://pmc.acronis.work/browse/VSTOR-70758<o:p></o:p></p>
<p class="MsoNormal">https://pmc.acronis.work/browse/VSTOR-60807<o:p></o:p></p>
<p class="MsoNormal">https://pmc.acronis.work/browse/VSTOR-57903<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Signed-off-by: Liu Kui <Kui.Liu@acronis.com><o:p></o:p></p>
<p class="MsoNormal">---<o:p></o:p></p>
<p class="MsoNormal">fs/fuse/kio/pcs/pcs_rdma_io.c | 58 +++++++++++++++++++++++++++++++----<o:p></o:p></p>
<p class="MsoNormal">fs/fuse/kio/pcs/pcs_rdma_io.h | 3 ++<o:p></o:p></p>
<p class="MsoNormal">2 files changed, 55 insertions(+), 6 deletions(-)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">diff --git a/fs/fuse/kio/pcs/pcs_rdma_io.c b/fs/fuse/kio/pcs/pcs_rdma_io.c<o:p></o:p></p>
<p class="MsoNormal">index 62d138c8b611..c78126ab1d79 100644<o:p></o:p></p>
<p class="MsoNormal">--- a/fs/fuse/kio/pcs/pcs_rdma_io.c<o:p></o:p></p>
<p class="MsoNormal">+++ b/fs/fuse/kio/pcs/pcs_rdma_io.c<o:p></o:p></p>
<p class="MsoNormal">@@ -130,6 +130,8 @@ static void rio_abort(struct pcs_rdmaio *rio, int error);<o:p></o:p></p>
<p class="MsoNormal">static void rio_rx_done(struct rio_cqe *cqe, bool sync_mode);<o:p></o:p></p>
<p class="MsoNormal">static void rio_tx_done(struct rio_cqe *cqe, bool sync_mode);<o:p></o:p></p>
<p class="MsoNormal">static void rio_tx_err_occured(struct rio_cqe *cqe, bool sync_mode);<o:p></o:p></p>
<p class="MsoNormal">+static int rio_submit(struct pcs_rdmaio *rio, struct pcs_msg *msg, int type, u64 xid, int status,<o:p></o:p></p>
<p class="MsoNormal">+ bool allow_again);<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">/* Only called when rio->write_queue is not empty */<o:p></o:p></p>
<p class="MsoNormal">static struct pcs_msg *rio_dequeue_msg(struct pcs_rdmaio *rio)<o:p></o:p></p>
<p class="MsoNormal">@@ -424,6 +426,10 @@ static int rio_submit_rdma_read(struct pcs_rdmaio *rio, struct pcs_msg *msg,<o:p></o:p></p>
<p class="MsoNormal"> struct pcs_rdma_device *dev = rio->dev;<o:p></o:p></p>
<p class="MsoNormal"> struct rio_tx *tx;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">+ /* Blocked until after pending RDMA_READ_ACKs are sent out to keep ACK in order */<o:p></o:p></p>
<p class="MsoNormal">+ if (rio->n_rdma_read_ack_pending)<o:p></o:p></p>
<p class="MsoNormal">+ return -EAGAIN;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal"> tx = RE_NULL(rio_get_tx(dev));<o:p></o:p></p>
<p class="MsoNormal"> if (!tx) {<o:p></o:p></p>
<p class="MsoNormal"> if (allow_again)<o:p></o:p></p>
<p class="MsoNormal">@@ -467,6 +473,8 @@ static int rio_submit_rdma_read(struct pcs_rdmaio *rio, struct pcs_msg *msg,<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ongoing++;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal"> return 0;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">fail:<o:p></o:p></p>
<p class="MsoNormal">@@ -478,6 +486,21 @@ static int rio_submit_rdma_read(struct pcs_rdmaio *rio, struct pcs_msg *msg,<o:p></o:p></p>
<p class="MsoNormal"> return -EIO;<o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">+static int rio_submit_rdma_read_ack(struct pcs_rdmaio *rio, u64 xid)<o:p></o:p></p>
<p class="MsoNormal">+{<o:p></o:p></p>
<p class="MsoNormal">+ int ret;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal">+ /* Can only be sent after all ongoing RDMA_READ_REQs complete to keep ack in order */<o:p></o:p></p>
<p class="MsoNormal">+ if (rio->n_rdma_read_ongoing)<o:p></o:p></p>
<p class="MsoNormal">+ return -EAGAIN;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal">+ ret = rio_submit(rio, NULL, SUBMIT_RDMA_READ_ACK, xid, 0, true);<o:p></o:p></p>
<p class="MsoNormal">+ if (!ret)<o:p></o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ack_pending--;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal">+ return ret;<o:p></o:p></p>
<p class="MsoNormal">+}<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal">static int rio_rdma_read_job_work(struct rio_job *j)<o:p></o:p></p>
<p class="MsoNormal">{<o:p></o:p></p>
<p class="MsoNormal"> struct rio_rdma_read_job *job = container_of(j, struct rio_rdma_read_job, job);<o:p></o:p></p>
<p class="MsoNormal">@@ -488,8 +511,15 @@ static int rio_rdma_read_job_work(struct rio_job *j)<o:p></o:p></p>
<p class="MsoNormal"> return 0;<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">- return rio_submit_rdma_read(rio, job->msg, job->offset,<o:p></o:p></p>
<p class="MsoNormal">- &job->rb, true);<o:p></o:p></p>
<p class="MsoNormal">+ /*<o:p></o:p></p>
<p class="MsoNormal">+ * Return RDMA_READ_ACK directly if the original request msg had been killed,<o:p></o:p></p>
<p class="MsoNormal">+ * however must wait until all previous RDMA_READ_REQs have been acked.<o:p></o:p></p>
<p class="MsoNormal">+ */<o:p></o:p></p>
<p class="MsoNormal">+ if (job->msg == PCS_TRASH_MSG)<o:p></o:p></p>
<p class="MsoNormal">+ return rio_submit_rdma_read_ack(rio, job->rb.xid);<o:p></o:p></p>
<p class="MsoNormal">+ else<o:p></o:p></p>
<p class="MsoNormal">+ return rio_submit_rdma_read(rio, job->msg, job->offset,<o:p></o:p></p>
<p class="MsoNormal">+ &job->rb, true);<o:p></o:p></p>
<p class="MsoNormal">}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">static void rio_rdma_read_job_destroy(struct rio_job *j)<o:p></o:p></p>
<p class="MsoNormal">@@ -766,6 +796,7 @@ static void rio_handle_tx(struct pcs_rdmaio *rio, struct rio_tx *tx, int ok)<o:p></o:p></p>
<p class="MsoNormal"> case TX_SUBMIT_RDMA_READ_ACK:<o:p></o:p></p>
<p class="MsoNormal"> rio_put_tx(rio->dev, tx);<o:p></o:p></p>
<p class="MsoNormal"> rio_submit(rio, NULL, SUBMIT_RDMA_READ_ACK, xid, !ok, false);<o:p></o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ongoing--;<o:p></o:p></p>
<p class="MsoNormal"> break;<o:p></o:p></p>
<p class="MsoNormal"> case TX_WAIT_FOR_TX_COMPL:<o:p></o:p></p>
<p class="MsoNormal"> case TX_WAIT_FOR_READ_ACK:<o:p></o:p></p>
<p class="MsoNormal">@@ -798,6 +829,7 @@ static int rio_handle_rx_immediate(struct pcs_rdmaio *rio, char *buf, int len,<o:p></o:p></p>
<p class="MsoNormal"> u32 msg_size;<o:p></o:p></p>
<p class="MsoNormal"> int offset = rio->hdr_size;<o:p></o:p></p>
<p class="MsoNormal"> struct iov_iter it;<o:p></o:p></p>
<p class="MsoNormal">+ struct rio_rdma_read_job *job;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"> if (rio->throttled) {<o:p></o:p></p>
<p class="MsoNormal"> *throttle = 1;<o:p></o:p></p>
<p class="MsoNormal">@@ -820,6 +852,19 @@ static int rio_handle_rx_immediate(struct pcs_rdmaio *rio, char *buf, int len,<o:p></o:p></p>
<p class="MsoNormal"> return err;<o:p></o:p></p>
<p class="MsoNormal"> } else if (msg == PCS_TRASH_MSG) {<o:p></o:p></p>
<p class="MsoNormal"> TRACE("rio drop trash msg: %u, rio: 0x%p\n", msg_size, rio);<o:p></o:p></p>
<p class="MsoNormal">+ /*<o:p></o:p></p>
<p class="MsoNormal">+ * We must Ack every RDMA_READ_REQ received from our peer in order even it's going to be dropped.<o:p></o:p></p>
<p class="MsoNormal">+ * Missing ack will result in out of order ACK to our peer, which will cause it to crash.<o:p></o:p></p>
<p class="MsoNormal">+ * So we setup a job to ack this msg however it can only be sent out after all ongoing RDMA READ<o:p></o:p></p>
<p class="MsoNormal">+ * completes and will block future RDMA READ being issued.<o:p></o:p></p>
<p class="MsoNormal">+ */<o:p></o:p></p>
<p class="MsoNormal">+ if (rb) {<o:p></o:p></p>
<p class="MsoNormal">+ job = rio_rdma_read_job_alloc(rio, msg, 0, rb);<o:p></o:p></p>
<p class="MsoNormal">+ if (!job)<o:p></o:p></p>
<p class="MsoNormal">+ return PCS_ERR_NOMEM;<o:p></o:p></p>
<p class="MsoNormal">+ rio_post_tx_job(rio, &job->job);<o:p></o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ack_pending++;<o:p></o:p></p>
<p class="MsoNormal">+ }<o:p></o:p></p>
<p class="MsoNormal"> return 0;<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">@@ -852,12 +897,10 @@ static int rio_handle_rx_immediate(struct pcs_rdmaio *rio, char *buf, int len,<o:p></o:p></p>
<p class="MsoNormal"> if (len == msg->size) {<o:p></o:p></p>
<p class="MsoNormal"> msg->done(msg);<o:p></o:p></p>
<p class="MsoNormal"> } else if (rio_submit_rdma_read(rio, msg, offset, rb, true) == -EAGAIN) {<o:p></o:p></p>
<p class="MsoNormal">- struct rio_rdma_read_job *job;<o:p></o:p></p>
<p class="MsoNormal"> job = rio_rdma_read_job_alloc(rio, msg, offset, rb);<o:p></o:p></p>
<p class="MsoNormal"> if (!job)<o:p></o:p></p>
<p class="MsoNormal">- rio_submit_rdma_read(rio, msg, offset, rb, false);<o:p></o:p></p>
<p class="MsoNormal">- else<o:p></o:p></p>
<p class="MsoNormal">- rio_post_tx_job(rio, &job->job);<o:p></o:p></p>
<p class="MsoNormal">+ return PCS_ERR_NOMEM;<o:p></o:p></p>
<p class="MsoNormal">+ rio_post_tx_job(rio, &job->job);<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"> return 0;<o:p></o:p></p>
<p class="MsoNormal">@@ -1228,6 +1271,9 @@ struct pcs_rdmaio* pcs_rdma_create(int hdr_size, struct rdma_cm_id *cmid,<o:p></o:p></p>
<p class="MsoNormal"> rio->n_os_credits = 0;<o:p></o:p></p>
<p class="MsoNormal"> rio->n_th_credits = queue_depth / 2;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ongoing = 0;<o:p></o:p></p>
<p class="MsoNormal">+ rio->n_rdma_read_ack_pending = 0;<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal"> rio->cmid = cmid;<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"> INIT_LIST_HEAD(&rio->write_queue);<o:p></o:p></p>
<p class="MsoNormal">diff --git a/fs/fuse/kio/pcs/pcs_rdma_io.h b/fs/fuse/kio/pcs/pcs_rdma_io.h<o:p></o:p></p>
<p class="MsoNormal">index 18962208e4a2..c5109cbc5fe1 100644<o:p></o:p></p>
<p class="MsoNormal">--- a/fs/fuse/kio/pcs/pcs_rdma_io.h<o:p></o:p></p>
<p class="MsoNormal">+++ b/fs/fuse/kio/pcs/pcs_rdma_io.h<o:p></o:p></p>
<p class="MsoNormal">@@ -90,6 +90,9 @@ struct pcs_rdmaio<o:p></o:p></p>
<p class="MsoNormal"> int n_th_credits; /* threshold: when to return outstanding<o:p></o:p></p>
<p class="MsoNormal"> * credits urgently */<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">+ int n_rdma_read_ongoing; /* number of ongoing RDMA_READ. */<o:p></o:p></p>
<p class="MsoNormal">+ int n_rdma_read_ack_pending; /* number of RDMA_READ_ACK to be submitted */<o:p></o:p></p>
<p class="MsoNormal">+<o:p></o:p></p>
<p class="MsoNormal"> struct pcs_rdma_device *dev;<o:p></o:p></p>
<p class="MsoNormal"> struct rdma_cm_id *cmid;<o:p></o:p></p>
<p class="MsoNormal"> struct ib_cq *cq;<o:p></o:p></p>
<p class="MsoNormal">--<o:p></o:p></p>
<p class="MsoNormal">2.32.0 (Apple Git-132)<o:p></o:p></p>
</div>
</body>
</html>