/*
* Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2006 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
#include <linux/workqueue.h>
#include <rdma/ib_pack.h>
-#include <rdma/ib_sa.h>
#include <rdma/ib_cache.h>
+#include "sa.h"
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand subnet administration query support");
struct ib_sa_sm_ah {
struct ib_ah *ah;
struct kref ref;
+ u8 src_path_mask;
};
struct ib_sa_port {
struct ib_sa_query {
void (*callback)(struct ib_sa_query *, int, struct ib_sa_mad *);
void (*release)(struct ib_sa_query *);
+ struct ib_sa_client *client;
struct ib_sa_port *port;
struct ib_mad_send_buf *mad_buf;
struct ib_sa_sm_ah *sm_ah;
kfree(sm_ah);
}
-static void update_sm_ah(void *port_ptr)
+static void update_sm_ah(struct work_struct *work)
{
- struct ib_sa_port *port = port_ptr;
+ struct ib_sa_port *port =
+ container_of(work, struct ib_sa_port, update_task);
struct ib_sa_sm_ah *new_ah, *old_ah;
struct ib_port_attr port_attr;
struct ib_ah_attr ah_attr;
}
kref_init(&new_ah->ref);
+ new_ah->src_path_mask = (1 << port_attr.lmc) - 1;
memset(&ah_attr, 0, sizeof ah_attr);
ah_attr.dlid = port_attr.sm_lid;
}
}
+void ib_sa_register_client(struct ib_sa_client *client)
+{
+ atomic_set(&client->users, 1);
+ init_completion(&client->comp);
+}
+EXPORT_SYMBOL(ib_sa_register_client);
+
+void ib_sa_unregister_client(struct ib_sa_client *client)
+{
+ ib_sa_client_put(client);
+ wait_for_completion(&client->comp);
+}
+EXPORT_SYMBOL(ib_sa_unregister_client);
+
/**
* ib_sa_cancel_query - try to cancel an SA query
* @id:ID of query to cancel
}
EXPORT_SYMBOL(ib_sa_cancel_query);
+static u8 get_src_path_mask(struct ib_device *device, u8 port_num)
+{
+ struct ib_sa_device *sa_dev;
+ struct ib_sa_port *port;
+ unsigned long flags;
+ u8 src_path_mask;
+
+ sa_dev = ib_get_client_data(device, &sa_client);
+ if (!sa_dev)
+ return 0x7f;
+
+ port = &sa_dev->port[port_num - sa_dev->start_port];
+ spin_lock_irqsave(&port->ah_lock, flags);
+ src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f;
+ spin_unlock_irqrestore(&port->ah_lock, flags);
+
+ return src_path_mask;
+}
+
int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr)
{
memset(ah_attr, 0, sizeof *ah_attr);
ah_attr->dlid = be16_to_cpu(rec->dlid);
ah_attr->sl = rec->sl;
- ah_attr->src_path_bits = be16_to_cpu(rec->slid) & 0x7f;
+ ah_attr->src_path_bits = be16_to_cpu(rec->slid) &
+ get_src_path_mask(device, port_num);
ah_attr->port_num = port_num;
+ ah_attr->static_rate = rec->rate;
if (rec->hop_limit > 1) {
ah_attr->ah_flags = IB_AH_GRH;
/**
* ib_sa_path_rec_get - Start a Path get query
+ * @client:SA client
* @device:device to send query on
* @port_num: port number to send query on
* @rec:Path Record to send in query
* error code. Otherwise it is a query ID that can be used to cancel
* the query.
*/
-int ib_sa_path_rec_get(struct ib_device *device, u8 port_num,
+int ib_sa_path_rec_get(struct ib_sa_client *client,
+ struct ib_device *device, u8 port_num,
struct ib_sa_path_rec *rec,
ib_sa_comp_mask comp_mask,
int timeout_ms, gfp_t gfp_mask,
goto err1;
}
- query->callback = callback;
- query->context = context;
+ 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(mad, agent);
err2:
*sa_query = NULL;
+ ib_sa_client_put(query->sa_query.client);
ib_free_send_mad(query->sa_query.mad_buf);
err1:
/**
* ib_sa_service_rec_query - Start Service Record operation
+ * @client:SA client
* @device:device to send request on
* @port_num: port number to send request on
* @method:SA method - should be get, set, or delete
* error code. Otherwise it is a request ID that can be used to cancel
* the query.
*/
-int ib_sa_service_rec_query(struct ib_device *device, u8 port_num, u8 method,
+int ib_sa_service_rec_query(struct ib_sa_client *client,
+ struct ib_device *device, u8 port_num, u8 method,
struct ib_sa_service_rec *rec,
ib_sa_comp_mask comp_mask,
int timeout_ms, gfp_t gfp_mask,
goto err1;
}
- query->callback = callback;
- query->context = context;
+ 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(mad, agent);
err2:
*sa_query = NULL;
+ ib_sa_client_put(query->sa_query.client);
ib_free_send_mad(query->sa_query.mad_buf);
err1:
kfree(container_of(sa_query, struct ib_sa_mcmember_query, sa_query));
}
-int ib_sa_mcmember_rec_query(struct ib_device *device, u8 port_num,
+int ib_sa_mcmember_rec_query(struct ib_sa_client *client,
+ struct ib_device *device, u8 port_num,
u8 method,
struct ib_sa_mcmember_rec *rec,
ib_sa_comp_mask comp_mask,
goto err1;
}
- query->callback = callback;
- query->context = context;
+ 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(mad, agent);
err2:
*sa_query = NULL;
+ ib_sa_client_put(query->sa_query.client);
ib_free_send_mad(query->sa_query.mad_buf);
err1:
kfree(query);
return ret;
}
-EXPORT_SYMBOL(ib_sa_mcmember_rec_query);
static void send_handler(struct ib_mad_agent *agent,
struct ib_mad_send_wc *mad_send_wc)
ib_free_send_mad(mad_send_wc->send_buf);
kref_put(&query->sm_ah->ref, free_sm_ah);
+ ib_sa_client_put(query->client);
query->release(query);
}
struct ib_sa_device *sa_dev;
int s, e, i;
- if (device->node_type == IB_NODE_SWITCH)
+ if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
+ return;
+
+ if (device->node_type == RDMA_NODE_IB_SWITCH)
s = e = 0;
else {
s = 1;
if (IS_ERR(sa_dev->port[i].agent))
goto err;
- INIT_WORK(&sa_dev->port[i].update_task,
- update_sm_ah, &sa_dev->port[i]);
+ INIT_WORK(&sa_dev->port[i].update_task, update_sm_ah);
}
ib_set_client_data(device, &sa_client, sa_dev);
goto err;
for (i = 0; i <= e - s; ++i)
- update_sm_ah(&sa_dev->port[i]);
+ update_sm_ah(&sa_dev->port[i].update_task);
return;
get_random_bytes(&tid, sizeof tid);
ret = ib_register_client(&sa_client);
- if (ret)
+ if (ret) {
printk(KERN_ERR "Couldn't register ib_sa client\n");
+ goto err1;
+ }
+ ret = mcast_init();
+ if (ret) {
+ printk(KERN_ERR "Couldn't initialize multicast handling\n");
+ goto err2;
+ }
+
+ return 0;
+err2:
+ ib_unregister_client(&sa_client);
+err1:
return ret;
}
static void __exit ib_sa_cleanup(void)
{
+ mcast_cleanup();
ib_unregister_client(&sa_client);
idr_destroy(&query_idr);
}