Commit cb183a06 authored by Hal Rosenstock's avatar Hal Rosenstock Committed by Linus Torvalds

[PATCH] IB: Implementation for RMPP support in user MAD

Implementation for RMPP support in user MAD
Signed-off-by: default avatarHal Rosenstock <halr@voltaire.com>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3f75dadd
/* /*
* Copyright (c) 2004 Topspin Communications. All rights reserved. * Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005 Voltaire, Inc. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
* *
* This software is available to you under a choice of one of two * 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 * licenses. You may choose to be licensed under the terms of the GNU
...@@ -29,7 +31,7 @@ ...@@ -29,7 +31,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
* *
* $Id: user_mad.c 1389 2004-12-27 22:56:47Z roland $ * $Id: user_mad.c 2814 2005-07-06 19:14:09Z halr $
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -94,10 +96,12 @@ struct ib_umad_file { ...@@ -94,10 +96,12 @@ struct ib_umad_file {
}; };
struct ib_umad_packet { struct ib_umad_packet {
struct ib_user_mad mad;
struct ib_ah *ah; struct ib_ah *ah;
struct ib_mad_send_buf *msg;
struct list_head list; struct list_head list;
int length;
DECLARE_PCI_UNMAP_ADDR(mapping) DECLARE_PCI_UNMAP_ADDR(mapping)
struct ib_user_mad mad;
}; };
static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE); static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE);
...@@ -114,10 +118,10 @@ static int queue_packet(struct ib_umad_file *file, ...@@ -114,10 +118,10 @@ static int queue_packet(struct ib_umad_file *file,
int ret = 1; int ret = 1;
down_read(&file->agent_mutex); down_read(&file->agent_mutex);
for (packet->mad.id = 0; for (packet->mad.hdr.id = 0;
packet->mad.id < IB_UMAD_MAX_AGENTS; packet->mad.hdr.id < IB_UMAD_MAX_AGENTS;
packet->mad.id++) packet->mad.hdr.id++)
if (agent == file->agent[packet->mad.id]) { if (agent == file->agent[packet->mad.hdr.id]) {
spin_lock_irq(&file->recv_lock); spin_lock_irq(&file->recv_lock);
list_add_tail(&packet->list, &file->recv_list); list_add_tail(&packet->list, &file->recv_list);
spin_unlock_irq(&file->recv_lock); spin_unlock_irq(&file->recv_lock);
...@@ -135,22 +139,30 @@ static void send_handler(struct ib_mad_agent *agent, ...@@ -135,22 +139,30 @@ static void send_handler(struct ib_mad_agent *agent,
struct ib_mad_send_wc *send_wc) struct ib_mad_send_wc *send_wc)
{ {
struct ib_umad_file *file = agent->context; struct ib_umad_file *file = agent->context;
struct ib_umad_packet *packet = struct ib_umad_packet *timeout, *packet =
(void *) (unsigned long) send_wc->wr_id; (void *) (unsigned long) send_wc->wr_id;
dma_unmap_single(agent->device->dma_device, ib_destroy_ah(packet->msg->send_wr.wr.ud.ah);
pci_unmap_addr(packet, mapping), ib_free_send_mad(packet->msg);
sizeof packet->mad.data,
DMA_TO_DEVICE);
ib_destroy_ah(packet->ah);
if (send_wc->status == IB_WC_RESP_TIMEOUT_ERR) { if (send_wc->status == IB_WC_RESP_TIMEOUT_ERR) {
packet->mad.status = ETIMEDOUT; timeout = kmalloc(sizeof *timeout + sizeof (struct ib_mad_hdr),
GFP_KERNEL);
if (!timeout)
goto out;
if (!queue_packet(file, agent, packet)) memset(timeout, 0, sizeof *timeout + sizeof (struct ib_mad_hdr));
return;
}
timeout->length = sizeof (struct ib_mad_hdr);
timeout->mad.hdr.id = packet->mad.hdr.id;
timeout->mad.hdr.status = ETIMEDOUT;
memcpy(timeout->mad.data, packet->mad.data,
sizeof (struct ib_mad_hdr));
if (!queue_packet(file, agent, timeout))
return;
}
out:
kfree(packet); kfree(packet);
} }
...@@ -159,30 +171,35 @@ static void recv_handler(struct ib_mad_agent *agent, ...@@ -159,30 +171,35 @@ static void recv_handler(struct ib_mad_agent *agent,
{ {
struct ib_umad_file *file = agent->context; struct ib_umad_file *file = agent->context;
struct ib_umad_packet *packet; struct ib_umad_packet *packet;
int length;
if (mad_recv_wc->wc->status != IB_WC_SUCCESS) if (mad_recv_wc->wc->status != IB_WC_SUCCESS)
goto out; goto out;
packet = kmalloc(sizeof *packet, GFP_KERNEL); length = mad_recv_wc->mad_len;
packet = kmalloc(sizeof *packet + length, GFP_KERNEL);
if (!packet) if (!packet)
goto out; goto out;
memset(packet, 0, sizeof *packet); memset(packet, 0, sizeof *packet + length);
packet->length = length;
ib_coalesce_recv_mad(mad_recv_wc, packet->mad.data);
memcpy(packet->mad.data, mad_recv_wc->recv_buf.mad, sizeof packet->mad.data); packet->mad.hdr.status = 0;
packet->mad.status = 0; packet->mad.hdr.length = length + sizeof (struct ib_user_mad);
packet->mad.qpn = cpu_to_be32(mad_recv_wc->wc->src_qp); packet->mad.hdr.qpn = cpu_to_be32(mad_recv_wc->wc->src_qp);
packet->mad.lid = cpu_to_be16(mad_recv_wc->wc->slid); packet->mad.hdr.lid = cpu_to_be16(mad_recv_wc->wc->slid);
packet->mad.sl = mad_recv_wc->wc->sl; packet->mad.hdr.sl = mad_recv_wc->wc->sl;
packet->mad.path_bits = mad_recv_wc->wc->dlid_path_bits; packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits;
packet->mad.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH); packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH);
if (packet->mad.grh_present) { if (packet->mad.hdr.grh_present) {
/* XXX parse GRH */ /* XXX parse GRH */
packet->mad.gid_index = 0; packet->mad.hdr.gid_index = 0;
packet->mad.hop_limit = 0; packet->mad.hdr.hop_limit = 0;
packet->mad.traffic_class = 0; packet->mad.hdr.traffic_class = 0;
memset(packet->mad.gid, 0, 16); memset(packet->mad.hdr.gid, 0, 16);
packet->mad.flow_label = 0; packet->mad.hdr.flow_label = 0;
} }
if (queue_packet(file, agent, packet)) if (queue_packet(file, agent, packet))
...@@ -199,7 +216,7 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf, ...@@ -199,7 +216,7 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf,
struct ib_umad_packet *packet; struct ib_umad_packet *packet;
ssize_t ret; ssize_t ret;
if (count < sizeof (struct ib_user_mad)) if (count < sizeof (struct ib_user_mad) + sizeof (struct ib_mad))
return -EINVAL; return -EINVAL;
spin_lock_irq(&file->recv_lock); spin_lock_irq(&file->recv_lock);
...@@ -222,12 +239,25 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf, ...@@ -222,12 +239,25 @@ static ssize_t ib_umad_read(struct file *filp, char __user *buf,
spin_unlock_irq(&file->recv_lock); spin_unlock_irq(&file->recv_lock);
if (copy_to_user(buf, &packet->mad, sizeof packet->mad)) if (count < packet->length + sizeof (struct ib_user_mad)) {
/* Return length needed (and first RMPP segment) if too small */
if (copy_to_user(buf, &packet->mad,
sizeof (struct ib_user_mad) + sizeof (struct ib_mad)))
ret = -EFAULT;
else
ret = -ENOSPC;
} else if (copy_to_user(buf, &packet->mad,
packet->length + sizeof (struct ib_user_mad)))
ret = -EFAULT; ret = -EFAULT;
else else
ret = sizeof packet->mad; ret = packet->length + sizeof (struct ib_user_mad);
if (ret < 0) {
kfree(packet); /* Requeue packet */
spin_lock_irq(&file->recv_lock);
list_add(&packet->list, &file->recv_list);
spin_unlock_irq(&file->recv_lock);
} else
kfree(packet);
return ret; return ret;
} }
...@@ -238,69 +268,57 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, ...@@ -238,69 +268,57 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
struct ib_umad_packet *packet; struct ib_umad_packet *packet;
struct ib_mad_agent *agent; struct ib_mad_agent *agent;
struct ib_ah_attr ah_attr; struct ib_ah_attr ah_attr;
struct ib_sge gather_list; struct ib_send_wr *bad_wr;
struct ib_send_wr *bad_wr, wr = { struct ib_rmpp_mad *rmpp_mad;
.opcode = IB_WR_SEND,
.sg_list = &gather_list,
.num_sge = 1,
.send_flags = IB_SEND_SIGNALED,
};
u8 method; u8 method;
u64 *tid; u64 *tid;
int ret; int ret, length, hdr_len, data_len, rmpp_hdr_size;
int rmpp_active = 0;
if (count < sizeof (struct ib_user_mad)) if (count < sizeof (struct ib_user_mad))
return -EINVAL; return -EINVAL;
packet = kmalloc(sizeof *packet, GFP_KERNEL); length = count - sizeof (struct ib_user_mad);
packet = kmalloc(sizeof *packet + sizeof(struct ib_mad_hdr) +
sizeof(struct ib_rmpp_hdr), GFP_KERNEL);
if (!packet) if (!packet)
return -ENOMEM; return -ENOMEM;
if (copy_from_user(&packet->mad, buf, sizeof packet->mad)) { if (copy_from_user(&packet->mad, buf,
kfree(packet); sizeof (struct ib_user_mad) +
return -EFAULT; sizeof(struct ib_mad_hdr) +
sizeof(struct ib_rmpp_hdr))) {
ret = -EFAULT;
goto err;
} }
if (packet->mad.id < 0 || packet->mad.id >= IB_UMAD_MAX_AGENTS) { if (packet->mad.hdr.id < 0 ||
packet->mad.hdr.id >= IB_UMAD_MAX_AGENTS) {
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
} }
packet->length = length;
down_read(&file->agent_mutex); down_read(&file->agent_mutex);
agent = file->agent[packet->mad.id]; agent = file->agent[packet->mad.hdr.id];
if (!agent) { if (!agent) {
ret = -EINVAL; ret = -EINVAL;
goto err_up; goto err_up;
} }
/*
* If userspace is generating a request that will generate a
* response, we need to make sure the high-order part of the
* transaction ID matches the agent being used to send the
* MAD.
*/
method = ((struct ib_mad_hdr *) packet->mad.data)->method;
if (!(method & IB_MGMT_METHOD_RESP) &&
method != IB_MGMT_METHOD_TRAP_REPRESS &&
method != IB_MGMT_METHOD_SEND) {
tid = &((struct ib_mad_hdr *) packet->mad.data)->tid;
*tid = cpu_to_be64(((u64) agent->hi_tid) << 32 |
(be64_to_cpup(tid) & 0xffffffff));
}
memset(&ah_attr, 0, sizeof ah_attr); memset(&ah_attr, 0, sizeof ah_attr);
ah_attr.dlid = be16_to_cpu(packet->mad.lid); ah_attr.dlid = be16_to_cpu(packet->mad.hdr.lid);
ah_attr.sl = packet->mad.sl; ah_attr.sl = packet->mad.hdr.sl;
ah_attr.src_path_bits = packet->mad.path_bits; ah_attr.src_path_bits = packet->mad.hdr.path_bits;
ah_attr.port_num = file->port->port_num; ah_attr.port_num = file->port->port_num;
if (packet->mad.grh_present) { if (packet->mad.hdr.grh_present) {
ah_attr.ah_flags = IB_AH_GRH; ah_attr.ah_flags = IB_AH_GRH;
memcpy(ah_attr.grh.dgid.raw, packet->mad.gid, 16); memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16);
ah_attr.grh.flow_label = packet->mad.flow_label; ah_attr.grh.flow_label = packet->mad.hdr.flow_label;
ah_attr.grh.hop_limit = packet->mad.hop_limit; ah_attr.grh.hop_limit = packet->mad.hdr.hop_limit;
ah_attr.grh.traffic_class = packet->mad.traffic_class; ah_attr.grh.traffic_class = packet->mad.hdr.traffic_class;
} }
packet->ah = ib_create_ah(agent->qp->pd, &ah_attr); packet->ah = ib_create_ah(agent->qp->pd, &ah_attr);
...@@ -309,35 +327,104 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, ...@@ -309,35 +327,104 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
goto err_up; goto err_up;
} }
gather_list.addr = dma_map_single(agent->device->dma_device, rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data;
packet->mad.data, if (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & IB_MGMT_RMPP_FLAG_ACTIVE) {
sizeof packet->mad.data, /* RMPP active */
DMA_TO_DEVICE); if (!agent->rmpp_version) {
gather_list.length = sizeof packet->mad.data; ret = -EINVAL;
gather_list.lkey = file->mr[packet->mad.id]->lkey; goto err_ah;
pci_unmap_addr_set(packet, mapping, gather_list.addr); }
/* Validate that management class can support RMPP */
if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) {
hdr_len = offsetof(struct ib_sa_mad, data);
data_len = length;
} else if ((rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
(rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) {
hdr_len = offsetof(struct ib_vendor_mad, data);
data_len = length - hdr_len;
} else {
ret = -EINVAL;
goto err_ah;
}
rmpp_active = 1;
} else {
if (length > sizeof(struct ib_mad)) {
ret = -EINVAL;
goto err_ah;
}
hdr_len = offsetof(struct ib_mad, data);
data_len = length - hdr_len;
}
packet->msg = ib_create_send_mad(agent,
be32_to_cpu(packet->mad.hdr.qpn),
0, packet->ah, rmpp_active,
hdr_len, data_len,
GFP_KERNEL);
if (IS_ERR(packet->msg)) {
ret = PTR_ERR(packet->msg);
goto err_ah;
}
wr.wr.ud.mad_hdr = (struct ib_mad_hdr *) packet->mad.data; packet->msg->send_wr.wr.ud.timeout_ms = packet->mad.hdr.timeout_ms;
wr.wr.ud.ah = packet->ah; packet->msg->send_wr.wr.ud.retries = packet->mad.hdr.retries;
wr.wr.ud.remote_qpn = be32_to_cpu(packet->mad.qpn);
wr.wr.ud.remote_qkey = be32_to_cpu(packet->mad.qkey);
wr.wr.ud.timeout_ms = packet->mad.timeout_ms;
wr.wr.ud.retries = 0;
wr.wr_id = (unsigned long) packet; /* Override send WR WRID initialized in ib_create_send_mad */
packet->msg->send_wr.wr_id = (unsigned long) packet;
ret = ib_post_send_mad(agent, &wr, &bad_wr); if (!rmpp_active) {
if (ret) { /* Copy message from user into send buffer */
dma_unmap_single(agent->device->dma_device, if (copy_from_user(packet->msg->mad,
pci_unmap_addr(packet, mapping), buf + sizeof(struct ib_user_mad), length)) {
sizeof packet->mad.data, ret = -EFAULT;
DMA_TO_DEVICE); goto err_msg;
goto err_up; }
} else {
rmpp_hdr_size = sizeof(struct ib_mad_hdr) +
sizeof(struct ib_rmpp_hdr);
/* Only copy MAD headers (RMPP header in place) */
memcpy(packet->msg->mad, packet->mad.data,
sizeof(struct ib_mad_hdr));
/* Now, copy rest of message from user into send buffer */
if (copy_from_user(((struct ib_rmpp_mad *) packet->msg->mad)->data,
buf + sizeof (struct ib_user_mad) + rmpp_hdr_size,
length - rmpp_hdr_size)) {
ret = -EFAULT;
goto err_msg;
}
}
/*
* If userspace is generating a request that will generate a
* response, we need to make sure the high-order part of the
* transaction ID matches the agent being used to send the
* MAD.
*/
method = packet->msg->mad->mad_hdr.method;
if (!(method & IB_MGMT_METHOD_RESP) &&
method != IB_MGMT_METHOD_TRAP_REPRESS &&
method != IB_MGMT_METHOD_SEND) {
tid = &packet->msg->mad->mad_hdr.tid;
*tid = cpu_to_be64(((u64) agent->hi_tid) << 32 |
(be64_to_cpup(tid) & 0xffffffff));
} }
ret = ib_post_send_mad(agent, &packet->msg->send_wr, &bad_wr);
if (ret)
goto err_msg;
up_read(&file->agent_mutex); up_read(&file->agent_mutex);
return sizeof packet->mad; return sizeof (struct ib_user_mad_hdr) + packet->length;
err_msg:
ib_free_send_mad(packet->msg);
err_ah:
ib_destroy_ah(packet->ah);
err_up: err_up:
up_read(&file->agent_mutex); up_read(&file->agent_mutex);
...@@ -400,7 +487,8 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg) ...@@ -400,7 +487,8 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num, agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num,
ureq.qpn ? IB_QPT_GSI : IB_QPT_SMI, ureq.qpn ? IB_QPT_GSI : IB_QPT_SMI,
ureq.mgmt_class ? &req : NULL, ureq.mgmt_class ? &req : NULL,
0, send_handler, recv_handler, file); ureq.rmpp_version,
send_handler, recv_handler, file);
if (IS_ERR(agent)) { if (IS_ERR(agent)) {
ret = PTR_ERR(agent); ret = PTR_ERR(agent);
goto out; goto out;
...@@ -461,8 +549,8 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg) ...@@ -461,8 +549,8 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
return ret; return ret;
} }
static long ib_umad_ioctl(struct file *filp, static long ib_umad_ioctl(struct file *filp, unsigned int cmd,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
switch (cmd) { switch (cmd) {
case IB_USER_MAD_REGISTER_AGENT: case IB_USER_MAD_REGISTER_AGENT:
...@@ -518,14 +606,14 @@ static int ib_umad_close(struct inode *inode, struct file *filp) ...@@ -518,14 +606,14 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
} }
static struct file_operations umad_fops = { static struct file_operations umad_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = ib_umad_read, .read = ib_umad_read,
.write = ib_umad_write, .write = ib_umad_write,
.poll = ib_umad_poll, .poll = ib_umad_poll,
.unlocked_ioctl = ib_umad_ioctl, .unlocked_ioctl = ib_umad_ioctl,
.compat_ioctl = ib_umad_ioctl, .compat_ioctl = ib_umad_ioctl,
.open = ib_umad_open, .open = ib_umad_open,
.release = ib_umad_close .release = ib_umad_close
}; };
static int ib_umad_sm_open(struct inode *inode, struct file *filp) static int ib_umad_sm_open(struct inode *inode, struct file *filp)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment