📄 cm.c
字号:
/* * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. * Copyright (c) 2004 Topspin Corporation. All rights reserved. * Copyright (c) 2004, 2005 Voltaire Corporation. 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 * 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. * * $Id: cm.c 2821 2005-07-08 17:07:28Z sean.hefty $ */#include <linux/dma-mapping.h>#include <linux/err.h>#include <linux/idr.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/rbtree.h>#include <linux/spinlock.h>#include <linux/workqueue.h>#include <rdma/ib_cache.h>#include <rdma/ib_cm.h>#include "cm_msgs.h"MODULE_AUTHOR("Sean Hefty");MODULE_DESCRIPTION("InfiniBand CM");MODULE_LICENSE("Dual BSD/GPL");static void cm_add_one(struct ib_device *device);static void cm_remove_one(struct ib_device *device);static struct ib_client cm_client = { .name = "cm", .add = cm_add_one, .remove = cm_remove_one};static struct ib_cm { spinlock_t lock; struct list_head device_list; rwlock_t device_lock; struct rb_root listen_service_table; u64 listen_service_id; /* struct rb_root peer_service_table; todo: fix peer to peer */ struct rb_root remote_qp_table; struct rb_root remote_id_table; struct rb_root remote_sidr_table; struct idr local_id_table; struct workqueue_struct *wq;} cm;struct cm_port { struct cm_device *cm_dev; struct ib_mad_agent *mad_agent; u8 port_num;};struct cm_device { struct list_head list; struct ib_device *device; __be64 ca_guid; struct cm_port port[0];};struct cm_av { struct cm_port *port; union ib_gid dgid; struct ib_ah_attr ah_attr; u16 pkey_index; u8 packet_life_time;};struct cm_work { struct work_struct work; struct list_head list; struct cm_port *port; struct ib_mad_recv_wc *mad_recv_wc; /* Received MADs */ __be32 local_id; /* Established / timewait */ __be32 remote_id; struct ib_cm_event cm_event; struct ib_sa_path_rec path[0];};struct cm_timewait_info { struct cm_work work; /* Must be first. */ struct rb_node remote_qp_node; struct rb_node remote_id_node; __be64 remote_ca_guid; __be32 remote_qpn; u8 inserted_remote_qp; u8 inserted_remote_id;};struct cm_id_private { struct ib_cm_id id; struct rb_node service_node; struct rb_node sidr_id_node; spinlock_t lock; wait_queue_head_t wait; atomic_t refcount; struct ib_mad_send_buf *msg; struct cm_timewait_info *timewait_info; /* todo: use alternate port on send failure */ struct cm_av av; struct cm_av alt_av; void *private_data; __be64 tid; __be32 local_qpn; __be32 remote_qpn; enum ib_qp_type qp_type; __be32 sq_psn; __be32 rq_psn; int timeout_ms; enum ib_mtu path_mtu; u8 private_data_len; u8 max_cm_retries; u8 peer_to_peer; u8 responder_resources; u8 initiator_depth; u8 local_ack_timeout; u8 retry_count; u8 rnr_retry_count; u8 service_timeout; struct list_head work_list; atomic_t work_count;};static void cm_work_handler(void *data);static inline void cm_deref_id(struct cm_id_private *cm_id_priv){ if (atomic_dec_and_test(&cm_id_priv->refcount)) wake_up(&cm_id_priv->wait);}static int cm_alloc_msg(struct cm_id_private *cm_id_priv, struct ib_mad_send_buf **msg){ struct ib_mad_agent *mad_agent; struct ib_mad_send_buf *m; struct ib_ah *ah; mad_agent = cm_id_priv->av.port->mad_agent; ah = ib_create_ah(mad_agent->qp->pd, &cm_id_priv->av.ah_attr); if (IS_ERR(ah)) return PTR_ERR(ah); m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn, cm_id_priv->av.pkey_index, 0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA, GFP_ATOMIC); if (IS_ERR(m)) { ib_destroy_ah(ah); return PTR_ERR(m); } /* Timeout set by caller if response is expected. */ m->ah = ah; m->retries = cm_id_priv->max_cm_retries; atomic_inc(&cm_id_priv->refcount); m->context[0] = cm_id_priv; *msg = m; return 0;}static int cm_alloc_response_msg(struct cm_port *port, struct ib_mad_recv_wc *mad_recv_wc, struct ib_mad_send_buf **msg){ struct ib_mad_send_buf *m; struct ib_ah *ah; ah = ib_create_ah_from_wc(port->mad_agent->qp->pd, mad_recv_wc->wc, mad_recv_wc->recv_buf.grh, port->port_num); if (IS_ERR(ah)) return PTR_ERR(ah); m = ib_create_send_mad(port->mad_agent, 1, mad_recv_wc->wc->pkey_index, 0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA, GFP_ATOMIC); if (IS_ERR(m)) { ib_destroy_ah(ah); return PTR_ERR(m); } m->ah = ah; *msg = m; return 0;}static void cm_free_msg(struct ib_mad_send_buf *msg){ ib_destroy_ah(msg->ah); if (msg->context[0]) cm_deref_id(msg->context[0]); ib_free_send_mad(msg);}static void * cm_copy_private_data(const void *private_data, u8 private_data_len){ void *data; if (!private_data || !private_data_len) return NULL; data = kmalloc(private_data_len, GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); memcpy(data, private_data, private_data_len); return data;}static void cm_set_private_data(struct cm_id_private *cm_id_priv, void *private_data, u8 private_data_len){ if (cm_id_priv->private_data && cm_id_priv->private_data_len) kfree(cm_id_priv->private_data); cm_id_priv->private_data = private_data; cm_id_priv->private_data_len = private_data_len;}static void cm_set_ah_attr(struct ib_ah_attr *ah_attr, u8 port_num, u16 dlid, u8 sl, u16 src_path_bits){ memset(ah_attr, 0, sizeof ah_attr); ah_attr->dlid = dlid; ah_attr->sl = sl; ah_attr->src_path_bits = src_path_bits; ah_attr->port_num = port_num;}static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc, struct cm_av *av){ av->port = port; av->pkey_index = wc->pkey_index; cm_set_ah_attr(&av->ah_attr, port->port_num, wc->slid, wc->sl, wc->dlid_path_bits);}static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av){ struct cm_device *cm_dev; struct cm_port *port = NULL; unsigned long flags; int ret; u8 p; read_lock_irqsave(&cm.device_lock, flags); list_for_each_entry(cm_dev, &cm.device_list, list) { if (!ib_find_cached_gid(cm_dev->device, &path->sgid, &p, NULL)) { port = &cm_dev->port[p-1]; break; } } read_unlock_irqrestore(&cm.device_lock, flags); if (!port) return -EINVAL; ret = ib_find_cached_pkey(cm_dev->device, port->port_num, be16_to_cpu(path->pkey), &av->pkey_index); if (ret) return ret; av->port = port; cm_set_ah_attr(&av->ah_attr, av->port->port_num, be16_to_cpu(path->dlid), path->sl, be16_to_cpu(path->slid) & 0x7F); av->packet_life_time = path->packet_life_time; return 0;}static int cm_alloc_id(struct cm_id_private *cm_id_priv){ unsigned long flags; int ret; do { spin_lock_irqsave(&cm.lock, flags); ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, 1, (__force int *) &cm_id_priv->id.local_id); spin_unlock_irqrestore(&cm.lock, flags); } while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) ); return ret;}static void cm_free_id(__be32 local_id){ unsigned long flags; spin_lock_irqsave(&cm.lock, flags); idr_remove(&cm.local_id_table, (__force int) local_id); spin_unlock_irqrestore(&cm.lock, flags);}static struct cm_id_private * cm_get_id(__be32 local_id, __be32 remote_id){ struct cm_id_private *cm_id_priv; cm_id_priv = idr_find(&cm.local_id_table, (__force int) local_id); if (cm_id_priv) { if (cm_id_priv->id.remote_id == remote_id) atomic_inc(&cm_id_priv->refcount); else cm_id_priv = NULL; } return cm_id_priv;}static struct cm_id_private * cm_acquire_id(__be32 local_id, __be32 remote_id){ struct cm_id_private *cm_id_priv; unsigned long flags; spin_lock_irqsave(&cm.lock, flags); cm_id_priv = cm_get_id(local_id, remote_id); spin_unlock_irqrestore(&cm.lock, flags); return cm_id_priv;}static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv){ struct rb_node **link = &cm.listen_service_table.rb_node; struct rb_node *parent = NULL; struct cm_id_private *cur_cm_id_priv; __be64 service_id = cm_id_priv->id.service_id; __be64 service_mask = cm_id_priv->id.service_mask; while (*link) { parent = *link; cur_cm_id_priv = rb_entry(parent, struct cm_id_private, service_node); if ((cur_cm_id_priv->id.service_mask & service_id) == (service_mask & cur_cm_id_priv->id.service_id) && (cm_id_priv->id.device == cur_cm_id_priv->id.device)) return cur_cm_id_priv; if (cm_id_priv->id.device < cur_cm_id_priv->id.device) link = &(*link)->rb_left; else if (cm_id_priv->id.device > cur_cm_id_priv->id.device) link = &(*link)->rb_right; else if (service_id < cur_cm_id_priv->id.service_id) link = &(*link)->rb_left; else link = &(*link)->rb_right; } rb_link_node(&cm_id_priv->service_node, parent, link); rb_insert_color(&cm_id_priv->service_node, &cm.listen_service_table); return NULL;}static struct cm_id_private * cm_find_listen(struct ib_device *device, __be64 service_id){ struct rb_node *node = cm.listen_service_table.rb_node; struct cm_id_private *cm_id_priv; while (node) { cm_id_priv = rb_entry(node, struct cm_id_private, service_node); if ((cm_id_priv->id.service_mask & service_id) == cm_id_priv->id.service_id && (cm_id_priv->id.device == device)) return cm_id_priv; if (device < cm_id_priv->id.device) node = node->rb_left; else if (device > cm_id_priv->id.device) node = node->rb_right; else if (service_id < cm_id_priv->id.service_id) node = node->rb_left; else node = node->rb_right; } return NULL;}static struct cm_timewait_info * cm_insert_remote_id(struct cm_timewait_info *timewait_info){ struct rb_node **link = &cm.remote_id_table.rb_node; struct rb_node *parent = NULL; struct cm_timewait_info *cur_timewait_info; __be64 remote_ca_guid = timewait_info->remote_ca_guid; __be32 remote_id = timewait_info->work.remote_id; while (*link) { parent = *link; cur_timewait_info = rb_entry(parent, struct cm_timewait_info, remote_id_node); if (remote_id < cur_timewait_info->work.remote_id) link = &(*link)->rb_left; else if (remote_id > cur_timewait_info->work.remote_id) link = &(*link)->rb_right; else if (remote_ca_guid < cur_timewait_info->remote_ca_guid) link = &(*link)->rb_left; else if (remote_ca_guid > cur_timewait_info->remote_ca_guid) link = &(*link)->rb_right; else return cur_timewait_info; } timewait_info->inserted_remote_id = 1; rb_link_node(&timewait_info->remote_id_node, parent, link); rb_insert_color(&timewait_info->remote_id_node, &cm.remote_id_table); return NULL;}static struct cm_timewait_info * cm_find_remote_id(__be64 remote_ca_guid, __be32 remote_id){ struct rb_node *node = cm.remote_id_table.rb_node; struct cm_timewait_info *timewait_info;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -