Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
47
drivers/infiniband/ulp/ipoib/Kconfig
Normal file
47
drivers/infiniband/ulp/ipoib/Kconfig
Normal file
@@ -0,0 +1,47 @@
|
||||
config INFINIBAND_IPOIB
|
||||
tristate "IP-over-InfiniBand"
|
||||
depends on INFINIBAND && NETDEVICES && INET && (IPV6 || IPV6=n)
|
||||
---help---
|
||||
Support for the IP-over-InfiniBand protocol (IPoIB). This
|
||||
transports IP packets over InfiniBand so you can use your IB
|
||||
device as a fancy NIC.
|
||||
|
||||
See Documentation/infiniband/ipoib.txt for more information
|
||||
|
||||
config INFINIBAND_IPOIB_CM
|
||||
bool "IP-over-InfiniBand Connected Mode support"
|
||||
depends on INFINIBAND_IPOIB && EXPERIMENTAL
|
||||
default n
|
||||
---help---
|
||||
This option enables experimental support for IPoIB connected mode.
|
||||
After enabling this option, you need to switch to connected mode through
|
||||
/sys/class/net/ibXXX/mode to actually create connections, and then increase
|
||||
the interface MTU with e.g. ifconfig ib0 mtu 65520.
|
||||
|
||||
WARNING: Enabling connected mode will trigger some
|
||||
packet drops for multicast and UD mode traffic from this interface,
|
||||
unless you limit mtu for these destinations to 2044.
|
||||
|
||||
config INFINIBAND_IPOIB_DEBUG
|
||||
bool "IP-over-InfiniBand debugging" if EMBEDDED
|
||||
depends on INFINIBAND_IPOIB
|
||||
default y
|
||||
---help---
|
||||
This option causes debugging code to be compiled into the
|
||||
IPoIB driver. The output can be turned on via the
|
||||
debug_level and mcast_debug_level module parameters (which
|
||||
can also be set after the driver is loaded through sysfs).
|
||||
|
||||
This option also creates an "ipoib_debugfs," which can be
|
||||
mounted to expose debugging information about IB multicast
|
||||
groups used by the IPoIB driver.
|
||||
|
||||
config INFINIBAND_IPOIB_DEBUG_DATA
|
||||
bool "IP-over-InfiniBand data path debugging"
|
||||
depends on INFINIBAND_IPOIB_DEBUG
|
||||
---help---
|
||||
This option compiles debugging code into the data path
|
||||
of the IPoIB driver. The output can be turned on via the
|
||||
data_debug_level module parameter; however, even with output
|
||||
turned off, this debugging code will have some performance
|
||||
impact.
|
||||
10
drivers/infiniband/ulp/ipoib/Makefile
Normal file
10
drivers/infiniband/ulp/ipoib/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
obj-$(CONFIG_INFINIBAND_IPOIB) += ib_ipoib.o
|
||||
|
||||
ib_ipoib-y := ipoib_main.o \
|
||||
ipoib_ib.o \
|
||||
ipoib_multicast.o \
|
||||
ipoib_verbs.o \
|
||||
ipoib_vlan.o
|
||||
ib_ipoib-$(CONFIG_INFINIBAND_IPOIB_CM) += ipoib_cm.o
|
||||
ib_ipoib-$(CONFIG_INFINIBAND_IPOIB_DEBUG) += ipoib_fs.o
|
||||
|
||||
609
drivers/infiniband/ulp/ipoib/ipoib.h
Normal file
609
drivers/infiniband/ulp/ipoib/ipoib.h
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
|
||||
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Copyright (c) 2004 Voltaire, 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: ipoib.h,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#ifndef _IPOIB_H
|
||||
#define _IPOIB_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/if_infiniband.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <net/neighbour.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/ib_pack.h>
|
||||
#include <rdma/ib_sa.h>
|
||||
|
||||
/* constants */
|
||||
|
||||
enum {
|
||||
IPOIB_PACKET_SIZE = 2048,
|
||||
IPOIB_BUF_SIZE = IPOIB_PACKET_SIZE + IB_GRH_BYTES,
|
||||
|
||||
IPOIB_ENCAP_LEN = 4,
|
||||
|
||||
IPOIB_CM_MTU = 0x10000 - 0x10, /* padding to align header to 16 */
|
||||
IPOIB_CM_BUF_SIZE = IPOIB_CM_MTU + IPOIB_ENCAP_LEN,
|
||||
IPOIB_CM_HEAD_SIZE = IPOIB_CM_BUF_SIZE % PAGE_SIZE,
|
||||
IPOIB_CM_RX_SG = ALIGN(IPOIB_CM_BUF_SIZE, PAGE_SIZE) / PAGE_SIZE,
|
||||
IPOIB_RX_RING_SIZE = 128,
|
||||
IPOIB_TX_RING_SIZE = 64,
|
||||
IPOIB_MAX_QUEUE_SIZE = 8192,
|
||||
IPOIB_MIN_QUEUE_SIZE = 2,
|
||||
|
||||
IPOIB_NUM_WC = 4,
|
||||
|
||||
IPOIB_MAX_PATH_REC_QUEUE = 3,
|
||||
IPOIB_MAX_MCAST_QUEUE = 3,
|
||||
|
||||
IPOIB_FLAG_OPER_UP = 0,
|
||||
IPOIB_FLAG_INITIALIZED = 1,
|
||||
IPOIB_FLAG_ADMIN_UP = 2,
|
||||
IPOIB_PKEY_ASSIGNED = 3,
|
||||
IPOIB_PKEY_STOP = 4,
|
||||
IPOIB_FLAG_SUBINTERFACE = 5,
|
||||
IPOIB_MCAST_RUN = 6,
|
||||
IPOIB_STOP_REAPER = 7,
|
||||
IPOIB_MCAST_STARTED = 8,
|
||||
IPOIB_FLAG_NETIF_STOPPED = 9,
|
||||
IPOIB_FLAG_ADMIN_CM = 10,
|
||||
|
||||
IPOIB_MAX_BACKOFF_SECONDS = 16,
|
||||
|
||||
IPOIB_MCAST_FLAG_FOUND = 0, /* used in set_multicast_list */
|
||||
IPOIB_MCAST_FLAG_SENDONLY = 1,
|
||||
IPOIB_MCAST_FLAG_BUSY = 2, /* joining or already joined */
|
||||
IPOIB_MCAST_FLAG_ATTACHED = 3,
|
||||
};
|
||||
|
||||
#define IPOIB_OP_RECV (1ul << 31)
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_CM
|
||||
#define IPOIB_CM_OP_SRQ (1ul << 30)
|
||||
#else
|
||||
#define IPOIB_CM_OP_SRQ (0)
|
||||
#endif
|
||||
|
||||
/* structs */
|
||||
|
||||
struct ipoib_header {
|
||||
__be16 proto;
|
||||
u16 reserved;
|
||||
};
|
||||
|
||||
struct ipoib_pseudoheader {
|
||||
u8 hwaddr[INFINIBAND_ALEN];
|
||||
};
|
||||
|
||||
struct ipoib_mcast;
|
||||
|
||||
struct ipoib_rx_buf {
|
||||
struct sk_buff *skb;
|
||||
u64 mapping;
|
||||
};
|
||||
|
||||
struct ipoib_tx_buf {
|
||||
struct sk_buff *skb;
|
||||
u64 mapping;
|
||||
};
|
||||
|
||||
struct ib_cm_id;
|
||||
|
||||
struct ipoib_cm_data {
|
||||
__be32 qpn; /* High byte MUST be ignored on receive */
|
||||
__be32 mtu;
|
||||
};
|
||||
|
||||
struct ipoib_cm_rx {
|
||||
struct ib_cm_id *id;
|
||||
struct ib_qp *qp;
|
||||
struct list_head list;
|
||||
struct net_device *dev;
|
||||
unsigned long jiffies;
|
||||
};
|
||||
|
||||
struct ipoib_cm_tx {
|
||||
struct ib_cm_id *id;
|
||||
struct ib_cq *cq;
|
||||
struct ib_qp *qp;
|
||||
struct list_head list;
|
||||
struct net_device *dev;
|
||||
struct ipoib_neigh *neigh;
|
||||
struct ipoib_path *path;
|
||||
struct ipoib_tx_buf *tx_ring;
|
||||
unsigned tx_head;
|
||||
unsigned tx_tail;
|
||||
unsigned long flags;
|
||||
u32 mtu;
|
||||
struct ib_wc ibwc[IPOIB_NUM_WC];
|
||||
};
|
||||
|
||||
struct ipoib_cm_rx_buf {
|
||||
struct sk_buff *skb;
|
||||
u64 mapping[IPOIB_CM_RX_SG];
|
||||
};
|
||||
|
||||
struct ipoib_cm_dev_priv {
|
||||
struct ib_srq *srq;
|
||||
struct ipoib_cm_rx_buf *srq_ring;
|
||||
struct ib_cm_id *id;
|
||||
struct list_head passive_ids;
|
||||
struct work_struct start_task;
|
||||
struct work_struct reap_task;
|
||||
struct work_struct skb_task;
|
||||
struct delayed_work stale_task;
|
||||
struct sk_buff_head skb_queue;
|
||||
struct list_head start_list;
|
||||
struct list_head reap_list;
|
||||
struct ib_wc ibwc[IPOIB_NUM_WC];
|
||||
struct ib_sge rx_sge[IPOIB_CM_RX_SG];
|
||||
struct ib_recv_wr rx_wr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Device private locking: tx_lock protects members used in TX fast
|
||||
* path (and we use LLTX so upper layers don't do extra locking).
|
||||
* lock protects everything else. lock nests inside of tx_lock (ie
|
||||
* tx_lock must be acquired first if needed).
|
||||
*/
|
||||
struct ipoib_dev_priv {
|
||||
spinlock_t lock;
|
||||
|
||||
struct net_device *dev;
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
struct mutex mcast_mutex;
|
||||
struct mutex vlan_mutex;
|
||||
|
||||
struct rb_root path_tree;
|
||||
struct list_head path_list;
|
||||
|
||||
struct ipoib_mcast *broadcast;
|
||||
struct list_head multicast_list;
|
||||
struct rb_root multicast_tree;
|
||||
|
||||
struct delayed_work pkey_task;
|
||||
struct delayed_work mcast_task;
|
||||
struct work_struct flush_task;
|
||||
struct work_struct restart_task;
|
||||
struct delayed_work ah_reap_task;
|
||||
|
||||
struct ib_device *ca;
|
||||
u8 port;
|
||||
u16 pkey;
|
||||
struct ib_pd *pd;
|
||||
struct ib_mr *mr;
|
||||
struct ib_cq *cq;
|
||||
struct ib_qp *qp;
|
||||
u32 qkey;
|
||||
|
||||
union ib_gid local_gid;
|
||||
u16 local_lid;
|
||||
|
||||
unsigned int admin_mtu;
|
||||
unsigned int mcast_mtu;
|
||||
|
||||
struct ipoib_rx_buf *rx_ring;
|
||||
|
||||
spinlock_t tx_lock;
|
||||
struct ipoib_tx_buf *tx_ring;
|
||||
unsigned tx_head;
|
||||
unsigned tx_tail;
|
||||
struct ib_sge tx_sge;
|
||||
struct ib_send_wr tx_wr;
|
||||
|
||||
struct ib_wc ibwc[IPOIB_NUM_WC];
|
||||
|
||||
struct list_head dead_ahs;
|
||||
|
||||
struct ib_event_handler event_handler;
|
||||
|
||||
struct net_device_stats stats;
|
||||
|
||||
struct net_device *parent;
|
||||
struct list_head child_intfs;
|
||||
struct list_head list;
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_CM
|
||||
struct ipoib_cm_dev_priv cm;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
struct list_head fs_list;
|
||||
struct dentry *mcg_dentry;
|
||||
struct dentry *path_dentry;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ipoib_ah {
|
||||
struct net_device *dev;
|
||||
struct ib_ah *ah;
|
||||
struct list_head list;
|
||||
struct kref ref;
|
||||
unsigned last_send;
|
||||
};
|
||||
|
||||
struct ipoib_path {
|
||||
struct net_device *dev;
|
||||
struct ib_sa_path_rec pathrec;
|
||||
struct ipoib_ah *ah;
|
||||
struct sk_buff_head queue;
|
||||
|
||||
struct list_head neigh_list;
|
||||
|
||||
int query_id;
|
||||
struct ib_sa_query *query;
|
||||
struct completion done;
|
||||
|
||||
struct rb_node rb_node;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ipoib_neigh {
|
||||
struct ipoib_ah *ah;
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_CM
|
||||
struct ipoib_cm_tx *cm;
|
||||
#endif
|
||||
union ib_gid dgid;
|
||||
struct sk_buff_head queue;
|
||||
|
||||
struct neighbour *neighbour;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/*
|
||||
* We stash a pointer to our private neighbour information after our
|
||||
* hardware address in neigh->ha. The ALIGN() expression here makes
|
||||
* sure that this pointer is stored aligned so that an unaligned
|
||||
* load is not needed to dereference it.
|
||||
*/
|
||||
static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
|
||||
{
|
||||
return (void*) neigh + ALIGN(offsetof(struct neighbour, ha) +
|
||||
INFINIBAND_ALEN, sizeof(void *));
|
||||
}
|
||||
|
||||
struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh);
|
||||
void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
|
||||
|
||||
extern struct workqueue_struct *ipoib_workqueue;
|
||||
|
||||
/* functions */
|
||||
|
||||
void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr);
|
||||
|
||||
struct ipoib_ah *ipoib_create_ah(struct net_device *dev,
|
||||
struct ib_pd *pd, struct ib_ah_attr *attr);
|
||||
void ipoib_free_ah(struct kref *kref);
|
||||
static inline void ipoib_put_ah(struct ipoib_ah *ah)
|
||||
{
|
||||
kref_put(&ah->ref, ipoib_free_ah);
|
||||
}
|
||||
|
||||
int ipoib_open(struct net_device *dev);
|
||||
int ipoib_add_pkey_attr(struct net_device *dev);
|
||||
|
||||
void ipoib_send(struct net_device *dev, struct sk_buff *skb,
|
||||
struct ipoib_ah *address, u32 qpn);
|
||||
void ipoib_reap_ah(struct work_struct *work);
|
||||
|
||||
void ipoib_flush_paths(struct net_device *dev);
|
||||
struct ipoib_dev_priv *ipoib_intf_alloc(const char *format);
|
||||
|
||||
int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port);
|
||||
void ipoib_ib_dev_flush(struct work_struct *work);
|
||||
void ipoib_ib_dev_cleanup(struct net_device *dev);
|
||||
|
||||
int ipoib_ib_dev_open(struct net_device *dev);
|
||||
int ipoib_ib_dev_up(struct net_device *dev);
|
||||
int ipoib_ib_dev_down(struct net_device *dev, int flush);
|
||||
int ipoib_ib_dev_stop(struct net_device *dev);
|
||||
|
||||
int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port);
|
||||
void ipoib_dev_cleanup(struct net_device *dev);
|
||||
|
||||
void ipoib_mcast_join_task(struct work_struct *work);
|
||||
void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb);
|
||||
|
||||
void ipoib_mcast_restart_task(struct work_struct *work);
|
||||
int ipoib_mcast_start_thread(struct net_device *dev);
|
||||
int ipoib_mcast_stop_thread(struct net_device *dev, int flush);
|
||||
|
||||
void ipoib_mcast_dev_down(struct net_device *dev);
|
||||
void ipoib_mcast_dev_flush(struct net_device *dev);
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
struct ipoib_mcast_iter *ipoib_mcast_iter_init(struct net_device *dev);
|
||||
int ipoib_mcast_iter_next(struct ipoib_mcast_iter *iter);
|
||||
void ipoib_mcast_iter_read(struct ipoib_mcast_iter *iter,
|
||||
union ib_gid *gid,
|
||||
unsigned long *created,
|
||||
unsigned int *queuelen,
|
||||
unsigned int *complete,
|
||||
unsigned int *send_only);
|
||||
|
||||
struct ipoib_path_iter *ipoib_path_iter_init(struct net_device *dev);
|
||||
int ipoib_path_iter_next(struct ipoib_path_iter *iter);
|
||||
void ipoib_path_iter_read(struct ipoib_path_iter *iter,
|
||||
struct ipoib_path *path);
|
||||
#endif
|
||||
|
||||
int ipoib_mcast_attach(struct net_device *dev, u16 mlid,
|
||||
union ib_gid *mgid);
|
||||
int ipoib_mcast_detach(struct net_device *dev, u16 mlid,
|
||||
union ib_gid *mgid);
|
||||
|
||||
int ipoib_init_qp(struct net_device *dev);
|
||||
int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca);
|
||||
void ipoib_transport_dev_cleanup(struct net_device *dev);
|
||||
|
||||
void ipoib_event(struct ib_event_handler *handler,
|
||||
struct ib_event *record);
|
||||
|
||||
int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey);
|
||||
int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey);
|
||||
|
||||
void ipoib_pkey_poll(struct work_struct *work);
|
||||
int ipoib_pkey_dev_delay_open(struct net_device *dev);
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_CM
|
||||
|
||||
#define IPOIB_FLAGS_RC 0x80
|
||||
#define IPOIB_FLAGS_UC 0x40
|
||||
|
||||
/* We don't support UC connections at the moment */
|
||||
#define IPOIB_CM_SUPPORTED(ha) (ha[0] & (IPOIB_FLAGS_RC))
|
||||
|
||||
static inline int ipoib_cm_admin_enabled(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
return IPOIB_CM_SUPPORTED(dev->dev_addr) &&
|
||||
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
|
||||
}
|
||||
|
||||
static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
return IPOIB_CM_SUPPORTED(n->ha) &&
|
||||
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
|
||||
}
|
||||
|
||||
static inline int ipoib_cm_up(struct ipoib_neigh *neigh)
|
||||
|
||||
{
|
||||
return test_bit(IPOIB_FLAG_OPER_UP, &neigh->cm->flags);
|
||||
}
|
||||
|
||||
static inline struct ipoib_cm_tx *ipoib_cm_get(struct ipoib_neigh *neigh)
|
||||
{
|
||||
return neigh->cm;
|
||||
}
|
||||
|
||||
static inline void ipoib_cm_set(struct ipoib_neigh *neigh, struct ipoib_cm_tx *tx)
|
||||
{
|
||||
neigh->cm = tx;
|
||||
}
|
||||
|
||||
void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_tx *tx);
|
||||
int ipoib_cm_dev_open(struct net_device *dev);
|
||||
void ipoib_cm_dev_stop(struct net_device *dev);
|
||||
int ipoib_cm_dev_init(struct net_device *dev);
|
||||
int ipoib_cm_add_mode_attr(struct net_device *dev);
|
||||
void ipoib_cm_dev_cleanup(struct net_device *dev);
|
||||
struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path *path,
|
||||
struct ipoib_neigh *neigh);
|
||||
void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx);
|
||||
void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
|
||||
unsigned int mtu);
|
||||
void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc);
|
||||
#else
|
||||
|
||||
struct ipoib_cm_tx;
|
||||
|
||||
static inline int ipoib_cm_admin_enabled(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
|
||||
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ipoib_cm_up(struct ipoib_neigh *neigh)
|
||||
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct ipoib_cm_tx *ipoib_cm_get(struct ipoib_neigh *neigh)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void ipoib_cm_set(struct ipoib_neigh *neigh, struct ipoib_cm_tx *tx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_tx *tx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline
|
||||
int ipoib_cm_dev_open(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void ipoib_cm_dev_stop(struct net_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline
|
||||
int ipoib_cm_dev_init(struct net_device *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline
|
||||
void ipoib_cm_dev_cleanup(struct net_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path *path,
|
||||
struct ipoib_neigh *neigh)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline
|
||||
int ipoib_cm_add_mode_attr(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb,
|
||||
unsigned int mtu)
|
||||
{
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
static inline void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
void ipoib_create_debug_files(struct net_device *dev);
|
||||
void ipoib_delete_debug_files(struct net_device *dev);
|
||||
int ipoib_register_debugfs(void);
|
||||
void ipoib_unregister_debugfs(void);
|
||||
#else
|
||||
static inline void ipoib_create_debug_files(struct net_device *dev) { }
|
||||
static inline void ipoib_delete_debug_files(struct net_device *dev) { }
|
||||
static inline int ipoib_register_debugfs(void) { return 0; }
|
||||
static inline void ipoib_unregister_debugfs(void) { }
|
||||
#endif
|
||||
|
||||
|
||||
#define ipoib_printk(level, priv, format, arg...) \
|
||||
printk(level "%s: " format, ((struct ipoib_dev_priv *) priv)->dev->name , ## arg)
|
||||
#define ipoib_warn(priv, format, arg...) \
|
||||
ipoib_printk(KERN_WARNING, priv, format , ## arg)
|
||||
|
||||
extern int ipoib_sendq_size;
|
||||
extern int ipoib_recvq_size;
|
||||
|
||||
extern struct ib_sa_client ipoib_sa_client;
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
extern int ipoib_debug_level;
|
||||
|
||||
#define ipoib_dbg(priv, format, arg...) \
|
||||
do { \
|
||||
if (ipoib_debug_level > 0) \
|
||||
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
|
||||
} while (0)
|
||||
#define ipoib_dbg_mcast(priv, format, arg...) \
|
||||
do { \
|
||||
if (mcast_debug_level > 0) \
|
||||
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
|
||||
} while (0)
|
||||
#else /* CONFIG_INFINIBAND_IPOIB_DEBUG */
|
||||
#define ipoib_dbg(priv, format, arg...) \
|
||||
do { (void) (priv); } while (0)
|
||||
#define ipoib_dbg_mcast(priv, format, arg...) \
|
||||
do { (void) (priv); } while (0)
|
||||
#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG */
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
|
||||
#define ipoib_dbg_data(priv, format, arg...) \
|
||||
do { \
|
||||
if (data_debug_level > 0) \
|
||||
ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
|
||||
} while (0)
|
||||
#else /* CONFIG_INFINIBAND_IPOIB_DEBUG_DATA */
|
||||
#define ipoib_dbg_data(priv, format, arg...) \
|
||||
do { (void) (priv); } while (0)
|
||||
#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG_DATA */
|
||||
|
||||
|
||||
#define IPOIB_GID_FMT "%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:" \
|
||||
"%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x"
|
||||
|
||||
#define IPOIB_GID_RAW_ARG(gid) ((u8 *)(gid))[0], \
|
||||
((u8 *)(gid))[1], \
|
||||
((u8 *)(gid))[2], \
|
||||
((u8 *)(gid))[3], \
|
||||
((u8 *)(gid))[4], \
|
||||
((u8 *)(gid))[5], \
|
||||
((u8 *)(gid))[6], \
|
||||
((u8 *)(gid))[7], \
|
||||
((u8 *)(gid))[8], \
|
||||
((u8 *)(gid))[9], \
|
||||
((u8 *)(gid))[10],\
|
||||
((u8 *)(gid))[11],\
|
||||
((u8 *)(gid))[12],\
|
||||
((u8 *)(gid))[13],\
|
||||
((u8 *)(gid))[14],\
|
||||
((u8 *)(gid))[15]
|
||||
|
||||
#define IPOIB_GID_ARG(gid) IPOIB_GID_RAW_ARG((gid).raw)
|
||||
|
||||
#define IPOIB_QPN(ha) (be32_to_cpup((__be32 *) ha) & 0xffffff)
|
||||
|
||||
#endif /* _IPOIB_H */
|
||||
1245
drivers/infiniband/ulp/ipoib/ipoib_cm.c
Normal file
1245
drivers/infiniband/ulp/ipoib/ipoib_cm.c
Normal file
File diff suppressed because it is too large
Load Diff
300
drivers/infiniband/ulp/ipoib/ipoib_fs.c
Normal file
300
drivers/infiniband/ulp/ipoib/ipoib_fs.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Topspin Communications. 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: ipoib_fs.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
struct file_operations;
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "ipoib.h"
|
||||
|
||||
static struct dentry *ipoib_root;
|
||||
|
||||
static void format_gid(union ib_gid *gid, char *buf)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
for (n = 0, i = 0; i < 8; ++i) {
|
||||
n += sprintf(buf + n, "%x",
|
||||
be16_to_cpu(((__be16 *) gid->raw)[i]));
|
||||
if (i < 7)
|
||||
buf[n++] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
static void *ipoib_mcg_seq_start(struct seq_file *file, loff_t *pos)
|
||||
{
|
||||
struct ipoib_mcast_iter *iter;
|
||||
loff_t n = *pos;
|
||||
|
||||
iter = ipoib_mcast_iter_init(file->private);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
while (n--) {
|
||||
if (ipoib_mcast_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void *ipoib_mcg_seq_next(struct seq_file *file, void *iter_ptr,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct ipoib_mcast_iter *iter = iter_ptr;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (ipoib_mcast_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void ipoib_mcg_seq_stop(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
/* nothing for now */
|
||||
}
|
||||
|
||||
static int ipoib_mcg_seq_show(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
struct ipoib_mcast_iter *iter = iter_ptr;
|
||||
char gid_buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
|
||||
union ib_gid mgid;
|
||||
unsigned long created;
|
||||
unsigned int queuelen, complete, send_only;
|
||||
|
||||
if (!iter)
|
||||
return 0;
|
||||
|
||||
ipoib_mcast_iter_read(iter, &mgid, &created, &queuelen,
|
||||
&complete, &send_only);
|
||||
|
||||
format_gid(&mgid, gid_buf);
|
||||
|
||||
seq_printf(file,
|
||||
"GID: %s\n"
|
||||
" created: %10ld\n"
|
||||
" queuelen: %9d\n"
|
||||
" complete: %9s\n"
|
||||
" send_only: %8s\n"
|
||||
"\n",
|
||||
gid_buf, created, queuelen,
|
||||
complete ? "yes" : "no",
|
||||
send_only ? "yes" : "no");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ipoib_mcg_seq_ops = {
|
||||
.start = ipoib_mcg_seq_start,
|
||||
.next = ipoib_mcg_seq_next,
|
||||
.stop = ipoib_mcg_seq_stop,
|
||||
.show = ipoib_mcg_seq_show,
|
||||
};
|
||||
|
||||
static int ipoib_mcg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &ipoib_mcg_seq_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ipoib_mcg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ipoib_mcg_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
static void *ipoib_path_seq_start(struct seq_file *file, loff_t *pos)
|
||||
{
|
||||
struct ipoib_path_iter *iter;
|
||||
loff_t n = *pos;
|
||||
|
||||
iter = ipoib_path_iter_init(file->private);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
while (n--) {
|
||||
if (ipoib_path_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void *ipoib_path_seq_next(struct seq_file *file, void *iter_ptr,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct ipoib_path_iter *iter = iter_ptr;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (ipoib_path_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static void ipoib_path_seq_stop(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
/* nothing for now */
|
||||
}
|
||||
|
||||
static int ipoib_path_seq_show(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
struct ipoib_path_iter *iter = iter_ptr;
|
||||
char gid_buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
|
||||
struct ipoib_path path;
|
||||
int rate;
|
||||
|
||||
if (!iter)
|
||||
return 0;
|
||||
|
||||
ipoib_path_iter_read(iter, &path);
|
||||
|
||||
format_gid(&path.pathrec.dgid, gid_buf);
|
||||
|
||||
seq_printf(file,
|
||||
"GID: %s\n"
|
||||
" complete: %6s\n",
|
||||
gid_buf, path.pathrec.dlid ? "yes" : "no");
|
||||
|
||||
if (path.pathrec.dlid) {
|
||||
rate = ib_rate_to_mult(path.pathrec.rate) * 25;
|
||||
|
||||
seq_printf(file,
|
||||
" DLID: 0x%04x\n"
|
||||
" SL: %12d\n"
|
||||
" rate: %*d%s Gb/sec\n",
|
||||
be16_to_cpu(path.pathrec.dlid),
|
||||
path.pathrec.sl,
|
||||
10 - ((rate % 10) ? 2 : 0),
|
||||
rate / 10, rate % 10 ? ".5" : "");
|
||||
}
|
||||
|
||||
seq_putc(file, '\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ipoib_path_seq_ops = {
|
||||
.start = ipoib_path_seq_start,
|
||||
.next = ipoib_path_seq_next,
|
||||
.stop = ipoib_path_seq_stop,
|
||||
.show = ipoib_path_seq_show,
|
||||
};
|
||||
|
||||
static int ipoib_path_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &ipoib_path_seq_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations ipoib_path_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ipoib_path_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
void ipoib_create_debug_files(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
char name[IFNAMSIZ + sizeof "_path"];
|
||||
|
||||
snprintf(name, sizeof name, "%s_mcg", dev->name);
|
||||
priv->mcg_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
|
||||
ipoib_root, dev, &ipoib_mcg_fops);
|
||||
if (!priv->mcg_dentry)
|
||||
ipoib_warn(priv, "failed to create mcg debug file\n");
|
||||
|
||||
snprintf(name, sizeof name, "%s_path", dev->name);
|
||||
priv->path_dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
|
||||
ipoib_root, dev, &ipoib_path_fops);
|
||||
if (!priv->path_dentry)
|
||||
ipoib_warn(priv, "failed to create path debug file\n");
|
||||
}
|
||||
|
||||
void ipoib_delete_debug_files(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->mcg_dentry)
|
||||
debugfs_remove(priv->mcg_dentry);
|
||||
if (priv->path_dentry)
|
||||
debugfs_remove(priv->path_dentry);
|
||||
}
|
||||
|
||||
int ipoib_register_debugfs(void)
|
||||
{
|
||||
ipoib_root = debugfs_create_dir("ipoib", NULL);
|
||||
return ipoib_root ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void ipoib_unregister_debugfs(void)
|
||||
{
|
||||
debugfs_remove(ipoib_root);
|
||||
}
|
||||
725
drivers/infiniband/ulp/ipoib/ipoib_ib.c
Normal file
725
drivers/infiniband/ulp/ipoib/ipoib_ib.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
|
||||
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Copyright (c) 2005 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2004, 2005 Voltaire, 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: ipoib_ib.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <rdma/ib_cache.h>
|
||||
|
||||
#include "ipoib.h"
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
|
||||
static int data_debug_level;
|
||||
|
||||
module_param(data_debug_level, int, 0644);
|
||||
MODULE_PARM_DESC(data_debug_level,
|
||||
"Enable data path debug tracing if > 0");
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(pkey_mutex);
|
||||
|
||||
struct ipoib_ah *ipoib_create_ah(struct net_device *dev,
|
||||
struct ib_pd *pd, struct ib_ah_attr *attr)
|
||||
{
|
||||
struct ipoib_ah *ah;
|
||||
|
||||
ah = kmalloc(sizeof *ah, GFP_KERNEL);
|
||||
if (!ah)
|
||||
return NULL;
|
||||
|
||||
ah->dev = dev;
|
||||
ah->last_send = 0;
|
||||
kref_init(&ah->ref);
|
||||
|
||||
ah->ah = ib_create_ah(pd, attr);
|
||||
if (IS_ERR(ah->ah)) {
|
||||
kfree(ah);
|
||||
ah = NULL;
|
||||
} else
|
||||
ipoib_dbg(netdev_priv(dev), "Created ah %p\n", ah->ah);
|
||||
|
||||
return ah;
|
||||
}
|
||||
|
||||
void ipoib_free_ah(struct kref *kref)
|
||||
{
|
||||
struct ipoib_ah *ah = container_of(kref, struct ipoib_ah, ref);
|
||||
struct ipoib_dev_priv *priv = netdev_priv(ah->dev);
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
list_add_tail(&ah->list, &priv->dead_ahs);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int ipoib_ib_post_receive(struct net_device *dev, int id)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_sge list;
|
||||
struct ib_recv_wr param;
|
||||
struct ib_recv_wr *bad_wr;
|
||||
int ret;
|
||||
|
||||
list.addr = priv->rx_ring[id].mapping;
|
||||
list.length = IPOIB_BUF_SIZE;
|
||||
list.lkey = priv->mr->lkey;
|
||||
|
||||
param.next = NULL;
|
||||
param.wr_id = id | IPOIB_OP_RECV;
|
||||
param.sg_list = &list;
|
||||
param.num_sge = 1;
|
||||
|
||||
ret = ib_post_recv(priv->qp, ¶m, &bad_wr);
|
||||
if (unlikely(ret)) {
|
||||
ipoib_warn(priv, "receive failed for buf %d (%d)\n", id, ret);
|
||||
ib_dma_unmap_single(priv->ca, priv->rx_ring[id].mapping,
|
||||
IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
dev_kfree_skb_any(priv->rx_ring[id].skb);
|
||||
priv->rx_ring[id].skb = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipoib_alloc_rx_skb(struct net_device *dev, int id)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct sk_buff *skb;
|
||||
u64 addr;
|
||||
|
||||
skb = dev_alloc_skb(IPOIB_BUF_SIZE + 4);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte
|
||||
* header. So we need 4 more bytes to get to 48 and align the
|
||||
* IP header to a multiple of 16.
|
||||
*/
|
||||
skb_reserve(skb, 4);
|
||||
|
||||
addr = ib_dma_map_single(priv->ca, skb->data, IPOIB_BUF_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (unlikely(ib_dma_mapping_error(priv->ca, addr))) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
priv->rx_ring[id].skb = skb;
|
||||
priv->rx_ring[id].mapping = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipoib_ib_post_receives(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ipoib_recvq_size; ++i) {
|
||||
if (ipoib_alloc_rx_skb(dev, i)) {
|
||||
ipoib_warn(priv, "failed to allocate receive buffer %d\n", i);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ipoib_ib_post_receive(dev, i)) {
|
||||
ipoib_warn(priv, "ipoib_ib_post_receive failed for buf %d\n", i);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
unsigned int wr_id = wc->wr_id & ~IPOIB_OP_RECV;
|
||||
struct sk_buff *skb;
|
||||
u64 addr;
|
||||
|
||||
ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n",
|
||||
wr_id, wc->opcode, wc->status);
|
||||
|
||||
if (unlikely(wr_id >= ipoib_recvq_size)) {
|
||||
ipoib_warn(priv, "recv completion event with wrid %d (> %d)\n",
|
||||
wr_id, ipoib_recvq_size);
|
||||
return;
|
||||
}
|
||||
|
||||
skb = priv->rx_ring[wr_id].skb;
|
||||
addr = priv->rx_ring[wr_id].mapping;
|
||||
|
||||
if (unlikely(wc->status != IB_WC_SUCCESS)) {
|
||||
if (wc->status != IB_WC_WR_FLUSH_ERR)
|
||||
ipoib_warn(priv, "failed recv event "
|
||||
"(status=%d, wrid=%d vend_err %x)\n",
|
||||
wc->status, wr_id, wc->vendor_err);
|
||||
ib_dma_unmap_single(priv->ca, addr,
|
||||
IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
dev_kfree_skb_any(skb);
|
||||
priv->rx_ring[wr_id].skb = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can't allocate a new RX buffer, dump
|
||||
* this packet and reuse the old buffer.
|
||||
*/
|
||||
if (unlikely(ipoib_alloc_rx_skb(dev, wr_id))) {
|
||||
++priv->stats.rx_dropped;
|
||||
goto repost;
|
||||
}
|
||||
|
||||
ipoib_dbg_data(priv, "received %d bytes, SLID 0x%04x\n",
|
||||
wc->byte_len, wc->slid);
|
||||
|
||||
ib_dma_unmap_single(priv->ca, addr, IPOIB_BUF_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
skb_put(skb, wc->byte_len);
|
||||
skb_pull(skb, IB_GRH_BYTES);
|
||||
|
||||
if (wc->slid != priv->local_lid ||
|
||||
wc->src_qp != priv->qp->qp_num) {
|
||||
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
|
||||
skb->mac.raw = skb->data;
|
||||
skb_pull(skb, IPOIB_ENCAP_LEN);
|
||||
|
||||
dev->last_rx = jiffies;
|
||||
++priv->stats.rx_packets;
|
||||
priv->stats.rx_bytes += skb->len;
|
||||
|
||||
skb->dev = dev;
|
||||
/* XXX get correct PACKET_ type here */
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
netif_rx_ni(skb);
|
||||
} else {
|
||||
ipoib_dbg_data(priv, "dropping loopback packet\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
repost:
|
||||
if (unlikely(ipoib_ib_post_receive(dev, wr_id)))
|
||||
ipoib_warn(priv, "ipoib_ib_post_receive failed "
|
||||
"for buf %d\n", wr_id);
|
||||
}
|
||||
|
||||
static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
unsigned int wr_id = wc->wr_id;
|
||||
struct ipoib_tx_buf *tx_req;
|
||||
unsigned long flags;
|
||||
|
||||
ipoib_dbg_data(priv, "send completion: id %d, op %d, status: %d\n",
|
||||
wr_id, wc->opcode, wc->status);
|
||||
|
||||
if (unlikely(wr_id >= ipoib_sendq_size)) {
|
||||
ipoib_warn(priv, "send completion event with wrid %d (> %d)\n",
|
||||
wr_id, ipoib_sendq_size);
|
||||
return;
|
||||
}
|
||||
|
||||
tx_req = &priv->tx_ring[wr_id];
|
||||
|
||||
ib_dma_unmap_single(priv->ca, tx_req->mapping,
|
||||
tx_req->skb->len, DMA_TO_DEVICE);
|
||||
|
||||
++priv->stats.tx_packets;
|
||||
priv->stats.tx_bytes += tx_req->skb->len;
|
||||
|
||||
dev_kfree_skb_any(tx_req->skb);
|
||||
|
||||
spin_lock_irqsave(&priv->tx_lock, flags);
|
||||
++priv->tx_tail;
|
||||
if (unlikely(test_bit(IPOIB_FLAG_NETIF_STOPPED, &priv->flags)) &&
|
||||
priv->tx_head - priv->tx_tail <= ipoib_sendq_size >> 1) {
|
||||
clear_bit(IPOIB_FLAG_NETIF_STOPPED, &priv->flags);
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->tx_lock, flags);
|
||||
|
||||
if (wc->status != IB_WC_SUCCESS &&
|
||||
wc->status != IB_WC_WR_FLUSH_ERR)
|
||||
ipoib_warn(priv, "failed send event "
|
||||
"(status=%d, wrid=%d vend_err %x)\n",
|
||||
wc->status, wr_id, wc->vendor_err);
|
||||
}
|
||||
|
||||
static void ipoib_ib_handle_wc(struct net_device *dev, struct ib_wc *wc)
|
||||
{
|
||||
if (wc->wr_id & IPOIB_CM_OP_SRQ)
|
||||
ipoib_cm_handle_rx_wc(dev, wc);
|
||||
else if (wc->wr_id & IPOIB_OP_RECV)
|
||||
ipoib_ib_handle_rx_wc(dev, wc);
|
||||
else
|
||||
ipoib_ib_handle_tx_wc(dev, wc);
|
||||
}
|
||||
|
||||
void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *) dev_ptr;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int n, i;
|
||||
|
||||
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
|
||||
do {
|
||||
n = ib_poll_cq(cq, IPOIB_NUM_WC, priv->ibwc);
|
||||
for (i = 0; i < n; ++i)
|
||||
ipoib_ib_handle_wc(dev, priv->ibwc + i);
|
||||
} while (n == IPOIB_NUM_WC);
|
||||
}
|
||||
|
||||
static inline int post_send(struct ipoib_dev_priv *priv,
|
||||
unsigned int wr_id,
|
||||
struct ib_ah *address, u32 qpn,
|
||||
u64 addr, int len)
|
||||
{
|
||||
struct ib_send_wr *bad_wr;
|
||||
|
||||
priv->tx_sge.addr = addr;
|
||||
priv->tx_sge.length = len;
|
||||
|
||||
priv->tx_wr.wr_id = wr_id;
|
||||
priv->tx_wr.wr.ud.remote_qpn = qpn;
|
||||
priv->tx_wr.wr.ud.ah = address;
|
||||
|
||||
return ib_post_send(priv->qp, &priv->tx_wr, &bad_wr);
|
||||
}
|
||||
|
||||
void ipoib_send(struct net_device *dev, struct sk_buff *skb,
|
||||
struct ipoib_ah *address, u32 qpn)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_tx_buf *tx_req;
|
||||
u64 addr;
|
||||
|
||||
if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) {
|
||||
ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
|
||||
skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN);
|
||||
++priv->stats.tx_dropped;
|
||||
++priv->stats.tx_errors;
|
||||
ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu);
|
||||
return;
|
||||
}
|
||||
|
||||
ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n",
|
||||
skb->len, address, qpn);
|
||||
|
||||
/*
|
||||
* We put the skb into the tx_ring _before_ we call post_send()
|
||||
* because it's entirely possible that the completion handler will
|
||||
* run before we execute anything after the post_send(). That
|
||||
* means we have to make sure everything is properly recorded and
|
||||
* our state is consistent before we call post_send().
|
||||
*/
|
||||
tx_req = &priv->tx_ring[priv->tx_head & (ipoib_sendq_size - 1)];
|
||||
tx_req->skb = skb;
|
||||
addr = ib_dma_map_single(priv->ca, skb->data, skb->len,
|
||||
DMA_TO_DEVICE);
|
||||
if (unlikely(ib_dma_mapping_error(priv->ca, addr))) {
|
||||
++priv->stats.tx_errors;
|
||||
dev_kfree_skb_any(skb);
|
||||
return;
|
||||
}
|
||||
tx_req->mapping = addr;
|
||||
|
||||
if (unlikely(post_send(priv, priv->tx_head & (ipoib_sendq_size - 1),
|
||||
address->ah, qpn, addr, skb->len))) {
|
||||
ipoib_warn(priv, "post_send failed\n");
|
||||
++priv->stats.tx_errors;
|
||||
ib_dma_unmap_single(priv->ca, addr, skb->len, DMA_TO_DEVICE);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
address->last_send = priv->tx_head;
|
||||
++priv->tx_head;
|
||||
|
||||
if (priv->tx_head - priv->tx_tail == ipoib_sendq_size) {
|
||||
ipoib_dbg(priv, "TX ring full, stopping kernel net queue\n");
|
||||
netif_stop_queue(dev);
|
||||
set_bit(IPOIB_FLAG_NETIF_STOPPED, &priv->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __ipoib_reap_ah(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_ah *ah, *tah;
|
||||
LIST_HEAD(remove_list);
|
||||
|
||||
spin_lock_irq(&priv->tx_lock);
|
||||
spin_lock(&priv->lock);
|
||||
list_for_each_entry_safe(ah, tah, &priv->dead_ahs, list)
|
||||
if ((int) priv->tx_tail - (int) ah->last_send >= 0) {
|
||||
list_del(&ah->list);
|
||||
ib_destroy_ah(ah->ah);
|
||||
kfree(ah);
|
||||
}
|
||||
spin_unlock(&priv->lock);
|
||||
spin_unlock_irq(&priv->tx_lock);
|
||||
}
|
||||
|
||||
void ipoib_reap_ah(struct work_struct *work)
|
||||
{
|
||||
struct ipoib_dev_priv *priv =
|
||||
container_of(work, struct ipoib_dev_priv, ah_reap_task.work);
|
||||
struct net_device *dev = priv->dev;
|
||||
|
||||
__ipoib_reap_ah(dev);
|
||||
|
||||
if (!test_bit(IPOIB_STOP_REAPER, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, HZ);
|
||||
}
|
||||
|
||||
int ipoib_ib_dev_open(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = ipoib_init_qp(dev);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "ipoib_init_qp returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ipoib_ib_post_receives(dev);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "ipoib_ib_post_receives returned %d\n", ret);
|
||||
ipoib_ib_dev_stop(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ipoib_cm_dev_open(dev);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "ipoib_ib_post_receives returned %d\n", ret);
|
||||
ipoib_ib_dev_stop(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
clear_bit(IPOIB_STOP_REAPER, &priv->flags);
|
||||
queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, HZ);
|
||||
|
||||
set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipoib_pkey_dev_check_presence(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
u16 pkey_index = 0;
|
||||
|
||||
if (ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index))
|
||||
clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
else
|
||||
set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
}
|
||||
|
||||
int ipoib_ib_dev_up(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_pkey_dev_check_presence(dev);
|
||||
|
||||
if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
|
||||
ipoib_dbg(priv, "PKEY is not assigned.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
|
||||
|
||||
return ipoib_mcast_start_thread(dev);
|
||||
}
|
||||
|
||||
int ipoib_ib_dev_down(struct net_device *dev, int flush)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_dbg(priv, "downing ib_dev\n");
|
||||
|
||||
clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
|
||||
netif_carrier_off(dev);
|
||||
|
||||
/* Shutdown the P_Key thread if still active */
|
||||
if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
|
||||
mutex_lock(&pkey_mutex);
|
||||
set_bit(IPOIB_PKEY_STOP, &priv->flags);
|
||||
cancel_delayed_work(&priv->pkey_task);
|
||||
mutex_unlock(&pkey_mutex);
|
||||
if (flush)
|
||||
flush_workqueue(ipoib_workqueue);
|
||||
}
|
||||
|
||||
ipoib_mcast_stop_thread(dev, flush);
|
||||
ipoib_mcast_dev_flush(dev);
|
||||
|
||||
ipoib_flush_paths(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recvs_pending(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int pending = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ipoib_recvq_size; ++i)
|
||||
if (priv->rx_ring[i].skb)
|
||||
++pending;
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
int ipoib_ib_dev_stop(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_qp_attr qp_attr;
|
||||
unsigned long begin;
|
||||
struct ipoib_tx_buf *tx_req;
|
||||
int i;
|
||||
|
||||
clear_bit(IPOIB_FLAG_INITIALIZED, &priv->flags);
|
||||
|
||||
ipoib_cm_dev_stop(dev);
|
||||
|
||||
/*
|
||||
* Move our QP to the error state and then reinitialize in
|
||||
* when all work requests have completed or have been flushed.
|
||||
*/
|
||||
qp_attr.qp_state = IB_QPS_ERR;
|
||||
if (ib_modify_qp(priv->qp, &qp_attr, IB_QP_STATE))
|
||||
ipoib_warn(priv, "Failed to modify QP to ERROR state\n");
|
||||
|
||||
/* Wait for all sends and receives to complete */
|
||||
begin = jiffies;
|
||||
|
||||
while (priv->tx_head != priv->tx_tail || recvs_pending(dev)) {
|
||||
if (time_after(jiffies, begin + 5 * HZ)) {
|
||||
ipoib_warn(priv, "timing out; %d sends %d receives not completed\n",
|
||||
priv->tx_head - priv->tx_tail, recvs_pending(dev));
|
||||
|
||||
/*
|
||||
* assume the HW is wedged and just free up
|
||||
* all our pending work requests.
|
||||
*/
|
||||
while ((int) priv->tx_tail - (int) priv->tx_head < 0) {
|
||||
tx_req = &priv->tx_ring[priv->tx_tail &
|
||||
(ipoib_sendq_size - 1)];
|
||||
ib_dma_unmap_single(priv->ca,
|
||||
tx_req->mapping,
|
||||
tx_req->skb->len,
|
||||
DMA_TO_DEVICE);
|
||||
dev_kfree_skb_any(tx_req->skb);
|
||||
++priv->tx_tail;
|
||||
}
|
||||
|
||||
for (i = 0; i < ipoib_recvq_size; ++i) {
|
||||
struct ipoib_rx_buf *rx_req;
|
||||
|
||||
rx_req = &priv->rx_ring[i];
|
||||
if (!rx_req->skb)
|
||||
continue;
|
||||
ib_dma_unmap_single(priv->ca,
|
||||
rx_req->mapping,
|
||||
IPOIB_BUF_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
dev_kfree_skb_any(rx_req->skb);
|
||||
rx_req->skb = NULL;
|
||||
}
|
||||
|
||||
goto timeout;
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
ipoib_dbg(priv, "All sends and receives done.\n");
|
||||
|
||||
timeout:
|
||||
qp_attr.qp_state = IB_QPS_RESET;
|
||||
if (ib_modify_qp(priv->qp, &qp_attr, IB_QP_STATE))
|
||||
ipoib_warn(priv, "Failed to modify QP to RESET state\n");
|
||||
|
||||
/* Wait for all AHs to be reaped */
|
||||
set_bit(IPOIB_STOP_REAPER, &priv->flags);
|
||||
cancel_delayed_work(&priv->ah_reap_task);
|
||||
flush_workqueue(ipoib_workqueue);
|
||||
|
||||
begin = jiffies;
|
||||
|
||||
while (!list_empty(&priv->dead_ahs)) {
|
||||
__ipoib_reap_ah(dev);
|
||||
|
||||
if (time_after(jiffies, begin + HZ)) {
|
||||
ipoib_warn(priv, "timing out; will leak address handles\n");
|
||||
break;
|
||||
}
|
||||
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
priv->ca = ca;
|
||||
priv->port = port;
|
||||
priv->qp = NULL;
|
||||
|
||||
if (ipoib_transport_dev_init(dev, ca)) {
|
||||
printk(KERN_WARNING "%s: ipoib_transport_dev_init failed\n", ca->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (dev->flags & IFF_UP) {
|
||||
if (ipoib_ib_dev_open(dev)) {
|
||||
ipoib_transport_dev_cleanup(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipoib_ib_dev_flush(struct work_struct *work)
|
||||
{
|
||||
struct ipoib_dev_priv *cpriv, *priv =
|
||||
container_of(work, struct ipoib_dev_priv, flush_task);
|
||||
struct net_device *dev = priv->dev;
|
||||
|
||||
if (!test_bit(IPOIB_FLAG_INITIALIZED, &priv->flags) ) {
|
||||
ipoib_dbg(priv, "Not flushing - IPOIB_FLAG_INITIALIZED not set.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)) {
|
||||
ipoib_dbg(priv, "Not flushing - IPOIB_FLAG_ADMIN_UP not set.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipoib_dbg(priv, "flushing\n");
|
||||
|
||||
ipoib_ib_dev_down(dev, 0);
|
||||
|
||||
/*
|
||||
* The device could have been brought down between the start and when
|
||||
* we get here, don't bring it back up if it's not configured up
|
||||
*/
|
||||
if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)) {
|
||||
ipoib_ib_dev_up(dev);
|
||||
ipoib_mcast_restart_task(&priv->restart_task);
|
||||
}
|
||||
|
||||
mutex_lock(&priv->vlan_mutex);
|
||||
|
||||
/* Flush any child interfaces too */
|
||||
list_for_each_entry(cpriv, &priv->child_intfs, list)
|
||||
ipoib_ib_dev_flush(&cpriv->flush_task);
|
||||
|
||||
mutex_unlock(&priv->vlan_mutex);
|
||||
}
|
||||
|
||||
void ipoib_ib_dev_cleanup(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_dbg(priv, "cleaning up ib_dev\n");
|
||||
|
||||
ipoib_mcast_stop_thread(dev, 1);
|
||||
ipoib_mcast_dev_flush(dev);
|
||||
|
||||
ipoib_transport_dev_cleanup(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delayed P_Key Assigment Interim Support
|
||||
*
|
||||
* The following is initial implementation of delayed P_Key assigment
|
||||
* mechanism. It is using the same approach implemented for the multicast
|
||||
* group join. The single goal of this implementation is to quickly address
|
||||
* Bug #2507. This implementation will probably be removed when the P_Key
|
||||
* change async notification is available.
|
||||
*/
|
||||
|
||||
void ipoib_pkey_poll(struct work_struct *work)
|
||||
{
|
||||
struct ipoib_dev_priv *priv =
|
||||
container_of(work, struct ipoib_dev_priv, pkey_task.work);
|
||||
struct net_device *dev = priv->dev;
|
||||
|
||||
ipoib_pkey_dev_check_presence(dev);
|
||||
|
||||
if (test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags))
|
||||
ipoib_open(dev);
|
||||
else {
|
||||
mutex_lock(&pkey_mutex);
|
||||
if (!test_bit(IPOIB_PKEY_STOP, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue,
|
||||
&priv->pkey_task,
|
||||
HZ);
|
||||
mutex_unlock(&pkey_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int ipoib_pkey_dev_delay_open(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Look for the interface pkey value in the IB Port P_Key table and */
|
||||
/* set the interface pkey assigment flag */
|
||||
ipoib_pkey_dev_check_presence(dev);
|
||||
|
||||
/* P_Key value not assigned yet - start polling */
|
||||
if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
|
||||
mutex_lock(&pkey_mutex);
|
||||
clear_bit(IPOIB_PKEY_STOP, &priv->flags);
|
||||
queue_delayed_work(ipoib_workqueue,
|
||||
&priv->pkey_task,
|
||||
HZ);
|
||||
mutex_unlock(&pkey_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
1277
drivers/infiniband/ulp/ipoib/ipoib_main.c
Normal file
1277
drivers/infiniband/ulp/ipoib/ipoib_main.c
Normal file
File diff suppressed because it is too large
Load Diff
948
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
Normal file
948
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
Normal file
@@ -0,0 +1,948 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
|
||||
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Copyright (c) 2004 Voltaire, 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: ipoib_multicast.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/igmp.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include <net/dst.h>
|
||||
|
||||
#include "ipoib.h"
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
static int mcast_debug_level;
|
||||
|
||||
module_param(mcast_debug_level, int, 0644);
|
||||
MODULE_PARM_DESC(mcast_debug_level,
|
||||
"Enable multicast debug tracing if > 0");
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(mcast_mutex);
|
||||
|
||||
/* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */
|
||||
struct ipoib_mcast {
|
||||
struct ib_sa_mcmember_rec mcmember;
|
||||
struct ib_sa_multicast *mc;
|
||||
struct ipoib_ah *ah;
|
||||
|
||||
struct rb_node rb_node;
|
||||
struct list_head list;
|
||||
|
||||
unsigned long created;
|
||||
unsigned long backoff;
|
||||
|
||||
unsigned long flags;
|
||||
unsigned char logcount;
|
||||
|
||||
struct list_head neigh_list;
|
||||
|
||||
struct sk_buff_head pkt_queue;
|
||||
|
||||
struct net_device *dev;
|
||||
};
|
||||
|
||||
struct ipoib_mcast_iter {
|
||||
struct net_device *dev;
|
||||
union ib_gid mgid;
|
||||
unsigned long created;
|
||||
unsigned int queuelen;
|
||||
unsigned int complete;
|
||||
unsigned int send_only;
|
||||
};
|
||||
|
||||
static void ipoib_mcast_free(struct ipoib_mcast *mcast)
|
||||
{
|
||||
struct net_device *dev = mcast->dev;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_neigh *neigh, *tmp;
|
||||
unsigned long flags;
|
||||
int tx_dropped = 0;
|
||||
|
||||
ipoib_dbg_mcast(netdev_priv(dev),
|
||||
"deleting multicast group " IPOIB_GID_FMT "\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) {
|
||||
/*
|
||||
* It's safe to call ipoib_put_ah() inside priv->lock
|
||||
* here, because we know that mcast->ah will always
|
||||
* hold one more reference, so ipoib_put_ah() will
|
||||
* never do more than decrement the ref count.
|
||||
*/
|
||||
if (neigh->ah)
|
||||
ipoib_put_ah(neigh->ah);
|
||||
ipoib_neigh_free(dev, neigh);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (mcast->ah)
|
||||
ipoib_put_ah(mcast->ah);
|
||||
|
||||
while (!skb_queue_empty(&mcast->pkt_queue)) {
|
||||
++tx_dropped;
|
||||
dev_kfree_skb_any(skb_dequeue(&mcast->pkt_queue));
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv->tx_lock, flags);
|
||||
priv->stats.tx_dropped += tx_dropped;
|
||||
spin_unlock_irqrestore(&priv->tx_lock, flags);
|
||||
|
||||
kfree(mcast);
|
||||
}
|
||||
|
||||
static struct ipoib_mcast *ipoib_mcast_alloc(struct net_device *dev,
|
||||
int can_sleep)
|
||||
{
|
||||
struct ipoib_mcast *mcast;
|
||||
|
||||
mcast = kzalloc(sizeof *mcast, can_sleep ? GFP_KERNEL : GFP_ATOMIC);
|
||||
if (!mcast)
|
||||
return NULL;
|
||||
|
||||
mcast->dev = dev;
|
||||
mcast->created = jiffies;
|
||||
mcast->backoff = 1;
|
||||
|
||||
INIT_LIST_HEAD(&mcast->list);
|
||||
INIT_LIST_HEAD(&mcast->neigh_list);
|
||||
skb_queue_head_init(&mcast->pkt_queue);
|
||||
|
||||
return mcast;
|
||||
}
|
||||
|
||||
static struct ipoib_mcast *__ipoib_mcast_find(struct net_device *dev, void *mgid)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct rb_node *n = priv->multicast_tree.rb_node;
|
||||
|
||||
while (n) {
|
||||
struct ipoib_mcast *mcast;
|
||||
int ret;
|
||||
|
||||
mcast = rb_entry(n, struct ipoib_mcast, rb_node);
|
||||
|
||||
ret = memcmp(mgid, mcast->mcmember.mgid.raw,
|
||||
sizeof (union ib_gid));
|
||||
if (ret < 0)
|
||||
n = n->rb_left;
|
||||
else if (ret > 0)
|
||||
n = n->rb_right;
|
||||
else
|
||||
return mcast;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __ipoib_mcast_add(struct net_device *dev, struct ipoib_mcast *mcast)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct rb_node **n = &priv->multicast_tree.rb_node, *pn = NULL;
|
||||
|
||||
while (*n) {
|
||||
struct ipoib_mcast *tmcast;
|
||||
int ret;
|
||||
|
||||
pn = *n;
|
||||
tmcast = rb_entry(pn, struct ipoib_mcast, rb_node);
|
||||
|
||||
ret = memcmp(mcast->mcmember.mgid.raw, tmcast->mcmember.mgid.raw,
|
||||
sizeof (union ib_gid));
|
||||
if (ret < 0)
|
||||
n = &pn->rb_left;
|
||||
else if (ret > 0)
|
||||
n = &pn->rb_right;
|
||||
else
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
rb_link_node(&mcast->rb_node, pn, n);
|
||||
rb_insert_color(&mcast->rb_node, &priv->multicast_tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
|
||||
struct ib_sa_mcmember_rec *mcmember)
|
||||
{
|
||||
struct net_device *dev = mcast->dev;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_ah *ah;
|
||||
int ret;
|
||||
|
||||
mcast->mcmember = *mcmember;
|
||||
|
||||
/* Set the cached Q_Key before we attach if it's the broadcast group */
|
||||
if (!memcmp(mcast->mcmember.mgid.raw, priv->dev->broadcast + 4,
|
||||
sizeof (union ib_gid))) {
|
||||
priv->qkey = be32_to_cpu(priv->broadcast->mcmember.qkey);
|
||||
priv->tx_wr.wr.ud.remote_qkey = priv->qkey;
|
||||
}
|
||||
|
||||
if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
|
||||
if (test_and_set_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
|
||||
ipoib_warn(priv, "multicast group " IPOIB_GID_FMT
|
||||
" already attached\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ipoib_mcast_attach(dev, be16_to_cpu(mcast->mcmember.mlid),
|
||||
&mcast->mcmember.mgid);
|
||||
if (ret < 0) {
|
||||
ipoib_warn(priv, "couldn't attach QP to multicast group "
|
||||
IPOIB_GID_FMT "\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
struct ib_ah_attr av = {
|
||||
.dlid = be16_to_cpu(mcast->mcmember.mlid),
|
||||
.port_num = priv->port,
|
||||
.sl = mcast->mcmember.sl,
|
||||
.ah_flags = IB_AH_GRH,
|
||||
.static_rate = mcast->mcmember.rate,
|
||||
.grh = {
|
||||
.flow_label = be32_to_cpu(mcast->mcmember.flow_label),
|
||||
.hop_limit = mcast->mcmember.hop_limit,
|
||||
.sgid_index = 0,
|
||||
.traffic_class = mcast->mcmember.traffic_class
|
||||
}
|
||||
};
|
||||
av.grh.dgid = mcast->mcmember.mgid;
|
||||
|
||||
ah = ipoib_create_ah(dev, priv->pd, &av);
|
||||
if (!ah) {
|
||||
ipoib_warn(priv, "ib_address_create failed\n");
|
||||
} else {
|
||||
spin_lock_irq(&priv->lock);
|
||||
mcast->ah = ah;
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
ipoib_dbg_mcast(priv, "MGID " IPOIB_GID_FMT
|
||||
" AV %p, LID 0x%04x, SL %d\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid),
|
||||
mcast->ah->ah,
|
||||
be16_to_cpu(mcast->mcmember.mlid),
|
||||
mcast->mcmember.sl);
|
||||
}
|
||||
}
|
||||
|
||||
/* actually send any queued packets */
|
||||
spin_lock_irq(&priv->tx_lock);
|
||||
while (!skb_queue_empty(&mcast->pkt_queue)) {
|
||||
struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
|
||||
spin_unlock_irq(&priv->tx_lock);
|
||||
|
||||
skb->dev = dev;
|
||||
|
||||
if (!skb->dst || !skb->dst->neighbour) {
|
||||
/* put pseudoheader back on for next time */
|
||||
skb_push(skb, sizeof (struct ipoib_pseudoheader));
|
||||
}
|
||||
|
||||
if (dev_queue_xmit(skb))
|
||||
ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n");
|
||||
spin_lock_irq(&priv->tx_lock);
|
||||
}
|
||||
spin_unlock_irq(&priv->tx_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ipoib_mcast_sendonly_join_complete(int status,
|
||||
struct ib_sa_multicast *multicast)
|
||||
{
|
||||
struct ipoib_mcast *mcast = multicast->context;
|
||||
struct net_device *dev = mcast->dev;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* We trap for port events ourselves. */
|
||||
if (status == -ENETRESET)
|
||||
return 0;
|
||||
|
||||
if (!status)
|
||||
status = ipoib_mcast_join_finish(mcast, &multicast->rec);
|
||||
|
||||
if (status) {
|
||||
if (mcast->logcount++ < 20)
|
||||
ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for "
|
||||
IPOIB_GID_FMT ", status %d\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid), status);
|
||||
|
||||
/* Flush out any queued packets */
|
||||
spin_lock_irq(&priv->tx_lock);
|
||||
while (!skb_queue_empty(&mcast->pkt_queue)) {
|
||||
++priv->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb_dequeue(&mcast->pkt_queue));
|
||||
}
|
||||
spin_unlock_irq(&priv->tx_lock);
|
||||
|
||||
/* Clear the busy flag so we try again */
|
||||
status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY,
|
||||
&mcast->flags);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
|
||||
{
|
||||
struct net_device *dev = mcast->dev;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_sa_mcmember_rec rec = {
|
||||
#if 0 /* Some SMs don't support send-only yet */
|
||||
.join_state = 4
|
||||
#else
|
||||
.join_state = 1
|
||||
#endif
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) {
|
||||
ipoib_dbg_mcast(priv, "device shutting down, no multicast joins\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) {
|
||||
ipoib_dbg_mcast(priv, "multicast entry busy, skipping\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
rec.mgid = mcast->mcmember.mgid;
|
||||
rec.port_gid = priv->local_gid;
|
||||
rec.pkey = cpu_to_be16(priv->pkey);
|
||||
|
||||
mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca,
|
||||
priv->port, &rec,
|
||||
IB_SA_MCMEMBER_REC_MGID |
|
||||
IB_SA_MCMEMBER_REC_PORT_GID |
|
||||
IB_SA_MCMEMBER_REC_PKEY |
|
||||
IB_SA_MCMEMBER_REC_JOIN_STATE,
|
||||
GFP_ATOMIC,
|
||||
ipoib_mcast_sendonly_join_complete,
|
||||
mcast);
|
||||
if (IS_ERR(mcast->mc)) {
|
||||
ret = PTR_ERR(mcast->mc);
|
||||
clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
|
||||
ipoib_warn(priv, "ib_sa_join_multicast failed (ret = %d)\n",
|
||||
ret);
|
||||
} else {
|
||||
ipoib_dbg_mcast(priv, "no multicast record for " IPOIB_GID_FMT
|
||||
", starting join\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipoib_mcast_join_complete(int status,
|
||||
struct ib_sa_multicast *multicast)
|
||||
{
|
||||
struct ipoib_mcast *mcast = multicast->context;
|
||||
struct net_device *dev = mcast->dev;
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_dbg_mcast(priv, "join completion for " IPOIB_GID_FMT
|
||||
" (status %d)\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid), status);
|
||||
|
||||
/* We trap for port events ourselves. */
|
||||
if (status == -ENETRESET)
|
||||
return 0;
|
||||
|
||||
if (!status)
|
||||
status = ipoib_mcast_join_finish(mcast, &multicast->rec);
|
||||
|
||||
if (!status) {
|
||||
mcast->backoff = 1;
|
||||
mutex_lock(&mcast_mutex);
|
||||
if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue,
|
||||
&priv->mcast_task, 0);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
|
||||
if (mcast == priv->broadcast)
|
||||
netif_carrier_on(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mcast->logcount++ < 20) {
|
||||
if (status == -ETIMEDOUT) {
|
||||
ipoib_dbg_mcast(priv, "multicast join failed for " IPOIB_GID_FMT
|
||||
", status %d\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid),
|
||||
status);
|
||||
} else {
|
||||
ipoib_warn(priv, "multicast join failed for "
|
||||
IPOIB_GID_FMT ", status %d\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid),
|
||||
status);
|
||||
}
|
||||
}
|
||||
|
||||
mcast->backoff *= 2;
|
||||
if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
|
||||
mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
|
||||
|
||||
/* Clear the busy flag so we try again */
|
||||
status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
|
||||
|
||||
mutex_lock(&mcast_mutex);
|
||||
spin_lock_irq(&priv->lock);
|
||||
if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue, &priv->mcast_task,
|
||||
mcast->backoff * HZ);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
|
||||
int create)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_sa_mcmember_rec rec = {
|
||||
.join_state = 1
|
||||
};
|
||||
ib_sa_comp_mask comp_mask;
|
||||
int ret = 0;
|
||||
|
||||
ipoib_dbg_mcast(priv, "joining MGID " IPOIB_GID_FMT "\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
rec.mgid = mcast->mcmember.mgid;
|
||||
rec.port_gid = priv->local_gid;
|
||||
rec.pkey = cpu_to_be16(priv->pkey);
|
||||
|
||||
comp_mask =
|
||||
IB_SA_MCMEMBER_REC_MGID |
|
||||
IB_SA_MCMEMBER_REC_PORT_GID |
|
||||
IB_SA_MCMEMBER_REC_PKEY |
|
||||
IB_SA_MCMEMBER_REC_JOIN_STATE;
|
||||
|
||||
if (create) {
|
||||
comp_mask |=
|
||||
IB_SA_MCMEMBER_REC_QKEY |
|
||||
IB_SA_MCMEMBER_REC_MTU_SELECTOR |
|
||||
IB_SA_MCMEMBER_REC_MTU |
|
||||
IB_SA_MCMEMBER_REC_TRAFFIC_CLASS |
|
||||
IB_SA_MCMEMBER_REC_RATE_SELECTOR |
|
||||
IB_SA_MCMEMBER_REC_RATE |
|
||||
IB_SA_MCMEMBER_REC_SL |
|
||||
IB_SA_MCMEMBER_REC_FLOW_LABEL |
|
||||
IB_SA_MCMEMBER_REC_HOP_LIMIT;
|
||||
|
||||
rec.qkey = priv->broadcast->mcmember.qkey;
|
||||
rec.mtu_selector = IB_SA_EQ;
|
||||
rec.mtu = priv->broadcast->mcmember.mtu;
|
||||
rec.traffic_class = priv->broadcast->mcmember.traffic_class;
|
||||
rec.rate_selector = IB_SA_EQ;
|
||||
rec.rate = priv->broadcast->mcmember.rate;
|
||||
rec.sl = priv->broadcast->mcmember.sl;
|
||||
rec.flow_label = priv->broadcast->mcmember.flow_label;
|
||||
rec.hop_limit = priv->broadcast->mcmember.hop_limit;
|
||||
}
|
||||
|
||||
set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
|
||||
mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
|
||||
&rec, comp_mask, GFP_KERNEL,
|
||||
ipoib_mcast_join_complete, mcast);
|
||||
if (IS_ERR(mcast->mc)) {
|
||||
clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
|
||||
ret = PTR_ERR(mcast->mc);
|
||||
ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret);
|
||||
|
||||
mcast->backoff *= 2;
|
||||
if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
|
||||
mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
|
||||
|
||||
mutex_lock(&mcast_mutex);
|
||||
if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue,
|
||||
&priv->mcast_task,
|
||||
mcast->backoff * HZ);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void ipoib_mcast_join_task(struct work_struct *work)
|
||||
{
|
||||
struct ipoib_dev_priv *priv =
|
||||
container_of(work, struct ipoib_dev_priv, mcast_task.work);
|
||||
struct net_device *dev = priv->dev;
|
||||
|
||||
if (!test_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
return;
|
||||
|
||||
if (ib_query_gid(priv->ca, priv->port, 0, &priv->local_gid))
|
||||
ipoib_warn(priv, "ib_gid_entry_get() failed\n");
|
||||
else
|
||||
memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, sizeof (union ib_gid));
|
||||
|
||||
{
|
||||
struct ib_port_attr attr;
|
||||
|
||||
if (!ib_query_port(priv->ca, priv->port, &attr))
|
||||
priv->local_lid = attr.lid;
|
||||
else
|
||||
ipoib_warn(priv, "ib_query_port failed\n");
|
||||
}
|
||||
|
||||
if (!priv->broadcast) {
|
||||
struct ipoib_mcast *broadcast;
|
||||
|
||||
broadcast = ipoib_mcast_alloc(dev, 1);
|
||||
if (!broadcast) {
|
||||
ipoib_warn(priv, "failed to allocate broadcast group\n");
|
||||
mutex_lock(&mcast_mutex);
|
||||
if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue,
|
||||
&priv->mcast_task, HZ);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
memcpy(broadcast->mcmember.mgid.raw, priv->dev->broadcast + 4,
|
||||
sizeof (union ib_gid));
|
||||
priv->broadcast = broadcast;
|
||||
|
||||
__ipoib_mcast_add(dev, priv->broadcast);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
}
|
||||
|
||||
if (!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) {
|
||||
if (!test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags))
|
||||
ipoib_mcast_join(dev, priv->broadcast, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
struct ipoib_mcast *mcast = NULL;
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
list_for_each_entry(mcast, &priv->multicast_list, list) {
|
||||
if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)
|
||||
&& !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)
|
||||
&& !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
|
||||
/* Found the next unjoined group */
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
if (&mcast->list == &priv->multicast_list) {
|
||||
/* All done */
|
||||
break;
|
||||
}
|
||||
|
||||
ipoib_mcast_join(dev, mcast, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->mcast_mtu = ib_mtu_enum_to_int(priv->broadcast->mcmember.mtu) -
|
||||
IPOIB_ENCAP_LEN;
|
||||
|
||||
if (!ipoib_cm_admin_enabled(dev))
|
||||
dev->mtu = min(priv->mcast_mtu, priv->admin_mtu);
|
||||
|
||||
ipoib_dbg_mcast(priv, "successfully joined all multicast groups\n");
|
||||
|
||||
clear_bit(IPOIB_MCAST_RUN, &priv->flags);
|
||||
}
|
||||
|
||||
int ipoib_mcast_start_thread(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_dbg_mcast(priv, "starting multicast thread\n");
|
||||
|
||||
mutex_lock(&mcast_mutex);
|
||||
if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags))
|
||||
queue_delayed_work(ipoib_workqueue, &priv->mcast_task, 0);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
set_bit(IPOIB_MCAST_STARTED, &priv->flags);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipoib_mcast_stop_thread(struct net_device *dev, int flush)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
ipoib_dbg_mcast(priv, "stopping multicast thread\n");
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
clear_bit(IPOIB_MCAST_STARTED, &priv->flags);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
mutex_lock(&mcast_mutex);
|
||||
clear_bit(IPOIB_MCAST_RUN, &priv->flags);
|
||||
cancel_delayed_work(&priv->mcast_task);
|
||||
mutex_unlock(&mcast_mutex);
|
||||
|
||||
if (flush)
|
||||
flush_workqueue(ipoib_workqueue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
|
||||
ib_sa_free_multicast(mcast->mc);
|
||||
|
||||
if (test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
|
||||
ipoib_dbg_mcast(priv, "leaving MGID " IPOIB_GID_FMT "\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
/* Remove ourselves from the multicast group */
|
||||
ret = ipoib_mcast_detach(dev, be16_to_cpu(mcast->mcmember.mlid),
|
||||
&mcast->mcmember.mgid);
|
||||
if (ret)
|
||||
ipoib_warn(priv, "ipoib_mcast_detach failed (result = %d)\n", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ipoib_mcast *mcast;
|
||||
|
||||
/*
|
||||
* We can only be called from ipoib_start_xmit, so we're
|
||||
* inside tx_lock -- no need to save/restore flags.
|
||||
*/
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
if (!test_bit(IPOIB_MCAST_STARTED, &priv->flags) ||
|
||||
!priv->broadcast ||
|
||||
!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) {
|
||||
++priv->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
mcast = __ipoib_mcast_find(dev, mgid);
|
||||
if (!mcast) {
|
||||
/* Let's create a new send only group now */
|
||||
ipoib_dbg_mcast(priv, "setting up send only multicast group for "
|
||||
IPOIB_GID_FMT "\n", IPOIB_GID_RAW_ARG(mgid));
|
||||
|
||||
mcast = ipoib_mcast_alloc(dev, 0);
|
||||
if (!mcast) {
|
||||
ipoib_warn(priv, "unable to allocate memory for "
|
||||
"multicast structure\n");
|
||||
++priv->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags);
|
||||
memcpy(mcast->mcmember.mgid.raw, mgid, sizeof (union ib_gid));
|
||||
__ipoib_mcast_add(dev, mcast);
|
||||
list_add_tail(&mcast->list, &priv->multicast_list);
|
||||
}
|
||||
|
||||
if (!mcast->ah) {
|
||||
if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE)
|
||||
skb_queue_tail(&mcast->pkt_queue, skb);
|
||||
else {
|
||||
++priv->stats.tx_dropped;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags))
|
||||
ipoib_dbg_mcast(priv, "no address vector, "
|
||||
"but multicast join already started\n");
|
||||
else if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags))
|
||||
ipoib_mcast_sendonly_join(mcast);
|
||||
|
||||
/*
|
||||
* If lookup completes between here and out:, don't
|
||||
* want to send packet twice.
|
||||
*/
|
||||
mcast = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if (mcast && mcast->ah) {
|
||||
if (skb->dst &&
|
||||
skb->dst->neighbour &&
|
||||
!*to_ipoib_neigh(skb->dst->neighbour)) {
|
||||
struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb->dst->neighbour);
|
||||
|
||||
if (neigh) {
|
||||
kref_get(&mcast->ah->ref);
|
||||
neigh->ah = mcast->ah;
|
||||
list_add_tail(&neigh->list, &mcast->neigh_list);
|
||||
}
|
||||
}
|
||||
|
||||
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
void ipoib_mcast_dev_flush(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
LIST_HEAD(remove_list);
|
||||
struct ipoib_mcast *mcast, *tmcast;
|
||||
unsigned long flags;
|
||||
|
||||
ipoib_dbg_mcast(priv, "flushing multicast list\n");
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(mcast, tmcast, &priv->multicast_list, list) {
|
||||
list_del(&mcast->list);
|
||||
rb_erase(&mcast->rb_node, &priv->multicast_tree);
|
||||
list_add_tail(&mcast->list, &remove_list);
|
||||
}
|
||||
|
||||
if (priv->broadcast) {
|
||||
rb_erase(&priv->broadcast->rb_node, &priv->multicast_tree);
|
||||
list_add_tail(&priv->broadcast->list, &remove_list);
|
||||
priv->broadcast = NULL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(mcast, tmcast, &remove_list, list) {
|
||||
ipoib_mcast_leave(dev, mcast);
|
||||
ipoib_mcast_free(mcast);
|
||||
}
|
||||
}
|
||||
|
||||
void ipoib_mcast_restart_task(struct work_struct *work)
|
||||
{
|
||||
struct ipoib_dev_priv *priv =
|
||||
container_of(work, struct ipoib_dev_priv, restart_task);
|
||||
struct net_device *dev = priv->dev;
|
||||
struct dev_mc_list *mclist;
|
||||
struct ipoib_mcast *mcast, *tmcast;
|
||||
LIST_HEAD(remove_list);
|
||||
unsigned long flags;
|
||||
|
||||
ipoib_dbg_mcast(priv, "restarting multicast task\n");
|
||||
|
||||
ipoib_mcast_stop_thread(dev, 0);
|
||||
|
||||
local_irq_save(flags);
|
||||
netif_tx_lock(dev);
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/*
|
||||
* Unfortunately, the networking core only gives us a list of all of
|
||||
* the multicast hardware addresses. We need to figure out which ones
|
||||
* are new and which ones have been removed
|
||||
*/
|
||||
|
||||
/* Clear out the found flag */
|
||||
list_for_each_entry(mcast, &priv->multicast_list, list)
|
||||
clear_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags);
|
||||
|
||||
/* Mark all of the entries that are found or don't exist */
|
||||
for (mclist = dev->mc_list; mclist; mclist = mclist->next) {
|
||||
union ib_gid mgid;
|
||||
|
||||
memcpy(mgid.raw, mclist->dmi_addr + 4, sizeof mgid);
|
||||
|
||||
/* Add in the P_Key */
|
||||
mgid.raw[4] = (priv->pkey >> 8) & 0xff;
|
||||
mgid.raw[5] = priv->pkey & 0xff;
|
||||
|
||||
mcast = __ipoib_mcast_find(dev, &mgid);
|
||||
if (!mcast || test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
|
||||
struct ipoib_mcast *nmcast;
|
||||
|
||||
/* Not found or send-only group, let's add a new entry */
|
||||
ipoib_dbg_mcast(priv, "adding multicast entry for mgid "
|
||||
IPOIB_GID_FMT "\n", IPOIB_GID_ARG(mgid));
|
||||
|
||||
nmcast = ipoib_mcast_alloc(dev, 0);
|
||||
if (!nmcast) {
|
||||
ipoib_warn(priv, "unable to allocate memory for multicast structure\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
set_bit(IPOIB_MCAST_FLAG_FOUND, &nmcast->flags);
|
||||
|
||||
nmcast->mcmember.mgid = mgid;
|
||||
|
||||
if (mcast) {
|
||||
/* Destroy the send only entry */
|
||||
list_move_tail(&mcast->list, &remove_list);
|
||||
|
||||
rb_replace_node(&mcast->rb_node,
|
||||
&nmcast->rb_node,
|
||||
&priv->multicast_tree);
|
||||
} else
|
||||
__ipoib_mcast_add(dev, nmcast);
|
||||
|
||||
list_add_tail(&nmcast->list, &priv->multicast_list);
|
||||
}
|
||||
|
||||
if (mcast)
|
||||
set_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags);
|
||||
}
|
||||
|
||||
/* Remove all of the entries don't exist anymore */
|
||||
list_for_each_entry_safe(mcast, tmcast, &priv->multicast_list, list) {
|
||||
if (!test_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags) &&
|
||||
!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
|
||||
ipoib_dbg_mcast(priv, "deleting multicast group " IPOIB_GID_FMT "\n",
|
||||
IPOIB_GID_ARG(mcast->mcmember.mgid));
|
||||
|
||||
rb_erase(&mcast->rb_node, &priv->multicast_tree);
|
||||
|
||||
/* Move to the remove list */
|
||||
list_move_tail(&mcast->list, &remove_list);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
netif_tx_unlock(dev);
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* We have to cancel outside of the spinlock */
|
||||
list_for_each_entry_safe(mcast, tmcast, &remove_list, list) {
|
||||
ipoib_mcast_leave(mcast->dev, mcast);
|
||||
ipoib_mcast_free(mcast);
|
||||
}
|
||||
|
||||
if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
|
||||
ipoib_mcast_start_thread(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
|
||||
|
||||
struct ipoib_mcast_iter *ipoib_mcast_iter_init(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_mcast_iter *iter;
|
||||
|
||||
iter = kmalloc(sizeof *iter, GFP_KERNEL);
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
iter->dev = dev;
|
||||
memset(iter->mgid.raw, 0, 16);
|
||||
|
||||
if (ipoib_mcast_iter_next(iter)) {
|
||||
kfree(iter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
int ipoib_mcast_iter_next(struct ipoib_mcast_iter *iter)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(iter->dev);
|
||||
struct rb_node *n;
|
||||
struct ipoib_mcast *mcast;
|
||||
int ret = 1;
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
|
||||
n = rb_first(&priv->multicast_tree);
|
||||
|
||||
while (n) {
|
||||
mcast = rb_entry(n, struct ipoib_mcast, rb_node);
|
||||
|
||||
if (memcmp(iter->mgid.raw, mcast->mcmember.mgid.raw,
|
||||
sizeof (union ib_gid)) < 0) {
|
||||
iter->mgid = mcast->mcmember.mgid;
|
||||
iter->created = mcast->created;
|
||||
iter->queuelen = skb_queue_len(&mcast->pkt_queue);
|
||||
iter->complete = !!mcast->ah;
|
||||
iter->send_only = !!(mcast->flags & (1 << IPOIB_MCAST_FLAG_SENDONLY));
|
||||
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
n = rb_next(n);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ipoib_mcast_iter_read(struct ipoib_mcast_iter *iter,
|
||||
union ib_gid *mgid,
|
||||
unsigned long *created,
|
||||
unsigned int *queuelen,
|
||||
unsigned int *complete,
|
||||
unsigned int *send_only)
|
||||
{
|
||||
*mgid = iter->mgid;
|
||||
*created = iter->created;
|
||||
*queuelen = iter->queuelen;
|
||||
*complete = iter->complete;
|
||||
*send_only = iter->send_only;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG */
|
||||
272
drivers/infiniband/ulp/ipoib/ipoib_verbs.c
Normal file
272
drivers/infiniband/ulp/ipoib/ipoib_verbs.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
|
||||
* Copyright (c) 2005 Mellanox Technologies. 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: ipoib_verbs.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <rdma/ib_cache.h>
|
||||
|
||||
#include "ipoib.h"
|
||||
|
||||
int ipoib_mcast_attach(struct net_device *dev, u16 mlid, union ib_gid *mgid)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_qp_attr *qp_attr;
|
||||
int ret;
|
||||
u16 pkey_index;
|
||||
|
||||
ret = -ENOMEM;
|
||||
qp_attr = kmalloc(sizeof *qp_attr, GFP_KERNEL);
|
||||
if (!qp_attr)
|
||||
goto out;
|
||||
|
||||
if (ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index)) {
|
||||
clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
|
||||
/* set correct QKey for QP */
|
||||
qp_attr->qkey = priv->qkey;
|
||||
ret = ib_modify_qp(priv->qp, qp_attr, IB_QP_QKEY);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "failed to modify QP, ret = %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* attach QP to multicast group */
|
||||
mutex_lock(&priv->mcast_mutex);
|
||||
ret = ib_attach_mcast(priv->qp, mgid, mlid);
|
||||
mutex_unlock(&priv->mcast_mutex);
|
||||
if (ret)
|
||||
ipoib_warn(priv, "failed to attach to multicast group, ret = %d\n", ret);
|
||||
|
||||
out:
|
||||
kfree(qp_attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipoib_mcast_detach(struct net_device *dev, u16 mlid, union ib_gid *mgid)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->mcast_mutex);
|
||||
ret = ib_detach_mcast(priv->qp, mgid, mlid);
|
||||
mutex_unlock(&priv->mcast_mutex);
|
||||
if (ret)
|
||||
ipoib_warn(priv, "ib_detach_mcast failed (result = %d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipoib_init_qp(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
int ret;
|
||||
u16 pkey_index;
|
||||
struct ib_qp_attr qp_attr;
|
||||
int attr_mask;
|
||||
|
||||
/*
|
||||
* Search through the port P_Key table for the requested pkey value.
|
||||
* The port has to be assigned to the respective IB partition in
|
||||
* advance.
|
||||
*/
|
||||
ret = ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index);
|
||||
if (ret) {
|
||||
clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
return ret;
|
||||
}
|
||||
set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
|
||||
qp_attr.qp_state = IB_QPS_INIT;
|
||||
qp_attr.qkey = 0;
|
||||
qp_attr.port_num = priv->port;
|
||||
qp_attr.pkey_index = pkey_index;
|
||||
attr_mask =
|
||||
IB_QP_QKEY |
|
||||
IB_QP_PORT |
|
||||
IB_QP_PKEY_INDEX |
|
||||
IB_QP_STATE;
|
||||
ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "failed to modify QP to init, ret = %d\n", ret);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
qp_attr.qp_state = IB_QPS_RTR;
|
||||
/* Can't set this in a INIT->RTR transition */
|
||||
attr_mask &= ~IB_QP_PORT;
|
||||
ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "failed to modify QP to RTR, ret = %d\n", ret);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
qp_attr.qp_state = IB_QPS_RTS;
|
||||
qp_attr.sq_psn = 0;
|
||||
attr_mask |= IB_QP_SQ_PSN;
|
||||
attr_mask &= ~IB_QP_PKEY_INDEX;
|
||||
ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
|
||||
if (ret) {
|
||||
ipoib_warn(priv, "failed to modify QP to RTS, ret = %d\n", ret);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
qp_attr.qp_state = IB_QPS_RESET;
|
||||
if (ib_modify_qp(priv->qp, &qp_attr, IB_QP_STATE))
|
||||
ipoib_warn(priv, "Failed to modify QP to RESET state\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
struct ib_qp_init_attr init_attr = {
|
||||
.cap = {
|
||||
.max_send_wr = ipoib_sendq_size,
|
||||
.max_recv_wr = ipoib_recvq_size,
|
||||
.max_send_sge = 1,
|
||||
.max_recv_sge = 1
|
||||
},
|
||||
.sq_sig_type = IB_SIGNAL_ALL_WR,
|
||||
.qp_type = IB_QPT_UD
|
||||
};
|
||||
|
||||
int ret, size;
|
||||
|
||||
priv->pd = ib_alloc_pd(priv->ca);
|
||||
if (IS_ERR(priv->pd)) {
|
||||
printk(KERN_WARNING "%s: failed to allocate PD\n", ca->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->mr = ib_get_dma_mr(priv->pd, IB_ACCESS_LOCAL_WRITE);
|
||||
if (IS_ERR(priv->mr)) {
|
||||
printk(KERN_WARNING "%s: ib_get_dma_mr failed\n", ca->name);
|
||||
goto out_free_pd;
|
||||
}
|
||||
|
||||
size = ipoib_sendq_size + ipoib_recvq_size + 1;
|
||||
ret = ipoib_cm_dev_init(dev);
|
||||
if (!ret)
|
||||
size += ipoib_recvq_size;
|
||||
|
||||
priv->cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL, dev, size);
|
||||
if (IS_ERR(priv->cq)) {
|
||||
printk(KERN_WARNING "%s: failed to create CQ\n", ca->name);
|
||||
goto out_free_mr;
|
||||
}
|
||||
|
||||
if (ib_req_notify_cq(priv->cq, IB_CQ_NEXT_COMP))
|
||||
goto out_free_cq;
|
||||
|
||||
init_attr.send_cq = priv->cq;
|
||||
init_attr.recv_cq = priv->cq,
|
||||
|
||||
priv->qp = ib_create_qp(priv->pd, &init_attr);
|
||||
if (IS_ERR(priv->qp)) {
|
||||
printk(KERN_WARNING "%s: failed to create QP\n", ca->name);
|
||||
goto out_free_cq;
|
||||
}
|
||||
|
||||
priv->dev->dev_addr[1] = (priv->qp->qp_num >> 16) & 0xff;
|
||||
priv->dev->dev_addr[2] = (priv->qp->qp_num >> 8) & 0xff;
|
||||
priv->dev->dev_addr[3] = (priv->qp->qp_num ) & 0xff;
|
||||
|
||||
priv->tx_sge.lkey = priv->mr->lkey;
|
||||
|
||||
priv->tx_wr.opcode = IB_WR_SEND;
|
||||
priv->tx_wr.sg_list = &priv->tx_sge;
|
||||
priv->tx_wr.num_sge = 1;
|
||||
priv->tx_wr.send_flags = IB_SEND_SIGNALED;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_cq:
|
||||
ib_destroy_cq(priv->cq);
|
||||
|
||||
out_free_mr:
|
||||
ib_dereg_mr(priv->mr);
|
||||
|
||||
out_free_pd:
|
||||
ib_dealloc_pd(priv->pd);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void ipoib_transport_dev_cleanup(struct net_device *dev)
|
||||
{
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->qp) {
|
||||
if (ib_destroy_qp(priv->qp))
|
||||
ipoib_warn(priv, "ib_qp_destroy failed\n");
|
||||
|
||||
priv->qp = NULL;
|
||||
clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
|
||||
}
|
||||
|
||||
if (ib_destroy_cq(priv->cq))
|
||||
ipoib_warn(priv, "ib_cq_destroy failed\n");
|
||||
|
||||
ipoib_cm_dev_cleanup(dev);
|
||||
|
||||
if (ib_dereg_mr(priv->mr))
|
||||
ipoib_warn(priv, "ib_dereg_mr failed\n");
|
||||
|
||||
if (ib_dealloc_pd(priv->pd))
|
||||
ipoib_warn(priv, "ib_dealloc_pd failed\n");
|
||||
}
|
||||
|
||||
void ipoib_event(struct ib_event_handler *handler,
|
||||
struct ib_event *record)
|
||||
{
|
||||
struct ipoib_dev_priv *priv =
|
||||
container_of(handler, struct ipoib_dev_priv, event_handler);
|
||||
|
||||
if ((record->event == IB_EVENT_PORT_ERR ||
|
||||
record->event == IB_EVENT_PKEY_CHANGE ||
|
||||
record->event == IB_EVENT_PORT_ACTIVE ||
|
||||
record->event == IB_EVENT_LID_CHANGE ||
|
||||
record->event == IB_EVENT_SM_CHANGE ||
|
||||
record->event == IB_EVENT_CLIENT_REREGISTER) &&
|
||||
record->element.port_num == priv->port) {
|
||||
ipoib_dbg(priv, "Port state change event\n");
|
||||
queue_work(ipoib_workqueue, &priv->flush_task);
|
||||
}
|
||||
}
|
||||
172
drivers/infiniband/ulp/ipoib/ipoib_vlan.c
Normal file
172
drivers/infiniband/ulp/ipoib/ipoib_vlan.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Topspin Communications. 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: ipoib_vlan.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "ipoib.h"
|
||||
|
||||
static ssize_t show_parent(struct device *d, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_device *dev = to_net_dev(d);
|
||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", priv->parent->name);
|
||||
}
|
||||
static DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL);
|
||||
|
||||
int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
||||
{
|
||||
struct ipoib_dev_priv *ppriv, *priv;
|
||||
char intf_name[IFNAMSIZ];
|
||||
int result;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
ppriv = netdev_priv(pdev);
|
||||
|
||||
mutex_lock(&ppriv->vlan_mutex);
|
||||
|
||||
/*
|
||||
* First ensure this isn't a duplicate. We check the parent device and
|
||||
* then all of the child interfaces to make sure the Pkey doesn't match.
|
||||
*/
|
||||
if (ppriv->pkey == pkey) {
|
||||
result = -ENOTUNIQ;
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_for_each_entry(priv, &ppriv->child_intfs, list) {
|
||||
if (priv->pkey == pkey) {
|
||||
result = -ENOTUNIQ;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(intf_name, sizeof intf_name, "%s.%04x",
|
||||
ppriv->dev->name, pkey);
|
||||
priv = ipoib_intf_alloc(intf_name);
|
||||
if (!priv) {
|
||||
result = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags);
|
||||
|
||||
priv->pkey = pkey;
|
||||
|
||||
memcpy(priv->dev->dev_addr, ppriv->dev->dev_addr, INFINIBAND_ALEN);
|
||||
priv->dev->broadcast[8] = pkey >> 8;
|
||||
priv->dev->broadcast[9] = pkey & 0xff;
|
||||
|
||||
result = ipoib_dev_init(priv->dev, ppriv->ca, ppriv->port);
|
||||
if (result < 0) {
|
||||
ipoib_warn(ppriv, "failed to initialize subinterface: "
|
||||
"device %s, port %d",
|
||||
ppriv->ca->name, ppriv->port);
|
||||
goto device_init_failed;
|
||||
}
|
||||
|
||||
result = register_netdev(priv->dev);
|
||||
if (result) {
|
||||
ipoib_warn(priv, "failed to initialize; error %i", result);
|
||||
goto register_failed;
|
||||
}
|
||||
|
||||
priv->parent = ppriv->dev;
|
||||
|
||||
ipoib_create_debug_files(priv->dev);
|
||||
|
||||
if (ipoib_cm_add_mode_attr(priv->dev))
|
||||
goto sysfs_failed;
|
||||
if (ipoib_add_pkey_attr(priv->dev))
|
||||
goto sysfs_failed;
|
||||
|
||||
if (device_create_file(&priv->dev->dev, &dev_attr_parent))
|
||||
goto sysfs_failed;
|
||||
|
||||
list_add_tail(&priv->list, &ppriv->child_intfs);
|
||||
|
||||
mutex_unlock(&ppriv->vlan_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
sysfs_failed:
|
||||
ipoib_delete_debug_files(priv->dev);
|
||||
unregister_netdev(priv->dev);
|
||||
|
||||
register_failed:
|
||||
ipoib_dev_cleanup(priv->dev);
|
||||
|
||||
device_init_failed:
|
||||
free_netdev(priv->dev);
|
||||
|
||||
err:
|
||||
mutex_unlock(&ppriv->vlan_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
||||
{
|
||||
struct ipoib_dev_priv *ppriv, *priv, *tpriv;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
ppriv = netdev_priv(pdev);
|
||||
|
||||
mutex_lock(&ppriv->vlan_mutex);
|
||||
list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
|
||||
if (priv->pkey == pkey) {
|
||||
unregister_netdev(priv->dev);
|
||||
ipoib_dev_cleanup(priv->dev);
|
||||
list_del(&priv->list);
|
||||
free_netdev(priv->dev);
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ppriv->vlan_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
12
drivers/infiniband/ulp/iser/Kconfig
Normal file
12
drivers/infiniband/ulp/iser/Kconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
config INFINIBAND_ISER
|
||||
tristate "iSCSI Extensions for RDMA (iSER)"
|
||||
depends on INFINIBAND && SCSI && INET
|
||||
select SCSI_ISCSI_ATTRS
|
||||
---help---
|
||||
Support for the iSCSI Extensions for RDMA (iSER) Protocol
|
||||
over InfiniBand. This allows you to access storage devices
|
||||
that speak iSCSI over iSER over InfiniBand.
|
||||
|
||||
The iSER protocol is defined by IETF.
|
||||
See <http://www.ietf.org/internet-drafts/draft-ietf-ips-iser-05.txt>
|
||||
and <http://www.infinibandta.org/members/spec/iser_annex_060418.pdf>
|
||||
4
drivers/infiniband/ulp/iser/Makefile
Normal file
4
drivers/infiniband/ulp/iser/Makefile
Normal file
@@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_INFINIBAND_ISER) += ib_iser.o
|
||||
|
||||
ib_iser-y := iser_verbs.o iser_initiator.o iser_memory.o \
|
||||
iscsi_iser.o
|
||||
660
drivers/infiniband/ulp/iser/iscsi_iser.c
Normal file
660
drivers/infiniband/ulp/iser/iscsi_iser.c
Normal file
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* iSCSI Initiator over iSER Data-Path
|
||||
*
|
||||
* Copyright (C) 2004 Dmitry Yusupov
|
||||
* Copyright (C) 2004 Alex Aizman
|
||||
* Copyright (C) 2005 Mike Christie
|
||||
* Copyright (c) 2005, 2006 Voltaire, Inc. All rights reserved.
|
||||
* maintained by openib-general@openib.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Credits:
|
||||
* Christoph Hellwig
|
||||
* FUJITA Tomonori
|
||||
* Arne Redlich
|
||||
* Zhenyu Wang
|
||||
* Modified by:
|
||||
* Erez Zilber
|
||||
*
|
||||
*
|
||||
* $Id: iscsi_iser.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_eh.h>
|
||||
#include <scsi/scsi_tcq.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_transport_iscsi.h>
|
||||
|
||||
#include "iscsi_iser.h"
|
||||
|
||||
static unsigned int iscsi_max_lun = 512;
|
||||
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
|
||||
|
||||
int iser_debug_level = 0;
|
||||
|
||||
MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover "
|
||||
"v" DRV_VER " (" DRV_DATE ")");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
|
||||
|
||||
module_param_named(debug_level, iser_debug_level, int, 0644);
|
||||
MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)");
|
||||
|
||||
struct iser_global ig;
|
||||
|
||||
void
|
||||
iscsi_iser_recv(struct iscsi_conn *conn,
|
||||
struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
|
||||
{
|
||||
int rc = 0;
|
||||
uint32_t ret_itt;
|
||||
int datalen;
|
||||
int ahslen;
|
||||
|
||||
/* verify PDU length */
|
||||
datalen = ntoh24(hdr->dlength);
|
||||
if (datalen != rx_data_len) {
|
||||
printk(KERN_ERR "iscsi_iser: datalen %d (hdr) != %d (IB) \n",
|
||||
datalen, rx_data_len);
|
||||
rc = ISCSI_ERR_DATALEN;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* read AHS */
|
||||
ahslen = hdr->hlength * 4;
|
||||
|
||||
/* verify itt (itt encoding: age+cid+itt) */
|
||||
rc = iscsi_verify_itt(conn, hdr, &ret_itt);
|
||||
|
||||
if (!rc)
|
||||
rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
|
||||
|
||||
if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
|
||||
goto error;
|
||||
|
||||
return;
|
||||
error:
|
||||
iscsi_conn_failure(conn, rc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
|
||||
*
|
||||
**/
|
||||
static void
|
||||
iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct scsi_cmnd *sc = ctask->sc;
|
||||
|
||||
iser_ctask->command_sent = 0;
|
||||
iser_ctask->iser_conn = iser_conn;
|
||||
|
||||
if (sc->sc_data_direction == DMA_TO_DEVICE) {
|
||||
BUG_ON(ctask->total_length == 0);
|
||||
|
||||
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
|
||||
ctask->itt, ctask->total_length, ctask->imm_count,
|
||||
ctask->unsol_count);
|
||||
}
|
||||
|
||||
iser_ctask_rdma_init(iser_ctask);
|
||||
}
|
||||
|
||||
/**
|
||||
* iscsi_mtask_xmit - xmit management(immediate) task
|
||||
* @conn: iscsi connection
|
||||
* @mtask: task management task
|
||||
*
|
||||
* Notes:
|
||||
* The function can return -EAGAIN in which case caller must
|
||||
* call it again later, or recover. '0' return code means successful
|
||||
* xmit.
|
||||
*
|
||||
**/
|
||||
static int
|
||||
iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
|
||||
|
||||
error = iser_send_control(conn, mtask);
|
||||
|
||||
/* since iser xmits control with zero copy, mtasks can not be recycled
|
||||
* right after sending them.
|
||||
* The recycling scheme is based on whether a response is expected
|
||||
* - if yes, the mtask is recycled at iscsi_complete_pdu
|
||||
* - if no, the mtask is recycled at iser_snd_completion
|
||||
*/
|
||||
if (error && error != -ENOBUFS)
|
||||
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_data hdr;
|
||||
int error = 0;
|
||||
|
||||
/* Send data-out PDUs while there's still unsolicited data to send */
|
||||
while (ctask->unsol_count > 0) {
|
||||
iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
|
||||
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
|
||||
hdr.itt, ctask->data_count);
|
||||
|
||||
/* the buffer description has been passed with the command */
|
||||
/* Send the command */
|
||||
error = iser_send_data_out(conn, ctask, &hdr);
|
||||
if (error) {
|
||||
ctask->unsol_datasn--;
|
||||
goto iscsi_iser_ctask_xmit_unsol_data_exit;
|
||||
}
|
||||
ctask->unsol_count -= ctask->data_count;
|
||||
debug_scsi("Need to send %d more as data-out PDUs\n",
|
||||
ctask->unsol_count);
|
||||
}
|
||||
|
||||
iscsi_iser_ctask_xmit_unsol_data_exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
int error = 0;
|
||||
|
||||
debug_scsi("ctask deq [cid %d itt 0x%x]\n",
|
||||
conn->id, ctask->itt);
|
||||
|
||||
/*
|
||||
* serialize with TMF AbortTask
|
||||
*/
|
||||
if (ctask->mtask)
|
||||
return error;
|
||||
|
||||
/* Send the cmd PDU */
|
||||
if (!iser_ctask->command_sent) {
|
||||
error = iser_send_command(conn, ctask);
|
||||
if (error)
|
||||
goto iscsi_iser_ctask_xmit_exit;
|
||||
iser_ctask->command_sent = 1;
|
||||
}
|
||||
|
||||
/* Send unsolicited data-out PDU(s) if necessary */
|
||||
if (ctask->unsol_count)
|
||||
error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
|
||||
|
||||
iscsi_iser_ctask_xmit_exit:
|
||||
if (error && error != -ENOBUFS)
|
||||
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
|
||||
if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
|
||||
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_ctask_rdma_finalize(iser_ctask);
|
||||
}
|
||||
}
|
||||
|
||||
static struct iser_conn *
|
||||
iscsi_iser_ib_conn_lookup(__u64 ep_handle)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
|
||||
|
||||
mutex_lock(&ig.connlist_mutex);
|
||||
list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
|
||||
if (ib_conn == uib_conn) {
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
return ib_conn;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct iscsi_cls_conn *
|
||||
iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
|
||||
{
|
||||
struct iscsi_conn *conn;
|
||||
struct iscsi_cls_conn *cls_conn;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
|
||||
cls_conn = iscsi_conn_setup(cls_session, conn_idx);
|
||||
if (!cls_conn)
|
||||
return NULL;
|
||||
conn = cls_conn->dd_data;
|
||||
|
||||
/*
|
||||
* due to issues with the login code re iser sematics
|
||||
* this not set in iscsi_conn_setup - FIXME
|
||||
*/
|
||||
conn->max_recv_dlength = 128;
|
||||
|
||||
iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
|
||||
if (!iser_conn)
|
||||
goto conn_alloc_fail;
|
||||
|
||||
/* currently this is the only field which need to be initiated */
|
||||
rwlock_init(&iser_conn->lock);
|
||||
|
||||
conn->dd_data = iser_conn;
|
||||
iser_conn->iscsi_conn = conn;
|
||||
|
||||
return cls_conn;
|
||||
|
||||
conn_alloc_fail:
|
||||
iscsi_conn_teardown(cls_conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
|
||||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
|
||||
iscsi_conn_teardown(cls_conn);
|
||||
if (iser_conn->ib_conn)
|
||||
iser_conn->ib_conn->iser_conn = NULL;
|
||||
kfree(iser_conn);
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
|
||||
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
|
||||
int is_leading)
|
||||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
struct iser_conn *ib_conn;
|
||||
int error;
|
||||
|
||||
error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* the transport ep handle comes from user space so it must be
|
||||
* verified against the global ib connections list */
|
||||
ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
|
||||
if (!ib_conn) {
|
||||
iser_err("can't bind eph %llx\n",
|
||||
(unsigned long long)transport_eph);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* binds the iSER connection retrieved from the previously
|
||||
* connected ep_handle to the iSCSI layer connection. exchanges
|
||||
* connection pointers */
|
||||
iser_err("binding iscsi conn %p to iser_conn %p\n",conn,ib_conn);
|
||||
iser_conn = conn->dd_data;
|
||||
ib_conn->iser_conn = iser_conn;
|
||||
iser_conn->ib_conn = ib_conn;
|
||||
|
||||
conn->recv_lock = &iser_conn->lock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
|
||||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
int err;
|
||||
|
||||
err = iser_conn_set_full_featured_mode(conn);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return iscsi_conn_start(cls_conn);
|
||||
}
|
||||
|
||||
static struct iscsi_transport iscsi_iser_transport;
|
||||
|
||||
static struct iscsi_cls_session *
|
||||
iscsi_iser_session_create(struct iscsi_transport *iscsit,
|
||||
struct scsi_transport_template *scsit,
|
||||
uint32_t initial_cmdsn, uint32_t *hostno)
|
||||
{
|
||||
struct iscsi_cls_session *cls_session;
|
||||
struct iscsi_session *session;
|
||||
int i;
|
||||
uint32_t hn;
|
||||
struct iscsi_cmd_task *ctask;
|
||||
struct iscsi_mgmt_task *mtask;
|
||||
struct iscsi_iser_cmd_task *iser_ctask;
|
||||
struct iser_desc *desc;
|
||||
|
||||
cls_session = iscsi_session_setup(iscsit, scsit,
|
||||
sizeof(struct iscsi_iser_cmd_task),
|
||||
sizeof(struct iser_desc),
|
||||
initial_cmdsn, &hn);
|
||||
if (!cls_session)
|
||||
return NULL;
|
||||
|
||||
*hostno = hn;
|
||||
session = class_to_transport_session(cls_session);
|
||||
|
||||
/* libiscsi setup itts, data and pool so just set desc fields */
|
||||
for (i = 0; i < session->cmds_max; i++) {
|
||||
ctask = session->cmds[i];
|
||||
iser_ctask = ctask->dd_data;
|
||||
ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
|
||||
}
|
||||
|
||||
for (i = 0; i < session->mgmtpool_max; i++) {
|
||||
mtask = session->mgmt_cmds[i];
|
||||
desc = mtask->dd_data;
|
||||
mtask->hdr = &desc->iscsi_header;
|
||||
desc->data = mtask->data;
|
||||
}
|
||||
|
||||
return cls_session;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_set_param(struct iscsi_cls_conn *cls_conn,
|
||||
enum iscsi_param param, char *buf, int buflen)
|
||||
{
|
||||
int value;
|
||||
|
||||
switch (param) {
|
||||
case ISCSI_PARAM_MAX_RECV_DLENGTH:
|
||||
/* TBD */
|
||||
break;
|
||||
case ISCSI_PARAM_HDRDGST_EN:
|
||||
sscanf(buf, "%d", &value);
|
||||
if (value) {
|
||||
printk(KERN_ERR "DataDigest wasn't negotiated to None");
|
||||
return -EPROTO;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PARAM_DATADGST_EN:
|
||||
sscanf(buf, "%d", &value);
|
||||
if (value) {
|
||||
printk(KERN_ERR "DataDigest wasn't negotiated to None");
|
||||
return -EPROTO;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PARAM_IFMARKER_EN:
|
||||
sscanf(buf, "%d", &value);
|
||||
if (value) {
|
||||
printk(KERN_ERR "IFMarker wasn't negotiated to No");
|
||||
return -EPROTO;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PARAM_OFMARKER_EN:
|
||||
sscanf(buf, "%d", &value);
|
||||
if (value) {
|
||||
printk(KERN_ERR "OFMarker wasn't negotiated to No");
|
||||
return -EPROTO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return iscsi_set_param(cls_conn, param, buf, buflen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
|
||||
{
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
|
||||
stats->txdata_octets = conn->txdata_octets;
|
||||
stats->rxdata_octets = conn->rxdata_octets;
|
||||
stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
|
||||
stats->dataout_pdus = conn->dataout_pdus_cnt;
|
||||
stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
|
||||
stats->datain_pdus = conn->datain_pdus_cnt; /* always 0 */
|
||||
stats->r2t_pdus = conn->r2t_pdus_cnt; /* always 0 */
|
||||
stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
|
||||
stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
|
||||
stats->custom_length = 3;
|
||||
strcpy(stats->custom[0].desc, "qp_tx_queue_full");
|
||||
stats->custom[0].value = 0; /* TB iser_conn->qp_tx_queue_full; */
|
||||
strcpy(stats->custom[1].desc, "fmr_map_not_avail");
|
||||
stats->custom[1].value = 0; /* TB iser_conn->fmr_map_not_avail */;
|
||||
strcpy(stats->custom[2].desc, "eh_abort_cnt");
|
||||
stats->custom[2].value = conn->eh_abort_cnt;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
|
||||
__u64 *ep_handle)
|
||||
{
|
||||
int err;
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
err = iser_conn_init(&ib_conn);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
|
||||
if (!err)
|
||||
*ep_handle = (__u64)(unsigned long)ib_conn;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
|
||||
{
|
||||
struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
|
||||
int rc;
|
||||
|
||||
if (!ib_conn)
|
||||
return -EINVAL;
|
||||
|
||||
rc = wait_event_interruptible_timeout(ib_conn->wait,
|
||||
ib_conn->state == ISER_CONN_UP,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
|
||||
/* if conn establishment failed, return error code to iscsi */
|
||||
if (!rc &&
|
||||
(ib_conn->state == ISER_CONN_TERMINATING ||
|
||||
ib_conn->state == ISER_CONN_DOWN))
|
||||
rc = -1;
|
||||
|
||||
iser_err("ib conn %p rc = %d\n", ib_conn, rc);
|
||||
|
||||
if (rc > 0)
|
||||
return 1; /* success, this is the equivalent of POLLOUT */
|
||||
else if (!rc)
|
||||
return 0; /* timeout */
|
||||
else
|
||||
return rc; /* signal */
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_iser_ep_disconnect(__u64 ep_handle)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
|
||||
if (!ib_conn)
|
||||
return;
|
||||
|
||||
iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
|
||||
iser_conn_terminate(ib_conn);
|
||||
}
|
||||
|
||||
static struct scsi_host_template iscsi_iser_sht = {
|
||||
.name = "iSCSI Initiator over iSER, v." DRV_VER,
|
||||
.queuecommand = iscsi_queuecommand,
|
||||
.can_queue = ISCSI_XMIT_CMDS_MAX - 1,
|
||||
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
|
||||
.max_sectors = 1024,
|
||||
.cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
|
||||
.eh_abort_handler = iscsi_eh_abort,
|
||||
.eh_host_reset_handler = iscsi_eh_host_reset,
|
||||
.use_clustering = DISABLE_CLUSTERING,
|
||||
.proc_name = "iscsi_iser",
|
||||
.this_id = -1,
|
||||
};
|
||||
|
||||
static struct iscsi_transport iscsi_iser_transport = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "iser",
|
||||
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T,
|
||||
.param_mask = ISCSI_MAX_RECV_DLENGTH |
|
||||
ISCSI_MAX_XMIT_DLENGTH |
|
||||
ISCSI_HDRDGST_EN |
|
||||
ISCSI_DATADGST_EN |
|
||||
ISCSI_INITIAL_R2T_EN |
|
||||
ISCSI_MAX_R2T |
|
||||
ISCSI_IMM_DATA_EN |
|
||||
ISCSI_FIRST_BURST |
|
||||
ISCSI_MAX_BURST |
|
||||
ISCSI_PDU_INORDER_EN |
|
||||
ISCSI_DATASEQ_INORDER_EN |
|
||||
ISCSI_EXP_STATSN |
|
||||
ISCSI_PERSISTENT_PORT |
|
||||
ISCSI_PERSISTENT_ADDRESS |
|
||||
ISCSI_TARGET_NAME |
|
||||
ISCSI_TPGT,
|
||||
.host_template = &iscsi_iser_sht,
|
||||
.conndata_size = sizeof(struct iscsi_conn),
|
||||
.max_lun = ISCSI_ISER_MAX_LUN,
|
||||
.max_cmd_len = ISCSI_ISER_MAX_CMD_LEN,
|
||||
/* session management */
|
||||
.create_session = iscsi_iser_session_create,
|
||||
.destroy_session = iscsi_session_teardown,
|
||||
/* connection management */
|
||||
.create_conn = iscsi_iser_conn_create,
|
||||
.bind_conn = iscsi_iser_conn_bind,
|
||||
.destroy_conn = iscsi_iser_conn_destroy,
|
||||
.set_param = iscsi_iser_set_param,
|
||||
.get_conn_param = iscsi_conn_get_param,
|
||||
.get_session_param = iscsi_session_get_param,
|
||||
.start_conn = iscsi_iser_conn_start,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
/* IO */
|
||||
.send_pdu = iscsi_conn_send_pdu,
|
||||
.get_stats = iscsi_iser_conn_get_stats,
|
||||
.init_cmd_task = iscsi_iser_cmd_init,
|
||||
.xmit_cmd_task = iscsi_iser_ctask_xmit,
|
||||
.xmit_mgmt_task = iscsi_iser_mtask_xmit,
|
||||
.cleanup_cmd_task = iscsi_iser_cleanup_ctask,
|
||||
/* recovery */
|
||||
.session_recovery_timedout = iscsi_session_recovery_timedout,
|
||||
|
||||
.ep_connect = iscsi_iser_ep_connect,
|
||||
.ep_poll = iscsi_iser_ep_poll,
|
||||
.ep_disconnect = iscsi_iser_ep_disconnect
|
||||
};
|
||||
|
||||
static int __init iser_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
iser_dbg("Starting iSER datamover...\n");
|
||||
|
||||
if (iscsi_max_lun < 1) {
|
||||
printk(KERN_ERR "Invalid max_lun value of %u\n", iscsi_max_lun);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iscsi_iser_transport.max_lun = iscsi_max_lun;
|
||||
|
||||
memset(&ig, 0, sizeof(struct iser_global));
|
||||
|
||||
ig.desc_cache = kmem_cache_create("iser_descriptors",
|
||||
sizeof (struct iser_desc),
|
||||
0, SLAB_HWCACHE_ALIGN,
|
||||
NULL, NULL);
|
||||
if (ig.desc_cache == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* device init is called only after the first addr resolution */
|
||||
mutex_init(&ig.device_list_mutex);
|
||||
INIT_LIST_HEAD(&ig.device_list);
|
||||
mutex_init(&ig.connlist_mutex);
|
||||
INIT_LIST_HEAD(&ig.connlist);
|
||||
|
||||
if (!iscsi_register_transport(&iscsi_iser_transport)) {
|
||||
iser_err("iscsi_register_transport failed\n");
|
||||
err = -EINVAL;
|
||||
goto register_transport_failure;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
register_transport_failure:
|
||||
kmem_cache_destroy(ig.desc_cache);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit iser_exit(void)
|
||||
{
|
||||
iser_dbg("Removing iSER datamover...\n");
|
||||
iscsi_unregister_transport(&iscsi_iser_transport);
|
||||
kmem_cache_destroy(ig.desc_cache);
|
||||
}
|
||||
|
||||
module_init(iser_init);
|
||||
module_exit(iser_exit);
|
||||
364
drivers/infiniband/ulp/iser/iscsi_iser.h
Normal file
364
drivers/infiniband/ulp/iser/iscsi_iser.h
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* iSER transport for the Open iSCSI Initiator & iSER transport internals
|
||||
*
|
||||
* Copyright (C) 2004 Dmitry Yusupov
|
||||
* Copyright (C) 2004 Alex Aizman
|
||||
* Copyright (C) 2005 Mike Christie
|
||||
* based on code maintained by open-iscsi@googlegroups.com
|
||||
*
|
||||
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 Cisco Systems. 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: iscsi_iser.h,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#ifndef __ISCSI_ISER_H__
|
||||
#define __ISCSI_ISER_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/net.h>
|
||||
#include <scsi/libiscsi.h>
|
||||
#include <scsi/scsi_transport_iscsi.h>
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/ib_fmr_pool.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#define DRV_NAME "iser"
|
||||
#define PFX DRV_NAME ": "
|
||||
#define DRV_VER "0.1"
|
||||
#define DRV_DATE "May 7th, 2006"
|
||||
|
||||
#define iser_dbg(fmt, arg...) \
|
||||
do { \
|
||||
if (iser_debug_level > 0) \
|
||||
printk(KERN_DEBUG PFX "%s:" fmt,\
|
||||
__func__ , ## arg); \
|
||||
} while (0)
|
||||
|
||||
#define iser_err(fmt, arg...) \
|
||||
do { \
|
||||
printk(KERN_ERR PFX "%s:" fmt, \
|
||||
__func__ , ## arg); \
|
||||
} while (0)
|
||||
|
||||
#define SHIFT_4K 12
|
||||
#define SIZE_4K (1UL << SHIFT_4K)
|
||||
#define MASK_4K (~(SIZE_4K-1))
|
||||
|
||||
/* support upto 512KB in one RDMA */
|
||||
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
|
||||
#define ISCSI_ISER_MAX_LUN 256
|
||||
#define ISCSI_ISER_MAX_CMD_LEN 16
|
||||
|
||||
/* QP settings */
|
||||
/* Maximal bounds on received asynchronous PDUs */
|
||||
#define ISER_MAX_RX_MISC_PDUS 4 /* NOOP_IN(2) , ASYNC_EVENT(2) */
|
||||
|
||||
#define ISER_MAX_TX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
|
||||
* SCSI_TMFUNC(2), LOGOUT(1) */
|
||||
|
||||
#define ISER_QP_MAX_RECV_DTOS (ISCSI_XMIT_CMDS_MAX + \
|
||||
ISER_MAX_RX_MISC_PDUS + \
|
||||
ISER_MAX_TX_MISC_PDUS)
|
||||
|
||||
/* the max TX (send) WR supported by the iSER QP is defined by *
|
||||
* max_send_wr = T * (1 + D) + C ; D is how many inflight dataouts we expect *
|
||||
* to have at max for SCSI command. The tx posting & completion handling code *
|
||||
* supports -EAGAIN scheme where tx is suspended till the QP has room for more *
|
||||
* send WR. D=8 comes from 64K/8K */
|
||||
|
||||
#define ISER_INFLIGHT_DATAOUTS 8
|
||||
|
||||
#define ISER_QP_MAX_REQ_DTOS (ISCSI_XMIT_CMDS_MAX * \
|
||||
(1 + ISER_INFLIGHT_DATAOUTS) + \
|
||||
ISER_MAX_TX_MISC_PDUS + \
|
||||
ISER_MAX_RX_MISC_PDUS)
|
||||
|
||||
#define ISER_VER 0x10
|
||||
#define ISER_WSV 0x08
|
||||
#define ISER_RSV 0x04
|
||||
|
||||
struct iser_hdr {
|
||||
u8 flags;
|
||||
u8 rsvd[3];
|
||||
__be32 write_stag; /* write rkey */
|
||||
__be64 write_va;
|
||||
__be32 read_stag; /* read rkey */
|
||||
__be64 read_va;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* Length of an object name string */
|
||||
#define ISER_OBJECT_NAME_SIZE 64
|
||||
|
||||
enum iser_ib_conn_state {
|
||||
ISER_CONN_INIT, /* descriptor allocd, no conn */
|
||||
ISER_CONN_PENDING, /* in the process of being established */
|
||||
ISER_CONN_UP, /* up and running */
|
||||
ISER_CONN_TERMINATING, /* in the process of being terminated */
|
||||
ISER_CONN_DOWN, /* shut down */
|
||||
ISER_CONN_STATES_NUM
|
||||
};
|
||||
|
||||
enum iser_task_status {
|
||||
ISER_TASK_STATUS_INIT = 0,
|
||||
ISER_TASK_STATUS_STARTED,
|
||||
ISER_TASK_STATUS_COMPLETED
|
||||
};
|
||||
|
||||
enum iser_data_dir {
|
||||
ISER_DIR_IN = 0, /* to initiator */
|
||||
ISER_DIR_OUT, /* from initiator */
|
||||
ISER_DIRS_NUM
|
||||
};
|
||||
|
||||
struct iser_data_buf {
|
||||
void *buf; /* pointer to the sg list */
|
||||
unsigned int size; /* num entries of this sg */
|
||||
unsigned long data_len; /* total data len */
|
||||
unsigned int dma_nents; /* returned by dma_map_sg */
|
||||
char *copy_buf; /* allocated copy buf for SGs unaligned *
|
||||
* for rdma which are copied */
|
||||
struct scatterlist sg_single; /* SG-ified clone of a non SG SC or *
|
||||
* unaligned SG */
|
||||
};
|
||||
|
||||
/* fwd declarations */
|
||||
struct iser_device;
|
||||
struct iscsi_iser_conn;
|
||||
struct iscsi_iser_cmd_task;
|
||||
|
||||
struct iser_mem_reg {
|
||||
u32 lkey;
|
||||
u32 rkey;
|
||||
u64 va;
|
||||
u64 len;
|
||||
void *mem_h;
|
||||
int is_fmr;
|
||||
};
|
||||
|
||||
struct iser_regd_buf {
|
||||
struct iser_mem_reg reg; /* memory registration info */
|
||||
void *virt_addr;
|
||||
struct iser_device *device; /* device->device for dma_unmap */
|
||||
u64 dma_addr; /* if non zero, addr for dma_unmap */
|
||||
enum dma_data_direction direction; /* direction for dma_unmap */
|
||||
unsigned int data_size;
|
||||
atomic_t ref_count; /* refcount, freed when dec to 0 */
|
||||
};
|
||||
|
||||
#define MAX_REGD_BUF_VECTOR_LEN 2
|
||||
|
||||
struct iser_dto {
|
||||
struct iscsi_iser_cmd_task *ctask;
|
||||
struct iser_conn *ib_conn;
|
||||
int notify_enable;
|
||||
|
||||
/* vector of registered buffers */
|
||||
unsigned int regd_vector_len;
|
||||
struct iser_regd_buf *regd[MAX_REGD_BUF_VECTOR_LEN];
|
||||
|
||||
/* offset into the registered buffer may be specified */
|
||||
unsigned int offset[MAX_REGD_BUF_VECTOR_LEN];
|
||||
|
||||
/* a smaller size may be specified, if 0, then full size is used */
|
||||
unsigned int used_sz[MAX_REGD_BUF_VECTOR_LEN];
|
||||
};
|
||||
|
||||
enum iser_desc_type {
|
||||
ISCSI_RX,
|
||||
ISCSI_TX_CONTROL ,
|
||||
ISCSI_TX_SCSI_COMMAND,
|
||||
ISCSI_TX_DATAOUT
|
||||
};
|
||||
|
||||
struct iser_desc {
|
||||
struct iser_hdr iser_header;
|
||||
struct iscsi_hdr iscsi_header;
|
||||
struct iser_regd_buf hdr_regd_buf;
|
||||
void *data; /* used by RX & TX_CONTROL */
|
||||
struct iser_regd_buf data_regd_buf; /* used by RX & TX_CONTROL */
|
||||
enum iser_desc_type type;
|
||||
struct iser_dto dto;
|
||||
};
|
||||
|
||||
struct iser_device {
|
||||
struct ib_device *ib_device;
|
||||
struct ib_pd *pd;
|
||||
struct ib_cq *cq;
|
||||
struct ib_mr *mr;
|
||||
struct tasklet_struct cq_tasklet;
|
||||
struct list_head ig_list; /* entry in ig devices list */
|
||||
int refcount;
|
||||
};
|
||||
|
||||
struct iser_conn {
|
||||
struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
|
||||
enum iser_ib_conn_state state; /* rdma connection state */
|
||||
spinlock_t lock; /* used for state changes */
|
||||
struct iser_device *device; /* device context */
|
||||
struct rdma_cm_id *cma_id; /* CMA ID */
|
||||
struct ib_qp *qp; /* QP */
|
||||
struct ib_fmr_pool *fmr_pool; /* pool of IB FMRs */
|
||||
int disc_evt_flag; /* disconn event delivered */
|
||||
wait_queue_head_t wait; /* waitq for conn/disconn */
|
||||
atomic_t post_recv_buf_count; /* posted rx count */
|
||||
atomic_t post_send_buf_count; /* posted tx count */
|
||||
char name[ISER_OBJECT_NAME_SIZE];
|
||||
struct iser_page_vec *page_vec; /* represents SG to fmr maps*
|
||||
* maps serialized as tx is*/
|
||||
struct list_head conn_list; /* entry in ig conn list */
|
||||
};
|
||||
|
||||
struct iscsi_iser_conn {
|
||||
struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
|
||||
struct iser_conn *ib_conn; /* iSER IB conn */
|
||||
|
||||
rwlock_t lock;
|
||||
};
|
||||
|
||||
struct iscsi_iser_cmd_task {
|
||||
struct iser_desc desc;
|
||||
struct iscsi_iser_conn *iser_conn;
|
||||
enum iser_task_status status;
|
||||
int command_sent; /* set if command sent */
|
||||
int dir[ISER_DIRS_NUM]; /* set if dir use*/
|
||||
struct iser_regd_buf rdma_regd[ISER_DIRS_NUM];/* regd rdma buf */
|
||||
struct iser_data_buf data[ISER_DIRS_NUM]; /* orig. data des*/
|
||||
struct iser_data_buf data_copy[ISER_DIRS_NUM];/* contig. copy */
|
||||
};
|
||||
|
||||
struct iser_page_vec {
|
||||
u64 *pages;
|
||||
int length;
|
||||
int offset;
|
||||
int data_size;
|
||||
};
|
||||
|
||||
struct iser_global {
|
||||
struct mutex device_list_mutex;/* */
|
||||
struct list_head device_list; /* all iSER devices */
|
||||
struct mutex connlist_mutex;
|
||||
struct list_head connlist; /* all iSER IB connections */
|
||||
|
||||
struct kmem_cache *desc_cache;
|
||||
};
|
||||
|
||||
extern struct iser_global ig;
|
||||
extern int iser_debug_level;
|
||||
|
||||
/* allocate connection resources needed for rdma functionality */
|
||||
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
|
||||
|
||||
int iser_send_control(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask);
|
||||
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask);
|
||||
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask,
|
||||
struct iscsi_data *hdr);
|
||||
|
||||
void iscsi_iser_recv(struct iscsi_conn *conn,
|
||||
struct iscsi_hdr *hdr,
|
||||
char *rx_data,
|
||||
int rx_data_len);
|
||||
|
||||
int iser_conn_init(struct iser_conn **ib_conn);
|
||||
|
||||
void iser_conn_terminate(struct iser_conn *ib_conn);
|
||||
|
||||
void iser_conn_release(struct iser_conn *ib_conn);
|
||||
|
||||
void iser_rcv_completion(struct iser_desc *desc,
|
||||
unsigned long dto_xfer_len);
|
||||
|
||||
void iser_snd_completion(struct iser_desc *desc);
|
||||
|
||||
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *ctask);
|
||||
|
||||
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
|
||||
|
||||
void iser_dto_buffs_release(struct iser_dto *dto);
|
||||
|
||||
int iser_regd_buff_release(struct iser_regd_buf *regd_buf);
|
||||
|
||||
void iser_reg_single(struct iser_device *device,
|
||||
struct iser_regd_buf *regd_buf,
|
||||
enum dma_data_direction direction);
|
||||
|
||||
int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
|
||||
enum iser_data_dir cmd_dir);
|
||||
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
|
||||
enum iser_data_dir cmd_dir);
|
||||
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
|
||||
enum iser_data_dir cmd_dir);
|
||||
|
||||
int iser_connect(struct iser_conn *ib_conn,
|
||||
struct sockaddr_in *src_addr,
|
||||
struct sockaddr_in *dst_addr,
|
||||
int non_blocking);
|
||||
|
||||
int iser_reg_page_vec(struct iser_conn *ib_conn,
|
||||
struct iser_page_vec *page_vec,
|
||||
struct iser_mem_reg *mem_reg);
|
||||
|
||||
void iser_unreg_mem(struct iser_mem_reg *mem_reg);
|
||||
|
||||
int iser_post_recv(struct iser_desc *rx_desc);
|
||||
int iser_post_send(struct iser_desc *tx_desc);
|
||||
|
||||
int iser_conn_state_comp(struct iser_conn *ib_conn,
|
||||
enum iser_ib_conn_state comp);
|
||||
|
||||
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
struct iser_data_buf *data,
|
||||
enum iser_data_dir iser_dir,
|
||||
enum dma_data_direction dma_dir);
|
||||
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
|
||||
#endif
|
||||
705
drivers/infiniband/ulp/iser/iser_initiator.c
Normal file
705
drivers/infiniband/ulp/iser/iser_initiator.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005, 2006 Voltaire, 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: iser_initiator.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
|
||||
#include "iscsi_iser.h"
|
||||
|
||||
/* Constant PDU lengths calculations */
|
||||
#define ISER_TOTAL_HEADERS_LEN (sizeof (struct iser_hdr) + \
|
||||
sizeof (struct iscsi_hdr))
|
||||
|
||||
/* iser_dto_add_regd_buff - increments the reference count for *
|
||||
* the registered buffer & adds it to the DTO object */
|
||||
static void iser_dto_add_regd_buff(struct iser_dto *dto,
|
||||
struct iser_regd_buf *regd_buf,
|
||||
unsigned long use_offset,
|
||||
unsigned long use_size)
|
||||
{
|
||||
int add_idx;
|
||||
|
||||
atomic_inc(®d_buf->ref_count);
|
||||
|
||||
add_idx = dto->regd_vector_len;
|
||||
dto->regd[add_idx] = regd_buf;
|
||||
dto->used_sz[add_idx] = use_size;
|
||||
dto->offset[add_idx] = use_offset;
|
||||
|
||||
dto->regd_vector_len++;
|
||||
}
|
||||
|
||||
/* Register user buffer memory and initialize passive rdma
|
||||
* dto descriptor. Total data size is stored in
|
||||
* iser_ctask->data[ISER_DIR_IN].data_len
|
||||
*/
|
||||
static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
|
||||
unsigned int edtl)
|
||||
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int err;
|
||||
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
|
||||
struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
|
||||
|
||||
err = iser_dma_map_task_data(iser_ctask,
|
||||
buf_in,
|
||||
ISER_DIR_IN,
|
||||
DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
|
||||
iser_err("Total data length: %ld, less than EDTL: "
|
||||
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
|
||||
iser_ctask->data[ISER_DIR_IN].data_len, edtl,
|
||||
ctask->itt, iser_ctask->iser_conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
|
||||
if (err) {
|
||||
iser_err("Failed to set up Data-IN RDMA\n");
|
||||
return err;
|
||||
}
|
||||
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
|
||||
|
||||
hdr->flags |= ISER_RSV;
|
||||
hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
|
||||
hdr->read_va = cpu_to_be64(regd_buf->reg.va);
|
||||
|
||||
iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
|
||||
ctask->itt, regd_buf->reg.rkey,
|
||||
(unsigned long long)regd_buf->reg.va);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register user buffer memory and initialize passive rdma
|
||||
* dto descriptor. Total data size is stored in
|
||||
* ctask->data[ISER_DIR_OUT].data_len
|
||||
*/
|
||||
static int
|
||||
iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
|
||||
unsigned int imm_sz,
|
||||
unsigned int unsol_sz,
|
||||
unsigned int edtl)
|
||||
{
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int err;
|
||||
struct iser_dto *send_dto = &iser_ctask->desc.dto;
|
||||
struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
|
||||
struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
|
||||
|
||||
err = iser_dma_map_task_data(iser_ctask,
|
||||
buf_out,
|
||||
ISER_DIR_OUT,
|
||||
DMA_TO_DEVICE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
|
||||
iser_err("Total data length: %ld, less than EDTL: %d, "
|
||||
"in WRITE cmd BHS itt: %d, conn: 0x%p\n",
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len,
|
||||
edtl, ctask->itt, ctask->conn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
|
||||
if (err != 0) {
|
||||
iser_err("Failed to register write cmd RDMA mem\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
|
||||
|
||||
if (unsol_sz < edtl) {
|
||||
hdr->flags |= ISER_WSV;
|
||||
hdr->write_stag = cpu_to_be32(regd_buf->reg.rkey);
|
||||
hdr->write_va = cpu_to_be64(regd_buf->reg.va + unsol_sz);
|
||||
|
||||
iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
|
||||
"VA:%#llX + unsol:%d\n",
|
||||
ctask->itt, regd_buf->reg.rkey,
|
||||
(unsigned long long)regd_buf->reg.va, unsol_sz);
|
||||
}
|
||||
|
||||
if (imm_sz > 0) {
|
||||
iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
|
||||
ctask->itt, imm_sz);
|
||||
iser_dto_add_regd_buff(send_dto,
|
||||
regd_buf,
|
||||
0,
|
||||
imm_sz);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_post_receive_control - allocates, initializes and posts receive DTO.
|
||||
*/
|
||||
static int iser_post_receive_control(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iser_desc *rx_desc;
|
||||
struct iser_regd_buf *regd_hdr;
|
||||
struct iser_regd_buf *regd_data;
|
||||
struct iser_dto *recv_dto = NULL;
|
||||
struct iser_device *device = iser_conn->ib_conn->device;
|
||||
int rx_data_size, err = 0;
|
||||
|
||||
rx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
|
||||
if (rx_desc == NULL) {
|
||||
iser_err("Failed to alloc desc for post recv\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
rx_desc->type = ISCSI_RX;
|
||||
|
||||
/* for the login sequence we must support rx of upto 8K; login is done
|
||||
* after conn create/bind (connect) and conn stop/bind (reconnect),
|
||||
* what's common for both schemes is that the connection is not started
|
||||
*/
|
||||
if (conn->c_stage != ISCSI_CONN_STARTED)
|
||||
rx_data_size = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
|
||||
else /* FIXME till user space sets conn->max_recv_dlength correctly */
|
||||
rx_data_size = 128;
|
||||
|
||||
rx_desc->data = kmalloc(rx_data_size, GFP_NOIO);
|
||||
if (rx_desc->data == NULL) {
|
||||
iser_err("Failed to alloc data buf for post recv\n");
|
||||
err = -ENOMEM;
|
||||
goto post_rx_kmalloc_failure;
|
||||
}
|
||||
|
||||
recv_dto = &rx_desc->dto;
|
||||
recv_dto->ib_conn = iser_conn->ib_conn;
|
||||
recv_dto->regd_vector_len = 0;
|
||||
|
||||
regd_hdr = &rx_desc->hdr_regd_buf;
|
||||
memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
|
||||
regd_hdr->device = device;
|
||||
regd_hdr->virt_addr = rx_desc; /* == &rx_desc->iser_header */
|
||||
regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
|
||||
|
||||
iser_reg_single(device, regd_hdr, DMA_FROM_DEVICE);
|
||||
|
||||
iser_dto_add_regd_buff(recv_dto, regd_hdr, 0, 0);
|
||||
|
||||
regd_data = &rx_desc->data_regd_buf;
|
||||
memset(regd_data, 0, sizeof(struct iser_regd_buf));
|
||||
regd_data->device = device;
|
||||
regd_data->virt_addr = rx_desc->data;
|
||||
regd_data->data_size = rx_data_size;
|
||||
|
||||
iser_reg_single(device, regd_data, DMA_FROM_DEVICE);
|
||||
|
||||
iser_dto_add_regd_buff(recv_dto, regd_data, 0, 0);
|
||||
|
||||
err = iser_post_recv(rx_desc);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
/* iser_post_recv failed */
|
||||
iser_dto_buffs_release(recv_dto);
|
||||
kfree(rx_desc->data);
|
||||
post_rx_kmalloc_failure:
|
||||
kmem_cache_free(ig.desc_cache, rx_desc);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* creates a new tx descriptor and adds header regd buffer */
|
||||
static void iser_create_send_desc(struct iscsi_iser_conn *iser_conn,
|
||||
struct iser_desc *tx_desc)
|
||||
{
|
||||
struct iser_regd_buf *regd_hdr = &tx_desc->hdr_regd_buf;
|
||||
struct iser_dto *send_dto = &tx_desc->dto;
|
||||
|
||||
memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
|
||||
regd_hdr->device = iser_conn->ib_conn->device;
|
||||
regd_hdr->virt_addr = tx_desc; /* == &tx_desc->iser_header */
|
||||
regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
|
||||
|
||||
send_dto->ib_conn = iser_conn->ib_conn;
|
||||
send_dto->notify_enable = 1;
|
||||
send_dto->regd_vector_len = 0;
|
||||
|
||||
memset(&tx_desc->iser_header, 0, sizeof(struct iser_hdr));
|
||||
tx_desc->iser_header.flags = ISER_VER;
|
||||
|
||||
iser_dto_add_regd_buff(send_dto, regd_hdr, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_conn_set_full_featured_mode - (iSER API)
|
||||
*/
|
||||
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
|
||||
int i;
|
||||
/* no need to keep it in a var, we are after login so if this should
|
||||
* be negotiated, by now the result should be available here */
|
||||
int initial_post_recv_bufs_num = ISER_MAX_RX_MISC_PDUS;
|
||||
|
||||
iser_dbg("Initially post: %d\n", initial_post_recv_bufs_num);
|
||||
|
||||
/* Check that there is no posted recv or send buffers left - */
|
||||
/* they must be consumed during the login phase */
|
||||
BUG_ON(atomic_read(&iser_conn->ib_conn->post_recv_buf_count) != 0);
|
||||
BUG_ON(atomic_read(&iser_conn->ib_conn->post_send_buf_count) != 0);
|
||||
|
||||
/* Initial post receive buffers */
|
||||
for (i = 0; i < initial_post_recv_bufs_num; i++) {
|
||||
if (iser_post_receive_control(conn) != 0) {
|
||||
iser_err("Failed to post recv bufs at:%d conn:0x%p\n",
|
||||
i, conn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
iser_dbg("Posted %d post recv bufs, conn:0x%p\n", i, conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iser_check_xmit(struct iscsi_conn *conn, void *task)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
|
||||
if (atomic_read(&iser_conn->ib_conn->post_send_buf_count) ==
|
||||
ISER_QP_MAX_REQ_DTOS) {
|
||||
iser_dbg("%ld can't xmit task %p\n",jiffies,task);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iser_send_command - send command PDU
|
||||
*/
|
||||
int iser_send_command(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long edtl;
|
||||
int err = 0;
|
||||
struct iser_data_buf *data_buf;
|
||||
|
||||
struct iscsi_cmd *hdr = ctask->hdr;
|
||||
struct scsi_cmnd *sc = ctask->sc;
|
||||
|
||||
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
|
||||
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
|
||||
return -EPERM;
|
||||
}
|
||||
if (iser_check_xmit(conn, ctask))
|
||||
return -ENOBUFS;
|
||||
|
||||
edtl = ntohl(hdr->data_length);
|
||||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
|
||||
send_dto = &iser_ctask->desc.dto;
|
||||
send_dto->ctask = iser_ctask;
|
||||
iser_create_send_desc(iser_conn, &iser_ctask->desc);
|
||||
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_READ)
|
||||
data_buf = &iser_ctask->data[ISER_DIR_IN];
|
||||
else
|
||||
data_buf = &iser_ctask->data[ISER_DIR_OUT];
|
||||
|
||||
if (sc->use_sg) { /* using a scatter list */
|
||||
data_buf->buf = sc->request_buffer;
|
||||
data_buf->size = sc->use_sg;
|
||||
} else if (sc->request_bufflen) {
|
||||
/* using a single buffer - convert it into one entry SG */
|
||||
sg_init_one(&data_buf->sg_single,
|
||||
sc->request_buffer, sc->request_bufflen);
|
||||
data_buf->buf = &data_buf->sg_single;
|
||||
data_buf->size = 1;
|
||||
}
|
||||
|
||||
data_buf->data_len = sc->request_bufflen;
|
||||
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_READ) {
|
||||
err = iser_prepare_read_cmd(ctask, edtl);
|
||||
if (err)
|
||||
goto send_command_error;
|
||||
}
|
||||
if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
|
||||
err = iser_prepare_write_cmd(ctask,
|
||||
ctask->imm_count,
|
||||
ctask->imm_count +
|
||||
ctask->unsol_count,
|
||||
edtl);
|
||||
if (err)
|
||||
goto send_command_error;
|
||||
}
|
||||
|
||||
iser_reg_single(iser_conn->ib_conn->device,
|
||||
send_dto->regd[0], DMA_TO_DEVICE);
|
||||
|
||||
if (iser_post_receive_control(conn) != 0) {
|
||||
iser_err("post_recv failed!\n");
|
||||
err = -ENOMEM;
|
||||
goto send_command_error;
|
||||
}
|
||||
|
||||
iser_ctask->status = ISER_TASK_STATUS_STARTED;
|
||||
|
||||
err = iser_post_send(&iser_ctask->desc);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
send_command_error:
|
||||
iser_dto_buffs_release(send_dto);
|
||||
iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_send_data_out - send data out PDU
|
||||
*/
|
||||
int iser_send_data_out(struct iscsi_conn *conn,
|
||||
struct iscsi_cmd_task *ctask,
|
||||
struct iscsi_data *hdr)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
|
||||
struct iser_desc *tx_desc = NULL;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long buf_offset;
|
||||
unsigned long data_seg_len;
|
||||
unsigned int itt;
|
||||
int err = 0;
|
||||
|
||||
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
|
||||
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (iser_check_xmit(conn, ctask))
|
||||
return -ENOBUFS;
|
||||
|
||||
itt = ntohl(hdr->itt);
|
||||
data_seg_len = ntoh24(hdr->dlength);
|
||||
buf_offset = ntohl(hdr->offset);
|
||||
|
||||
iser_dbg("%s itt %d dseg_len %d offset %d\n",
|
||||
__func__,(int)itt,(int)data_seg_len,(int)buf_offset);
|
||||
|
||||
tx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
|
||||
if (tx_desc == NULL) {
|
||||
iser_err("Failed to alloc desc for post dataout\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tx_desc->type = ISCSI_TX_DATAOUT;
|
||||
memcpy(&tx_desc->iscsi_header, hdr, sizeof(struct iscsi_hdr));
|
||||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
send_dto = &tx_desc->dto;
|
||||
send_dto->ctask = iser_ctask;
|
||||
iser_create_send_desc(iser_conn, tx_desc);
|
||||
|
||||
iser_reg_single(iser_conn->ib_conn->device,
|
||||
send_dto->regd[0], DMA_TO_DEVICE);
|
||||
|
||||
/* all data was registered for RDMA, we can use the lkey */
|
||||
iser_dto_add_regd_buff(send_dto,
|
||||
&iser_ctask->rdma_regd[ISER_DIR_OUT],
|
||||
buf_offset,
|
||||
data_seg_len);
|
||||
|
||||
if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
|
||||
iser_err("Offset:%ld & DSL:%ld in Data-Out "
|
||||
"inconsistent with total len:%ld, itt:%d\n",
|
||||
buf_offset, data_seg_len,
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len, itt);
|
||||
err = -EINVAL;
|
||||
goto send_data_out_error;
|
||||
}
|
||||
iser_dbg("data-out itt: %d, offset: %ld, sz: %ld\n",
|
||||
itt, buf_offset, data_seg_len);
|
||||
|
||||
|
||||
err = iser_post_send(tx_desc);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
send_data_out_error:
|
||||
iser_dto_buffs_release(send_dto);
|
||||
kmem_cache_free(ig.desc_cache, tx_desc);
|
||||
iser_err("conn %p failed err %d\n",conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int iser_send_control(struct iscsi_conn *conn,
|
||||
struct iscsi_mgmt_task *mtask)
|
||||
{
|
||||
struct iscsi_iser_conn *iser_conn = conn->dd_data;
|
||||
struct iser_desc *mdesc = mtask->dd_data;
|
||||
struct iser_dto *send_dto = NULL;
|
||||
unsigned long data_seg_len;
|
||||
int err = 0;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
struct iser_device *device;
|
||||
|
||||
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
|
||||
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (iser_check_xmit(conn,mtask))
|
||||
return -ENOBUFS;
|
||||
|
||||
/* build the tx desc regd header and add it to the tx desc dto */
|
||||
mdesc->type = ISCSI_TX_CONTROL;
|
||||
send_dto = &mdesc->dto;
|
||||
send_dto->ctask = NULL;
|
||||
iser_create_send_desc(iser_conn, mdesc);
|
||||
|
||||
device = iser_conn->ib_conn->device;
|
||||
|
||||
iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
|
||||
|
||||
data_seg_len = ntoh24(mtask->hdr->dlength);
|
||||
|
||||
if (data_seg_len > 0) {
|
||||
regd_buf = &mdesc->data_regd_buf;
|
||||
memset(regd_buf, 0, sizeof(struct iser_regd_buf));
|
||||
regd_buf->device = device;
|
||||
regd_buf->virt_addr = mtask->data;
|
||||
regd_buf->data_size = mtask->data_count;
|
||||
iser_reg_single(device, regd_buf,
|
||||
DMA_TO_DEVICE);
|
||||
iser_dto_add_regd_buff(send_dto, regd_buf,
|
||||
0,
|
||||
data_seg_len);
|
||||
}
|
||||
|
||||
if (iser_post_receive_control(conn) != 0) {
|
||||
iser_err("post_rcv_buff failed!\n");
|
||||
err = -ENOMEM;
|
||||
goto send_control_error;
|
||||
}
|
||||
|
||||
err = iser_post_send(mdesc);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
send_control_error:
|
||||
iser_dto_buffs_release(send_dto);
|
||||
iser_err("conn %p failed err %d\n",conn, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_rcv_dto_completion - recv DTO completion
|
||||
*/
|
||||
void iser_rcv_completion(struct iser_desc *rx_desc,
|
||||
unsigned long dto_xfer_len)
|
||||
{
|
||||
struct iser_dto *dto = &rx_desc->dto;
|
||||
struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
|
||||
struct iscsi_session *session = conn->iscsi_conn->session;
|
||||
struct iscsi_cmd_task *ctask;
|
||||
struct iscsi_iser_cmd_task *iser_ctask;
|
||||
struct iscsi_hdr *hdr;
|
||||
char *rx_data = NULL;
|
||||
int rx_data_len = 0;
|
||||
unsigned int itt;
|
||||
unsigned char opcode;
|
||||
|
||||
hdr = &rx_desc->iscsi_header;
|
||||
|
||||
iser_dbg("op 0x%x itt 0x%x\n", hdr->opcode,hdr->itt);
|
||||
|
||||
if (dto_xfer_len > ISER_TOTAL_HEADERS_LEN) { /* we have data */
|
||||
rx_data_len = dto_xfer_len - ISER_TOTAL_HEADERS_LEN;
|
||||
rx_data = dto->regd[1]->virt_addr;
|
||||
rx_data += dto->offset[1];
|
||||
}
|
||||
|
||||
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
|
||||
|
||||
if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
|
||||
itt = get_itt(hdr->itt); /* mask out cid and age bits */
|
||||
if (!(itt < session->cmds_max))
|
||||
iser_err("itt can't be matched to task!!!"
|
||||
"conn %p opcode %d cmds_max %d itt %d\n",
|
||||
conn->iscsi_conn,opcode,session->cmds_max,itt);
|
||||
/* use the mapping given with the cmds array indexed by itt */
|
||||
ctask = (struct iscsi_cmd_task *)session->cmds[itt];
|
||||
iser_ctask = ctask->dd_data;
|
||||
iser_dbg("itt %d ctask %p\n",itt,ctask);
|
||||
iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
|
||||
iser_ctask_rdma_finalize(iser_ctask);
|
||||
}
|
||||
|
||||
iser_dto_buffs_release(dto);
|
||||
|
||||
iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
|
||||
|
||||
kfree(rx_desc->data);
|
||||
kmem_cache_free(ig.desc_cache, rx_desc);
|
||||
|
||||
/* decrementing conn->post_recv_buf_count only --after-- freeing the *
|
||||
* task eliminates the need to worry on tasks which are completed in *
|
||||
* parallel to the execution of iser_conn_term. So the code that waits *
|
||||
* for the posted rx bufs refcount to become zero handles everything */
|
||||
atomic_dec(&conn->ib_conn->post_recv_buf_count);
|
||||
}
|
||||
|
||||
void iser_snd_completion(struct iser_desc *tx_desc)
|
||||
{
|
||||
struct iser_dto *dto = &tx_desc->dto;
|
||||
struct iser_conn *ib_conn = dto->ib_conn;
|
||||
struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
|
||||
struct iscsi_conn *conn = iser_conn->iscsi_conn;
|
||||
struct iscsi_mgmt_task *mtask;
|
||||
int resume_tx = 0;
|
||||
|
||||
iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
|
||||
|
||||
iser_dto_buffs_release(dto);
|
||||
|
||||
if (tx_desc->type == ISCSI_TX_DATAOUT)
|
||||
kmem_cache_free(ig.desc_cache, tx_desc);
|
||||
|
||||
if (atomic_read(&iser_conn->ib_conn->post_send_buf_count) ==
|
||||
ISER_QP_MAX_REQ_DTOS)
|
||||
resume_tx = 1;
|
||||
|
||||
atomic_dec(&ib_conn->post_send_buf_count);
|
||||
|
||||
if (resume_tx) {
|
||||
iser_dbg("%ld resuming tx\n",jiffies);
|
||||
scsi_queue_work(conn->session->host, &conn->xmitwork);
|
||||
}
|
||||
|
||||
if (tx_desc->type == ISCSI_TX_CONTROL) {
|
||||
/* this arithmetic is legal by libiscsi dd_data allocation */
|
||||
mtask = (void *) ((long)(void *)tx_desc -
|
||||
sizeof(struct iscsi_mgmt_task));
|
||||
if (mtask->hdr->itt == RESERVED_ITT) {
|
||||
struct iscsi_session *session = conn->session;
|
||||
|
||||
spin_lock(&conn->session->lock);
|
||||
list_del(&mtask->running);
|
||||
__kfifo_put(session->mgmtpool.queue, (void*)&mtask,
|
||||
sizeof(void*));
|
||||
spin_unlock(&session->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
|
||||
{
|
||||
iser_ctask->status = ISER_TASK_STATUS_INIT;
|
||||
|
||||
iser_ctask->dir[ISER_DIR_IN] = 0;
|
||||
iser_ctask->dir[ISER_DIR_OUT] = 0;
|
||||
|
||||
iser_ctask->data[ISER_DIR_IN].data_len = 0;
|
||||
iser_ctask->data[ISER_DIR_OUT].data_len = 0;
|
||||
|
||||
memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
|
||||
sizeof(struct iser_regd_buf));
|
||||
memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
|
||||
sizeof(struct iser_regd_buf));
|
||||
}
|
||||
|
||||
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
{
|
||||
int deferred;
|
||||
int is_rdma_aligned = 1;
|
||||
struct iser_regd_buf *regd;
|
||||
|
||||
/* if we were reading, copy back to unaligned sglist,
|
||||
* anyway dma_unmap and free the copy
|
||||
*/
|
||||
if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
|
||||
is_rdma_aligned = 0;
|
||||
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
|
||||
}
|
||||
if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
|
||||
is_rdma_aligned = 0;
|
||||
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_IN]) {
|
||||
regd = &iser_ctask->rdma_regd[ISER_DIR_IN];
|
||||
deferred = iser_regd_buff_release(regd);
|
||||
if (deferred) {
|
||||
iser_err("%d references remain for BUF-IN rdma reg\n",
|
||||
atomic_read(®d->ref_count));
|
||||
}
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_OUT]) {
|
||||
regd = &iser_ctask->rdma_regd[ISER_DIR_OUT];
|
||||
deferred = iser_regd_buff_release(regd);
|
||||
if (deferred) {
|
||||
iser_err("%d references remain for BUF-OUT rdma reg\n",
|
||||
atomic_read(®d->ref_count));
|
||||
}
|
||||
}
|
||||
|
||||
/* if the data was unaligned, it was already unmapped and then copied */
|
||||
if (is_rdma_aligned)
|
||||
iser_dma_unmap_task_data(iser_ctask);
|
||||
}
|
||||
|
||||
void iser_dto_buffs_release(struct iser_dto *dto)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dto->regd_vector_len; i++)
|
||||
iser_regd_buff_release(dto->regd[i]);
|
||||
}
|
||||
|
||||
481
drivers/infiniband/ulp/iser/iser_memory.c
Normal file
481
drivers/infiniband/ulp/iser/iser_memory.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005, 2006 Voltaire, 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: iser_memory.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include "iscsi_iser.h"
|
||||
|
||||
#define ISER_KMALLOC_THRESHOLD 0x20000 /* 128K - kmalloc limit */
|
||||
|
||||
/**
|
||||
* Decrements the reference count for the
|
||||
* registered buffer & releases it
|
||||
*
|
||||
* returns 0 if released, 1 if deferred
|
||||
*/
|
||||
int iser_regd_buff_release(struct iser_regd_buf *regd_buf)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
|
||||
if ((atomic_read(®d_buf->ref_count) == 0) ||
|
||||
atomic_dec_and_test(®d_buf->ref_count)) {
|
||||
/* if we used the dma mr, unreg is just NOP */
|
||||
if (regd_buf->reg.is_fmr)
|
||||
iser_unreg_mem(®d_buf->reg);
|
||||
|
||||
if (regd_buf->dma_addr) {
|
||||
dev = regd_buf->device->ib_device;
|
||||
ib_dma_unmap_single(dev,
|
||||
regd_buf->dma_addr,
|
||||
regd_buf->data_size,
|
||||
regd_buf->direction);
|
||||
}
|
||||
/* else this regd buf is associated with task which we */
|
||||
/* dma_unmap_single/sg later */
|
||||
return 0;
|
||||
} else {
|
||||
iser_dbg("Release deferred, regd.buff: 0x%p\n", regd_buf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_reg_single - fills registered buffer descriptor with
|
||||
* registration information
|
||||
*/
|
||||
void iser_reg_single(struct iser_device *device,
|
||||
struct iser_regd_buf *regd_buf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
u64 dma_addr;
|
||||
|
||||
dma_addr = ib_dma_map_single(device->ib_device,
|
||||
regd_buf->virt_addr,
|
||||
regd_buf->data_size, direction);
|
||||
BUG_ON(ib_dma_mapping_error(device->ib_device, dma_addr));
|
||||
|
||||
regd_buf->reg.lkey = device->mr->lkey;
|
||||
regd_buf->reg.len = regd_buf->data_size;
|
||||
regd_buf->reg.va = dma_addr;
|
||||
regd_buf->reg.is_fmr = 0;
|
||||
|
||||
regd_buf->dma_addr = dma_addr;
|
||||
regd_buf->direction = direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_start_rdma_unaligned_sg
|
||||
*/
|
||||
int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
int dma_nents;
|
||||
struct ib_device *dev;
|
||||
char *mem = NULL;
|
||||
struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
|
||||
unsigned long cmd_data_len = data->data_len;
|
||||
|
||||
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
|
||||
mem = (void *)__get_free_pages(GFP_NOIO,
|
||||
ilog2(roundup_pow_of_two(cmd_data_len)) - PAGE_SHIFT);
|
||||
else
|
||||
mem = kmalloc(cmd_data_len, GFP_NOIO);
|
||||
|
||||
if (mem == NULL) {
|
||||
iser_err("Failed to allocate mem size %d %d for copying sglist\n",
|
||||
data->size,(int)cmd_data_len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cmd_dir == ISER_DIR_OUT) {
|
||||
/* copy the unaligned sg the buffer which is used for RDMA */
|
||||
struct scatterlist *sg = (struct scatterlist *)data->buf;
|
||||
int i;
|
||||
char *p, *from;
|
||||
|
||||
for (p = mem, i = 0; i < data->size; i++) {
|
||||
from = kmap_atomic(sg[i].page, KM_USER0);
|
||||
memcpy(p,
|
||||
from + sg[i].offset,
|
||||
sg[i].length);
|
||||
kunmap_atomic(from, KM_USER0);
|
||||
p += sg[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
|
||||
iser_ctask->data_copy[cmd_dir].buf =
|
||||
&iser_ctask->data_copy[cmd_dir].sg_single;
|
||||
iser_ctask->data_copy[cmd_dir].size = 1;
|
||||
|
||||
iser_ctask->data_copy[cmd_dir].copy_buf = mem;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
dma_nents = ib_dma_map_sg(dev,
|
||||
&iser_ctask->data_copy[cmd_dir].sg_single,
|
||||
1,
|
||||
(cmd_dir == ISER_DIR_OUT) ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
BUG_ON(dma_nents == 0);
|
||||
|
||||
iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_finalize_rdma_unaligned_sg
|
||||
*/
|
||||
void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
struct iser_data_buf *mem_copy;
|
||||
unsigned long cmd_data_len;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
mem_copy = &iser_ctask->data_copy[cmd_dir];
|
||||
|
||||
ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
|
||||
(cmd_dir == ISER_DIR_OUT) ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (cmd_dir == ISER_DIR_IN) {
|
||||
char *mem;
|
||||
struct scatterlist *sg;
|
||||
unsigned char *p, *to;
|
||||
unsigned int sg_size;
|
||||
int i;
|
||||
|
||||
/* copy back read RDMA to unaligned sg */
|
||||
mem = mem_copy->copy_buf;
|
||||
|
||||
sg = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
|
||||
sg_size = iser_ctask->data[ISER_DIR_IN].size;
|
||||
|
||||
for (p = mem, i = 0; i < sg_size; i++){
|
||||
to = kmap_atomic(sg[i].page, KM_SOFTIRQ0);
|
||||
memcpy(to + sg[i].offset,
|
||||
p,
|
||||
sg[i].length);
|
||||
kunmap_atomic(to, KM_SOFTIRQ0);
|
||||
p += sg[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
cmd_data_len = iser_ctask->data[cmd_dir].data_len;
|
||||
|
||||
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
|
||||
free_pages((unsigned long)mem_copy->copy_buf,
|
||||
ilog2(roundup_pow_of_two(cmd_data_len)) - PAGE_SHIFT);
|
||||
else
|
||||
kfree(mem_copy->copy_buf);
|
||||
|
||||
mem_copy->copy_buf = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_sg_to_page_vec - Translates scatterlist entries to physical addresses
|
||||
* and returns the length of resulting physical address array (may be less than
|
||||
* the original due to possible compaction).
|
||||
*
|
||||
* we build a "page vec" under the assumption that the SG meets the RDMA
|
||||
* alignment requirements. Other then the first and last SG elements, all
|
||||
* the "internal" elements can be compacted into a list whose elements are
|
||||
* dma addresses of physical pages. The code supports also the weird case
|
||||
* where --few fragments of the same page-- are present in the SG as
|
||||
* consecutive elements. Also, it handles one entry SG.
|
||||
*/
|
||||
static int iser_sg_to_page_vec(struct iser_data_buf *data,
|
||||
struct iser_page_vec *page_vec,
|
||||
struct ib_device *ibdev)
|
||||
{
|
||||
struct scatterlist *sg = (struct scatterlist *)data->buf;
|
||||
u64 first_addr, last_addr, page;
|
||||
int end_aligned;
|
||||
unsigned int cur_page = 0;
|
||||
unsigned long total_sz = 0;
|
||||
int i;
|
||||
|
||||
/* compute the offset of first element */
|
||||
page_vec->offset = (u64) sg[0].offset & ~MASK_4K;
|
||||
|
||||
for (i = 0; i < data->dma_nents; i++) {
|
||||
unsigned int dma_len = ib_sg_dma_len(ibdev, &sg[i]);
|
||||
|
||||
total_sz += dma_len;
|
||||
|
||||
first_addr = ib_sg_dma_address(ibdev, &sg[i]);
|
||||
last_addr = first_addr + dma_len;
|
||||
|
||||
end_aligned = !(last_addr & ~MASK_4K);
|
||||
|
||||
/* continue to collect page fragments till aligned or SG ends */
|
||||
while (!end_aligned && (i + 1 < data->dma_nents)) {
|
||||
i++;
|
||||
dma_len = ib_sg_dma_len(ibdev, &sg[i]);
|
||||
total_sz += dma_len;
|
||||
last_addr = ib_sg_dma_address(ibdev, &sg[i]) + dma_len;
|
||||
end_aligned = !(last_addr & ~MASK_4K);
|
||||
}
|
||||
|
||||
/* handle the 1st page in the 1st DMA element */
|
||||
if (cur_page == 0) {
|
||||
page = first_addr & MASK_4K;
|
||||
page_vec->pages[cur_page] = page;
|
||||
cur_page++;
|
||||
page += SIZE_4K;
|
||||
} else
|
||||
page = first_addr;
|
||||
|
||||
for (; page < last_addr; page += SIZE_4K) {
|
||||
page_vec->pages[cur_page] = page;
|
||||
cur_page++;
|
||||
}
|
||||
|
||||
}
|
||||
page_vec->data_size = total_sz;
|
||||
iser_dbg("page_vec->data_size:%d cur_page %d\n", page_vec->data_size,cur_page);
|
||||
return cur_page;
|
||||
}
|
||||
|
||||
#define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
|
||||
|
||||
/**
|
||||
* iser_data_buf_aligned_len - Tries to determine the maximal correctly aligned
|
||||
* for RDMA sub-list of a scatter-gather list of memory buffers, and returns
|
||||
* the number of entries which are aligned correctly. Supports the case where
|
||||
* consecutive SG elements are actually fragments of the same physcial page.
|
||||
*/
|
||||
static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data,
|
||||
struct ib_device *ibdev)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u64 end_addr, next_addr;
|
||||
int i, cnt;
|
||||
unsigned int ret_len = 0;
|
||||
|
||||
sg = (struct scatterlist *)data->buf;
|
||||
|
||||
for (cnt = 0, i = 0; i < data->dma_nents; i++, cnt++) {
|
||||
/* iser_dbg("Checking sg iobuf [%d]: phys=0x%08lX "
|
||||
"offset: %ld sz: %ld\n", i,
|
||||
(unsigned long)page_to_phys(sg[i].page),
|
||||
(unsigned long)sg[i].offset,
|
||||
(unsigned long)sg[i].length); */
|
||||
end_addr = ib_sg_dma_address(ibdev, &sg[i]) +
|
||||
ib_sg_dma_len(ibdev, &sg[i]);
|
||||
/* iser_dbg("Checking sg iobuf end address "
|
||||
"0x%08lX\n", end_addr); */
|
||||
if (i + 1 < data->dma_nents) {
|
||||
next_addr = ib_sg_dma_address(ibdev, &sg[i+1]);
|
||||
/* are i, i+1 fragments of the same page? */
|
||||
if (end_addr == next_addr)
|
||||
continue;
|
||||
else if (!IS_4K_ALIGNED(end_addr)) {
|
||||
ret_len = cnt + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == data->dma_nents)
|
||||
ret_len = cnt; /* loop ended */
|
||||
iser_dbg("Found %d aligned entries out of %d in sg:0x%p\n",
|
||||
ret_len, data->dma_nents, data);
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
static void iser_data_buf_dump(struct iser_data_buf *data,
|
||||
struct ib_device *ibdev)
|
||||
{
|
||||
struct scatterlist *sg = (struct scatterlist *)data->buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->dma_nents; i++)
|
||||
iser_err("sg[%d] dma_addr:0x%lX page:0x%p "
|
||||
"off:0x%x sz:0x%x dma_len:0x%x\n",
|
||||
i, (unsigned long)ib_sg_dma_address(ibdev, &sg[i]),
|
||||
sg[i].page, sg[i].offset,
|
||||
sg[i].length, ib_sg_dma_len(ibdev, &sg[i]));
|
||||
}
|
||||
|
||||
static void iser_dump_page_vec(struct iser_page_vec *page_vec)
|
||||
{
|
||||
int i;
|
||||
|
||||
iser_err("page vec length %d data size %d\n",
|
||||
page_vec->length, page_vec->data_size);
|
||||
for (i = 0; i < page_vec->length; i++)
|
||||
iser_err("%d %lx\n",i,(unsigned long)page_vec->pages[i]);
|
||||
}
|
||||
|
||||
static void iser_page_vec_build(struct iser_data_buf *data,
|
||||
struct iser_page_vec *page_vec,
|
||||
struct ib_device *ibdev)
|
||||
{
|
||||
int page_vec_len = 0;
|
||||
|
||||
page_vec->length = 0;
|
||||
page_vec->offset = 0;
|
||||
|
||||
iser_dbg("Translating sg sz: %d\n", data->dma_nents);
|
||||
page_vec_len = iser_sg_to_page_vec(data, page_vec, ibdev);
|
||||
iser_dbg("sg len %d page_vec_len %d\n", data->dma_nents,page_vec_len);
|
||||
|
||||
page_vec->length = page_vec_len;
|
||||
|
||||
if (page_vec_len * SIZE_4K < page_vec->data_size) {
|
||||
iser_err("page_vec too short to hold this SG\n");
|
||||
iser_data_buf_dump(data, ibdev);
|
||||
iser_dump_page_vec(page_vec);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
struct iser_data_buf *data,
|
||||
enum iser_data_dir iser_dir,
|
||||
enum dma_data_direction dma_dir)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
|
||||
iser_ctask->dir[iser_dir] = 1;
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
|
||||
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
|
||||
if (data->dma_nents == 0) {
|
||||
iser_err("dma_map_sg failed!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
|
||||
{
|
||||
struct ib_device *dev;
|
||||
struct iser_data_buf *data;
|
||||
|
||||
dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_IN]) {
|
||||
data = &iser_ctask->data[ISER_DIR_IN];
|
||||
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
if (iser_ctask->dir[ISER_DIR_OUT]) {
|
||||
data = &iser_ctask->data[ISER_DIR_OUT];
|
||||
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_reg_rdma_mem - Registers memory intended for RDMA,
|
||||
* obtaining rkey and va
|
||||
*
|
||||
* returns 0 on success, errno code on failure
|
||||
*/
|
||||
int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
|
||||
enum iser_data_dir cmd_dir)
|
||||
{
|
||||
struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
|
||||
struct iser_device *device = ib_conn->device;
|
||||
struct ib_device *ibdev = device->ib_device;
|
||||
struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
|
||||
struct iser_regd_buf *regd_buf;
|
||||
int aligned_len;
|
||||
int err;
|
||||
int i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
regd_buf = &iser_ctask->rdma_regd[cmd_dir];
|
||||
|
||||
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
|
||||
if (aligned_len != mem->dma_nents) {
|
||||
iser_err("rdma alignment violation %d/%d aligned\n",
|
||||
aligned_len, mem->size);
|
||||
iser_data_buf_dump(mem, ibdev);
|
||||
|
||||
/* unmap the command data before accessing it */
|
||||
iser_dma_unmap_task_data(iser_ctask);
|
||||
|
||||
/* allocate copy buf, if we are writing, copy the */
|
||||
/* unaligned scatterlist, dma map the copy */
|
||||
if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
|
||||
return -ENOMEM;
|
||||
mem = &iser_ctask->data_copy[cmd_dir];
|
||||
}
|
||||
|
||||
/* if there a single dma entry, FMR is not needed */
|
||||
if (mem->dma_nents == 1) {
|
||||
sg = (struct scatterlist *)mem->buf;
|
||||
|
||||
regd_buf->reg.lkey = device->mr->lkey;
|
||||
regd_buf->reg.rkey = device->mr->rkey;
|
||||
regd_buf->reg.len = ib_sg_dma_len(ibdev, &sg[0]);
|
||||
regd_buf->reg.va = ib_sg_dma_address(ibdev, &sg[0]);
|
||||
regd_buf->reg.is_fmr = 0;
|
||||
|
||||
iser_dbg("PHYSICAL Mem.register: lkey: 0x%08X rkey: 0x%08X "
|
||||
"va: 0x%08lX sz: %ld]\n",
|
||||
(unsigned int)regd_buf->reg.lkey,
|
||||
(unsigned int)regd_buf->reg.rkey,
|
||||
(unsigned long)regd_buf->reg.va,
|
||||
(unsigned long)regd_buf->reg.len);
|
||||
} else { /* use FMR for multiple dma entries */
|
||||
iser_page_vec_build(mem, ib_conn->page_vec, ibdev);
|
||||
err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, ®d_buf->reg);
|
||||
if (err) {
|
||||
iser_data_buf_dump(mem, ibdev);
|
||||
iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
|
||||
ntoh24(iser_ctask->desc.iscsi_header.dlength));
|
||||
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
|
||||
ib_conn->page_vec->data_size, ib_conn->page_vec->length,
|
||||
ib_conn->page_vec->offset);
|
||||
for (i=0 ; i<ib_conn->page_vec->length ; i++)
|
||||
iser_err("page_vec[%d] = 0x%llx\n", i,
|
||||
(unsigned long long) ib_conn->page_vec->pages[i]);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* take a reference on this regd buf such that it will not be released *
|
||||
* (eg in send dto completion) before we get the scsi response */
|
||||
atomic_inc(®d_buf->ref_count);
|
||||
return 0;
|
||||
}
|
||||
823
drivers/infiniband/ulp/iser/iser_verbs.c
Normal file
823
drivers/infiniband/ulp/iser/iser_verbs.c
Normal file
@@ -0,0 +1,823 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
|
||||
* Copyright (c) 2005, 2006 Cisco Systems. 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: iser_verbs.c,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
#include <asm/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "iscsi_iser.h"
|
||||
|
||||
#define ISCSI_ISER_MAX_CONN 8
|
||||
#define ISER_MAX_CQ_LEN ((ISER_QP_MAX_RECV_DTOS + \
|
||||
ISER_QP_MAX_REQ_DTOS) * \
|
||||
ISCSI_ISER_MAX_CONN)
|
||||
|
||||
static void iser_cq_tasklet_fn(unsigned long data);
|
||||
static void iser_cq_callback(struct ib_cq *cq, void *cq_context);
|
||||
|
||||
static void iser_cq_event_callback(struct ib_event *cause, void *context)
|
||||
{
|
||||
iser_err("got cq event %d \n", cause->event);
|
||||
}
|
||||
|
||||
static void iser_qp_event_callback(struct ib_event *cause, void *context)
|
||||
{
|
||||
iser_err("got qp event %d\n",cause->event);
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_create_device_ib_res - creates Protection Domain (PD), Completion
|
||||
* Queue (CQ), DMA Memory Region (DMA MR) with the device associated with
|
||||
* the adapator.
|
||||
*
|
||||
* returns 0 on success, -1 on failure
|
||||
*/
|
||||
static int iser_create_device_ib_res(struct iser_device *device)
|
||||
{
|
||||
device->pd = ib_alloc_pd(device->ib_device);
|
||||
if (IS_ERR(device->pd))
|
||||
goto pd_err;
|
||||
|
||||
device->cq = ib_create_cq(device->ib_device,
|
||||
iser_cq_callback,
|
||||
iser_cq_event_callback,
|
||||
(void *)device,
|
||||
ISER_MAX_CQ_LEN);
|
||||
if (IS_ERR(device->cq))
|
||||
goto cq_err;
|
||||
|
||||
if (ib_req_notify_cq(device->cq, IB_CQ_NEXT_COMP))
|
||||
goto cq_arm_err;
|
||||
|
||||
tasklet_init(&device->cq_tasklet,
|
||||
iser_cq_tasklet_fn,
|
||||
(unsigned long)device);
|
||||
|
||||
device->mr = ib_get_dma_mr(device->pd, IB_ACCESS_LOCAL_WRITE |
|
||||
IB_ACCESS_REMOTE_WRITE |
|
||||
IB_ACCESS_REMOTE_READ);
|
||||
if (IS_ERR(device->mr))
|
||||
goto dma_mr_err;
|
||||
|
||||
return 0;
|
||||
|
||||
dma_mr_err:
|
||||
tasklet_kill(&device->cq_tasklet);
|
||||
cq_arm_err:
|
||||
ib_destroy_cq(device->cq);
|
||||
cq_err:
|
||||
ib_dealloc_pd(device->pd);
|
||||
pd_err:
|
||||
iser_err("failed to allocate an IB resource\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_free_device_ib_res - destory/dealloc/dereg the DMA MR,
|
||||
* CQ and PD created with the device associated with the adapator.
|
||||
*/
|
||||
static void iser_free_device_ib_res(struct iser_device *device)
|
||||
{
|
||||
BUG_ON(device->mr == NULL);
|
||||
|
||||
tasklet_kill(&device->cq_tasklet);
|
||||
|
||||
(void)ib_dereg_mr(device->mr);
|
||||
(void)ib_destroy_cq(device->cq);
|
||||
(void)ib_dealloc_pd(device->pd);
|
||||
|
||||
device->mr = NULL;
|
||||
device->cq = NULL;
|
||||
device->pd = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_create_ib_conn_res - Creates FMR pool and Queue-Pair (QP)
|
||||
*
|
||||
* returns 0 on success, -1 on failure
|
||||
*/
|
||||
static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
|
||||
{
|
||||
struct iser_device *device;
|
||||
struct ib_qp_init_attr init_attr;
|
||||
int ret;
|
||||
struct ib_fmr_pool_param params;
|
||||
|
||||
BUG_ON(ib_conn->device == NULL);
|
||||
|
||||
device = ib_conn->device;
|
||||
|
||||
ib_conn->page_vec = kmalloc(sizeof(struct iser_page_vec) +
|
||||
(sizeof(u64) * (ISCSI_ISER_SG_TABLESIZE +1)),
|
||||
GFP_KERNEL);
|
||||
if (!ib_conn->page_vec) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
ib_conn->page_vec->pages = (u64 *) (ib_conn->page_vec + 1);
|
||||
|
||||
params.page_shift = SHIFT_4K;
|
||||
/* when the first/last SG element are not start/end *
|
||||
* page aligned, the map whould be of N+1 pages */
|
||||
params.max_pages_per_fmr = ISCSI_ISER_SG_TABLESIZE + 1;
|
||||
/* make the pool size twice the max number of SCSI commands *
|
||||
* the ML is expected to queue, watermark for unmap at 50% */
|
||||
params.pool_size = ISCSI_XMIT_CMDS_MAX * 2;
|
||||
params.dirty_watermark = ISCSI_XMIT_CMDS_MAX;
|
||||
params.cache = 0;
|
||||
params.flush_function = NULL;
|
||||
params.access = (IB_ACCESS_LOCAL_WRITE |
|
||||
IB_ACCESS_REMOTE_WRITE |
|
||||
IB_ACCESS_REMOTE_READ);
|
||||
|
||||
ib_conn->fmr_pool = ib_create_fmr_pool(device->pd, ¶ms);
|
||||
if (IS_ERR(ib_conn->fmr_pool)) {
|
||||
ret = PTR_ERR(ib_conn->fmr_pool);
|
||||
goto fmr_pool_err;
|
||||
}
|
||||
|
||||
memset(&init_attr, 0, sizeof init_attr);
|
||||
|
||||
init_attr.event_handler = iser_qp_event_callback;
|
||||
init_attr.qp_context = (void *)ib_conn;
|
||||
init_attr.send_cq = device->cq;
|
||||
init_attr.recv_cq = device->cq;
|
||||
init_attr.cap.max_send_wr = ISER_QP_MAX_REQ_DTOS;
|
||||
init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS;
|
||||
init_attr.cap.max_send_sge = MAX_REGD_BUF_VECTOR_LEN;
|
||||
init_attr.cap.max_recv_sge = 2;
|
||||
init_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
||||
init_attr.qp_type = IB_QPT_RC;
|
||||
|
||||
ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr);
|
||||
if (ret)
|
||||
goto qp_err;
|
||||
|
||||
ib_conn->qp = ib_conn->cma_id->qp;
|
||||
iser_err("setting conn %p cma_id %p: fmr_pool %p qp %p\n",
|
||||
ib_conn, ib_conn->cma_id,
|
||||
ib_conn->fmr_pool, ib_conn->cma_id->qp);
|
||||
return ret;
|
||||
|
||||
qp_err:
|
||||
(void)ib_destroy_fmr_pool(ib_conn->fmr_pool);
|
||||
fmr_pool_err:
|
||||
kfree(ib_conn->page_vec);
|
||||
alloc_err:
|
||||
iser_err("unable to alloc mem or create resource, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* releases the FMR pool, QP and CMA ID objects, returns 0 on success,
|
||||
* -1 on failure
|
||||
*/
|
||||
static int iser_free_ib_conn_res(struct iser_conn *ib_conn)
|
||||
{
|
||||
BUG_ON(ib_conn == NULL);
|
||||
|
||||
iser_err("freeing conn %p cma_id %p fmr pool %p qp %p\n",
|
||||
ib_conn, ib_conn->cma_id,
|
||||
ib_conn->fmr_pool, ib_conn->qp);
|
||||
|
||||
/* qp is created only once both addr & route are resolved */
|
||||
if (ib_conn->fmr_pool != NULL)
|
||||
ib_destroy_fmr_pool(ib_conn->fmr_pool);
|
||||
|
||||
if (ib_conn->qp != NULL)
|
||||
rdma_destroy_qp(ib_conn->cma_id);
|
||||
|
||||
if (ib_conn->cma_id != NULL)
|
||||
rdma_destroy_id(ib_conn->cma_id);
|
||||
|
||||
ib_conn->fmr_pool = NULL;
|
||||
ib_conn->qp = NULL;
|
||||
ib_conn->cma_id = NULL;
|
||||
kfree(ib_conn->page_vec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* based on the resolved device node GUID see if there already allocated
|
||||
* device for this device. If there's no such, create one.
|
||||
*/
|
||||
static
|
||||
struct iser_device *iser_device_find_by_ib_device(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct list_head *p_list;
|
||||
struct iser_device *device = NULL;
|
||||
|
||||
mutex_lock(&ig.device_list_mutex);
|
||||
|
||||
p_list = ig.device_list.next;
|
||||
while (p_list != &ig.device_list) {
|
||||
device = list_entry(p_list, struct iser_device, ig_list);
|
||||
/* find if there's a match using the node GUID */
|
||||
if (device->ib_device->node_guid == cma_id->device->node_guid)
|
||||
break;
|
||||
}
|
||||
|
||||
if (device == NULL) {
|
||||
device = kzalloc(sizeof *device, GFP_KERNEL);
|
||||
if (device == NULL)
|
||||
goto out;
|
||||
/* assign this device to the device */
|
||||
device->ib_device = cma_id->device;
|
||||
/* init the device and link it into ig device list */
|
||||
if (iser_create_device_ib_res(device)) {
|
||||
kfree(device);
|
||||
device = NULL;
|
||||
goto out;
|
||||
}
|
||||
list_add(&device->ig_list, &ig.device_list);
|
||||
}
|
||||
out:
|
||||
BUG_ON(device == NULL);
|
||||
device->refcount++;
|
||||
mutex_unlock(&ig.device_list_mutex);
|
||||
return device;
|
||||
}
|
||||
|
||||
/* if there's no demand for this device, release it */
|
||||
static void iser_device_try_release(struct iser_device *device)
|
||||
{
|
||||
mutex_lock(&ig.device_list_mutex);
|
||||
device->refcount--;
|
||||
iser_err("device %p refcount %d\n",device,device->refcount);
|
||||
if (!device->refcount) {
|
||||
iser_free_device_ib_res(device);
|
||||
list_del(&device->ig_list);
|
||||
kfree(device);
|
||||
}
|
||||
mutex_unlock(&ig.device_list_mutex);
|
||||
}
|
||||
|
||||
int iser_conn_state_comp(struct iser_conn *ib_conn,
|
||||
enum iser_ib_conn_state comp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&ib_conn->lock);
|
||||
ret = (ib_conn->state == comp);
|
||||
spin_unlock_bh(&ib_conn->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iser_conn_state_comp_exch(struct iser_conn *ib_conn,
|
||||
enum iser_ib_conn_state comp,
|
||||
enum iser_ib_conn_state exch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&ib_conn->lock);
|
||||
if ((ret = (ib_conn->state == comp)))
|
||||
ib_conn->state = exch;
|
||||
spin_unlock_bh(&ib_conn->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* triggers start of the disconnect procedures and wait for them to be done
|
||||
*/
|
||||
void iser_conn_terminate(struct iser_conn *ib_conn)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/* change the ib conn state only if the conn is UP, however always call
|
||||
* rdma_disconnect since this is the only way to cause the CMA to change
|
||||
* the QP state to ERROR
|
||||
*/
|
||||
|
||||
iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, ISER_CONN_TERMINATING);
|
||||
err = rdma_disconnect(ib_conn->cma_id);
|
||||
if (err)
|
||||
iser_err("Failed to disconnect, conn: 0x%p err %d\n",
|
||||
ib_conn,err);
|
||||
|
||||
wait_event_interruptible(ib_conn->wait,
|
||||
ib_conn->state == ISER_CONN_DOWN);
|
||||
|
||||
iser_conn_release(ib_conn);
|
||||
}
|
||||
|
||||
static void iser_connect_error(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
ib_conn = (struct iser_conn *)cma_id->context;
|
||||
|
||||
ib_conn->state = ISER_CONN_DOWN;
|
||||
wake_up_interruptible(&ib_conn->wait);
|
||||
}
|
||||
|
||||
static void iser_addr_handler(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct iser_device *device;
|
||||
struct iser_conn *ib_conn;
|
||||
int ret;
|
||||
|
||||
device = iser_device_find_by_ib_device(cma_id);
|
||||
ib_conn = (struct iser_conn *)cma_id->context;
|
||||
ib_conn->device = device;
|
||||
|
||||
ret = rdma_resolve_route(cma_id, 1000);
|
||||
if (ret) {
|
||||
iser_err("resolve route failed: %d\n", ret);
|
||||
iser_connect_error(cma_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void iser_route_handler(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct rdma_conn_param conn_param;
|
||||
int ret;
|
||||
|
||||
ret = iser_create_ib_conn_res((struct iser_conn *)cma_id->context);
|
||||
if (ret)
|
||||
goto failure;
|
||||
|
||||
iser_dbg("path.mtu is %d setting it to %d\n",
|
||||
cma_id->route.path_rec->mtu, IB_MTU_1024);
|
||||
|
||||
/* we must set the MTU to 1024 as this is what the target is assuming */
|
||||
if (cma_id->route.path_rec->mtu > IB_MTU_1024)
|
||||
cma_id->route.path_rec->mtu = IB_MTU_1024;
|
||||
|
||||
memset(&conn_param, 0, sizeof conn_param);
|
||||
conn_param.responder_resources = 4;
|
||||
conn_param.initiator_depth = 1;
|
||||
conn_param.retry_count = 7;
|
||||
conn_param.rnr_retry_count = 6;
|
||||
|
||||
ret = rdma_connect(cma_id, &conn_param);
|
||||
if (ret) {
|
||||
iser_err("failure connecting: %d\n", ret);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
return;
|
||||
failure:
|
||||
iser_connect_error(cma_id);
|
||||
}
|
||||
|
||||
static void iser_connected_handler(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = (struct iser_conn *)cma_id->context;
|
||||
ib_conn->state = ISER_CONN_UP;
|
||||
wake_up_interruptible(&ib_conn->wait);
|
||||
}
|
||||
|
||||
static void iser_disconnected_handler(struct rdma_cm_id *cma_id)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = (struct iser_conn *)cma_id->context;
|
||||
ib_conn->disc_evt_flag = 1;
|
||||
|
||||
/* getting here when the state is UP means that the conn is being *
|
||||
* terminated asynchronously from the iSCSI layer's perspective. */
|
||||
if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
|
||||
ISER_CONN_TERMINATING))
|
||||
iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
|
||||
ISCSI_ERR_CONN_FAILED);
|
||||
|
||||
/* Complete the termination process if no posts are pending */
|
||||
if ((atomic_read(&ib_conn->post_recv_buf_count) == 0) &&
|
||||
(atomic_read(&ib_conn->post_send_buf_count) == 0)) {
|
||||
ib_conn->state = ISER_CONN_DOWN;
|
||||
wake_up_interruptible(&ib_conn->wait);
|
||||
}
|
||||
}
|
||||
|
||||
static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
iser_err("event %d conn %p id %p\n",event->event,cma_id->context,cma_id);
|
||||
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
||||
iser_addr_handler(cma_id);
|
||||
break;
|
||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
||||
iser_route_handler(cma_id);
|
||||
break;
|
||||
case RDMA_CM_EVENT_ESTABLISHED:
|
||||
iser_connected_handler(cma_id);
|
||||
break;
|
||||
case RDMA_CM_EVENT_ADDR_ERROR:
|
||||
case RDMA_CM_EVENT_ROUTE_ERROR:
|
||||
case RDMA_CM_EVENT_CONNECT_ERROR:
|
||||
case RDMA_CM_EVENT_UNREACHABLE:
|
||||
case RDMA_CM_EVENT_REJECTED:
|
||||
iser_err("event: %d, error: %d\n", event->event, event->status);
|
||||
iser_connect_error(cma_id);
|
||||
break;
|
||||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
iser_disconnected_handler(cma_id);
|
||||
break;
|
||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
||||
BUG();
|
||||
break;
|
||||
case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
||||
BUG();
|
||||
break;
|
||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iser_conn_init(struct iser_conn **ibconn)
|
||||
{
|
||||
struct iser_conn *ib_conn;
|
||||
|
||||
ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
|
||||
if (!ib_conn) {
|
||||
iser_err("can't alloc memory for struct iser_conn\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ib_conn->state = ISER_CONN_INIT;
|
||||
init_waitqueue_head(&ib_conn->wait);
|
||||
atomic_set(&ib_conn->post_recv_buf_count, 0);
|
||||
atomic_set(&ib_conn->post_send_buf_count, 0);
|
||||
INIT_LIST_HEAD(&ib_conn->conn_list);
|
||||
spin_lock_init(&ib_conn->lock);
|
||||
|
||||
*ibconn = ib_conn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* starts the process of connecting to the target
|
||||
* sleeps untill the connection is established or rejected
|
||||
*/
|
||||
int iser_connect(struct iser_conn *ib_conn,
|
||||
struct sockaddr_in *src_addr,
|
||||
struct sockaddr_in *dst_addr,
|
||||
int non_blocking)
|
||||
{
|
||||
struct sockaddr *src, *dst;
|
||||
int err = 0;
|
||||
|
||||
sprintf(ib_conn->name,"%d.%d.%d.%d:%d",
|
||||
NIPQUAD(dst_addr->sin_addr.s_addr), dst_addr->sin_port);
|
||||
|
||||
/* the device is known only --after-- address resolution */
|
||||
ib_conn->device = NULL;
|
||||
|
||||
iser_err("connecting to: %d.%d.%d.%d, port 0x%x\n",
|
||||
NIPQUAD(dst_addr->sin_addr), dst_addr->sin_port);
|
||||
|
||||
ib_conn->state = ISER_CONN_PENDING;
|
||||
|
||||
ib_conn->cma_id = rdma_create_id(iser_cma_handler,
|
||||
(void *)ib_conn,
|
||||
RDMA_PS_TCP);
|
||||
if (IS_ERR(ib_conn->cma_id)) {
|
||||
err = PTR_ERR(ib_conn->cma_id);
|
||||
iser_err("rdma_create_id failed: %d\n", err);
|
||||
goto id_failure;
|
||||
}
|
||||
|
||||
src = (struct sockaddr *)src_addr;
|
||||
dst = (struct sockaddr *)dst_addr;
|
||||
err = rdma_resolve_addr(ib_conn->cma_id, src, dst, 1000);
|
||||
if (err) {
|
||||
iser_err("rdma_resolve_addr failed: %d\n", err);
|
||||
goto addr_failure;
|
||||
}
|
||||
|
||||
if (!non_blocking) {
|
||||
wait_event_interruptible(ib_conn->wait,
|
||||
(ib_conn->state != ISER_CONN_PENDING));
|
||||
|
||||
if (ib_conn->state != ISER_CONN_UP) {
|
||||
err = -EIO;
|
||||
goto connect_failure;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&ig.connlist_mutex);
|
||||
list_add(&ib_conn->conn_list, &ig.connlist);
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
return 0;
|
||||
|
||||
id_failure:
|
||||
ib_conn->cma_id = NULL;
|
||||
addr_failure:
|
||||
ib_conn->state = ISER_CONN_DOWN;
|
||||
connect_failure:
|
||||
iser_conn_release(ib_conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees all conn objects and deallocs conn descriptor
|
||||
*/
|
||||
void iser_conn_release(struct iser_conn *ib_conn)
|
||||
{
|
||||
struct iser_device *device = ib_conn->device;
|
||||
|
||||
BUG_ON(ib_conn->state != ISER_CONN_DOWN);
|
||||
|
||||
mutex_lock(&ig.connlist_mutex);
|
||||
list_del(&ib_conn->conn_list);
|
||||
mutex_unlock(&ig.connlist_mutex);
|
||||
|
||||
iser_free_ib_conn_res(ib_conn);
|
||||
ib_conn->device = NULL;
|
||||
/* on EVENT_ADDR_ERROR there's no device yet for this conn */
|
||||
if (device != NULL)
|
||||
iser_device_try_release(device);
|
||||
if (ib_conn->iser_conn)
|
||||
ib_conn->iser_conn->ib_conn = NULL;
|
||||
kfree(ib_conn);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iser_reg_page_vec - Register physical memory
|
||||
*
|
||||
* returns: 0 on success, errno code on failure
|
||||
*/
|
||||
int iser_reg_page_vec(struct iser_conn *ib_conn,
|
||||
struct iser_page_vec *page_vec,
|
||||
struct iser_mem_reg *mem_reg)
|
||||
{
|
||||
struct ib_pool_fmr *mem;
|
||||
u64 io_addr;
|
||||
u64 *page_list;
|
||||
int status;
|
||||
|
||||
page_list = page_vec->pages;
|
||||
io_addr = page_list[0];
|
||||
|
||||
mem = ib_fmr_pool_map_phys(ib_conn->fmr_pool,
|
||||
page_list,
|
||||
page_vec->length,
|
||||
io_addr);
|
||||
|
||||
if (IS_ERR(mem)) {
|
||||
status = (int)PTR_ERR(mem);
|
||||
iser_err("ib_fmr_pool_map_phys failed: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
mem_reg->lkey = mem->fmr->lkey;
|
||||
mem_reg->rkey = mem->fmr->rkey;
|
||||
mem_reg->len = page_vec->length * SIZE_4K;
|
||||
mem_reg->va = io_addr;
|
||||
mem_reg->is_fmr = 1;
|
||||
mem_reg->mem_h = (void *)mem;
|
||||
|
||||
mem_reg->va += page_vec->offset;
|
||||
mem_reg->len = page_vec->data_size;
|
||||
|
||||
iser_dbg("PHYSICAL Mem.register, [PHYS p_array: 0x%p, sz: %d, "
|
||||
"entry[0]: (0x%08lx,%ld)] -> "
|
||||
"[lkey: 0x%08X mem_h: 0x%p va: 0x%08lX sz: %ld]\n",
|
||||
page_vec, page_vec->length,
|
||||
(unsigned long)page_vec->pages[0],
|
||||
(unsigned long)page_vec->data_size,
|
||||
(unsigned int)mem_reg->lkey, mem_reg->mem_h,
|
||||
(unsigned long)mem_reg->va, (unsigned long)mem_reg->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister (previosuly registered) memory.
|
||||
*/
|
||||
void iser_unreg_mem(struct iser_mem_reg *reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
iser_dbg("PHYSICAL Mem.Unregister mem_h %p\n",reg->mem_h);
|
||||
|
||||
ret = ib_fmr_pool_unmap((struct ib_pool_fmr *)reg->mem_h);
|
||||
if (ret)
|
||||
iser_err("ib_fmr_pool_unmap failed %d\n", ret);
|
||||
|
||||
reg->mem_h = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_dto_to_iov - builds IOV from a dto descriptor
|
||||
*/
|
||||
static void iser_dto_to_iov(struct iser_dto *dto, struct ib_sge *iov, int iov_len)
|
||||
{
|
||||
int i;
|
||||
struct ib_sge *sge;
|
||||
struct iser_regd_buf *regd_buf;
|
||||
|
||||
if (dto->regd_vector_len > iov_len) {
|
||||
iser_err("iov size %d too small for posting dto of len %d\n",
|
||||
iov_len, dto->regd_vector_len);
|
||||
BUG();
|
||||
}
|
||||
|
||||
for (i = 0; i < dto->regd_vector_len; i++) {
|
||||
sge = &iov[i];
|
||||
regd_buf = dto->regd[i];
|
||||
|
||||
sge->addr = regd_buf->reg.va;
|
||||
sge->length = regd_buf->reg.len;
|
||||
sge->lkey = regd_buf->reg.lkey;
|
||||
|
||||
if (dto->used_sz[i] > 0) /* Adjust size */
|
||||
sge->length = dto->used_sz[i];
|
||||
|
||||
/* offset and length should not exceed the regd buf length */
|
||||
if (sge->length + dto->offset[i] > regd_buf->reg.len) {
|
||||
iser_err("Used len:%ld + offset:%d, exceed reg.buf.len:"
|
||||
"%ld in dto:0x%p [%d], va:0x%08lX\n",
|
||||
(unsigned long)sge->length, dto->offset[i],
|
||||
(unsigned long)regd_buf->reg.len, dto, i,
|
||||
(unsigned long)sge->addr);
|
||||
BUG();
|
||||
}
|
||||
|
||||
sge->addr += dto->offset[i]; /* Adjust offset */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_post_recv - Posts a receive buffer.
|
||||
*
|
||||
* returns 0 on success, -1 on failure
|
||||
*/
|
||||
int iser_post_recv(struct iser_desc *rx_desc)
|
||||
{
|
||||
int ib_ret, ret_val = 0;
|
||||
struct ib_recv_wr recv_wr, *recv_wr_failed;
|
||||
struct ib_sge iov[2];
|
||||
struct iser_conn *ib_conn;
|
||||
struct iser_dto *recv_dto = &rx_desc->dto;
|
||||
|
||||
/* Retrieve conn */
|
||||
ib_conn = recv_dto->ib_conn;
|
||||
|
||||
iser_dto_to_iov(recv_dto, iov, 2);
|
||||
|
||||
recv_wr.next = NULL;
|
||||
recv_wr.sg_list = iov;
|
||||
recv_wr.num_sge = recv_dto->regd_vector_len;
|
||||
recv_wr.wr_id = (unsigned long)rx_desc;
|
||||
|
||||
atomic_inc(&ib_conn->post_recv_buf_count);
|
||||
ib_ret = ib_post_recv(ib_conn->qp, &recv_wr, &recv_wr_failed);
|
||||
if (ib_ret) {
|
||||
iser_err("ib_post_recv failed ret=%d\n", ib_ret);
|
||||
atomic_dec(&ib_conn->post_recv_buf_count);
|
||||
ret_val = -1;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* iser_start_send - Initiate a Send DTO operation
|
||||
*
|
||||
* returns 0 on success, -1 on failure
|
||||
*/
|
||||
int iser_post_send(struct iser_desc *tx_desc)
|
||||
{
|
||||
int ib_ret, ret_val = 0;
|
||||
struct ib_send_wr send_wr, *send_wr_failed;
|
||||
struct ib_sge iov[MAX_REGD_BUF_VECTOR_LEN];
|
||||
struct iser_conn *ib_conn;
|
||||
struct iser_dto *dto = &tx_desc->dto;
|
||||
|
||||
ib_conn = dto->ib_conn;
|
||||
|
||||
iser_dto_to_iov(dto, iov, MAX_REGD_BUF_VECTOR_LEN);
|
||||
|
||||
send_wr.next = NULL;
|
||||
send_wr.wr_id = (unsigned long)tx_desc;
|
||||
send_wr.sg_list = iov;
|
||||
send_wr.num_sge = dto->regd_vector_len;
|
||||
send_wr.opcode = IB_WR_SEND;
|
||||
send_wr.send_flags = dto->notify_enable ? IB_SEND_SIGNALED : 0;
|
||||
|
||||
atomic_inc(&ib_conn->post_send_buf_count);
|
||||
|
||||
ib_ret = ib_post_send(ib_conn->qp, &send_wr, &send_wr_failed);
|
||||
if (ib_ret) {
|
||||
iser_err("Failed to start SEND DTO, dto: 0x%p, IOV len: %d\n",
|
||||
dto, dto->regd_vector_len);
|
||||
iser_err("ib_post_send failed, ret:%d\n", ib_ret);
|
||||
atomic_dec(&ib_conn->post_send_buf_count);
|
||||
ret_val = -1;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static void iser_handle_comp_error(struct iser_desc *desc)
|
||||
{
|
||||
struct iser_dto *dto = &desc->dto;
|
||||
struct iser_conn *ib_conn = dto->ib_conn;
|
||||
|
||||
iser_dto_buffs_release(dto);
|
||||
|
||||
if (desc->type == ISCSI_RX) {
|
||||
kfree(desc->data);
|
||||
kmem_cache_free(ig.desc_cache, desc);
|
||||
atomic_dec(&ib_conn->post_recv_buf_count);
|
||||
} else { /* type is TX control/command/dataout */
|
||||
if (desc->type == ISCSI_TX_DATAOUT)
|
||||
kmem_cache_free(ig.desc_cache, desc);
|
||||
atomic_dec(&ib_conn->post_send_buf_count);
|
||||
}
|
||||
|
||||
if (atomic_read(&ib_conn->post_recv_buf_count) == 0 &&
|
||||
atomic_read(&ib_conn->post_send_buf_count) == 0) {
|
||||
/* getting here when the state is UP means that the conn is *
|
||||
* being terminated asynchronously from the iSCSI layer's *
|
||||
* perspective. */
|
||||
if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
|
||||
ISER_CONN_TERMINATING))
|
||||
iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
|
||||
ISCSI_ERR_CONN_FAILED);
|
||||
|
||||
/* complete the termination process if disconnect event was delivered *
|
||||
* note there are no more non completed posts to the QP */
|
||||
if (ib_conn->disc_evt_flag) {
|
||||
ib_conn->state = ISER_CONN_DOWN;
|
||||
wake_up_interruptible(&ib_conn->wait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void iser_cq_tasklet_fn(unsigned long data)
|
||||
{
|
||||
struct iser_device *device = (struct iser_device *)data;
|
||||
struct ib_cq *cq = device->cq;
|
||||
struct ib_wc wc;
|
||||
struct iser_desc *desc;
|
||||
unsigned long xfer_len;
|
||||
|
||||
while (ib_poll_cq(cq, 1, &wc) == 1) {
|
||||
desc = (struct iser_desc *) (unsigned long) wc.wr_id;
|
||||
BUG_ON(desc == NULL);
|
||||
|
||||
if (wc.status == IB_WC_SUCCESS) {
|
||||
if (desc->type == ISCSI_RX) {
|
||||
xfer_len = (unsigned long)wc.byte_len;
|
||||
iser_rcv_completion(desc, xfer_len);
|
||||
} else /* type == ISCSI_TX_CONTROL/SCSI_CMD/DOUT */
|
||||
iser_snd_completion(desc);
|
||||
} else {
|
||||
iser_err("comp w. error op %d status %d\n",desc->type,wc.status);
|
||||
iser_handle_comp_error(desc);
|
||||
}
|
||||
}
|
||||
/* #warning "it is assumed here that arming CQ only once its empty" *
|
||||
* " would not cause interrupts to be missed" */
|
||||
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
|
||||
}
|
||||
|
||||
static void iser_cq_callback(struct ib_cq *cq, void *cq_context)
|
||||
{
|
||||
struct iser_device *device = (struct iser_device *)cq_context;
|
||||
|
||||
tasklet_schedule(&device->cq_tasklet);
|
||||
}
|
||||
1
drivers/infiniband/ulp/srp/Kbuild
Normal file
1
drivers/infiniband/ulp/srp/Kbuild
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_INFINIBAND_SRP) += ib_srp.o
|
||||
11
drivers/infiniband/ulp/srp/Kconfig
Normal file
11
drivers/infiniband/ulp/srp/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
config INFINIBAND_SRP
|
||||
tristate "InfiniBand SCSI RDMA Protocol"
|
||||
depends on INFINIBAND && SCSI
|
||||
---help---
|
||||
Support for the SCSI RDMA Protocol over InfiniBand. This
|
||||
allows you to access storage devices that speak SRP over
|
||||
InfiniBand.
|
||||
|
||||
The SRP protocol is defined by the INCITS T10 technical
|
||||
committee. See <http://www.t10.org/>.
|
||||
|
||||
2078
drivers/infiniband/ulp/srp/ib_srp.c
Normal file
2078
drivers/infiniband/ulp/srp/ib_srp.c
Normal file
File diff suppressed because it is too large
Load Diff
171
drivers/infiniband/ulp/srp/ib_srp.h
Normal file
171
drivers/infiniband/ulp/srp/ib_srp.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2005 Cisco Systems. 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: ib_srp.h,v 1.1.1.1 2007/06/12 07:27:10 eyryu Exp $
|
||||
*/
|
||||
|
||||
#ifndef IB_SRP_H
|
||||
#define IB_SRP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/ib_sa.h>
|
||||
#include <rdma/ib_cm.h>
|
||||
#include <rdma/ib_fmr_pool.h>
|
||||
|
||||
enum {
|
||||
SRP_PATH_REC_TIMEOUT_MS = 1000,
|
||||
SRP_ABORT_TIMEOUT_MS = 5000,
|
||||
|
||||
SRP_PORT_REDIRECT = 1,
|
||||
SRP_DLID_REDIRECT = 2,
|
||||
|
||||
SRP_MAX_LUN = 512,
|
||||
SRP_DEF_SG_TABLESIZE = 12,
|
||||
|
||||
SRP_RQ_SHIFT = 6,
|
||||
SRP_RQ_SIZE = 1 << SRP_RQ_SHIFT,
|
||||
SRP_SQ_SIZE = SRP_RQ_SIZE - 1,
|
||||
SRP_CQ_SIZE = SRP_SQ_SIZE + SRP_RQ_SIZE,
|
||||
|
||||
SRP_TAG_TSK_MGMT = 1 << (SRP_RQ_SHIFT + 1),
|
||||
|
||||
SRP_FMR_SIZE = 256,
|
||||
SRP_FMR_POOL_SIZE = 1024,
|
||||
SRP_FMR_DIRTY_SIZE = SRP_FMR_POOL_SIZE / 4
|
||||
};
|
||||
|
||||
#define SRP_OP_RECV (1 << 31)
|
||||
|
||||
enum srp_target_state {
|
||||
SRP_TARGET_LIVE,
|
||||
SRP_TARGET_CONNECTING,
|
||||
SRP_TARGET_DEAD,
|
||||
SRP_TARGET_REMOVED
|
||||
};
|
||||
|
||||
struct srp_device {
|
||||
struct list_head dev_list;
|
||||
struct ib_device *dev;
|
||||
struct ib_pd *pd;
|
||||
struct ib_mr *mr;
|
||||
struct ib_fmr_pool *fmr_pool;
|
||||
int fmr_page_shift;
|
||||
int fmr_page_size;
|
||||
u64 fmr_page_mask;
|
||||
};
|
||||
|
||||
struct srp_host {
|
||||
struct srp_device *dev;
|
||||
u8 port;
|
||||
struct class_device class_dev;
|
||||
struct list_head target_list;
|
||||
spinlock_t target_lock;
|
||||
struct completion released;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct srp_request {
|
||||
struct list_head list;
|
||||
struct scsi_cmnd *scmnd;
|
||||
struct srp_iu *cmd;
|
||||
struct srp_iu *tsk_mgmt;
|
||||
struct ib_pool_fmr *fmr;
|
||||
/*
|
||||
* Fake scatterlist used when scmnd->use_sg==0. Can be killed
|
||||
* when the SCSI midlayer no longer generates non-SG commands.
|
||||
*/
|
||||
struct scatterlist fake_sg;
|
||||
struct completion done;
|
||||
short index;
|
||||
u8 cmd_done;
|
||||
u8 tsk_status;
|
||||
};
|
||||
|
||||
struct srp_target_port {
|
||||
__be64 id_ext;
|
||||
__be64 ioc_guid;
|
||||
__be64 service_id;
|
||||
__be64 initiator_ext;
|
||||
u16 io_class;
|
||||
struct srp_host *srp_host;
|
||||
struct Scsi_Host *scsi_host;
|
||||
char target_name[32];
|
||||
unsigned int scsi_id;
|
||||
|
||||
struct ib_sa_path_rec path;
|
||||
struct ib_sa_query *path_query;
|
||||
int path_query_id;
|
||||
|
||||
struct ib_cm_id *cm_id;
|
||||
struct ib_cq *cq;
|
||||
struct ib_qp *qp;
|
||||
|
||||
int max_ti_iu_len;
|
||||
s32 req_lim;
|
||||
|
||||
int zero_req_lim;
|
||||
|
||||
unsigned rx_head;
|
||||
struct srp_iu *rx_ring[SRP_RQ_SIZE];
|
||||
|
||||
unsigned tx_head;
|
||||
unsigned tx_tail;
|
||||
struct srp_iu *tx_ring[SRP_SQ_SIZE + 1];
|
||||
|
||||
struct list_head free_reqs;
|
||||
struct list_head req_queue;
|
||||
struct srp_request req_ring[SRP_SQ_SIZE];
|
||||
|
||||
struct work_struct work;
|
||||
|
||||
struct list_head list;
|
||||
struct completion done;
|
||||
int status;
|
||||
enum srp_target_state state;
|
||||
int qp_in_error;
|
||||
};
|
||||
|
||||
struct srp_iu {
|
||||
u64 dma;
|
||||
void *buf;
|
||||
size_t size;
|
||||
enum dma_data_direction direction;
|
||||
};
|
||||
|
||||
#endif /* IB_SRP_H */
|
||||
Reference in New Issue
Block a user