---
date: 2018-11-27
footer: libibverbs
header: "Libibverbs Programmer's Manual"
layout: page
license: 'Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md'
section: 3
title: IBV_WR API
---

# NAME

ibv_wr_abort, ibv_wr_complete, ibv_wr_start - Manage regions allowed to post work

ibv_wr_atomic_cmp_swp, ibv_wr_atomic_fetch_add - Post remote atomic operation work requests

ibv_wr_bind_mw, ibv_wr_local_inv - Post work requests for memory windows

ibv_wr_rdma_read, ibv_wr_rdma_write, ibv_wr_rdma_write_imm, ibv_wr_flush - Post RDMA work requests

ibv_wr_send, ibv_wr_send_imm, ibv_wr_send_inv - Post send work requests

ibv_wr_send_tso - Post segmentation offload work requests

ibv_wr_set_inline_data, ibv_wr_set_inline_data_list - Attach inline data to the last work request

ibv_wr_set_sge, ibv_wr_set_sge_list - Attach data to the last work request

ibv_wr_set_ud_addr - Attach UD addressing info to the last work request

ibv_wr_set_xrc_srqn - Attach an XRC SRQN to the last work request

# SYNOPSIS

```c
#include <infiniband/verbs.h>

void ibv_wr_abort(struct ibv_qp_ex *qp);
int ibv_wr_complete(struct ibv_qp_ex *qp);
void ibv_wr_start(struct ibv_qp_ex *qp);

void ibv_wr_atomic_cmp_swp(struct ibv_qp_ex *qp, uint32_t rkey,
                           uint64_t remote_addr, uint64_t compare,
                           uint64_t swap);
void ibv_wr_atomic_fetch_add(struct ibv_qp_ex *qp, uint32_t rkey,
                             uint64_t remote_addr, uint64_t add);

void ibv_wr_bind_mw(struct ibv_qp_ex *qp, struct ibv_mw *mw, uint32_t rkey,
                    const struct ibv_mw_bind_info *bind_info);
void ibv_wr_local_inv(struct ibv_qp_ex *qp, uint32_t invalidate_rkey);

void ibv_wr_rdma_read(struct ibv_qp_ex *qp, uint32_t rkey,
                      uint64_t remote_addr);
void ibv_wr_rdma_write(struct ibv_qp_ex *qp, uint32_t rkey,
                       uint64_t remote_addr);
void ibv_wr_rdma_write_imm(struct ibv_qp_ex *qp, uint32_t rkey,
                           uint64_t remote_addr, __be32 imm_data);

void ibv_wr_send(struct ibv_qp_ex *qp);
void ibv_wr_send_imm(struct ibv_qp_ex *qp, __be32 imm_data);
void ibv_wr_send_inv(struct ibv_qp_ex *qp, uint32_t invalidate_rkey);
void ibv_wr_send_tso(struct ibv_qp_ex *qp, void *hdr, uint16_t hdr_sz,
                     uint16_t mss);

void ibv_wr_set_inline_data(struct ibv_qp_ex *qp, void *addr, size_t length);
void ibv_wr_set_inline_data_list(struct ibv_qp_ex *qp, size_t num_buf,
                                 const struct ibv_data_buf *buf_list);
void ibv_wr_set_sge(struct ibv_qp_ex *qp, uint32_t lkey, uint64_t addr,
                    uint32_t length);
void ibv_wr_set_sge_list(struct ibv_qp_ex *qp, size_t num_sge,
                         const struct ibv_sge *sg_list);

void ibv_wr_set_ud_addr(struct ibv_qp_ex *qp, struct ibv_ah *ah,
                        uint32_t remote_qpn, uint32_t remote_qkey);
void ibv_wr_set_xrc_srqn(struct ibv_qp_ex *qp, uint32_t remote_srqn);
void ibv_wr_flush(struct ibv_qp_ex *qp, uint32_t rkey, uint64_t remote_addr,
                  size_t len, uint8_t type, uint8_t level);
```

# DESCRIPTION

The verbs work request API (ibv_wr_\*) allows efficient posting of work to a send
queue using function calls instead of the struct based *ibv_post_send()*
scheme. This approach is designed to minimize CPU branching and locking during
the posting process.

This API is intended to be used to access additional functionality beyond
what is provided by *ibv_post_send()*.

WRs batches of *ibv_post_send()* and this API WRs batches can interleave
together just if they are not posted within the critical region of each other.
(A critical region in this API formed by *ibv_wr_start()* and
*ibv_wr_complete()*/*ibv_wr_abort()*)

# USAGE

To use these APIs the QP must be created using ibv_create_qp_ex() which allows
setting the **IBV_QP_INIT_ATTR_SEND_OPS_FLAGS** in *comp_mask*. The
*send_ops_flags* should be set to the OR of the work request types that will
be posted to the QP.

If the QP does not support all the requested work request types then QP
creation will fail.

Posting work requests to the QP is done within the critical region formed by
*ibv_wr_start()* and *ibv_wr_complete()*/*ibv_wr_abort()* (see CONCURRENCY below).

Each work request is created by calling a WR builder function (see the table
column WR builder below) to start creating the work request, followed by
allowed/required setter functions described below.

The WR builder and setter combination can be called multiple times to
efficiently post multiple work requests within a single critical region.

Each WR builder will use the *wr_id* member of *struct ibv_qp_ex* to set the
value to be returned in the completion. Some operations will also use the
*wr_flags* member to influence operation (see Flags below). These values
should be set before invoking the WR builder function.

For example a simple send could be formed as follows:

```C
qpx->wr_id = 1;
ibv_wr_send(qpx);
ibv_wr_set_sge(qpx, lkey, &data, sizeof(data));
```

The section WORK REQUESTS describes the various WR builders and setters in
details.

Posting work is completed by calling *ibv_wr_complete()* or *ibv_wr_abort()*.
No work is executed to the queue until *ibv_wr_complete()* returns
success. *ibv_wr_abort()* will discard all work prepared since *ibv_wr_start()*.

# WORK REQUESTS

Many of the operations match the opcodes available for *ibv_post_send()*. Each
operation has a WR builder function, a list of allowed setters, and a flag bit
to request the operation with *send_ops_flags* in *struct
ibv_qp_init_attr_ex* (see the EXAMPLE below).

| Operation            | WR builder                | QP Type Supported                | setters  |
|----------------------|---------------------------|----------------------------------|----------|
| ATOMIC_CMP_AND_SWP   | ibv_wr_atomic_cmp_swp()   | RC, XRC_SEND                     | DATA, QP |
| ATOMIC_FETCH_AND_ADD | ibv_wr_atomic_fetch_add() | RC, XRC_SEND                     | DATA, QP |
| BIND_MW              | ibv_wr_bind_mw()          | UC, RC, XRC_SEND                 | NONE     |
| LOCAL_INV            | ibv_wr_local_inv()        | UC, RC, XRC_SEND                 | NONE     |
| RDMA_READ            | ibv_wr_rdma_read()        | RC, XRC_SEND                     | DATA, QP |
| RDMA_WRITE           | ibv_wr_rdma_write()       | UC, RC, XRC_SEND                 | DATA, QP |
| FLUSH                | ibv_wr_flush()            | RC, RD, XRC_SEND                 | DATA, QP |
| RDMA_WRITE_WITH_IMM  | ibv_wr_rdma_write_imm()   | UC, RC, XRC_SEND                 | DATA, QP |
| SEND                 | ibv_wr_send()             | UD, UC, RC, XRC_SEND, RAW_PACKET | DATA, QP |
| SEND_WITH_IMM        | ibv_wr_send_imm()         | UD, UC, RC, SRC SEND             | DATA, QP |
| SEND_WITH_INV        | ibv_wr_send_inv()         | UC, RC, XRC_SEND                 | DATA, QP |
| TSO                  | ibv_wr_send_tso()         | UD, RAW_PACKET                   | DATA, QP |


## Atomic operations

Atomic operations are only atomic so long as all writes to memory go only
through the same RDMA hardware. It is not atomic with writes performed by the
CPU, or by other RDMA hardware in the system.

*ibv_wr_atomic_cmp_swp()*
:   If the remote 64 bit memory location specified by *rkey* and *remote_addr*
    equals *compare* then set it to *swap*.

*ibv_wr_atomic_fetch_add()*
:   Add *add* to the 64 bit memory location specified *rkey* and *remote_addr*.

## Memory Windows

Memory window type 2 operations (See man page for ibv_alloc_mw).

*ibv_wr_bind_mw()*
:   Bind a MW type 2 specified by **mw**, set a new **rkey** and set its
    properties by **bind_info**.

*ibv_wr_local_inv()*
:   Invalidate a MW type 2 which is associated with **rkey**.

## RDMA

*ibv_wr_rdma_read()*
:   Read from the remote memory location specified *rkey* and
    *remote_addr*. The number of bytes to read, and the local location to
    store the data, is determined by the DATA buffers set after this call.

*ibv_wr_rdma_write()*, *ibv_wr_rdma_write_imm()*
:   Write to the remote memory location specified *rkey* and
    *remote_addr*. The number of bytes to read, and the local location to get
    the data, is determined by the DATA buffers set after this call.

    The _imm version causes the remote side to get a IBV_WC_RECV_RDMA_WITH_IMM
    containing the 32 bits of immediate data.

## Message Send

*ibv_wr_send()*, *ibv_wr_send_imm()*
:   Send a message. The number of bytes to send, and the local location to get
    the data, is determined by the DATA buffers set after this call.

    The _imm version causes the remote side to get a IBV_WC_RECV_RDMA_WITH_IMM
    containing the 32 bits of immediate data.

*ibv_wr_send_inv()*
:   The data transfer is the same as for *ibv_wr_send()*, however the remote
    side will invalidate the MR specified by *invalidate_rkey* before
    delivering a completion.

*ibv_wr_send_tso()*
:   Produce multiple SEND messages using TCP Segmentation Offload. The SGE
    points to a TCP Stream buffer which will be segmented into
    MSS size SENDs. The hdr includes the entire network headers up to and
    including the TCP header and is prefixed before each segment.

## QP Specific setters

Certain QP types require each post to be accompanied by additional setters,
these setters are mandatory for any operation listing a QP setter in the above
table.

*UD* QPs
:   *ibv_wr_set_ud_addr()* must be called to set the destination address of
    the work.

*XRC_SEND* QPs
:   *ibv_wr_set_xrc_srqn()* must be called to set the destination SRQN field.

## DATA transfer setters

For work that requires to transfer data one of the following setters should
be called once after the WR builder:

*ibv_wr_set_sge()*
:   Transfer data to/from a single buffer given by the lkey, addr and
    length. This is equivalent to *ibv_wr_set_sge_list()* with a single
    element.

*ibv_wr_set_sge_list()*
:   Transfer data to/from a list of buffers, logically concatenated
    together. Each buffer is specified by an element in an array of *struct
    ibv_sge*.

Inline setters will copy the send data during the setter and allows the caller
to immediately re-use the buffer. This behavior is identical to the
IBV_SEND_INLINE flag. Generally this copy is done in a way that optimizes
SEND latency and is suitable for small messages. The provider will limit the
amount of data it can support in a single operation. This limit is requested
in the *max_inline_data* member of *struct ibv_qp_init_attr*. Valid only
for SEND and RDMA_WRITE.

*ibv_wr_set_inline_data()*
:   Copy send data from a single buffer given by the addr and length.
    This is equivalent to *ibv_wr_set_inline_data_list()* with a single
    element.

*ibv_wr_set_inline_data_list()*
:   Copy send data from a list of buffers, logically concatenated
    together. Each buffer is specified by an element in an array of *struct
    ibv_inl_data*.

## Flags

A bit mask of flags may be specified in *wr_flags* to control the behavior of
the work request.

**IBV_SEND_FENCE**
:   Do not start this work request until prior work has completed.

**IBV_SEND_IP_CSUM**
:   Offload the IPv4 and TCP/UDP checksum calculation

**IBV_SEND_SIGNALED**
:   A completion will be generated in the completion queue for the operation.

**IBV_SEND_SOLICITED**
:   Set the solicited bit in the RDMA packet. This informs the other side to
    generate a completion event upon receiving the RDMA operation.

# CONCURRENCY

The provider will provide locking to ensure that *ibv_wr_start()* and
*ibv_wr_complete()/abort()* form a per-QP critical section where no other
threads can enter.

If an *ibv_td* is provided during QP creation then no locking will be performed
and it is up to the caller to ensure that only one thread can be within the
critical region at a time.

# RETURN VALUE

Applications should use this API in a way that does not create failures. The
individual APIs do not return a failure indication to avoid branching.

If a failure is detected during operation, for instance due to an invalid
argument, then *ibv_wr_complete()* will return failure and the entire posting
will be aborted.

# EXAMPLE

```c
/* create RC QP type and specify the required send opcodes */
qp_init_attr_ex.qp_type = IBV_QPT_RC;
qp_init_attr_ex.comp_mask |= IBV_QP_INIT_ATTR_SEND_OPS_FLAGS;
qp_init_attr_ex.send_ops_flags |= IBV_QP_EX_WITH_RDMA_WRITE;
qp_init_attr_ex.send_ops_flags |= IBV_QP_EX_WITH_RDMA_WRITE_WITH_IMM;

ibv_qp *qp = ibv_create_qp_ex(ctx, qp_init_attr_ex);
ibv_qp_ex *qpx = ibv_qp_to_qp_ex(qp);

ibv_wr_start(qpx);

/* create 1st WRITE WR entry */
qpx->wr_id = my_wr_id_1;
ibv_wr_rdma_write(qpx, rkey, remote_addr_1);
ibv_wr_set_sge(qpx, lkey, local_addr_1, length_1);

/* create 2nd WRITE_WITH_IMM WR entry */
qpx->wr_id = my_wr_id_2;
qpx->wr_flags = IBV_SEND_SIGNALED;
ibv_wr_rdma_write_imm(qpx, rkey, remote_addr_2, htonl(0x1234));
ibv_set_wr_sge(qpx, lkey, local_addr_2, length_2);

/* Begin processing WRs */
ret = ibv_wr_complete(qpx);
```

# SEE ALSO

**ibv_post_send**(3), **ibv_create_qp_ex(3)**.

# AUTHOR

Jason Gunthorpe <jgg@mellanox.com>
Guy Levi <guyle@mellanox.com>
