Add support for userspace SA queries and multicast join operations. This allows a userspace library to issue SA queries and join IB multicast groups.
Signed-off-by: Sean Hefty <[EMAIL PROTECTED]> --- This patch depends on the generic RMPP query interface: http://openib.org/pipermail/openib-general/2006-August/025268.html Index: include/rdma/ib_usa.h =================================================================== --- include/rdma/ib_usa.h (revision 0) +++ include/rdma/ib_usa.h (revision 0) @@ -0,0 +1,123 @@ +/* + * 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IB_USA_H +#define IB_USA_H + +#include <linux/types.h> +#include <rdma/ib_sa.h> + +#define IB_USA_ABI_VERSION 1 + +#define IB_USA_EVENT_DATA 256 + +enum { + IB_USA_CMD_SEND_MAD, + IB_USA_CMD_GET_EVENT, + IB_USA_CMD_GET_DATA, + IB_USA_CMD_JOIN_MCAST, + IB_USA_CMD_FREE_ID, + IB_USA_CMD_GET_MCAST +}; + +enum { + IB_USA_EVENT_MAD, + IB_USA_EVENT_MCAST +}; + +struct ib_usa_cmd_hdr { + __u32 cmd; + __u16 in; + __u16 out; +}; + +struct ib_usa_send_mad { + __u64 response; /* unused - reserved */ + __u64 uid; + __u64 node_guid; + __u64 comp_mask; + __u64 attr; + __u8 port_num; + __u8 method; + __be16 attr_id; + __u32 timeout_ms; + __u32 retries; +}; + +struct ib_usa_join_mcast { + __u64 response; + __u64 uid; + __u64 node_guid; + __u64 comp_mask; + __u64 mcmember_rec; + __u8 port_num; +}; + +struct ib_usa_id_resp { + __u32 id; +}; + +struct ib_usa_free_resp { + __u32 events_reported; +}; + +struct ib_usa_free_id { + __u64 response; + __u32 id; +}; + +struct ib_usa_get_event { + __u64 response; +}; + +struct ib_usa_event_resp { + __u64 uid; + __u32 id; + __u32 event; + __u32 status; + __u32 data_len; + __u8 data[IB_USA_EVENT_DATA]; +}; + +struct ib_usa_get_data { + __u64 response; + __u32 id; +}; + +struct ib_usa_get_mcast { + __u64 response; + __u64 node_guid; + __u8 mgid[16]; + __u8 port_num; +}; + +#endif /* IB_USA_H */ Index: core/usa.c =================================================================== --- core/usa.c (revision 0) +++ core/usa.c (revision 0) @@ -0,0 +1,772 @@ +/* + * 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 + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/idr.h> +#include <linux/miscdevice.h> + +#include <rdma/ib_usa.h> +#include <rdma/ib_multicast.h> + +MODULE_AUTHOR("Sean Hefty"); +MODULE_DESCRIPTION("IB userspace SA query"); +MODULE_LICENSE("Dual BSD/GPL"); + +static void usa_add_one(struct ib_device *device); +static void usa_remove_one(struct ib_device *device); + +static struct ib_client usa_client = { + .name = "ib_usa", + .add = usa_add_one, + .remove = usa_remove_one +}; + +struct usa_device { + struct list_head list; + struct ib_device *device; + struct completion comp; + atomic_t refcount; + int start_port; + int end_port; +}; + +struct usa_file { + struct mutex file_mutex; + struct file *filp; + struct ib_sa_client sa_client; + struct list_head event_list; + struct list_head data_list; + struct list_head mcast_list; + wait_queue_head_t poll_wait; + int event_id; +}; + +struct usa_event { + struct usa_file *file; + struct list_head list; + struct ib_usa_event_resp resp; + struct ib_mad_recv_wc *mad_recv_wc; +}; + +struct usa_multicast { + struct usa_event event; + struct list_head list; + struct ib_multicast *multicast; + int events_reported; +}; + +static DEFINE_MUTEX(usa_mutex); +static LIST_HEAD(dev_list); +static DEFINE_IDR(usa_idr); + +static struct usa_device *acquire_dev(__be64 guid, __u8 port_num) +{ + struct usa_device *dev; + + mutex_lock(&usa_mutex); + list_for_each_entry(dev, &dev_list, list) { + if (dev->device->node_guid == guid) { + if (port_num < dev->start_port || + port_num > dev->end_port) + break; + atomic_inc(&dev->refcount); + mutex_unlock(&usa_mutex); + return dev; + } + } + mutex_unlock(&usa_mutex); + return NULL; +} + +static void deref_dev(struct usa_device *dev) +{ + if (atomic_dec_and_test(&dev->refcount)) + complete(&dev->comp); +} + +static int insert_obj(void *obj, int *id) +{ + int ret; + + do { + ret = idr_pre_get(&usa_idr, GFP_KERNEL); + if (!ret) + break; + + mutex_lock(&usa_mutex); + ret = idr_get_new(&usa_idr, obj, id); + mutex_unlock(&usa_mutex); + } while (ret == -EAGAIN); + + return ret; +} + +static void remove_obj(int id) +{ + mutex_lock(&usa_mutex); + idr_remove(&usa_idr, id); + mutex_unlock(&usa_mutex); +} + +static void finish_event(struct usa_event *event) +{ + struct usa_multicast *mcast; + + switch (event->resp.event) { + case IB_USA_EVENT_MAD: + list_del(&event->list); + if (event->resp.data_len > IB_USA_EVENT_DATA) + list_add_tail(&event->list, &event->file->data_list); + else + kfree(event); + break; + case IB_USA_EVENT_MCAST: + list_del_init(&event->list); + mcast = container_of(event, struct usa_multicast, event); + mcast->events_reported++; + break; + default: + break; + } +} + +static ssize_t usa_get_event(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct ib_usa_get_event cmd; + struct usa_event *event; + int ret = 0; + DEFINE_WAIT(wait); + + if (out_len < sizeof(struct ib_usa_event_resp)) + return -ENOSPC; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + mutex_lock(&file->file_mutex); + while (list_empty(&file->event_list)) { + if (file->filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE); + mutex_unlock(&file->file_mutex); + schedule(); + mutex_lock(&file->file_mutex); + finish_wait(&file->poll_wait, &wait); + } + + if (ret) + goto done; + + event = list_entry(file->event_list.next, struct usa_event, list); + + if (copy_to_user((void __user *)(unsigned long)cmd.response, + &event->resp, sizeof(event->resp))) { + ret = -EFAULT; + goto done; + } + + finish_event(event); +done: + mutex_unlock(&file->file_mutex); + return ret; +} + +static struct usa_event *get_event_data(struct usa_file *file, __u32 id) +{ + struct usa_event *event; + + mutex_lock(&file->file_mutex); + list_for_each_entry(event, &file->data_list, list) { + if (event->resp.id == id) { + list_del(&event->list); + mutex_unlock(&file->file_mutex); + return event; + } + } + mutex_unlock(&file->file_mutex); + return NULL; +} + +static int copy_event_data(struct usa_event *event, __u64 response) +{ + struct ib_sa_mad *mad; + struct ib_sa_iter *iter; + int attr_offset, ret = 0; + void *attr; + + mad = (struct ib_sa_mad *) event->mad_recv_wc->recv_buf.mad; + attr_offset = be16_to_cpu(mad->sa_hdr.attr_offset) * 8; + + iter = ib_sa_iter_create(event->mad_recv_wc); + while ((attr = ib_sa_iter_next(iter))) { + if (copy_to_user((void __user *) (unsigned long) response, + attr, attr_offset)) { + ret = -EFAULT; + break; + } + response += attr_offset; + } + + ib_sa_iter_free(iter); + return ret; +} + +static ssize_t usa_get_data(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct ib_usa_get_data cmd; + struct usa_event *event; + int ret = 0; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + event = get_event_data(file, cmd.id); + if (!event) + return -EINVAL; + + if (out_len < event->resp.data_len) { + ret = -ENOSPC; + goto out; + } + + ret = copy_event_data(event, cmd.response); +out: + ib_free_recv_mad(event->mad_recv_wc); + kfree(event); + return ret; +} + +static void usa_req_handler(int status, struct ib_mad_recv_wc *mad_recv_wc, + void *context) +{ + struct usa_event *event = context; + + if (mad_recv_wc) { + event->resp.data_len = mad_recv_wc->mad_len; + + if (event->resp.data_len <= IB_USA_EVENT_DATA) { + memcpy(event->resp.data, mad_recv_wc->recv_buf.mad, + event->resp.data_len); + ib_free_recv_mad(mad_recv_wc); + } else { + event->mad_recv_wc = mad_recv_wc; + memcpy(event->resp.data, mad_recv_wc->recv_buf.mad, + IB_USA_EVENT_DATA); + } + } + + event->resp.status = status; + + mutex_lock(&event->file->file_mutex); + list_add_tail(&event->list, &event->file->event_list); + wake_up_interruptible(&event->file->poll_wait); + mutex_unlock(&event->file->file_mutex); +} + +static int is_send_req(__u8 method) +{ + switch (method) { + case IB_MGMT_METHOD_GET: + case IB_MGMT_METHOD_SEND: + case IB_SA_METHOD_GET_TABLE: + case IB_SA_METHOD_GET_MULTI: + case IB_SA_METHOD_GET_TRACE_TBL: + return 1; + default: + return 0; + } +} + +static ssize_t usa_send_mad(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct usa_device *dev; + struct usa_event *event; + struct ib_usa_send_mad cmd; + struct ib_sa_query *query; + int attr_size, ret; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + attr_size = ib_sa_attr_size(cmd.attr_id); + if (!attr_size || !is_send_req(cmd.method)) + return -EINVAL; + + dev = acquire_dev(cmd.node_guid, cmd.port_num); + if (!dev) + return -ENODEV; + + event = kzalloc(sizeof *event, GFP_KERNEL); + if (!event) { + ret = -ENOMEM; + goto deref; + } + + if (copy_from_user(event->resp.data, + (void __user *) (unsigned long) cmd.attr, + attr_size)) { + ret = -EFAULT; + goto free; + } + + event->file = file; + event->resp.event = IB_USA_EVENT_MAD; + event->resp.uid = cmd.uid; + + mutex_lock(&file->file_mutex); + event->resp.id = file->event_id++; + mutex_unlock(&file->file_mutex); + + ret = ib_sa_send_mad(&file->sa_client, dev->device, cmd.port_num, + cmd.method, event->resp.data, cmd.attr_id, + (ib_sa_comp_mask) cmd.comp_mask, + cmd.timeout_ms, cmd.retries, GFP_KERNEL, + usa_req_handler, event, &query); + if (ret < 0) + goto free; + + deref_dev(dev); + return 0; +free: + kfree(event); +deref: + deref_dev(dev); + return ret; +} + +/* + * We can get up to two events for a single multicast member. A second event + * only occurs if there's an error on an existing multicast membership. + * Report only the last event. + */ +static int multicast_handler(int status, struct ib_multicast *multicast) +{ + struct usa_multicast *mcast = multicast->context; + + if (!status) { + mcast->event.resp.data_len = IB_SA_ATTR_MC_MEMBER_REC_LEN; + ib_sa_pack_attr(mcast->event.resp.data, &multicast->rec, + IB_SA_ATTR_MC_MEMBER_REC); + } + + mutex_lock(&mcast->event.file->file_mutex); + mcast->event.resp.status = status; + + list_del(&mcast->event.list); + list_add_tail(&mcast->event.list, &mcast->event.file->event_list); + wake_up_interruptible(&mcast->event.file->poll_wait); + mutex_unlock(&mcast->event.file->file_mutex); + return 0; +} + +static ssize_t usa_join_mcast(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct usa_device *dev; + struct usa_multicast *mcast; + struct ib_usa_join_mcast cmd; + struct ib_usa_id_resp resp; + struct ib_sa_mcmember_rec rec; + int ret; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + dev = acquire_dev(cmd.node_guid, cmd.port_num); + if (!dev) + return -ENODEV; + + mcast = kzalloc(sizeof *mcast, GFP_KERNEL); + if (!mcast) { + ret = -ENOMEM; + goto deref; + } + INIT_LIST_HEAD(&mcast->event.list); + mcast->event.file = file; + mcast->event.resp.event = IB_USA_EVENT_MCAST; + mcast->event.resp.uid = cmd.uid; + + ret = insert_obj(mcast, &mcast->event.resp.id); + if (ret) + goto free; + + resp.id = mcast->event.resp.id; + + mutex_lock(&file->file_mutex); + list_add_tail(&mcast->list, &file->mcast_list); + mutex_unlock(&file->file_mutex); + + if (copy_from_user(mcast->event.resp.data, + (void __user *) (unsigned long) cmd.mcmember_rec, + IB_SA_ATTR_MC_MEMBER_REC_LEN)) { + ret = -EFAULT; + goto remove; + } + + ib_sa_unpack_attr(&rec, mcast->event.resp.data, + IB_SA_ATTR_MC_MEMBER_REC); + mcast->multicast = ib_join_multicast(dev->device, cmd.port_num, &rec, + (ib_sa_comp_mask) cmd.comp_mask, + GFP_KERNEL, multicast_handler, + mcast); + if (IS_ERR(mcast->multicast)) { + ret = PTR_ERR(mcast->multicast); + goto remove; + } + + deref_dev(dev); + return 0; +remove: + mutex_lock(&file->file_mutex); + list_del(&mcast->list); + mutex_unlock(&file->file_mutex); + remove_obj(mcast->event.resp.id); +free: + kfree(mcast); +deref: + deref_dev(dev); + return ret; +} + +static ssize_t usa_free_id(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct ib_usa_free_id cmd; + struct ib_usa_free_resp resp; + struct usa_multicast *mcast; + int ret = 0; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + mutex_lock(&usa_mutex); + mcast = idr_find(&usa_idr, cmd.id); + if (!mcast) + mcast = ERR_PTR(-ENOENT); + else if (mcast->event.file != file) + mcast = ERR_PTR(-EINVAL); + else + idr_remove(&usa_idr, mcast->event.resp.id); + mutex_unlock(&usa_mutex); + + if (IS_ERR(mcast)) + return PTR_ERR(mcast); + + ib_free_multicast(mcast->multicast); + mutex_lock(&file->file_mutex); + list_del(&mcast->list); + mutex_unlock(&file->file_mutex); + + resp.events_reported = mcast->events_reported; + + if (copy_to_user((void __user *) (unsigned long) cmd.response, + &resp, sizeof resp)) + ret = -EFAULT; + + kfree(mcast); + return ret; +} + +static ssize_t usa_get_mcast(struct usa_file *file, const char __user *inbuf, + int in_len, int out_len) +{ + struct usa_device *dev; + struct ib_usa_get_mcast cmd; + struct ib_sa_mcmember_rec rec; + u8 mcmember_rec[IB_SA_ATTR_MC_MEMBER_REC_LEN]; + int ret; + + if (out_len < sizeof(IB_SA_ATTR_MC_MEMBER_REC_LEN)) + return -ENOSPC; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + dev = acquire_dev(cmd.node_guid, cmd.port_num); + if (!dev) + return -ENODEV; + + ret = ib_get_mcmember_rec(dev->device, cmd.port_num, + (union ib_gid *) cmd.mgid, &rec); + if (!ret) { + ib_sa_pack_attr(mcmember_rec, &rec, IB_SA_ATTR_MC_MEMBER_REC); + if (copy_to_user((void __user *) (unsigned long) cmd.response, + mcmember_rec, IB_SA_ATTR_MC_MEMBER_REC_LEN)) + ret = -EFAULT; + } + + deref_dev(dev); + return ret; +} + +static ssize_t (*usa_cmd_table[])(struct usa_file *file, + const char __user *inbuf, + int in_len, int out_len) = { + [IB_USA_CMD_SEND_MAD] = usa_send_mad, + [IB_USA_CMD_GET_EVENT] = usa_get_event, + [IB_USA_CMD_GET_DATA] = usa_get_data, + [IB_USA_CMD_JOIN_MCAST] = usa_join_mcast, + [IB_USA_CMD_FREE_ID] = usa_free_id, + [IB_USA_CMD_GET_MCAST] = usa_get_mcast +}; + + +static ssize_t usa_write(struct file *filp, const char __user *buf, + size_t len, loff_t *pos) +{ + struct usa_file *file = filp->private_data; + struct ib_usa_cmd_hdr hdr; + ssize_t ret; + + if (len < sizeof(hdr)) + return -EINVAL; + + if (copy_from_user(&hdr, buf, sizeof(hdr))) + return -EFAULT; + + if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(usa_cmd_table)) + return -EINVAL; + + if (hdr.in + sizeof(hdr) > len) + return -EINVAL; + + ret = usa_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out); + if (!ret) + ret = len; + + return ret; +} + +static unsigned int usa_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct usa_file *file = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &file->poll_wait, wait); + + if (!list_empty(&file->event_list)) + mask = POLLIN | POLLRDNORM; + + return mask; +} + +static int usa_open(struct inode *inode, struct file *filp) +{ + struct usa_file *file; + + file = kmalloc(sizeof *file, GFP_KERNEL); + if (!file) + return -ENOMEM; + + ib_sa_register_client(&file->sa_client); + + INIT_LIST_HEAD(&file->event_list); + INIT_LIST_HEAD(&file->data_list); + INIT_LIST_HEAD(&file->mcast_list); + init_waitqueue_head(&file->poll_wait); + mutex_init(&file->file_mutex); + + filp->private_data = file; + file->filp = filp; + return 0; +} + +static void cleanup_events(struct list_head *list) +{ + struct usa_event *event; + + while (!list_empty(list)) { + event = list_entry(list->next, struct usa_event, list); + list_del(&event->list); + + if (event->mad_recv_wc) + ib_free_recv_mad(event->mad_recv_wc); + + kfree(event); + } +} + +static void cleanup_mcast(struct usa_file *file) +{ + struct usa_multicast *mcast; + + while (!list_empty(&file->mcast_list)) { + mcast = list_entry(file->mcast_list.next, + struct usa_multicast, list); + list_del(&mcast->list); + + remove_obj(mcast->event.resp.id); + + ib_free_multicast(mcast->multicast); + + /* + * Other members may still be generating events, so we need + * to lock the event list to avoid corrupting it. + */ + mutex_lock(&file->file_mutex); + list_del(&mcast->event.list); + mutex_unlock(&file->file_mutex); + + kfree(mcast); + } +} + +static int usa_close(struct inode *inode, struct file *filp) +{ + struct usa_file *file = filp->private_data; + + ib_sa_unregister_client(&file->sa_client); + cleanup_mcast(file); + + cleanup_events(&file->event_list); + cleanup_events(&file->data_list); + kfree(file); + return 0; +} + +static void usa_add_one(struct ib_device *device) +{ + struct usa_device *dev; + + if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) + return; + + dev = kmalloc(sizeof *dev, GFP_KERNEL); + if (!dev) + return; + + dev->device = device; + if (device->node_type == RDMA_NODE_IB_SWITCH) + dev->start_port = dev->end_port = 0; + else { + dev->start_port = 1; + dev->end_port = device->phys_port_cnt; + } + + init_completion(&dev->comp); + atomic_set(&dev->refcount, 1); + ib_set_client_data(device, &usa_client, dev); + + mutex_lock(&usa_mutex); + list_add_tail(&dev->list, &dev_list); + mutex_unlock(&usa_mutex); +} + +static void usa_remove_one(struct ib_device *device) +{ + struct usa_device *dev; + + dev = ib_get_client_data(device, &usa_client); + if (!dev) + return; + + mutex_lock(&usa_mutex); + list_del(&dev->list); + mutex_unlock(&usa_mutex); + + deref_dev(dev); + wait_for_completion(&dev->comp); + kfree(dev); +} + +static struct file_operations usa_fops = { + .owner = THIS_MODULE, + .open = usa_open, + .release = usa_close, + .write = usa_write, + .poll = usa_poll, +}; + +static struct miscdevice usa_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ib_usa", + .fops = &usa_fops, +}; + +static ssize_t show_abi_version(struct class_device *class_dev, char *buf) +{ + return sprintf(buf, "%d\n", IB_USA_ABI_VERSION); +} +static CLASS_DEVICE_ATTR(abi_version, S_IRUGO, show_abi_version, NULL); + +static int __init usa_init(void) +{ + int ret; + + ret = misc_register(&usa_misc); + if (ret) + return ret; + + ret = class_device_create_file(usa_misc.class, + &class_device_attr_abi_version); + if (ret) { + printk(KERN_ERR "ib_usa: couldn't create abi_version attr\n"); + goto err1; + } + + ret = ib_register_client(&usa_client); + if (ret) + goto err2; + return 0; + +err2: + class_device_remove_file(usa_misc.class, + &class_device_attr_abi_version); +err1: + misc_deregister(&usa_misc); + return ret; +} + +static void __exit usa_cleanup(void) +{ + ib_unregister_client(&usa_client); + class_device_remove_file(usa_misc.class, + &class_device_attr_abi_version); + misc_deregister(&usa_misc); + idr_destroy(&usa_idr); +} + +module_init(usa_init); +module_exit(usa_cleanup); Index: Kconfig =================================================================== --- Kconfig (revision 8928) +++ Kconfig (working copy) @@ -17,15 +17,15 @@ config INFINIBAND_USER_MAD need libibumad from <http://www.openib.org>. config INFINIBAND_USER_ACCESS - tristate "InfiniBand userspace access (verbs and CM)" + tristate "InfiniBand userspace access (verbs, CM, SA client)" depends on INFINIBAND ---help--- Userspace InfiniBand access support. This enables the - kernel side of userspace verbs and the userspace - communication manager (CM). This allows userspace processes - to set up connections and directly access InfiniBand + kernel side of userspace verbs, the userspace communication + manager (CM), and userspace SA client. This allows userspace + processes to set up connections and directly access InfiniBand hardware for fast-path operations. You will also need - libibverbs, libibcm and a hardware driver library from + libibverbs, libibcm, libibsa, and a hardware driver library from <http://www.openib.org>. config INFINIBAND_ADDR_TRANS Index: core/Makefile =================================================================== --- core/Makefile (revision 8928) +++ core/Makefile (working copy) @@ -7,7 +7,8 @@ obj-$(CONFIG_INFINIBAND) += ib_core.o i ib_sa.o $(infiniband-y) \ findex.o ib_multicast.o obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o -obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o $(user_access-y) +obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o ib_usa.o \ + $(user_access-y) findex-y := index.o @@ -39,3 +40,5 @@ ib_uverbs-y := uverbs_main.o uverbs_cm ib_ucm-y := ucm.o +ib_usa-y := usa.o + _______________________________________________ openib-general mailing list [email protected] http://openib.org/mailman/listinfo/openib-general To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general
