libibverbs: Add ibv_reg_buf_mr() for provider-aware buffers

Add ibv_reg_buf_mr() to register memory returned by ibv_alloc_buf().

Implement it as a thin wrapper: set IBV_REG_MR_MASK_BUF and forward the
buffer handle to ibv_reg_mr_ex(), which resolves it to the appropriate
addr-based or DMA-buf-based registration and validates the range against
the stored buffer metadata. Keep it equivalent to calling
ibv_reg_mr_ex() with IBV_REG_MR_MASK_BUF.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
diff --git a/debian/libibverbs1.symbols b/debian/libibverbs1.symbols
index 509383b..5378bd8 100644
--- a/debian/libibverbs1.symbols
+++ b/debian/libibverbs1.symbols
@@ -114,6 +114,7 @@
  ibv_rate_to_mbps@IBVERBS_1.1 1.1.8
  ibv_rate_to_mult@IBVERBS_1.0 1.1.6
  ibv_read_sysfs_file@IBVERBS_1.0 1.1.6
+ ibv_reg_buf_mr@IBVERBS_1.17 64
  ibv_reg_dmabuf_mr@IBVERBS_1.12 34
  ibv_reg_mr@IBVERBS_1.0 1.1.6
  ibv_reg_mr@IBVERBS_1.1 1.1.6
diff --git a/libibverbs/libibverbs.map.in b/libibverbs/libibverbs.map.in
index d821124..300240b 100644
--- a/libibverbs/libibverbs.map.in
+++ b/libibverbs/libibverbs.map.in
@@ -181,6 +181,7 @@
 	global:
 		ibv_alloc_buf;
 		ibv_free_buf;
+		ibv_reg_buf_mr;
 } IBVERBS_1.16;
 
 /* If any symbols in this stanza change ABI then the entire staza gets a new symbol
diff --git a/libibverbs/man/CMakeLists.txt b/libibverbs/man/CMakeLists.txt
index 1711807..f0e2e7e 100644
--- a/libibverbs/man/CMakeLists.txt
+++ b/libibverbs/man/CMakeLists.txt
@@ -88,6 +88,7 @@
   )
 rdma_alias_man_pages(
   ibv_alloc_buf.3 ibv_free_buf.3
+  ibv_alloc_buf.3 ibv_reg_buf_mr.3
   ibv_alloc_dm.3 ibv_free_dm.3
   ibv_alloc_dm.3 ibv_reg_dm_mr.3
   ibv_alloc_dm.3 ibv_memcpy_to_dm.3
diff --git a/libibverbs/man/ibv_alloc_buf.3.md b/libibverbs/man/ibv_alloc_buf.3.md
index 310e9af..021c53f 100644
--- a/libibverbs/man/ibv_alloc_buf.3.md
+++ b/libibverbs/man/ibv_alloc_buf.3.md
@@ -10,7 +10,7 @@
 
 # NAME
 
-ibv_alloc_buf, ibv_free_buf - allocate and free provider-aware buffers
+ibv_alloc_buf, ibv_free_buf, ibv_reg_buf_mr - allocate provider-aware buffers and register them as memory regions
 
 # SYNOPSIS
 
@@ -20,6 +20,9 @@
 void *ibv_alloc_buf(struct ibv_pd *pd, size_t size, struct ibv_buf **buf);
 
 void ibv_free_buf(struct ibv_buf *buf);
+
+struct ibv_mr *ibv_reg_buf_mr(struct ibv_pd *pd, struct ibv_buf *buf, void *addr,
+                              size_t length, int access);
 ```
 
 # DESCRIPTION
@@ -27,22 +30,61 @@
 **ibv_alloc_buf()** allocates a buffer using the allocation method selected by
 the provider for the protection domain *pd*. On success it returns the mapped
 address and stores an opaque buffer handle in *buf*. The handle is used by
-**ibv_free_buf()** and must not be interpreted by applications.
+**ibv_free_buf()**, **ibv_reg_buf_mr()**, and **ibv_reg_mr_ex()** with
+**IBV_REG_MR_MASK_BUF**, and must not be interpreted by applications.
+
+**ibv_free_buf()** releases a buffer handle returned by **ibv_alloc_buf()**. The
+protection domain used for allocation must remain valid until the buffer is
+freed.
+
+**ibv_reg_buf_mr()** registers a memory region for a buffer returned by
+**ibv_alloc_buf()**. Applications can register the same buffer through
+**ibv_reg_mr_ex()** by setting both **IBV_REG_MR_MASK_BUF** and
+**IBV_REG_MR_MASK_ADDR**, passing the buffer handle in `mr_init_attr->buf` and
+the address to register in `mr_init_attr->addr`. When **IBV_REG_MR_MASK_BUF**
+is set the caller must not set **IBV_REG_MR_MASK_FD** or
+**IBV_REG_MR_MASK_FD_OFFSET**, and, for a DMA-buf backed buffer, must not set
+**IBV_REG_MR_MASK_IOVA**; libibverbs derives these from the buffer handle and
+otherwise fails with **EINVAL**. The *pd* argument must be
+the same protection domain that was used to allocate the buffer.
+If a different protection domain is supplied, registration fails
+with **EINVAL**. For ordinary memory it behaves like **ibv_reg_mr()**.
+For provider allocations backed by a DMA-buf, it registers
+the corresponding DMA-buf range using the metadata stored in
+the opaque *buf* handle.
 
 # ARGUMENTS
 
 *pd*
-:	The protection domain (or parent domain) to allocate from; its provider selects the buffer's backing allocation method. It must remain valid until the buffer is freed with **ibv_free_buf()**.
+:	For **ibv_alloc_buf()**, the protection domain (or parent domain) to allocate from; its provider selects the buffer's backing allocation method. It must remain valid until the buffer is freed with **ibv_free_buf()**. For **ibv_reg_buf_mr()**, the same protection domain that allocated *buf* (otherwise registration fails with **EINVAL**).
 
 *size*
-:	Size of the buffer to allocate, in bytes.
+:	Size of the buffer to allocate, in bytes (**ibv_alloc_buf()**).
 
 *buf*
-:	For **ibv_alloc_buf()**, an output parameter set on success to an opaque buffer handle. For **ibv_free_buf()**, the buffer handle returned by **ibv_alloc_buf()** that is to be released.
+:	For **ibv_alloc_buf()**, an output parameter set on success to an opaque buffer handle. For **ibv_free_buf()** and **ibv_reg_buf_mr()**, the buffer handle returned by **ibv_alloc_buf()** to be released or registered, respectively.
+
+*addr*
+:	The start address to register (**ibv_reg_buf_mr()**): the buffer base returned by **ibv_alloc_buf()** or an address within that buffer.
+
+*length*
+:	Length in bytes to register (**ibv_reg_buf_mr()**); *addr* + *length* must stay within the buffer.
+
+*access*
+:	Access flags for the memory region (**ibv_reg_buf_mr()**), the same as for **ibv_reg_mr()**.
 
 # RETURN VALUE
 
 **ibv_alloc_buf()** returns the mapped buffer address on success, or NULL if the
 request fails.
 
+**ibv_reg_buf_mr()** returns a pointer to the registered MR on success, or NULL
+if the request fails.
+
 **ibv_free_buf()** does not return a value.
+
+# SEE ALSO
+
+**ibv_reg_mr**(3),
+**ibv_reg_mr_ex**(3),
+**ibv_reg_dmabuf_mr**(3)
diff --git a/libibverbs/verbs.c b/libibverbs/verbs.c
index 8e5244d..0565be2 100644
--- a/libibverbs/verbs.c
+++ b/libibverbs/verbs.c
@@ -440,6 +440,20 @@
 	return get_ops(pd->context)->alloc_buf(pd, size, buf);
 }
 
+struct ibv_mr *ibv_reg_buf_mr(struct ibv_pd *pd, struct ibv_buf *buf,
+			      void *addr, size_t length, int access)
+{
+	struct ibv_mr_init_attr mr_init_attr = {
+		.length = length,
+		.access = access,
+		.comp_mask = IBV_REG_MR_MASK_BUF | IBV_REG_MR_MASK_ADDR,
+		.addr = addr,
+		.buf = buf,
+	};
+
+	return ibv_reg_mr_ex(pd, &mr_init_attr);
+}
+
 void ibv_free_buf(struct ibv_buf *buf)
 {
 	get_ops(buf->pd->context)->free_buf(buf);
diff --git a/libibverbs/verbs.h b/libibverbs/verbs.h
index 9a90f21..67832e4 100644
--- a/libibverbs/verbs.h
+++ b/libibverbs/verbs.h
@@ -3208,6 +3208,9 @@
  * @size: Buffer size in bytes
  * @buf: On success, set to a handle for ibv_free_buf()
  *
+ * The returned address is at least page aligned, so it can be registered
+ * with ibv_reg_buf_mr() regardless of how the buffer is backed.
+ *
  * Returns the usable buffer address on success, or NULL on failure with
  * errno set.
  */
@@ -3220,6 +3223,20 @@
 void ibv_free_buf(struct ibv_buf *buf);
 
 /**
+ * ibv_reg_buf_mr - Register an MR for a buffer from ibv_alloc_buf
+ * @pd: Protection domain
+ * @buf: Handle from ibv_alloc_buf()
+ * @addr: Start address to register; the buffer base returned by
+ *        ibv_alloc_buf() or an address within that buffer
+ * @length: Length in bytes; @addr + @length must stay within the buffer
+ * @access: Access flags (IBV_ACCESS_*)
+ *
+ * Returns the registered MR on success, or NULL on failure with errno set.
+ */
+struct ibv_mr *ibv_reg_buf_mr(struct ibv_pd *pd, struct ibv_buf *buf,
+			      void *addr, size_t length, int access);
+
+/**
  * ibv_alloc_dmah - Allocate a dma handle
  */
 struct ibv_dmah *ibv_alloc_dmah(struct ibv_context *context,