/*
* 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/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/random.h>
#include <rdma/ib_cache.h>
#include "sa.h"
static int mcast_add_one(struct ib_device *device);
static void mcast_remove_one(struct ib_device *device, void *client_data);
static struct ib_client mcast_client = {
.name = "ib_multicast",
.add = mcast_add_one,
.remove = mcast_remove_one
};
static struct ib_sa_client sa_client;
static struct workqueue_struct *mcast_wq;
static union ib_gid mgid0;
struct mcast_device;
struct mcast_port {
struct mcast_device *dev;
spinlock_t lock;
struct rb_root table;
refcount_t refcount;
struct completion comp;
u32 port_num;
};
struct mcast_device {
struct ib_device *device;
struct ib_event_handler event_handler;
int start_port;
int end_port;
struct mcast_port port[];
};
enum mcast_state {
MCAST_JOINING,
MCAST_MEMBER,
MCAST_ERROR,
};
enum mcast_group_state {
MCAST_IDLE,
MCAST_BUSY,
MCAST_GROUP_ERROR,
MCAST_PKEY_EVENT
};
enum {
MCAST_INVALID_PKEY_INDEX = 0xFFFF
};
struct mcast_member;
struct mcast_group {
struct ib_sa_mcmember_rec rec;
struct rb_node node;
struct mcast_port *port;
spinlock_t lock;
struct work_struct work;
struct list_head pending_list;
struct list_head active_list;
struct mcast_member *last_join;
int members[NUM_JOIN_MEMBERSHIP_TYPES];
atomic_t refcount;
enum mcast_group_state state;
struct ib_sa_query *query;
u16 pkey_index;
u8 leave_state;
int retries;
};
struct mcast_member {
struct ib_sa_multicast multicast;
struct ib_sa_client *client;
struct mcast_group *group;
struct list_head list;
enum mcast_state state;
refcount_t refcount;
struct completion comp;
};
static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
void *context);
static void leave_handler(int status, struct ib_sa_mcmember_rec *rec,
void *context);
static struct mcast_group *mcast_find(struct mcast_port *port,
union ib_gid *mgid)
{
struct rb_node *node = port->table.rb_node;
struct mcast_group *group;
int ret;
while (node) {
group = rb_entry(node, struct mcast_group, node);
ret = memcmp(mgid->raw, group->rec.mgid.raw, sizeof *mgid);
if (!ret)
return group;
if (ret < 0)
node = node->rb_left;
else
node = node->rb_right;
}
return NULL;
}
static struct mcast_group *mcast_insert(struct mcast_port *port,
struct mcast_group *group,
int allow_duplicates)
{
struct rb_node **link = &port->table.rb_node;
struct rb_node *parent = NULL;
struct mcast_group *cur_group;
int ret;
while (*link) {
parent = *link;
cur_group = rb_entry(parent, struct mcast_group, node);
ret = memcmp(group->rec.mgid.raw, cur_group->rec.mgid.raw,
sizeof group->rec.mgid);
if (ret < 0)
link = &(*link)->rb_left;
else if (ret > 0)
link = &(*link)->rb_right;
else if (allow_duplicates)
link = &(*link)->rb_left;
else
return cur_group;
}
rb_link_node(&group->node, parent, link);
rb_insert_color(&group->node, &port->table);
return