libibverbs: Fall back to legacy ops in default reg_mr_ex

The default (dummy) reg_mr_ex op returns EOPNOTSUPP, so ibv_reg_mr_ex()
fails on any provider that does not implement its own reg_mr_ex,
even when the provider supports the equivalent legacy registration ops.

Translate the ibv_mr_init_attr request into the provider's existing
reg_mr() or reg_dmabuf_mr() op, selecting the dmabuf-based op when the
request carries an fd and the address-based op otherwise.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
diff --git a/libibverbs/dummy_ops.c b/libibverbs/dummy_ops.c
index 67aae0c..a27e229 100644
--- a/libibverbs/dummy_ops.c
+++ b/libibverbs/dummy_ops.c
@@ -508,8 +508,49 @@
 static struct ibv_mr *reg_mr_ex(struct ibv_pd *pd,
 				struct ibv_mr_init_attr *mr_init_attr)
 {
-	errno = EOPNOTSUPP;
-	return NULL;
+	const struct verbs_context_ops *ops = get_ops(pd->context);
+	uint64_t comp_mask = mr_init_attr->comp_mask;
+	uint64_t supported_mask = IBV_REG_MR_MASK_IOVA |
+				  IBV_REG_MR_MASK_ADDR |
+				  IBV_REG_MR_MASK_FD |
+				  IBV_REG_MR_MASK_FD_OFFSET;
+
+	/*
+	 * The provider has no reg_mr_ex op, so translate the request into one
+	 * of the legacy registration ops. Fork handling and the common mr
+	 * fields are taken care of by ibv_reg_mr_ex(). Anything the legacy ops
+	 * cannot express (e.g. a dma handle) is rejected.
+	 */
+	if (!check_comp_mask(comp_mask, supported_mask)) {
+		errno = EOPNOTSUPP;
+		return NULL;
+	}
+
+	if (comp_mask & IBV_REG_MR_MASK_FD) {
+		if ((comp_mask & IBV_REG_MR_MASK_ADDR) ||
+		    !(comp_mask & IBV_REG_MR_MASK_FD_OFFSET) ||
+		    !(comp_mask & IBV_REG_MR_MASK_IOVA)) {
+			errno = EINVAL;
+			return NULL;
+		}
+
+		return ops->reg_dmabuf_mr(pd, mr_init_attr->fd_offset,
+					  mr_init_attr->length,
+					  mr_init_attr->iova, mr_init_attr->fd,
+					  mr_init_attr->access);
+	}
+
+	if (!(comp_mask & IBV_REG_MR_MASK_ADDR) ||
+	    (comp_mask & IBV_REG_MR_MASK_FD_OFFSET)) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	return ops->reg_mr(pd, mr_init_attr->addr, mr_init_attr->length,
+			   (comp_mask & IBV_REG_MR_MASK_IOVA) ?
+				   mr_init_attr->iova :
+				   (uintptr_t)mr_init_attr->addr,
+			   mr_init_attr->access);
 }
 
 static struct ibv_mr *reg_dmabuf_mr(struct ibv_pd *pd, uint64_t offset,