]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
RDMA/sa_query: Support IB service records resolution
authorMark Zhang <markzhang@nvidia.com>
Mon, 30 Jun 2025 10:52:32 +0000 (13:52 +0300)
committerLeon Romanovsky <leon@kernel.org>
Wed, 13 Aug 2025 10:15:56 +0000 (06:15 -0400)
Add an SA query API ib_sa_service_rec_get() to support building and
sending SA query MADs that ask for service records with a specific
name or ID, and receiving and parsing responses from the SM.

Signed-off-by: Or Har-Toov <ohartoov@nvidia.com>
Signed-off-by: Mark Zhang <markzhang@nvidia.com>
Reviewed-by: Vlad Dumitrescu <vdumitrescu@nvidia.com>
Link: https://patch.msgid.link/9af6c82f3a3a9d975115a33235fb4ffc7c8edb21.1751279793.git.leonro@nvidia.com
Signed-off-by: Leon Romanovsky <leon@kernel.org>
drivers/infiniband/core/sa_query.c
include/rdma/ib_mad.h
include/rdma/ib_sa.h

index 770e9f18349bb9a997af2171389d0bb7fd2bb496..c0a7af1b4fe4b1638d23b7d74daa6827a49c78f9 100644 (file)
@@ -152,6 +152,13 @@ struct ib_sa_mcmember_query {
        struct ib_sa_query sa_query;
 };
 
+struct ib_sa_service_query {
+       void (*callback)(int status, struct sa_service_rec *rec,
+                        unsigned int num_services, void *context);
+       void *context;
+       struct ib_sa_query sa_query;
+};
+
 static LIST_HEAD(ib_nl_request_list);
 static DEFINE_SPINLOCK(ib_nl_request_lock);
 static atomic_t ib_nl_sa_request_seq;
@@ -686,6 +693,58 @@ static const struct ib_field guidinfo_rec_table[] = {
          .size_bits    = 512 },
 };
 
+#define SERVICE_REC_FIELD(field) \
+       .struct_offset_bytes = offsetof(struct sa_service_rec, field),     \
+       .struct_size_bytes   = sizeof_field(struct sa_service_rec, field), \
+       .field_name          = "sa_service_rec:" #field
+
+static const struct ib_field service_rec_table[] = {
+       { SERVICE_REC_FIELD(id),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 64 },
+       { SERVICE_REC_FIELD(gid),
+         .offset_words = 2,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { SERVICE_REC_FIELD(pkey),
+         .offset_words = 6,
+         .offset_bits  = 0,
+         .size_bits    = 16 },
+       { RESERVED,
+         .offset_words = 6,
+         .offset_bits  = 16,
+         .size_bits    = 16 },
+       { SERVICE_REC_FIELD(lease),
+         .offset_words = 7,
+         .offset_bits  = 0,
+         .size_bits    = 32 },
+       { SERVICE_REC_FIELD(key),
+         .offset_words = 8,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { SERVICE_REC_FIELD(name),
+         .offset_words = 12,
+         .offset_bits  = 0,
+         .size_bits    = 512 },
+       { SERVICE_REC_FIELD(data_8),
+         .offset_words = 28,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { SERVICE_REC_FIELD(data_16),
+         .offset_words = 32,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { SERVICE_REC_FIELD(data_32),
+         .offset_words = 36,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { SERVICE_REC_FIELD(data_64),
+         .offset_words = 40,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+};
+
 #define RDMA_PRIMARY_PATH_MAX_REC_NUM 3
 
 static inline void ib_sa_disable_local_svc(struct ib_sa_query *query)
@@ -1392,6 +1451,20 @@ void ib_sa_pack_path(struct sa_path_rec *rec, void *attribute)
 }
 EXPORT_SYMBOL(ib_sa_pack_path);
 
+void ib_sa_pack_service(struct sa_service_rec *rec, void *attribute)
+{
+       ib_pack(service_rec_table, ARRAY_SIZE(service_rec_table), rec,
+               attribute);
+}
+EXPORT_SYMBOL(ib_sa_pack_service);
+
+void ib_sa_unpack_service(void *attribute, struct sa_service_rec *rec)
+{
+       ib_unpack(service_rec_table, ARRAY_SIZE(service_rec_table), attribute,
+                 rec);
+}
+EXPORT_SYMBOL(ib_sa_unpack_service);
+
 static bool ib_sa_opa_pathrecord_support(struct ib_sa_client *client,
                                         struct ib_sa_device *sa_dev,
                                         u32 port_num)
@@ -1481,6 +1554,68 @@ static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query,
        }
 }
 
+#define IB_SA_DATA_OFFS 56
+#define IB_SERVICE_REC_SZ 176
+
+static void ib_unpack_service_rmpp(struct sa_service_rec *rec,
+                                  struct ib_mad_recv_wc *mad_wc,
+                                  int num_services)
+{
+       unsigned int cp_sz, data_i, data_size, rec_i = 0, buf_i = 0;
+       struct ib_mad_recv_buf *mad_buf;
+       u8 buf[IB_SERVICE_REC_SZ];
+       u8 *data;
+
+       data_size = sizeof(((struct ib_sa_mad *) mad_buf->mad)->data);
+
+       list_for_each_entry(mad_buf, &mad_wc->rmpp_list, list) {
+               data = ((struct ib_sa_mad *) mad_buf->mad)->data;
+               data_i = 0;
+               while (data_i < data_size && rec_i < num_services) {
+                       cp_sz = min(IB_SERVICE_REC_SZ - buf_i,
+                                   data_size - data_i);
+                       memcpy(buf + buf_i, data + data_i, cp_sz);
+                       data_i += cp_sz;
+                       buf_i += cp_sz;
+                       if (buf_i == IB_SERVICE_REC_SZ) {
+                               ib_sa_unpack_service(buf, rec + rec_i);
+                               buf_i = 0;
+                               rec_i++;
+                       }
+               }
+       }
+}
+
+static void ib_sa_service_rec_callback(struct ib_sa_query *sa_query, int status,
+                                      struct ib_mad_recv_wc *mad_wc)
+{
+       struct ib_sa_service_query *query =
+               container_of(sa_query, struct ib_sa_service_query, sa_query);
+       struct sa_service_rec *rec;
+       int num_services;
+
+       if (!mad_wc || !mad_wc->recv_buf.mad) {
+               query->callback(status, NULL, 0, query->context);
+               return;
+       }
+
+       num_services = (mad_wc->mad_len - IB_SA_DATA_OFFS) / IB_SERVICE_REC_SZ;
+       if (!num_services) {
+               query->callback(-ENODATA, NULL, 0, query->context);
+               return;
+       }
+
+       rec = kmalloc_array(num_services, sizeof(*rec), GFP_KERNEL);
+       if (!rec) {
+               query->callback(-ENOMEM, NULL, 0, query->context);
+               return;
+       }
+
+       ib_unpack_service_rmpp(rec, mad_wc, num_services);
+       query->callback(status, rec, num_services, query->context);
+       kfree(rec);
+}
+
 static void ib_sa_path_rec_release(struct ib_sa_query *sa_query)
 {
        struct ib_sa_path_query *query =
@@ -1490,6 +1625,14 @@ static void ib_sa_path_rec_release(struct ib_sa_query *sa_query)
        kfree(query);
 }
 
+static void ib_sa_service_rec_release(struct ib_sa_query *sa_query)
+{
+       struct ib_sa_service_query *query =
+               container_of(sa_query, struct ib_sa_service_query, sa_query);
+
+       kfree(query);
+}
+
 /**
  * ib_sa_path_rec_get - Start a Path get query
  * @client:SA client
@@ -1620,6 +1763,101 @@ err1:
 }
 EXPORT_SYMBOL(ib_sa_path_rec_get);
 
+/**
+ * ib_sa_service_rec_get - Start a Service get query
+ * @client: SA client
+ * @device: device to send query on
+ * @port_num: port number to send query on
+ * @rec: Service Record to send in query
+ * @comp_mask: component mask to send in query
+ * @timeout_ms: time to wait for response
+ * @gfp_mask: GFP mask to use for internal allocations
+ * @callback: function called when query completes, times out or is
+ * canceled
+ * @context: opaque user context passed to callback
+ * @sa_query: query context, used to cancel query
+ *
+ * Send a Service Record Get query to the SA to look up a path.  The
+ * callback function will be called when the query completes (or
+ * fails); status is 0 for a successful response, -EINTR if the query
+ * is canceled, -ETIMEDOUT is the query timed out, or -EIO if an error
+ * occurred sending the query.  The resp parameter of the callback is
+ * only valid if status is 0.
+ *
+ * If the return value of ib_sa_service_rec_get() is negative, it is an
+ * error code. Otherwise it is a query ID that can be used to cancel
+ * the query.
+ */
+int ib_sa_service_rec_get(struct ib_sa_client *client,
+                         struct ib_device *device, u32 port_num,
+                         struct sa_service_rec *rec,
+                         ib_sa_comp_mask comp_mask,
+                         unsigned long timeout_ms, gfp_t gfp_mask,
+                         void (*callback)(int status,
+                                          struct sa_service_rec *resp,
+                                          unsigned int num_services,
+                                          void *context),
+                         void *context, struct ib_sa_query **sa_query)
+{
+       struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
+       struct ib_sa_service_query *query;
+       struct ib_mad_agent *agent;
+       struct ib_sa_port   *port;
+       struct ib_sa_mad *mad;
+       int ret;
+
+       if (!sa_dev)
+               return -ENODEV;
+
+       port = &sa_dev->port[port_num - sa_dev->start_port];
+       agent = port->agent;
+
+       query = kzalloc(sizeof(*query), gfp_mask);
+       if (!query)
+               return -ENOMEM;
+
+       query->sa_query.port = port;
+
+       ret = alloc_mad(&query->sa_query, gfp_mask);
+       if (ret)
+               goto err1;
+
+       ib_sa_client_get(client);
+       query->sa_query.client = client;
+       query->callback        = callback;
+       query->context         = context;
+
+       mad = query->sa_query.mad_buf->mad;
+       init_mad(&query->sa_query, agent);
+
+       query->sa_query.rmpp_callback = callback ? ib_sa_service_rec_callback :
+               NULL;
+       query->sa_query.release = ib_sa_service_rec_release;
+       mad->mad_hdr.method     = IB_MGMT_METHOD_GET_TABLE;
+       mad->mad_hdr.attr_id    = cpu_to_be16(IB_SA_ATTR_SERVICE_REC);
+       mad->sa_hdr.comp_mask   = comp_mask;
+
+       ib_sa_pack_service(rec, mad->data);
+
+       *sa_query = &query->sa_query;
+       query->sa_query.mad_buf->context[1] = rec;
+
+       ret = send_mad(&query->sa_query, timeout_ms, gfp_mask);
+       if (ret < 0)
+               goto err2;
+
+       return ret;
+
+err2:
+       *sa_query = NULL;
+       ib_sa_client_put(query->sa_query.client);
+       free_mad(&query->sa_query);
+err1:
+       kfree(query);
+       return ret;
+}
+EXPORT_SYMBOL(ib_sa_service_rec_get);
+
 static void ib_sa_mcmember_rec_callback(struct ib_sa_query *sa_query,
                                        int status, struct ib_sa_mad *mad)
 {
index 3f1b58d8b4bf4fbcdc10c92849fe822ee97e9782..8bd0e1eb393b2f76bcf64dbb515a4d92c28f5246 100644 (file)
@@ -48,6 +48,7 @@
 #define IB_MGMT_METHOD_REPORT                  0x06
 #define IB_MGMT_METHOD_REPORT_RESP             0x86
 #define IB_MGMT_METHOD_TRAP_REPRESS            0x07
+#define IB_MGMT_METHOD_GET_TABLE               0x12
 
 #define IB_MGMT_METHOD_RESP                    0x80
 #define IB_BM_ATTR_MOD_RESP                    cpu_to_be32(1)
index b46353fc53bf41447ad4c687f74a66685cfdf21f..95e8924ad56385f116a80dc2e927c558dfec9b8c 100644 (file)
@@ -189,6 +189,20 @@ struct sa_path_rec {
        u32 flags;
 };
 
+struct sa_service_rec {
+       __be64 id;
+       __u8 gid[16];
+       __be16 pkey;
+       __u8 reserved[2];
+       __be32 lease;
+       __u8 key[16];
+       __u8 name[64];
+       __u8 data_8[16];
+       __be16 data_16[8];
+       __be32 data_32[4];
+       __be64 data_64[2];
+};
+
 static inline enum ib_gid_type
                sa_conv_pathrec_to_gid_type(struct sa_path_rec *rec)
 {
@@ -417,6 +431,17 @@ int ib_sa_path_rec_get(struct ib_sa_client *client, struct ib_device *device,
                                        unsigned int num_prs, void *context),
                       void *context, struct ib_sa_query **query);
 
+int ib_sa_service_rec_get(struct ib_sa_client *client,
+                         struct ib_device *device, u32 port_num,
+                         struct sa_service_rec *rec,
+                         ib_sa_comp_mask comp_mask,
+                         unsigned long timeout_ms, gfp_t gfp_mask,
+                         void (*callback)(int status,
+                                          struct sa_service_rec *resp,
+                                          unsigned int num_services,
+                                          void *context),
+                         void *context, struct ib_sa_query **sa_query);
+
 struct ib_sa_multicast {
        struct ib_sa_mcmember_rec rec;
        ib_sa_comp_mask         comp_mask;
@@ -508,6 +533,18 @@ int ib_init_ah_attr_from_path(struct ib_device *device, u32 port_num,
  */
 void ib_sa_pack_path(struct sa_path_rec *rec, void *attribute);
 
+/**
+ * ib_sa_pack_service - Convert a service record from struct ib_sa_service_rec
+ * to IB MAD wire format.
+ */
+void ib_sa_pack_service(struct sa_service_rec *rec, void *attribute);
+
+/**
+ * ib_sa_unpack_service - Convert a service record from MAD format to struct
+ * ib_sa_service_rec.
+ */
+void ib_sa_unpack_service(void *attribute, struct sa_service_rec *rec);
+
 /**
  * ib_sa_unpack_path - Convert a path record from MAD format to struct
  * ib_sa_path_rec.