* [PATCH 2/5] Infiniband: connection abstraction
From: Sean Hefty @ 2006-02-01 20:10 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: openib-general
In-Reply-To: <ORSMSX401KZmcK8r6be00000097@orsmsx401.amr.corp.intel.com>
The following patch extends matching connection requests to listens in the
Infiniband CM to include private data.
Signed-off-by: Sean Hefty <sean.hefty@intel.com>
---
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/cm.c
linux-2.6.ib/drivers/infiniband/core/cm.c
--- linux-2.6.git/drivers/infiniband/core/cm.c 2006-01-16 10:25:26.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/cm.c 2006-01-16 16:03:35.000000000 -0800
@@ -32,7 +32,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * $Id: cm.c 2821 2005-07-08 17:07:28Z sean.hefty $
+ * $Id: cm.c 4311 2005-12-05 18:42:01Z sean.hefty $
*/
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -130,6 +130,7 @@ struct cm_id_private {
/* todo: use alternate port on send failure */
struct cm_av av;
struct cm_av alt_av;
+ struct ib_cm_compare_data *compare_data;
void *private_data;
__be64 tid;
@@ -355,6 +356,41 @@ static struct cm_id_private * cm_acquire
return cm_id_priv;
}
+static void cm_mask_copy(u8 *dst, u8 *src, u8 *mask)
+{
+ int i;
+
+ for (i = 0; i < IB_CM_COMPARE_SIZE / sizeof(unsigned long); i++)
+ ((unsigned long *) dst)[i] = ((unsigned long *) src)[i] &
+ ((unsigned long *) mask)[i];
+}
+
+static int cm_compare_data(struct ib_cm_compare_data *src_data,
+ struct ib_cm_compare_data *dst_data)
+{
+ u8 src[IB_CM_COMPARE_SIZE];
+ u8 dst[IB_CM_COMPARE_SIZE];
+
+ if (!src_data || !dst_data)
+ return 0;
+
+ cm_mask_copy(src, src_data->data, dst_data->mask);
+ cm_mask_copy(dst, dst_data->data, src_data->mask);
+ return memcmp(src, dst, IB_CM_COMPARE_SIZE);
+}
+
+static int cm_compare_private_data(u8 *private_data,
+ struct ib_cm_compare_data *dst_data)
+{
+ u8 src[IB_CM_COMPARE_SIZE];
+
+ if (!dst_data)
+ return 0;
+
+ cm_mask_copy(src, private_data, dst_data->mask);
+ return memcmp(src, dst_data->data, IB_CM_COMPARE_SIZE);
+}
+
static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
{
struct rb_node **link = &cm.listen_service_table.rb_node;
@@ -362,14 +397,18 @@ static struct cm_id_private * cm_insert_
struct cm_id_private *cur_cm_id_priv;
__be64 service_id = cm_id_priv->id.service_id;
__be64 service_mask = cm_id_priv->id.service_mask;
+ int data_cmp;
while (*link) {
parent = *link;
cur_cm_id_priv = rb_entry(parent, struct cm_id_private,
service_node);
+ data_cmp = cm_compare_data(cm_id_priv->compare_data,
+ cur_cm_id_priv->compare_data);
if ((cur_cm_id_priv->id.service_mask & service_id) ==
(service_mask & cur_cm_id_priv->id.service_id) &&
- (cm_id_priv->id.device == cur_cm_id_priv->id.device))
+ (cm_id_priv->id.device == cur_cm_id_priv->id.device) &&
+ !data_cmp)
return cur_cm_id_priv;
if (cm_id_priv->id.device < cur_cm_id_priv->id.device)
@@ -378,6 +417,10 @@ static struct cm_id_private * cm_insert_
link = &(*link)->rb_right;
else if (service_id < cur_cm_id_priv->id.service_id)
link = &(*link)->rb_left;
+ else if (service_id > cur_cm_id_priv->id.service_id)
+ link = &(*link)->rb_right;
+ else if (data_cmp < 0)
+ link = &(*link)->rb_left;
else
link = &(*link)->rb_right;
}
@@ -387,16 +430,20 @@ static struct cm_id_private * cm_insert_
}
static struct cm_id_private * cm_find_listen(struct ib_device *device,
- __be64 service_id)
+ __be64 service_id,
+ u8 *private_data)
{
struct rb_node *node = cm.listen_service_table.rb_node;
struct cm_id_private *cm_id_priv;
+ int data_cmp;
while (node) {
cm_id_priv = rb_entry(node, struct cm_id_private, service_node);
+ data_cmp = cm_compare_private_data(private_data,
+ cm_id_priv->compare_data);
if ((cm_id_priv->id.service_mask & service_id) ==
cm_id_priv->id.service_id &&
- (cm_id_priv->id.device == device))
+ (cm_id_priv->id.device == device) && !data_cmp)
return cm_id_priv;
if (device < cm_id_priv->id.device)
@@ -405,6 +452,10 @@ static struct cm_id_private * cm_find_li
node = node->rb_right;
else if (service_id < cm_id_priv->id.service_id)
node = node->rb_left;
+ else if (service_id > cm_id_priv->id.service_id)
+ node = node->rb_right;
+ else if (data_cmp < 0)
+ node = node->rb_left;
else
node = node->rb_right;
}
@@ -728,15 +779,14 @@ retest:
wait_event(cm_id_priv->wait, !atomic_read(&cm_id_priv->refcount));
while ((work = cm_dequeue_work(cm_id_priv)) != NULL)
cm_free_work(work);
- if (cm_id_priv->private_data && cm_id_priv->private_data_len)
- kfree(cm_id_priv->private_data);
+ kfree(cm_id_priv->compare_data);
+ kfree(cm_id_priv->private_data);
kfree(cm_id_priv);
}
EXPORT_SYMBOL(ib_destroy_cm_id);
-int ib_cm_listen(struct ib_cm_id *cm_id,
- __be64 service_id,
- __be64 service_mask)
+int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
+ struct ib_cm_compare_data *compare_data)
{
struct cm_id_private *cm_id_priv, *cur_cm_id_priv;
unsigned long flags;
@@ -750,7 +800,19 @@ int ib_cm_listen(struct ib_cm_id *cm_id,
return -EINVAL;
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
- BUG_ON(cm_id->state != IB_CM_IDLE);
+ if (cm_id->state != IB_CM_IDLE)
+ return -EINVAL;
+
+ if (compare_data) {
+ cm_id_priv->compare_data = kzalloc(sizeof *compare_data,
+ GFP_KERNEL);
+ if (!cm_id_priv->compare_data)
+ return -ENOMEM;
+ cm_mask_copy(cm_id_priv->compare_data->data,
+ compare_data->data, compare_data->mask);
+ memcpy(cm_id_priv->compare_data->mask, compare_data->mask,
+ IB_CM_COMPARE_SIZE);
+ }
cm_id->state = IB_CM_LISTEN;
@@ -767,6 +829,8 @@ int ib_cm_listen(struct ib_cm_id *cm_id,
if (cur_cm_id_priv) {
cm_id->state = IB_CM_IDLE;
+ kfree(cm_id_priv->compare_data);
+ cm_id_priv->compare_data = NULL;
ret = -EBUSY;
}
return ret;
@@ -1239,7 +1303,8 @@ static struct cm_id_private * cm_match_r
/* Find matching listen request. */
listen_cm_id_priv = cm_find_listen(cm_id_priv->id.device,
- req_msg->service_id);
+ req_msg->service_id,
+ req_msg->private_data);
if (!listen_cm_id_priv) {
spin_unlock_irqrestore(&cm.lock, flags);
cm_issue_rej(work->port, work->mad_recv_wc,
@@ -2646,7 +2711,8 @@ static int cm_sidr_req_handler(struct cm
goto out; /* Duplicate message. */
}
cur_cm_id_priv = cm_find_listen(cm_id->device,
- sidr_req_msg->service_id);
+ sidr_req_msg->service_id,
+ sidr_req_msg->private_data);
if (!cur_cm_id_priv) {
rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table);
spin_unlock_irqrestore(&cm.lock, flags);
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/ucm.c
linux-2.6.ib/drivers/infiniband/core/ucm.c
--- linux-2.6.git/drivers/infiniband/core/ucm.c 2006-01-16 16:03:08.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/ucm.c 2006-01-16 16:03:35.000000000 -0800
@@ -646,6 +646,17 @@ out:
return result;
}
+static int ucm_validate_listen(__be64 service_id, __be64 service_mask)
+{
+ service_id &= service_mask;
+
+ if (((service_id & IB_CMA_SERVICE_ID_MASK) == IB_CMA_SERVICE_ID) ||
+ ((service_id & IB_SDP_SERVICE_ID_MASK) == IB_SDP_SERVICE_ID))
+ return -EINVAL;
+
+ return 0;
+}
+
static ssize_t ib_ucm_listen(struct ib_ucm_file *file,
const char __user *inbuf,
int in_len, int out_len)
@@ -661,7 +672,13 @@ static ssize_t ib_ucm_listen(struct ib_u
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- result = ib_cm_listen(ctx->cm_id, cmd.service_id, cmd.service_mask);
+ result = ucm_validate_listen(cmd.service_id, cmd.service_mask);
+ if (result)
+ goto out;
+
+ result = ib_cm_listen(ctx->cm_id, cmd.service_id, cmd.service_mask,
+ NULL);
+out:
ib_ucm_ctx_put(ctx);
return result;
}
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/include/rdma/ib_cm.h
linux-2.6.ib/include/rdma/ib_cm.h
--- linux-2.6.git/include/rdma/ib_cm.h 2006-01-16 10:26:47.000000000 -0800
+++ linux-2.6.ib/include/rdma/ib_cm.h 2006-01-16 16:03:35.000000000 -0800
@@ -32,7 +32,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * $Id: ib_cm.h 2730 2005-06-28 16:43:03Z sean.hefty $
+ * $Id: ib_cm.h 4311 2005-12-05 18:42:01Z sean.hefty $
*/
#if !defined(IB_CM_H)
#define IB_CM_H
@@ -102,7 +102,8 @@ enum ib_cm_data_size {
IB_CM_APR_INFO_LENGTH = 72,
IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE = 216,
IB_CM_SIDR_REP_PRIVATE_DATA_SIZE = 136,
- IB_CM_SIDR_REP_INFO_LENGTH = 72
+ IB_CM_SIDR_REP_INFO_LENGTH = 72,
+ IB_CM_COMPARE_SIZE = 64
};
struct ib_cm_id;
@@ -238,7 +239,6 @@ struct ib_cm_sidr_rep_event_param {
u32 qpn;
void *info;
u8 info_len;
-
};
struct ib_cm_event {
@@ -317,6 +317,15 @@ void ib_destroy_cm_id(struct ib_cm_id *c
#define IB_SERVICE_ID_AGN_MASK __constant_cpu_to_be64(0xFF00000000000000ULL)
#define IB_CM_ASSIGN_SERVICE_ID __constant_cpu_to_be64(0x0200000000000000ULL)
+#define IB_CMA_SERVICE_ID __constant_cpu_to_be64(0x0000000001000000ULL)
+#define IB_CMA_SERVICE_ID_MASK __constant_cpu_to_be64(0xFFFFFFFFFF000000ULL)
+#define IB_SDP_SERVICE_ID __constant_cpu_to_be64(0x0000000000010000ULL)
+#define IB_SDP_SERVICE_ID_MASK __constant_cpu_to_be64(0xFFFFFFFFFFFF0000ULL)
+
+struct ib_cm_compare_data {
+ u8 data[IB_CM_COMPARE_SIZE];
+ u8 mask[IB_CM_COMPARE_SIZE];
+};
/**
* ib_cm_listen - Initiates listening on the specified service ID for
@@ -330,10 +339,12 @@ void ib_destroy_cm_id(struct ib_cm_id *c
* range of service IDs. If set to 0, the service ID is matched
* exactly. This parameter is ignored if %service_id is set to
* IB_CM_ASSIGN_SERVICE_ID.
+ * @compare_data: This parameter is optional. It specifies data that must
+ * appear in the private data of a connection request for the specified
+ * listen request.
*/
-int ib_cm_listen(struct ib_cm_id *cm_id,
- __be64 service_id,
- __be64 service_mask);
+int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
+ struct ib_cm_compare_data *compare_data);
struct ib_cm_req_param {
struct ib_sa_path_rec *primary_path;
^ permalink raw reply
* [PATCH 3/5] Infiniband: connection abstraction
From: Sean Hefty @ 2006-02-01 20:15 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: openib-general
In-Reply-To: <ORSMSX401KZmcK8r6be00000097@orsmsx401.amr.corp.intel.com>
The following provides an address translation service that maps IP addresses
to Infiniband addresses (GIDs) using IPoIB.
This patch exports ip_dev_find() to locate a net_device given an IP address.
Signed-off-by: Sean Hefty <sean.hefty@intel.com>
---
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/addr.c
linux-2.6.ib/drivers/infiniband/core/addr.c
--- linux-2.6.git/drivers/infiniband/core/addr.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/addr.c 2006-01-16 16:14:24.000000000 -0800
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
+ * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This Software is licensed under one of the following licenses:
+ *
+ * 1) under the terms of the "Common Public License 1.0" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/cpl.php.
+ *
+ * 2) under the terms of the "The BSD License" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/bsd-license.php.
+ *
+ * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
+ * copy of which is available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/gpl-license.php.
+ *
+ * Licensee has the right to choose one of the above licenses.
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice and one of the license notices.
+ *
+ * Redistributions in binary form must reproduce both the above copyright
+ * notice, one of the license notices in the documentation
+ * and/or other materials provided with the distribution.
+ */
+#include <linux/inetdevice.h>
+#include <linux/workqueue.h>
+#include <net/arp.h>
+#include <net/neighbour.h>
+#include <net/route.h>
+#include <rdma/ib_addr.h>
+
+MODULE_AUTHOR("Sean Hefty");
+MODULE_DESCRIPTION("IB Address Translation");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct addr_req {
+ struct list_head list;
+ struct sockaddr src_addr;
+ struct sockaddr dst_addr;
+ struct rdma_dev_addr *addr;
+ void *context;
+ void (*callback)(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *addr, void *context);
+ unsigned long timeout;
+ int status;
+};
+
+static void process_req(void *data);
+
+static DEFINE_MUTEX(lock);
+static LIST_HEAD(req_list);
+static DECLARE_WORK(work, process_req, NULL);
+struct workqueue_struct *rdma_wq;
+EXPORT_SYMBOL(rdma_wq);
+
+static int copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
+ unsigned char *dst_dev_addr)
+{
+ switch (dev->type) {
+ case ARPHRD_INFINIBAND:
+ dev_addr->dev_type = IB_NODE_CA;
+ break;
+ default:
+ return -EADDRNOTAVAIL;
+ }
+
+ memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN);
+ if (dst_dev_addr)
+ memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
+ return 0;
+}
+
+int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
+{
+ struct net_device *dev;
+ u32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+ int ret;
+
+ dev = ip_dev_find(ip);
+ if (!dev)
+ return -EADDRNOTAVAIL;
+
+ ret = copy_addr(dev_addr, dev, NULL);
+ dev_put(dev);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_translate_ip);
+
+static void set_timeout(unsigned long time)
+{
+ unsigned long delay;
+
+ cancel_delayed_work(&work);
+
+ delay = time - jiffies;
+ if ((long)delay <= 0)
+ delay = 1;
+
+ queue_delayed_work(rdma_wq, &work, delay);
+}
+
+static void queue_req(struct addr_req *req)
+{
+ struct addr_req *temp_req;
+
+ mutex_lock(&lock);
+ list_for_each_entry_reverse(temp_req, &req_list, list) {
+ if (time_after(req->timeout, temp_req->timeout))
+ break;
+ }
+
+ list_add(&req->list, &temp_req->list);
+
+ if (req_list.next == &req->list)
+ set_timeout(req->timeout);
+ mutex_unlock(&lock);
+}
+
+static void addr_send_arp(struct sockaddr_in *dst_in)
+{
+ struct rtable *rt;
+ struct flowi fl;
+ u32 dst_ip = dst_in->sin_addr.s_addr;
+
+ memset(&fl, 0, sizeof fl);
+ fl.nl_u.ip4_u.daddr = dst_ip;
+ if (ip_route_output_key(&rt, &fl))
+ return;
+
+ arp_send(ARPOP_REQUEST, ETH_P_ARP, rt->rt_gateway, rt->idev->dev,
+ rt->rt_src, NULL, rt->idev->dev->dev_addr, NULL);
+ ip_rt_put(rt);
+}
+
+static int addr_resolve_remote(struct sockaddr_in *src_in,
+ struct sockaddr_in *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ u32 src_ip = src_in->sin_addr.s_addr;
+ u32 dst_ip = dst_in->sin_addr.s_addr;
+ struct flowi fl;
+ struct rtable *rt;
+ struct neighbour *neigh;
+ int ret;
+
+ memset(&fl, 0, sizeof fl);
+ fl.nl_u.ip4_u.daddr = dst_ip;
+ fl.nl_u.ip4_u.saddr = src_ip;
+ ret = ip_route_output_key(&rt, &fl);
+ if (ret)
+ goto out;
+
+ neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->idev->dev);
+ if (!neigh) {
+ ret = -ENODATA;
+ goto err1;
+ }
+
+ if (!(neigh->nud_state & NUD_VALID)) {
+ ret = -ENODATA;
+ goto err2;
+ }
+
+ if (!src_ip) {
+ src_in->sin_family = dst_in->sin_family;
+ src_in->sin_addr.s_addr = rt->rt_src;
+ }
+
+ ret = copy_addr(addr, neigh->dev, neigh->ha);
+err2:
+ neigh_release(neigh);
+err1:
+ ip_rt_put(rt);
+out:
+ return ret;
+}
+
+static void process_req(void *data)
+{
+ struct addr_req *req, *temp_req;
+ struct sockaddr_in *src_in, *dst_in;
+ struct list_head done_list;
+
+ INIT_LIST_HEAD(&done_list);
+
+ mutex_lock(&lock);
+ list_for_each_entry_safe(req, temp_req, &req_list, list) {
+ if (req->status) {
+ src_in = (struct sockaddr_in *) &req->src_addr;
+ dst_in = (struct sockaddr_in *) &req->dst_addr;
+ req->status = addr_resolve_remote(src_in, dst_in,
+ req->addr);
+ }
+ if (req->status && time_after(jiffies, req->timeout))
+ req->status = -ETIMEDOUT;
+ else if (req->status == -ENODATA)
+ continue;
+
+ list_del(&req->list);
+ list_add_tail(&req->list, &done_list);
+ }
+
+ if (!list_empty(&req_list)) {
+ req = list_entry(req_list.next, struct addr_req, list);
+ set_timeout(req->timeout);
+ }
+ mutex_unlock(&lock);
+
+ list_for_each_entry_safe(req, temp_req, &done_list, list) {
+ list_del(&req->list);
+ req->callback(req->status, &req->src_addr, req->addr,
+ req->context);
+ kfree(req);
+ }
+}
+
+static int addr_resolve_local(struct sockaddr_in *src_in,
+ struct sockaddr_in *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ struct net_device *dev;
+ u32 src_ip = src_in->sin_addr.s_addr;
+ u32 dst_ip = dst_in->sin_addr.s_addr;
+ int ret;
+
+ dev = ip_dev_find(dst_ip);
+ if (!dev)
+ return -EADDRNOTAVAIL;
+
+ if (!src_ip) {
+ src_in->sin_family = dst_in->sin_family;
+ src_in->sin_addr.s_addr = dst_ip;
+ ret = copy_addr(addr, dev, dev->dev_addr);
+ } else {
+ ret = rdma_translate_ip((struct sockaddr *)src_in, addr);
+ if (!ret)
+ memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ }
+
+ dev_put(dev);
+ return ret;
+}
+
+int rdma_resolve_ip(struct sockaddr *src_addr, struct sockaddr *dst_addr,
+ struct rdma_dev_addr *addr, int timeout_ms,
+ void (*callback)(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *addr, void *context),
+ void *context)
+{
+ struct sockaddr_in *src_in, *dst_in;
+ struct addr_req *req;
+ int ret = 0;
+
+ req = kmalloc(sizeof *req, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+ memset(req, 0, sizeof *req);
+
+ if (src_addr)
+ memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
+ memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
+ req->addr = addr;
+ req->callback = callback;
+ req->context = context;
+
+ src_in = (struct sockaddr_in *) &req->src_addr;
+ dst_in = (struct sockaddr_in *) &req->dst_addr;
+
+ req->status = addr_resolve_local(src_in, dst_in, addr);
+ if (req->status == -EADDRNOTAVAIL)
+ req->status = addr_resolve_remote(src_in, dst_in, addr);
+
+ switch (req->status) {
+ case 0:
+ req->timeout = jiffies;
+ queue_req(req);
+ break;
+ case -ENODATA:
+ req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
+ queue_req(req);
+ addr_send_arp(dst_in);
+ break;
+ default:
+ ret = req->status;
+ kfree(req);
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(rdma_resolve_ip);
+
+void rdma_addr_cancel(struct rdma_dev_addr *addr)
+{
+ struct addr_req *req, *temp_req;
+
+ mutex_lock(&lock);
+ list_for_each_entry_safe(req, temp_req, &req_list, list) {
+ if (req->addr == addr) {
+ req->status = -ECANCELED;
+ req->timeout = jiffies;
+ list_del(&req->list);
+ list_add(&req->list, &req_list);
+ set_timeout(req->timeout);
+ break;
+ }
+ }
+ mutex_unlock(&lock);
+}
+EXPORT_SYMBOL(rdma_addr_cancel);
+
+static int addr_arp_recv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pkt, struct net_device *orig_dev)
+{
+ struct arphdr *arp_hdr;
+
+ arp_hdr = (struct arphdr *) skb->nh.raw;
+
+ if (dev->type == ARPHRD_INFINIBAND &&
+ (arp_hdr->ar_op == __constant_htons(ARPOP_REQUEST) ||
+ arp_hdr->ar_op == __constant_htons(ARPOP_REPLY)))
+ set_timeout(jiffies);
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct packet_type addr_arp = {
+ .type = __constant_htons(ETH_P_ARP),
+ .func = addr_arp_recv,
+ .af_packet_priv = (void*) 1,
+};
+
+static int addr_init(void)
+{
+ rdma_wq = create_singlethread_workqueue("rdma_wq");
+ if (!rdma_wq)
+ return -ENOMEM;
+
+ dev_add_pack(&addr_arp);
+ return 0;
+}
+
+static void addr_cleanup(void)
+{
+ dev_remove_pack(&addr_arp);
+ destroy_workqueue(rdma_wq);
+}
+
+module_init(addr_init);
+module_exit(addr_cleanup);
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/Makefile
linux-2.6.ib/drivers/infiniband/core/Makefile
--- linux-2.6.git/drivers/infiniband/core/Makefile 2006-01-16 16:03:08.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/Makefile 2006-01-16 16:14:24.000000000 -0800
@@ -1,5 +1,5 @@
obj-$(CONFIG_INFINIBAND) += ib_core.o ib_mad.o ib_sa.o \
- ib_cm.o
+ ib_cm.o ib_addr.o
obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o
obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o
@@ -12,6 +12,8 @@ ib_sa-y := sa_query.o
ib_cm-y := cm.o
+ib_addr-y := addr.o
+
ib_umad-y := user_mad.o
ib_ucm-y := ucm.o
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/include/rdma/ib_addr.h
linux-2.6.ib/include/rdma/ib_addr.h
--- linux-2.6.git/include/rdma/ib_addr.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/include/rdma/ib_addr.h 2006-01-16 16:14:24.000000000 -0800
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This Software is licensed under one of the following licenses:
+ *
+ * 1) under the terms of the "Common Public License 1.0" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/cpl.php.
+ *
+ * 2) under the terms of the "The BSD License" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/bsd-license.php.
+ *
+ * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
+ * copy of which is available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/gpl-license.php.
+ *
+ * Licensee has the right to choose one of the above licenses.
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice and one of the license notices.
+ *
+ * Redistributions in binary form must reproduce both the above copyright
+ * notice, one of the license notices in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ */
+
+#if !defined(IB_ADDR_H)
+#define IB_ADDR_H
+
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <rdma/ib_verbs.h>
+
+extern struct workqueue_struct *rdma_wq;
+
+struct rdma_dev_addr {
+ unsigned char src_dev_addr[MAX_ADDR_LEN];
+ unsigned char dst_dev_addr[MAX_ADDR_LEN];
+ unsigned char broadcast[MAX_ADDR_LEN];
+ enum ib_node_type dev_type;
+};
+
+/**
+ * rdma_translate_ip - Translate a local IP address to an RDMA hardware
+ * address.
+ */
+int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr);
+
+/**
+ * rdma_resolve_ip - Resolve source and destination IP addresses to
+ * RDMA hardware addresses.
+ * @src_addr: An optional source address to use in the resolution. If a
+ * source address is not provided, a usable address will be returned via
+ * the callback.
+ * @dst_addr: The destination address to resolve.
+ * @addr: A reference to a data location that will receive the resolved
+ * addresses. The data location must remain valid until the callback has
+ * been invoked.
+ * @timeout_ms: Amount of time to wait for the address resolution to complete.
+ * @callback: Call invoked once address resolution has completed, timed out,
+ * or been canceled. A status of 0 indicates success.
+ * @context: User-specified context associated with the call.
+ */
+int rdma_resolve_ip(struct sockaddr *src_addr, struct sockaddr *dst_addr,
+ struct rdma_dev_addr *addr, int timeout_ms,
+ void (*callback)(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *addr, void *context),
+ void *context);
+
+void rdma_addr_cancel(struct rdma_dev_addr *addr);
+
+static inline int ip_addr_size(struct sockaddr *addr)
+{
+ return addr->sa_family == AF_INET6 ?
+ sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
+}
+
+static inline u16 ib_addr_get_pkey(struct rdma_dev_addr *dev_addr)
+{
+ return ((u16)dev_addr->broadcast[8] << 8) | (u16)dev_addr->broadcast[9];
+}
+
+static inline void ib_addr_set_pkey(struct rdma_dev_addr *dev_addr, u16 pkey)
+{
+ dev_addr->broadcast[8] = pkey >> 8;
+ dev_addr->broadcast[9] = (unsigned char) pkey;
+}
+
+static inline union ib_gid* ib_addr_get_sgid(struct rdma_dev_addr *dev_addr)
+{
+ return (union ib_gid *) (dev_addr->src_dev_addr + 4);
+}
+
+static inline void ib_addr_set_sgid(struct rdma_dev_addr *dev_addr,
+ union ib_gid *gid)
+{
+ memcpy(dev_addr->src_dev_addr + 4, gid, sizeof *gid);
+}
+
+static inline union ib_gid* ib_addr_get_dgid(struct rdma_dev_addr *dev_addr)
+{
+ return (union ib_gid *) (dev_addr->dst_dev_addr + 4);
+}
+
+static inline void ib_addr_set_dgid(struct rdma_dev_addr *dev_addr,
+ union ib_gid *gid)
+{
+ memcpy(dev_addr->dst_dev_addr + 4, gid, sizeof *gid);
+}
+
+#endif /* IB_ADDR_H */
+
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/net/ipv4/fib_frontend.c
linux-2.6.ib/net/ipv4/fib_frontend.c
--- linux-2.6.git/net/ipv4/fib_frontend.c 2006-01-16 10:28:29.000000000 -0800
+++ linux-2.6.ib/net/ipv4/fib_frontend.c 2006-01-16 16:14:24.000000000 -0800
@@ -666,4 +666,5 @@ void __init ip_fib_init(void)
}
EXPORT_SYMBOL(inet_addr_type);
+EXPORT_SYMBOL(ip_dev_find);
EXPORT_SYMBOL(ip_rt_ioctl);
^ permalink raw reply
* [PATCH 4/5] Infiniband: connection abstraction
From: Sean Hefty @ 2006-02-01 20:18 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: openib-general
In-Reply-To: <ORSMSX401KZmcK8r6be00000097@orsmsx401.amr.corp.intel.com>
The following patch implements a kernel mode connection management agent
over Infiniband that connects based on IP addresses.
The agent defines a generic RDMA connection abstraction to support clients
wanting to connect over different RDMA devices.
It also handles RDMA device hotplug events on behalf of clients.
Signed-off-by: Sean Hefty <sean.hefty@intel.com>
---
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/cma.c
linux-2.6.ib/drivers/infiniband/core/cma.c
--- linux-2.6.git/drivers/infiniband/core/cma.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/cma.c 2006-01-16 16:17:34.000000000 -0800
@@ -0,0 +1,1639 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
+ * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This Software is licensed under one of the following licenses:
+ *
+ * 1) under the terms of the "Common Public License 1.0" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/cpl.php.
+ *
+ * 2) under the terms of the "The BSD License" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/bsd-license.php.
+ *
+ * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
+ * copy of which is available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/gpl-license.php.
+ *
+ * Licensee has the right to choose one of the above licenses.
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice and one of the license notices.
+ *
+ * Redistributions in binary form must reproduce both the above copyright
+ * notice, one of the license notices in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ */
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <rdma/rdma_cm.h>
+#include <rdma/ib_cache.h>
+#include <rdma/ib_cm.h>
+#include <rdma/ib_sa.h>
+
+MODULE_AUTHOR("Sean Hefty");
+MODULE_DESCRIPTION("Generic RDMA CM Agent");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define CMA_CM_RESPONSE_TIMEOUT 20
+#define CMA_MAX_CM_RETRIES 3
+
+static void cma_add_one(struct ib_device *device);
+static void cma_remove_one(struct ib_device *device);
+
+static struct ib_client cma_client = {
+ .name = "cma",
+ .add = cma_add_one,
+ .remove = cma_remove_one
+};
+
+static LIST_HEAD(dev_list);
+static LIST_HEAD(listen_any_list);
+static DEFINE_MUTEX(lock);
+
+struct cma_device {
+ struct list_head list;
+ struct ib_device *device;
+ __be64 node_guid;
+ wait_queue_head_t wait;
+ atomic_t refcount;
+ struct list_head id_list;
+};
+
+enum cma_state {
+ CMA_IDLE,
+ CMA_ADDR_QUERY,
+ CMA_ADDR_RESOLVED,
+ CMA_ROUTE_QUERY,
+ CMA_ROUTE_RESOLVED,
+ CMA_CONNECT,
+ CMA_ADDR_BOUND,
+ CMA_LISTEN,
+ CMA_DEVICE_REMOVAL,
+ CMA_DESTROYING
+};
+
+/*
+ * Device removal can occur at anytime, so we need extra handling to
+ * serialize notifying the user of device removal with other callbacks.
+ * We do this by disabling removal notification while a callback is in process,
+ * and reporting it after the callback completes.
+ */
+struct rdma_id_private {
+ struct rdma_cm_id id;
+
+ struct list_head list;
+ struct list_head listen_list;
+ struct cma_device *cma_dev;
+
+ enum cma_state state;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ atomic_t refcount;
+ wait_queue_head_t wait_remove;
+ atomic_t dev_remove;
+
+ int backlog;
+ int timeout_ms;
+ struct ib_sa_query *query;
+ int query_id;
+ struct ib_cm_id *cm_id;
+
+ u32 seq_num;
+ u32 qp_num;
+ enum ib_qp_type qp_type;
+ u8 srq;
+};
+
+struct cma_work {
+ struct work_struct work;
+ struct rdma_id_private *id;
+};
+
+union cma_ip_addr {
+ struct in6_addr ip6;
+ struct {
+ __u32 pad[3];
+ __u32 addr;
+ } ip4;
+};
+
+struct cma_hdr {
+ u8 cma_version;
+ u8 ip_version; /* IP version: 7:4 */
+ __u16 port;
+ union cma_ip_addr src_addr;
+ union cma_ip_addr dst_addr;
+};
+
+struct sdp_hh {
+ u8 sdp_version;
+ u8 ip_version; /* IP version: 7:4 */
+ u8 sdp_specific1[10];
+ __u16 port;
+ __u16 sdp_specific2;
+ union cma_ip_addr src_addr;
+ union cma_ip_addr dst_addr;
+};
+
+#define CMA_VERSION 0x00
+#define SDP_VERSION 0x22
+
+static int cma_comp(struct rdma_id_private *id_priv, enum cma_state comp)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&id_priv->lock, flags);
+ ret = (id_priv->state == comp);
+ spin_unlock_irqrestore(&id_priv->lock, flags);
+ return ret;
+}
+
+static int cma_comp_exch(struct rdma_id_private *id_priv,
+ enum cma_state comp, enum cma_state exch)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&id_priv->lock, flags);
+ if ((ret = (id_priv->state == comp)))
+ id_priv->state = exch;
+ spin_unlock_irqrestore(&id_priv->lock, flags);
+ return ret;
+}
+
+static enum cma_state cma_exch(struct rdma_id_private *id_priv,
+ enum cma_state exch)
+{
+ unsigned long flags;
+ enum cma_state old;
+
+ spin_lock_irqsave(&id_priv->lock, flags);
+ old = id_priv->state;
+ id_priv->state = exch;
+ spin_unlock_irqrestore(&id_priv->lock, flags);
+ return old;
+}
+
+static inline u8 cma_get_ip_ver(struct cma_hdr *hdr)
+{
+ return hdr->ip_version >> 4;
+}
+
+static inline void cma_set_ip_ver(struct cma_hdr *hdr, u8 ip_ver)
+{
+ hdr->ip_version = (ip_ver << 4) | (hdr->ip_version & 0xF);
+}
+
+static inline u8 sdp_get_ip_ver(struct sdp_hh *hh)
+{
+ return hh->ip_version >> 4;
+}
+
+static inline void sdp_set_ip_ver(struct sdp_hh *hh, u8 ip_ver)
+{
+ hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF);
+}
+
+static void cma_attach_to_dev(struct rdma_id_private *id_priv,
+ struct cma_device *cma_dev)
+{
+ atomic_inc(&cma_dev->refcount);
+ id_priv->cma_dev = cma_dev;
+ id_priv->id.device = cma_dev->device;
+ list_add_tail(&id_priv->list, &cma_dev->id_list);
+}
+
+static void cma_detach_from_dev(struct rdma_id_private *id_priv)
+{
+ list_del(&id_priv->list);
+ if (atomic_dec_and_test(&id_priv->cma_dev->refcount))
+ wake_up(&id_priv->cma_dev->wait);
+ id_priv->cma_dev = NULL;
+}
+
+static int cma_acquire_ib_dev(struct rdma_id_private *id_priv)
+{
+ struct cma_device *cma_dev;
+ union ib_gid *gid;
+ int ret = -ENODEV;
+
+ gid = ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr);
+
+ mutex_lock(&lock);
+ list_for_each_entry(cma_dev, &dev_list, list) {
+ ret = ib_find_cached_gid(cma_dev->device, gid,
+ &id_priv->id.port_num, NULL);
+ if (!ret) {
+ cma_attach_to_dev(id_priv, cma_dev);
+ break;
+ }
+ }
+ mutex_unlock(&lock);
+ return ret;
+}
+
+static int cma_acquire_dev(struct rdma_id_private *id_priv)
+{
+ switch (id_priv->id.route.addr.dev_addr.dev_type) {
+ case IB_NODE_CA:
+ return cma_acquire_ib_dev(id_priv);
+ default:
+ return -ENODEV;
+ }
+}
+
+static void cma_deref_id(struct rdma_id_private *id_priv)
+{
+ if (atomic_dec_and_test(&id_priv->refcount))
+ wake_up(&id_priv->wait);
+}
+
+static void cma_release_remove(struct rdma_id_private *id_priv)
+{
+ if (atomic_dec_and_test(&id_priv->dev_remove))
+ wake_up(&id_priv->wait_remove);
+}
+
+struct rdma_cm_id* rdma_create_id(rdma_cm_event_handler event_handler,
+ void *context, enum rdma_port_space ps)
+{
+ struct rdma_id_private *id_priv;
+
+ id_priv = kzalloc(sizeof *id_priv, GFP_KERNEL);
+ if (!id_priv)
+ return ERR_PTR(-ENOMEM);
+
+ id_priv->state = CMA_IDLE;
+ id_priv->id.context = context;
+ id_priv->id.event_handler = event_handler;
+ id_priv->id.ps = ps;
+ spin_lock_init(&id_priv->lock);
+ init_waitqueue_head(&id_priv->wait);
+ atomic_set(&id_priv->refcount, 1);
+ init_waitqueue_head(&id_priv->wait_remove);
+ atomic_set(&id_priv->dev_remove, 0);
+ INIT_LIST_HEAD(&id_priv->listen_list);
+ get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num);
+
+ return &id_priv->id;
+}
+EXPORT_SYMBOL(rdma_create_id);
+
+static int cma_init_ib_qp(struct rdma_id_private *id_priv, struct ib_qp *qp)
+{
+ struct ib_qp_attr qp_attr;
+ struct rdma_dev_addr *dev_addr;
+ int ret;
+
+ dev_addr = &id_priv->id.route.addr.dev_addr;
+ ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num,
+ ib_addr_get_pkey(dev_addr),
+ &qp_attr.pkey_index);
+ if (ret)
+ return ret;
+
+ qp_attr.qp_state = IB_QPS_INIT;
+ qp_attr.qp_access_flags = IB_ACCESS_LOCAL_WRITE;
+ qp_attr.port_num = id_priv->id.port_num;
+ return ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS |
+ IB_QP_PKEY_INDEX | IB_QP_PORT);
+}
+
+int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd,
+ struct ib_qp_init_attr *qp_init_attr)
+{
+ struct rdma_id_private *id_priv;
+ struct ib_qp *qp;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (id->device != pd->device)
+ return -EINVAL;
+
+ qp = ib_create_qp(pd, qp_init_attr);
+ if (IS_ERR(qp))
+ return PTR_ERR(qp);
+
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ ret = cma_init_ib_qp(id_priv, qp);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ if (ret)
+ goto err;
+
+ id->qp = qp;
+ id_priv->qp_num = qp->qp_num;
+ id_priv->qp_type = qp->qp_type;
+ id_priv->srq = (qp->srq != NULL);
+ return 0;
+err:
+ ib_destroy_qp(qp);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_create_qp);
+
+void rdma_destroy_qp(struct rdma_cm_id *id)
+{
+ ib_destroy_qp(id->qp);
+}
+EXPORT_SYMBOL(rdma_destroy_qp);
+
+static int cma_modify_qp_rtr(struct rdma_cm_id *id)
+{
+ struct ib_qp_attr qp_attr;
+ int qp_attr_mask, ret;
+
+ if (!id->qp)
+ return 0;
+
+ /* Need to update QP attributes from default values. */
+ qp_attr.qp_state = IB_QPS_INIT;
+ ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+ if (ret)
+ return ret;
+
+ ret = ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+ if (ret)
+ return ret;
+
+ qp_attr.qp_state = IB_QPS_RTR;
+ ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+ if (ret)
+ return ret;
+
+ return ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+}
+
+static int cma_modify_qp_rts(struct rdma_cm_id *id)
+{
+ struct ib_qp_attr qp_attr;
+ int qp_attr_mask, ret;
+
+ if (!id->qp)
+ return 0;
+
+ qp_attr.qp_state = IB_QPS_RTS;
+ ret = rdma_init_qp_attr(id, &qp_attr, &qp_attr_mask);
+ if (ret)
+ return ret;
+
+ return ib_modify_qp(id->qp, &qp_attr, qp_attr_mask);
+}
+
+static int cma_modify_qp_err(struct rdma_cm_id *id)
+{
+ struct ib_qp_attr qp_attr;
+
+ if (!id->qp)
+ return 0;
+
+ qp_attr.qp_state = IB_QPS_ERR;
+ return ib_modify_qp(id->qp, &qp_attr, IB_QP_STATE);
+}
+
+int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
+ int *qp_attr_mask)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ switch (id_priv->id.device->node_type) {
+ case IB_NODE_CA:
+ ret = ib_cm_init_qp_attr(id_priv->cm_id, qp_attr,
+ qp_attr_mask);
+ if (qp_attr->qp_state == IB_QPS_RTR)
+ qp_attr->rq_psn = id_priv->seq_num;
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(rdma_init_qp_attr);
+
+static inline int cma_any_addr(struct sockaddr *addr)
+{
+ struct in6_addr *ip6;
+
+ if (addr->sa_family == AF_INET)
+ return ((struct sockaddr_in *) addr)->sin_addr.s_addr ==
+ INADDR_ANY;
+ else {
+ ip6 = &((struct sockaddr_in6 *) addr)->sin6_addr;
+ return (ip6->s6_addr32[0] | ip6->s6_addr32[1] |
+ ip6->s6_addr32[3] | ip6->s6_addr32[4]) == 0;
+ }
+}
+
+static inline int cma_loopback_addr(struct sockaddr *addr)
+{
+ return ((struct sockaddr_in *) addr)->sin_addr.s_addr ==
+ ntohl(INADDR_LOOPBACK);
+}
+
+static int cma_get_net_info(void *hdr, enum rdma_port_space ps,
+ u8 *ip_ver, __u16 *port,
+ union cma_ip_addr **src, union cma_ip_addr **dst)
+{
+ switch (ps) {
+ case RDMA_PS_SDP:
+ if (((struct sdp_hh *) hdr)->sdp_version != SDP_VERSION)
+ return -EINVAL;
+
+ *ip_ver = sdp_get_ip_ver(hdr);
+ *port = ((struct sdp_hh *) hdr)->port;
+ *src = &((struct sdp_hh *) hdr)->src_addr;
+ *dst = &((struct sdp_hh *) hdr)->dst_addr;
+ break;
+ default:
+ if (((struct cma_hdr *) hdr)->cma_version != CMA_VERSION)
+ return -EINVAL;
+
+ *ip_ver = cma_get_ip_ver(hdr);
+ *port = ((struct cma_hdr *) hdr)->port;
+ *src = &((struct cma_hdr *) hdr)->src_addr;
+ *dst = &((struct cma_hdr *) hdr)->dst_addr;
+ break;
+ }
+ return 0;
+}
+
+static void cma_save_net_info(struct rdma_addr *addr,
+ struct rdma_addr *listen_addr,
+ u8 ip_ver, __u16 port,
+ union cma_ip_addr *src, union cma_ip_addr *dst)
+{
+ struct sockaddr_in *listen4, *ip4;
+ struct sockaddr_in6 *listen6, *ip6;
+
+ switch (ip_ver) {
+ case 4:
+ listen4 = (struct sockaddr_in *) &listen_addr->src_addr;
+ ip4 = (struct sockaddr_in *) &addr->src_addr;
+ ip4->sin_family = listen4->sin_family;
+ ip4->sin_addr.s_addr = dst->ip4.addr;
+ ip4->sin_port = listen4->sin_port;
+
+ ip4 = (struct sockaddr_in *) &addr->dst_addr;
+ ip4->sin_family = listen4->sin_family;
+ ip4->sin_addr.s_addr = src->ip4.addr;
+ ip4->sin_port = port;
+ break;
+ case 6:
+ listen6 = (struct sockaddr_in6 *) &listen_addr->src_addr;
+ ip6 = (struct sockaddr_in6 *) &addr->src_addr;
+ ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_addr = dst->ip6;
+ ip6->sin6_port = listen6->sin6_port;
+
+ ip6 = (struct sockaddr_in6 *) &addr->dst_addr;
+ ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_addr = src->ip6;
+ ip6->sin6_port = port;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline int cma_user_data_offset(enum rdma_port_space ps)
+{
+ switch (ps) {
+ case RDMA_PS_SDP:
+ return 0;
+ default:
+ return sizeof(struct cma_hdr);
+ }
+}
+
+static int cma_notify_user(struct rdma_id_private *id_priv,
+ enum rdma_cm_event_type type, int status,
+ void *data, u8 data_len)
+{
+ struct rdma_cm_event event;
+
+ event.event = type;
+ event.status = status;
+ event.private_data = data;
+ event.private_data_len = data_len;
+
+ return id_priv->id.event_handler(&id_priv->id, &event);
+}
+
+static void cma_cancel_addr(struct rdma_id_private *id_priv)
+{
+ switch (id_priv->id.device->node_type) {
+ case IB_NODE_CA:
+ rdma_addr_cancel(&id_priv->id.route.addr.dev_addr);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cma_cancel_route(struct rdma_id_private *id_priv)
+{
+ switch (id_priv->id.device->node_type) {
+ case IB_NODE_CA:
+ ib_sa_cancel_query(id_priv->query_id, id_priv->query);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline int cma_internal_listen(struct rdma_id_private *id_priv)
+{
+ return (id_priv->state == CMA_LISTEN) && id_priv->cma_dev &&
+ cma_any_addr(&id_priv->id.route.addr.src_addr);
+}
+
+static void cma_destroy_listen(struct rdma_id_private *id_priv)
+{
+ cma_exch(id_priv, CMA_DESTROYING);
+
+ if (id_priv->cm_id && !IS_ERR(id_priv->cm_id))
+ ib_destroy_cm_id(id_priv->cm_id);
+
+ list_del(&id_priv->listen_list);
+ if (id_priv->cma_dev)
+ cma_detach_from_dev(id_priv);
+
+ atomic_dec(&id_priv->refcount);
+ wait_event(id_priv->wait, !atomic_read(&id_priv->refcount));
+
+ kfree(id_priv);
+}
+
+static void cma_cancel_listens(struct rdma_id_private *id_priv)
+{
+ struct rdma_id_private *dev_id_priv;
+
+ mutex_lock(&lock);
+ list_del(&id_priv->list);
+
+ while (!list_empty(&id_priv->listen_list)) {
+ dev_id_priv = list_entry(id_priv->listen_list.next,
+ struct rdma_id_private, listen_list);
+ cma_destroy_listen(dev_id_priv);
+ }
+ mutex_unlock(&lock);
+}
+
+static void cma_cancel_operation(struct rdma_id_private *id_priv,
+ enum cma_state state)
+{
+ switch (state) {
+ case CMA_ADDR_QUERY:
+ cma_cancel_addr(id_priv);
+ break;
+ case CMA_ROUTE_QUERY:
+ cma_cancel_route(id_priv);
+ break;
+ case CMA_LISTEN:
+ if (cma_any_addr(&id_priv->id.route.addr.src_addr) &&
+ !id_priv->cma_dev)
+ cma_cancel_listens(id_priv);
+ break;
+ default:
+ break;
+ }
+}
+
+void rdma_destroy_id(struct rdma_cm_id *id)
+{
+ struct rdma_id_private *id_priv;
+ enum cma_state state;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ state = cma_exch(id_priv, CMA_DESTROYING);
+ cma_cancel_operation(id_priv, state);
+
+ if (id_priv->cm_id && !IS_ERR(id_priv->cm_id))
+ ib_destroy_cm_id(id_priv->cm_id);
+
+ if (id_priv->cma_dev) {
+ mutex_lock(&lock);
+ cma_detach_from_dev(id_priv);
+ mutex_unlock(&lock);
+ }
+
+ atomic_dec(&id_priv->refcount);
+ wait_event(id_priv->wait, !atomic_read(&id_priv->refcount));
+
+ kfree(id_priv->id.route.path_rec);
+ kfree(id_priv);
+}
+EXPORT_SYMBOL(rdma_destroy_id);
+
+static int cma_rep_recv(struct rdma_id_private *id_priv)
+{
+ int ret;
+
+ ret = cma_modify_qp_rtr(&id_priv->id);
+ if (ret)
+ goto reject;
+
+ ret = cma_modify_qp_rts(&id_priv->id);
+ if (ret)
+ goto reject;
+
+ ret = ib_send_cm_rtu(id_priv->cm_id, NULL, 0);
+ if (ret)
+ goto reject;
+
+ return 0;
+reject:
+ cma_modify_qp_err(&id_priv->id);
+ ib_send_cm_rej(id_priv->cm_id, IB_CM_REJ_CONSUMER_DEFINED,
+ NULL, 0, NULL, 0);
+ return ret;
+}
+
+static int cma_rtu_recv(struct rdma_id_private *id_priv)
+{
+ int ret;
+
+ ret = cma_modify_qp_rts(&id_priv->id);
+ if (ret)
+ goto reject;
+
+ return 0;
+reject:
+ cma_modify_qp_err(&id_priv->id);
+ ib_send_cm_rej(id_priv->cm_id, IB_CM_REJ_CONSUMER_DEFINED,
+ NULL, 0, NULL, 0);
+ return ret;
+}
+
+static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
+{
+ struct rdma_id_private *id_priv = cm_id->context;
+ enum rdma_cm_event_type event;
+ u8 private_data_len = 0;
+ int ret = 0, status = 0;
+
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ return 0;
+
+ atomic_inc(&id_priv->dev_remove);
+ switch (ib_event->event) {
+ case IB_CM_REQ_ERROR:
+ case IB_CM_REP_ERROR:
+ event = RDMA_CM_EVENT_UNREACHABLE;
+ status = -ETIMEDOUT;
+ break;
+ case IB_CM_REP_RECEIVED:
+ if (id_priv->id.qp) {
+ status = cma_rep_recv(id_priv);
+ event = status ? RDMA_CM_EVENT_CONNECT_ERROR :
+ RDMA_CM_EVENT_ESTABLISHED;
+ } else
+ event = RDMA_CM_EVENT_CONNECT_RESPONSE;
+ private_data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
+ break;
+ case IB_CM_RTU_RECEIVED:
+ status = cma_rtu_recv(id_priv);
+ event = status ? RDMA_CM_EVENT_CONNECT_ERROR :
+ RDMA_CM_EVENT_ESTABLISHED;
+ break;
+ case IB_CM_DREQ_ERROR:
+ status = -ETIMEDOUT; /* fall through */
+ case IB_CM_DREQ_RECEIVED:
+ case IB_CM_DREP_RECEIVED:
+ event = RDMA_CM_EVENT_DISCONNECTED;
+ break;
+ case IB_CM_TIMEWAIT_EXIT:
+ case IB_CM_MRA_RECEIVED:
+ /* ignore event */
+ goto out;
+ case IB_CM_REJ_RECEIVED:
+ cma_modify_qp_err(&id_priv->id);
+ status = ib_event->param.rej_rcvd.reason;
+ event = RDMA_CM_EVENT_REJECTED;
+ break;
+ default:
+ printk(KERN_ERR "RDMA CMA: unexpected IB CM event: %d",
+ ib_event->event);
+ goto out;
+ }
+
+ ret = cma_notify_user(id_priv, event, status, ib_event->private_data,
+ private_data_len);
+ if (ret) {
+ /* Destroy the CM ID by returning a non-zero value. */
+ id_priv->cm_id = NULL;
+ cma_exch(id_priv, CMA_DESTROYING);
+ cma_release_remove(id_priv);
+ rdma_destroy_id(&id_priv->id);
+ return ret;
+ }
+out:
+ cma_release_remove(id_priv);
+ return ret;
+}
+
+static struct rdma_id_private* cma_new_id(struct rdma_cm_id *listen_id,
+ struct ib_cm_event *ib_event)
+{
+ struct rdma_id_private *id_priv;
+ struct rdma_cm_id *id;
+ struct rdma_route *rt;
+ union cma_ip_addr *src, *dst;
+ __u16 port;
+ u8 ip_ver;
+
+ id = rdma_create_id(listen_id->event_handler, listen_id->context,
+ listen_id->ps);
+ if (IS_ERR(id))
+ return NULL;
+
+ rt = &id->route;
+ rt->num_paths = ib_event->param.req_rcvd.alternate_path ? 2 : 1;
+ rt->path_rec = kmalloc(sizeof *rt->path_rec * rt->num_paths, GFP_KERNEL);
+ if (!rt->path_rec)
+ goto err;
+
+ if (cma_get_net_info(ib_event->private_data, listen_id->ps,
+ &ip_ver, &port, &src, &dst))
+ goto err;
+
+ cma_save_net_info(&id->route.addr, &listen_id->route.addr,
+ ip_ver, port, src, dst);
+ rt->path_rec[0] = *ib_event->param.req_rcvd.primary_path;
+ if (rt->num_paths == 2)
+ rt->path_rec[1] = *ib_event->param.req_rcvd.alternate_path;
+
+ ib_addr_set_sgid(&rt->addr.dev_addr, &rt->path_rec[0].sgid);
+ ib_addr_set_dgid(&rt->addr.dev_addr, &rt->path_rec[0].dgid);
+ ib_addr_set_pkey(&rt->addr.dev_addr, be16_to_cpu(rt->path_rec[0].pkey));
+ rt->addr.dev_addr.dev_type = IB_NODE_CA;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ id_priv->state = CMA_CONNECT;
+ return id_priv;
+err:
+ rdma_destroy_id(id);
+ return NULL;
+}
+
+static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
+{
+ struct rdma_id_private *listen_id, *conn_id;
+ int offset, ret;
+
+ listen_id = cm_id->context;
+ atomic_inc(&listen_id->dev_remove);
+ if (!cma_comp(listen_id, CMA_LISTEN)) {
+ ret = -ECONNABORTED;
+ goto out;
+ }
+
+ conn_id = cma_new_id(&listen_id->id, ib_event);
+ if (!conn_id) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ atomic_inc(&conn_id->dev_remove);
+ ret = cma_acquire_ib_dev(conn_id);
+ if (ret) {
+ ret = -ENODEV;
+ cma_release_remove(conn_id);
+ rdma_destroy_id(&conn_id->id);
+ goto out;
+ }
+
+ conn_id->cm_id = cm_id;
+ cm_id->context = conn_id;
+ cm_id->cm_handler = cma_ib_handler;
+
+ offset = cma_user_data_offset(listen_id->id.ps);
+ ret = cma_notify_user(conn_id, RDMA_CM_EVENT_CONNECT_REQUEST, 0,
+ ib_event->private_data + offset,
+ IB_CM_REQ_PRIVATE_DATA_SIZE - offset);
+ if (ret) {
+ /* Destroy the CM ID by returning a non-zero value. */
+ conn_id->cm_id = NULL;
+ cma_exch(conn_id, CMA_DESTROYING);
+ cma_release_remove(conn_id);
+ rdma_destroy_id(&conn_id->id);
+ }
+out:
+ cma_release_remove(listen_id);
+ return ret;
+}
+
+static __be64 cma_get_service_id(enum rdma_port_space ps, struct sockaddr *addr)
+{
+ return cpu_to_be64(((u64)ps << 16) +
+ be16_to_cpu(((struct sockaddr_in *) addr)->sin_port));
+}
+
+static void cma_set_compare_data(struct sockaddr *addr,
+ struct ib_cm_compare_data *compare)
+{
+ struct cma_hdr *data, *mask;
+
+ memset(compare, 0, sizeof *compare);
+ data = (void *) compare->data;
+ mask = (void *) compare->mask;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ cma_set_ip_ver(data, 4);
+ cma_set_ip_ver(mask, 0xF);
+ data->dst_addr.ip4.addr = ((struct sockaddr_in *) addr)->
+ sin_addr.s_addr;
+ mask->dst_addr.ip4.addr = ~0;
+ break;
+ case AF_INET6:
+ cma_set_ip_ver(data, 6);
+ cma_set_ip_ver(mask, 0xF);
+ data->dst_addr.ip6 = ((struct sockaddr_in6 *) addr)->
+ sin6_addr;
+ memset(&mask->dst_addr.ip6, 1, sizeof mask->dst_addr.ip6);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cma_ib_listen(struct rdma_id_private *id_priv)
+{
+ struct ib_cm_compare_data compare_data;
+ struct sockaddr *addr;
+ __be64 svc_id;
+ int ret;
+
+ id_priv->cm_id = ib_create_cm_id(id_priv->id.device, cma_req_handler,
+ id_priv);
+ if (IS_ERR(id_priv->cm_id))
+ return PTR_ERR(id_priv->cm_id);
+
+ addr = &id_priv->id.route.addr.src_addr;
+ svc_id = cma_get_service_id(id_priv->id.ps, addr);
+ if (cma_any_addr(addr))
+ ret = ib_cm_listen(id_priv->cm_id, svc_id, 0, NULL);
+ else {
+ cma_set_compare_data(addr, &compare_data);
+ ret = ib_cm_listen(id_priv->cm_id, svc_id, 0, &compare_data);
+ }
+
+ if (ret) {
+ ib_destroy_cm_id(id_priv->cm_id);
+ id_priv->cm_id = NULL;
+ }
+
+ return ret;
+}
+
+static int cma_duplicate_listen(struct rdma_id_private *id_priv)
+{
+ struct rdma_id_private *cur_id_priv;
+ struct sockaddr_in *cur_addr, *new_addr;
+
+ new_addr = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
+ list_for_each_entry(cur_id_priv, &listen_any_list, listen_list) {
+ cur_addr = (struct sockaddr_in *)
+ &cur_id_priv->id.route.addr.src_addr;
+ if (cur_addr->sin_port == new_addr->sin_port)
+ return -EADDRINUSE;
+ }
+ return 0;
+}
+
+static int cma_listen_handler(struct rdma_cm_id *id,
+ struct rdma_cm_event *event)
+{
+ struct rdma_id_private *id_priv = id->context;
+
+ id->context = id_priv->id.context;
+ id->event_handler = id_priv->id.event_handler;
+ return id_priv->id.event_handler(id, event);
+}
+
+static void cma_listen_on_dev(struct rdma_id_private *id_priv,
+ struct cma_device *cma_dev)
+{
+ struct rdma_id_private *dev_id_priv;
+ struct rdma_cm_id *id;
+ int ret;
+
+ id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps);
+ if (IS_ERR(id))
+ return;
+
+ dev_id_priv = container_of(id, struct rdma_id_private, id);
+ ret = rdma_bind_addr(id, &id_priv->id.route.addr.src_addr);
+ if (ret)
+ goto err;
+
+ cma_attach_to_dev(dev_id_priv, cma_dev);
+ list_add_tail(&dev_id_priv->listen_list, &id_priv->listen_list);
+
+ ret = rdma_listen(id, id_priv->backlog);
+ if (ret)
+ goto err;
+
+ return;
+err:
+ cma_destroy_listen(dev_id_priv);
+}
+
+static int cma_listen_on_all(struct rdma_id_private *id_priv)
+{
+ struct cma_device *cma_dev;
+ int ret;
+
+ mutex_lock(&lock);
+ ret = cma_duplicate_listen(id_priv);
+ if (ret)
+ goto out;
+
+ list_add_tail(&id_priv->list, &listen_any_list);
+ list_for_each_entry(cma_dev, &dev_list, list)
+ cma_listen_on_dev(id_priv, cma_dev);
+out:
+ mutex_unlock(&lock);
+ return ret;
+}
+
+int rdma_listen(struct rdma_cm_id *id, int backlog)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_LISTEN))
+ return -EINVAL;
+
+ if (id->device) {
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ ret = cma_ib_listen(id_priv);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+ } else
+ ret = cma_listen_on_all(id_priv);
+
+ if (ret)
+ goto err;
+
+ id_priv->backlog = backlog;
+ return 0;
+err:
+ cma_comp_exch(id_priv, CMA_LISTEN, CMA_ADDR_BOUND);
+ return ret;
+};
+EXPORT_SYMBOL(rdma_listen);
+
+static void cma_query_handler(int status, struct ib_sa_path_rec *path_rec,
+ void *context)
+{
+ struct rdma_id_private *id_priv = context;
+ struct rdma_route *route = &id_priv->id.route;
+ enum rdma_cm_event_type event = RDMA_CM_EVENT_ROUTE_RESOLVED;
+
+ atomic_inc(&id_priv->dev_remove);
+ if (!status) {
+ route->path_rec = kmalloc(sizeof *route->path_rec, GFP_KERNEL);
+ if (route->path_rec) {
+ route->num_paths = 1;
+ *route->path_rec = *path_rec;
+ if (!cma_comp_exch(id_priv, CMA_ROUTE_QUERY,
+ CMA_ROUTE_RESOLVED)) {
+ kfree(route->path_rec);
+ goto out;
+ }
+ } else
+ status = -ENOMEM;
+ }
+
+ if (status) {
+ if (!cma_comp_exch(id_priv, CMA_ROUTE_QUERY, CMA_ADDR_RESOLVED))
+ goto out;
+ event = RDMA_CM_EVENT_ROUTE_ERROR;
+ }
+
+ if (cma_notify_user(id_priv, event, status, NULL, 0)) {
+ cma_exch(id_priv, CMA_DESTROYING);
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+ rdma_destroy_id(&id_priv->id);
+ return;
+ }
+out:
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+}
+
+static int cma_resolve_ib_route(struct rdma_id_private *id_priv, int timeout_ms)
+{
+ struct rdma_dev_addr *addr = &id_priv->id.route.addr.dev_addr;
+ struct ib_sa_path_rec path_rec;
+
+ memset(&path_rec, 0, sizeof path_rec);
+ path_rec.sgid = *ib_addr_get_sgid(addr);
+ path_rec.dgid = *ib_addr_get_dgid(addr);
+ path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(addr));
+ path_rec.numb_path = 1;
+
+ id_priv->query_id = ib_sa_path_rec_get(id_priv->id.device,
+ id_priv->id.port_num, &path_rec,
+ IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID |
+ IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH,
+ timeout_ms, GFP_KERNEL,
+ cma_query_handler, id_priv, &id_priv->query);
+
+ return (id_priv->query_id < 0) ? id_priv->query_id : 0;
+}
+
+int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ROUTE_QUERY))
+ return -EINVAL;
+
+ atomic_inc(&id_priv->refcount);
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ ret = cma_resolve_ib_route(id_priv, timeout_ms);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ cma_comp_exch(id_priv, CMA_ROUTE_QUERY, CMA_ADDR_RESOLVED);
+ cma_deref_id(id_priv);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_resolve_route);
+
+static int cma_bind_loopback(struct rdma_id_private *id_priv)
+{
+ struct cma_device *cma_dev;
+ union ib_gid *gid;
+ u16 pkey;
+ int ret;
+
+ mutex_lock(&lock);
+ if (list_empty(&dev_list)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ cma_dev = list_entry(dev_list.next, struct cma_device, list);
+ gid = ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr);
+ ret = ib_get_cached_gid(cma_dev->device, 1, 0, gid);
+ if (ret)
+ goto out;
+
+ ret = ib_get_cached_pkey(cma_dev->device, 1, 0, &pkey);
+ if (ret)
+ goto out;
+
+ ib_addr_set_pkey(&id_priv->id.route.addr.dev_addr, pkey);
+ id_priv->id.port_num = 1;
+ cma_attach_to_dev(id_priv, cma_dev);
+out:
+ mutex_unlock(&lock);
+ return ret;
+}
+
+static void addr_handler(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *dev_addr, void *context)
+{
+ struct rdma_id_private *id_priv = context;
+ enum rdma_cm_event_type event;
+ enum cma_state old_state;
+
+ atomic_inc(&id_priv->dev_remove);
+ if (!id_priv->cma_dev) {
+ old_state = CMA_IDLE;
+ if (!status)
+ status = cma_acquire_dev(id_priv);
+ } else
+ old_state = CMA_ADDR_BOUND;
+
+ if (status) {
+ if (!cma_comp_exch(id_priv, CMA_ADDR_QUERY, old_state))
+ goto out;
+ event = RDMA_CM_EVENT_ADDR_ERROR;
+ } else {
+ if (!cma_comp_exch(id_priv, CMA_ADDR_QUERY, CMA_ADDR_RESOLVED))
+ goto out;
+ memcpy(&id_priv->id.route.addr.src_addr, src_addr,
+ ip_addr_size(src_addr));
+ event = RDMA_CM_EVENT_ADDR_RESOLVED;
+ }
+
+ if (cma_notify_user(id_priv, event, status, NULL, 0)) {
+ cma_exch(id_priv, CMA_DESTROYING);
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+ rdma_destroy_id(&id_priv->id);
+ return;
+ }
+out:
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+}
+
+static void loopback_addr_handler(void *data)
+{
+ struct cma_work *work = data;
+ struct rdma_id_private *id_priv = work->id;
+
+ kfree(work);
+ atomic_inc(&id_priv->dev_remove);
+
+ if (!cma_comp_exch(id_priv, CMA_ADDR_QUERY, CMA_ADDR_RESOLVED))
+ goto out;
+
+ if (cma_notify_user(id_priv, RDMA_CM_EVENT_ADDR_RESOLVED, 0, NULL, 0)) {
+ cma_exch(id_priv, CMA_DESTROYING);
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+ rdma_destroy_id(&id_priv->id);
+ return;
+ }
+out:
+ cma_release_remove(id_priv);
+ cma_deref_id(id_priv);
+}
+
+static int cma_resolve_loopback(struct rdma_id_private *id_priv,
+ struct sockaddr *src_addr, enum cma_state state)
+{
+ struct cma_work *work;
+ struct rdma_dev_addr *dev_addr;
+ int ret;
+
+ work = kmalloc(sizeof *work, GFP_KERNEL);
+ if (!work)
+ return -ENOMEM;
+
+ if (state == CMA_IDLE) {
+ ret = cma_bind_loopback(id_priv);
+ if (ret)
+ goto err;
+ dev_addr = &id_priv->id.route.addr.dev_addr;
+ ib_addr_set_dgid(dev_addr, ib_addr_get_sgid(dev_addr));
+ if (!src_addr || cma_any_addr(src_addr))
+ src_addr = &id_priv->id.route.addr.dst_addr;
+ memcpy(&id_priv->id.route.addr.src_addr, src_addr,
+ ip_addr_size(src_addr));
+ }
+
+ work->id = id_priv;
+ INIT_WORK(&work->work, loopback_addr_handler, work);
+ queue_work(rdma_wq, &work->work);
+ return 0;
+err:
+ kfree(work);
+ return ret;
+}
+
+int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
+ struct sockaddr *dst_addr, int timeout_ms)
+{
+ struct rdma_id_private *id_priv;
+ enum cma_state expected_state;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (id_priv->cma_dev) {
+ expected_state = CMA_ADDR_BOUND;
+ src_addr = &id->route.addr.src_addr;
+ } else
+ expected_state = CMA_IDLE;
+
+ if (!cma_comp_exch(id_priv, expected_state, CMA_ADDR_QUERY))
+ return -EINVAL;
+
+ atomic_inc(&id_priv->refcount);
+ memcpy(&id->route.addr.dst_addr, dst_addr, ip_addr_size(dst_addr));
+ if (cma_loopback_addr(dst_addr))
+ ret = cma_resolve_loopback(id_priv, src_addr, expected_state);
+ else
+ ret = rdma_resolve_ip(src_addr, dst_addr,
+ &id->route.addr.dev_addr,
+ timeout_ms, addr_handler, id_priv);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ cma_comp_exch(id_priv, CMA_ADDR_QUERY, expected_state);
+ cma_deref_id(id_priv);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_resolve_addr);
+
+int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
+{
+ struct rdma_id_private *id_priv;
+ struct rdma_dev_addr *dev_addr;
+ int ret;
+
+ if (addr->sa_family != AF_INET)
+ return -EINVAL;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND))
+ return -EINVAL;
+
+ if (cma_any_addr(addr)) {
+ ret = 0;
+ } else if (cma_loopback_addr(addr)) {
+ ret = cma_bind_loopback(id_priv);
+ } else {
+ dev_addr = &id->route.addr.dev_addr;
+ ret = rdma_translate_ip(addr, dev_addr);
+ if (!ret)
+ ret = cma_acquire_dev(id_priv);
+ }
+
+ if (ret)
+ goto err;
+
+ memcpy(&id->route.addr.src_addr, addr, ip_addr_size(addr));
+ return 0;
+err:
+ cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_IDLE);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_bind_addr);
+
+static void cma_format_hdr(void *hdr, enum rdma_port_space ps,
+ struct rdma_route *route)
+{
+ struct sockaddr_in *src4, *dst4;
+ struct cma_hdr *cma_hdr;
+ struct sdp_hh *sdp_hdr;
+
+ src4 = (struct sockaddr_in *) &route->addr.src_addr;
+ dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
+
+ switch (ps) {
+ case RDMA_PS_SDP:
+ sdp_hdr = hdr;
+ sdp_hdr->sdp_version = SDP_VERSION;
+ sdp_set_ip_ver(sdp_hdr, 4);
+ sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+ sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+ sdp_hdr->port = src4->sin_port;
+ break;
+ default:
+ cma_hdr = hdr;
+ cma_hdr->cma_version = CMA_VERSION;
+ cma_set_ip_ver(cma_hdr, 4);
+ cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+ cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+ cma_hdr->port = src4->sin_port;
+ break;
+ }
+}
+
+static int cma_connect_ib(struct rdma_id_private *id_priv,
+ struct rdma_conn_param *conn_param)
+{
+ struct ib_cm_req_param req;
+ struct rdma_route *route;
+ void *private_data;
+ int offset, ret;
+
+ memset(&req, 0, sizeof req);
+ offset = cma_user_data_offset(id_priv->id.ps);
+ req.private_data_len = offset + conn_param->private_data_len;
+ private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
+ if (!private_data)
+ return -ENOMEM;
+
+ if (conn_param->private_data && conn_param->private_data_len)
+ memcpy(private_data + offset, conn_param->private_data,
+ conn_param->private_data_len);
+
+ id_priv->cm_id = ib_create_cm_id(id_priv->id.device, cma_ib_handler,
+ id_priv);
+ if (IS_ERR(id_priv->cm_id)) {
+ ret = PTR_ERR(id_priv->cm_id);
+ goto out;
+ }
+
+ route = &id_priv->id.route;
+ cma_format_hdr(private_data, id_priv->id.ps, route);
+ req.private_data = private_data;
+
+ req.primary_path = &route->path_rec[0];
+ if (route->num_paths == 2)
+ req.alternate_path = &route->path_rec[1];
+
+ req.service_id = cma_get_service_id(id_priv->id.ps,
+ &route->addr.dst_addr);
+ req.qp_num = id_priv->qp_num;
+ req.qp_type = id_priv->qp_type;
+ req.starting_psn = id_priv->seq_num;
+ req.responder_resources = conn_param->responder_resources;
+ req.initiator_depth = conn_param->initiator_depth;
+ req.flow_control = conn_param->flow_control;
+ req.retry_count = conn_param->retry_count;
+ req.rnr_retry_count = conn_param->rnr_retry_count;
+ req.remote_cm_response_timeout = CMA_CM_RESPONSE_TIMEOUT;
+ req.local_cm_response_timeout = CMA_CM_RESPONSE_TIMEOUT;
+ req.max_cm_retries = CMA_MAX_CM_RETRIES;
+ req.srq = id_priv->srq ? 1 : 0;
+
+ ret = ib_send_cm_req(id_priv->cm_id, &req);
+out:
+ kfree(private_data);
+ return ret;
+}
+
+int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp_exch(id_priv, CMA_ROUTE_RESOLVED, CMA_CONNECT))
+ return -EINVAL;
+
+ if (!id->qp) {
+ id_priv->qp_num = conn_param->qp_num;
+ id_priv->qp_type = conn_param->qp_type;
+ id_priv->srq = conn_param->srq;
+ }
+
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ ret = cma_connect_ib(id_priv, conn_param);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ cma_comp_exch(id_priv, CMA_CONNECT, CMA_ROUTE_RESOLVED);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_connect);
+
+static int cma_accept_ib(struct rdma_id_private *id_priv,
+ struct rdma_conn_param *conn_param)
+{
+ struct ib_cm_rep_param rep;
+ int ret;
+
+ ret = cma_modify_qp_rtr(&id_priv->id);
+ if (ret)
+ return ret;
+
+ memset(&rep, 0, sizeof rep);
+ rep.qp_num = id_priv->qp_num;
+ rep.starting_psn = id_priv->seq_num;
+ rep.private_data = conn_param->private_data;
+ rep.private_data_len = conn_param->private_data_len;
+ rep.responder_resources = conn_param->responder_resources;
+ rep.initiator_depth = conn_param->initiator_depth;
+ rep.target_ack_delay = CMA_CM_RESPONSE_TIMEOUT;
+ rep.failover_accepted = 0;
+ rep.flow_control = conn_param->flow_control;
+ rep.rnr_retry_count = conn_param->rnr_retry_count;
+ rep.srq = id_priv->srq ? 1 : 0;
+
+ return ib_send_cm_rep(id_priv->cm_id, &rep);
+}
+
+int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ return -EINVAL;
+
+ if (!id->qp && conn_param) {
+ id_priv->qp_num = conn_param->qp_num;
+ id_priv->qp_type = conn_param->qp_type;
+ id_priv->srq = conn_param->srq;
+ }
+
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ if (conn_param)
+ ret = cma_accept_ib(id_priv, conn_param);
+ else
+ ret = cma_rep_recv(id_priv);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ if (ret)
+ goto reject;
+
+ return 0;
+reject:
+ cma_modify_qp_err(id);
+ rdma_reject(id, NULL, 0);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_accept);
+
+int rdma_reject(struct rdma_cm_id *id, const void *private_data,
+ u8 private_data_len)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ return -EINVAL;
+
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ ret = ib_send_cm_rej(id_priv->cm_id, IB_CM_REJ_CONSUMER_DEFINED,
+ NULL, 0, private_data, private_data_len);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+};
+EXPORT_SYMBOL(rdma_reject);
+
+int rdma_disconnect(struct rdma_cm_id *id)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (!cma_comp(id_priv, CMA_CONNECT))
+ return -EINVAL;
+
+ ret = cma_modify_qp_err(id);
+ if (ret)
+ goto out;
+
+ switch (id->device->node_type) {
+ case IB_NODE_CA:
+ /* Initiate or respond to a disconnect. */
+ if (ib_send_cm_dreq(id_priv->cm_id, NULL, 0))
+ ib_send_cm_drep(id_priv->cm_id, NULL, 0);
+ break;
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL(rdma_disconnect);
+
+static void cma_add_one(struct ib_device *device)
+{
+ struct cma_device *cma_dev;
+ struct rdma_id_private *id_priv;
+
+ cma_dev = kmalloc(sizeof *cma_dev, GFP_KERNEL);
+ if (!cma_dev)
+ return;
+
+ cma_dev->device = device;
+ cma_dev->node_guid = device->node_guid;
+ if (!cma_dev->node_guid)
+ goto err;
+
+ init_waitqueue_head(&cma_dev->wait);
+ atomic_set(&cma_dev->refcount, 1);
+ INIT_LIST_HEAD(&cma_dev->id_list);
+ ib_set_client_data(device, &cma_client, cma_dev);
+
+ mutex_lock(&lock);
+ list_add_tail(&cma_dev->list, &dev_list);
+ list_for_each_entry(id_priv, &listen_any_list, list)
+ cma_listen_on_dev(id_priv, cma_dev);
+ mutex_unlock(&lock);
+ return;
+err:
+ kfree(cma_dev);
+}
+
+static int cma_remove_id_dev(struct rdma_id_private *id_priv)
+{
+ enum cma_state state;
+
+ /* Record that we want to remove the device */
+ state = cma_exch(id_priv, CMA_DEVICE_REMOVAL);
+ if (state == CMA_DESTROYING)
+ return 0;
+
+ cma_cancel_operation(id_priv, state);
+ wait_event(id_priv->wait_remove, !atomic_read(&id_priv->dev_remove));
+
+ /* Check for destruction from another callback. */
+ if (!cma_comp(id_priv, CMA_DEVICE_REMOVAL))
+ return 0;
+
+ return cma_notify_user(id_priv, RDMA_CM_EVENT_DEVICE_REMOVAL,
+ 0, NULL, 0);
+}
+
+static void cma_process_remove(struct cma_device *cma_dev)
+{
+ struct list_head remove_list;
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ INIT_LIST_HEAD(&remove_list);
+
+ mutex_lock(&lock);
+ while (!list_empty(&cma_dev->id_list)) {
+ id_priv = list_entry(cma_dev->id_list.next,
+ struct rdma_id_private, list);
+
+ if (cma_internal_listen(id_priv)) {
+ cma_destroy_listen(id_priv);
+ continue;
+ }
+
+ list_del(&id_priv->list);
+ list_add_tail(&id_priv->list, &remove_list);
+ atomic_inc(&id_priv->refcount);
+ mutex_unlock(&lock);
+
+ ret = cma_remove_id_dev(id_priv);
+ cma_deref_id(id_priv);
+ if (ret)
+ rdma_destroy_id(&id_priv->id);
+
+ mutex_lock(&lock);
+ }
+ mutex_unlock(&lock);
+
+ atomic_dec(&cma_dev->refcount);
+ wait_event(cma_dev->wait, !atomic_read(&cma_dev->refcount));
+}
+
+static void cma_remove_one(struct ib_device *device)
+{
+ struct cma_device *cma_dev;
+
+ cma_dev = ib_get_client_data(device, &cma_client);
+ if (!cma_dev)
+ return;
+
+ mutex_lock(&lock);
+ list_del(&cma_dev->list);
+ mutex_unlock(&lock);
+
+ cma_process_remove(cma_dev);
+ kfree(cma_dev);
+}
+
+static int cma_init(void)
+{
+ return ib_register_client(&cma_client);
+}
+
+static void cma_cleanup(void)
+{
+ ib_unregister_client(&cma_client);
+}
+
+module_init(cma_init);
+module_exit(cma_cleanup);
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/Makefile
linux-2.6.ib/drivers/infiniband/core/Makefile
--- linux-2.6.git/drivers/infiniband/core/Makefile 2006-01-16 16:16:18.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/Makefile 2006-01-16 16:35:48.000000000 -0800
@@ -1,5 +1,5 @@
obj-$(CONFIG_INFINIBAND) += ib_core.o ib_mad.o ib_sa.o \
- ib_cm.o ib_addr.o
+ ib_cm.o ib_addr.o rdma_cm.o
obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o
obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o
@@ -12,6 +12,8 @@ ib_sa-y := sa_query.o
ib_cm-y := cm.o
+rdma_cm-y := cma.o
+
ib_addr-y := addr.o
ib_umad-y := user_mad.o
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/include/rdma/rdma_cm.h
linux-2.6.ib/include/rdma/rdma_cm.h
--- linux-2.6.git/include/rdma/rdma_cm.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/include/rdma/rdma_cm.h 2006-01-16 16:19:12.000000000 -0800
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This Software is licensed under one of the following licenses:
+ *
+ * 1) under the terms of the "Common Public License 1.0" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/cpl.php.
+ *
+ * 2) under the terms of the "The BSD License" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/bsd-license.php.
+ *
+ * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
+ * copy of which is available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/gpl-license.php.
+ *
+ * Licensee has the right to choose one of the above licenses.
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice and one of the license notices.
+ *
+ * Redistributions in binary form must reproduce both the above copyright
+ * notice, one of the license notices in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ */
+
+#if !defined(RDMA_CM_H)
+#define RDMA_CM_H
+
+#include <linux/socket.h>
+#include <linux/in6.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_sa.h>
+
+/*
+ * Upon receiving a device removal event, users must destroy the associated
+ * RDMA identifier and release all resources allocated with the device.
+ */
+enum rdma_cm_event_type {
+ RDMA_CM_EVENT_ADDR_RESOLVED,
+ RDMA_CM_EVENT_ADDR_ERROR,
+ RDMA_CM_EVENT_ROUTE_RESOLVED,
+ RDMA_CM_EVENT_ROUTE_ERROR,
+ RDMA_CM_EVENT_CONNECT_REQUEST,
+ RDMA_CM_EVENT_CONNECT_RESPONSE,
+ RDMA_CM_EVENT_CONNECT_ERROR,
+ RDMA_CM_EVENT_UNREACHABLE,
+ RDMA_CM_EVENT_REJECTED,
+ RDMA_CM_EVENT_ESTABLISHED,
+ RDMA_CM_EVENT_DISCONNECTED,
+ RDMA_CM_EVENT_DEVICE_REMOVAL,
+};
+
+enum rdma_port_space {
+ RDMA_PS_SDP = 0x0001,
+ RDMA_PS_TCP = 0x0106,
+ RDMA_PS_UDP = 0x0111,
+ RDMA_PS_SCTP = 0x0183
+};
+
+struct rdma_addr {
+ struct sockaddr src_addr;
+ u8 src_pad[sizeof(struct sockaddr_in6) -
+ sizeof(struct sockaddr)];
+ struct sockaddr dst_addr;
+ u8 dst_pad[sizeof(struct sockaddr_in6) -
+ sizeof(struct sockaddr)];
+ struct rdma_dev_addr dev_addr;
+};
+
+struct rdma_route {
+ struct rdma_addr addr;
+ struct ib_sa_path_rec *path_rec;
+ int num_paths;
+};
+
+struct rdma_cm_event {
+ enum rdma_cm_event_type event;
+ int status;
+ void *private_data;
+ u8 private_data_len;
+};
+
+struct rdma_cm_id;
+
+/**
+ * rdma_cm_event_handler - Callback used to report user events.
+ *
+ * Notes: Users may not call rdma_destroy_id from this callback to destroy
+ * the passed in id, or a corresponding listen id. Returning a
+ * non-zero value from the callback will destroy the corresponding id.
+ */
+typedef int (*rdma_cm_event_handler)(struct rdma_cm_id *id,
+ struct rdma_cm_event *event);
+
+struct rdma_cm_id {
+ struct ib_device *device;
+ void *context;
+ struct ib_qp *qp;
+ rdma_cm_event_handler event_handler;
+ struct rdma_route route;
+ enum rdma_port_space ps;
+ u8 port_num;
+};
+
+/**
+ * rdma_create_id - Create an RDMA identifier.
+ *
+ * @event_handler: User callback invoked to report events associated with the
+ * returned rdma_id.
+ * @context: User specified context associated with the id.
+ * @ps: RDMA port space.
+ */
+struct rdma_cm_id* rdma_create_id(rdma_cm_event_handler event_handler,
+ void *context, enum rdma_port_space ps);
+
+void rdma_destroy_id(struct rdma_cm_id *id);
+
+/**
+ * rdma_bind_addr - Bind an RDMA identifier to a source address and
+ * associated RDMA device, if needed.
+ *
+ * @id: RDMA identifier.
+ * @addr: Local address information. Wildcard values are permitted.
+ *
+ * This associates a source address with the RDMA identifier before calling
+ * rdma_listen. If a specific local address is given, the RDMA identifier will
+ * be bound to a local RDMA device.
+ */
+int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr);
+
+/**
+ * rdma_resolve_addr - Resolve destination and optional source addresses
+ * from IP addresses to an RDMA address. If successful, the specified
+ * rdma_cm_id will be bound to a local device.
+ *
+ * @id: RDMA identifier.
+ * @src_addr: Source address information. This parameter may be NULL.
+ * @dst_addr: Destination address information.
+ * @timeout_ms: Time to wait for resolution to complete.
+ */
+int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
+ struct sockaddr *dst_addr, int timeout_ms);
+
+/**
+ * rdma_resolve_route - Resolve the RDMA address bound to the RDMA identifier
+ * into route information needed to establish a connection.
+ *
+ * This is called on the client side of a connection.
+ * Users must have first called rdma_resolve_addr to resolve a dst_addr
+ * into an RDMA address before calling this routine.
+ */
+int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms);
+
+/**
+ * rdma_create_qp - Allocate a QP and associate it with the specified RDMA
+ * identifier.
+ *
+ * QPs allocated to an rdma_cm_id will automatically be transitioned by the CMA
+ * through their states.
+ */
+int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd,
+ struct ib_qp_init_attr *qp_init_attr);
+
+/**
+ * rdma_destroy_qp - Deallocate the QP associated with the specified RDMA
+ * identifier.
+ *
+ * Users must destroy any QP associated with an RDMA identifier before
+ * destroying the RDMA ID.
+ */
+void rdma_destroy_qp(struct rdma_cm_id *id);
+
+/**
+ * rdma_init_qp_attr - Initializes the QP attributes for use in transitioning
+ * to a specified QP state.
+ * @id: Communication identifier associated with the QP attributes to
+ * initialize.
+ * @qp_attr: On input, specifies the desired QP state. On output, the
+ * mandatory and desired optional attributes will be set in order to
+ * modify the QP to the specified state.
+ * @qp_attr_mask: The QP attribute mask that may be used to transition the
+ * QP to the specified state.
+ *
+ * Users must set the @qp_attr->qp_state to the desired QP state. This call
+ * will set all required attributes for the given transition, along with
+ * known optional attributes. Users may override the attributes returned from
+ * this call before calling ib_modify_qp.
+ *
+ * Users that wish to have their QP automatically transitioned through its
+ * states can associate a QP with the rdma_cm_id by calling rdma_create_qp().
+ */
+int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
+ int *qp_attr_mask);
+
+struct rdma_conn_param {
+ const void *private_data;
+ u8 private_data_len;
+ u8 responder_resources;
+ u8 initiator_depth;
+ u8 flow_control;
+ u8 retry_count; /* ignored when accepting */
+ u8 rnr_retry_count;
+ /* Fields below ignored if a QP is created on the rdma_cm_id. */
+ u8 srq;
+ u32 qp_num;
+ enum ib_qp_type qp_type;
+};
+
+/**
+ * rdma_connect - Initiate an active connection request.
+ *
+ * Users must have resolved a route for the rdma_cm_id to connect with
+ * by having called rdma_resolve_route before calling this routine.
+ */
+int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param);
+
+/**
+ * rdma_listen - This function is called by the passive side to
+ * listen for incoming connection requests.
+ *
+ * Users must have bound the rdma_cm_id to a local address by calling
+ * rdma_bind_addr before calling this routine.
+ */
+int rdma_listen(struct rdma_cm_id *id, int backlog);
+
+/**
+ * rdma_accept - Called to accept a connection request or response.
+ * @id: Connection identifier associated with the request.
+ * @conn_param: Information needed to establish the connection. This must be
+ * provided if accepting a connection request. If accepting a connection
+ * response, this parameter must be NULL.
+ *
+ * Typically, this routine is only called by the listener to accept a connection
+ * request. It must also be called on the active side of a connection if the
+ * user is performing their own QP transitions.
+ */
+int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param);
+
+/**
+ * rdma_reject - Called on the passive side to reject a connection request.
+ */
+int rdma_reject(struct rdma_cm_id *id, const void *private_data,
+ u8 private_data_len);
+
+/**
+ * rdma_disconnect - This function disconnects the associated QP.
+ */
+int rdma_disconnect(struct rdma_cm_id *id);
+
+#endif /* RDMA_CM_H */
+
^ permalink raw reply
* [PATCH 5/5] Infiniband: connection abstraction
From: Sean Hefty @ 2006-02-01 20:19 UTC (permalink / raw)
To: netdev, linux-kernel; +Cc: openib-general
In-Reply-To: <ORSMSX401KZmcK8r6be00000097@orsmsx401.amr.corp.intel.com>
This patch adds the kernel component to support the userspace Infiniband/RDMA
connection agent library.
Signed-off-by: Sean Hefty <sean.hefty@intel.com>
---
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/Makefile
linux-2.6.ib/drivers/infiniband/core/Makefile
--- linux-2.6.git/drivers/infiniband/core/Makefile 2006-01-16 16:58:58.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/Makefile 2006-01-16 16:55:25.000000000 -0800
@@ -1,5 +1,5 @@
obj-$(CONFIG_INFINIBAND) += ib_core.o ib_mad.o ib_sa.o \
- ib_cm.o ib_addr.o rdma_cm.o
+ ib_cm.o ib_addr.o rdma_cm.o rdma_ucm.o
obj-$(CONFIG_INFINIBAND_USER_MAD) += ib_umad.o
obj-$(CONFIG_INFINIBAND_USER_ACCESS) += ib_uverbs.o ib_ucm.o
@@ -14,6 +14,8 @@ ib_cm-y := cm.o
rdma_cm-y := cma.o
+rdma_ucm-y := ucma.o
+
ib_addr-y := addr.o
ib_umad-y := user_mad.o
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/drivers/infiniband/core/ucma.c
linux-2.6.ib/drivers/infiniband/core/ucma.c
--- linux-2.6.git/drivers/infiniband/core/ucma.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/drivers/infiniband/core/ucma.c 2006-01-16 16:54:31.000000000 -0800
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/poll.h>
+#include <linux/idr.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/miscdevice.h>
+
+#include <rdma/rdma_user_cm.h>
+#include <rdma/ib_marshall.h>
+#include <rdma/rdma_cm.h>
+
+MODULE_AUTHOR("Sean Hefty");
+MODULE_DESCRIPTION("RDMA Userspace Connection Manager Access");
+MODULE_LICENSE("Dual BSD/GPL");
+
+enum {
+ UCMA_MAX_BACKLOG = 128
+};
+
+struct ucma_file {
+ struct mutex file_mutex;
+ struct file *filp;
+ struct list_head ctxs;
+ struct list_head events;
+ wait_queue_head_t poll_wait;
+};
+
+struct ucma_context {
+ int id;
+ wait_queue_head_t wait;
+ atomic_t ref;
+ int events_reported;
+ int backlog;
+
+ struct ucma_file *file;
+ struct rdma_cm_id *cm_id;
+ __u64 uid;
+
+ struct list_head events; /* list of pending events. */
+ struct list_head file_list; /* member in file ctx list */
+};
+
+struct ucma_event {
+ struct ucma_context *ctx;
+ struct list_head file_list; /* member in file event list */
+ struct list_head ctx_list; /* member in ctx event list */
+ struct rdma_cm_id *cm_id;
+ struct rdma_ucm_event_resp resp;
+};
+
+static DEFINE_MUTEX(ctx_mutex);
+static DEFINE_IDR(ctx_idr);
+
+static struct ucma_context* ucma_get_ctx(struct ucma_file *file, int id)
+{
+ struct ucma_context *ctx;
+
+ mutex_lock(&ctx_mutex);
+ ctx = idr_find(&ctx_idr, id);
+ if (!ctx)
+ ctx = ERR_PTR(-ENOENT);
+ else if (ctx->file != file)
+ ctx = ERR_PTR(-EINVAL);
+ else
+ atomic_inc(&ctx->ref);
+ mutex_unlock(&ctx_mutex);
+
+ return ctx;
+}
+
+static void ucma_put_ctx(struct ucma_context *ctx)
+{
+ if (atomic_dec_and_test(&ctx->ref))
+ wake_up(&ctx->wait);
+}
+
+static void ucma_cleanup_events(struct ucma_context *ctx)
+{
+ struct ucma_event *uevent;
+
+ mutex_lock(&ctx->file->file_mutex);
+ list_del(&ctx->file_list);
+ while (!list_empty(&ctx->events)) {
+
+ uevent = list_entry(ctx->events.next, struct ucma_event,
+ ctx_list);
+ list_del(&uevent->file_list);
+ list_del(&uevent->ctx_list);
+
+ /* clear incoming connections. */
+ if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST)
+ rdma_destroy_id(uevent->cm_id);
+
+ kfree(uevent);
+ }
+ mutex_unlock(&ctx->file->file_mutex);
+}
+
+static struct ucma_context* ucma_alloc_ctx(struct ucma_file *file)
+{
+ struct ucma_context *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ atomic_set(&ctx->ref, 1);
+ init_waitqueue_head(&ctx->wait);
+ ctx->file = file;
+ INIT_LIST_HEAD(&ctx->events);
+
+ do {
+ ret = idr_pre_get(&ctx_idr, GFP_KERNEL);
+ if (!ret)
+ goto error;
+
+ mutex_lock(&ctx_mutex);
+ ret = idr_get_new(&ctx_idr, ctx, &ctx->id);
+ mutex_unlock(&ctx_mutex);
+ } while (ret == -EAGAIN);
+
+ if (ret)
+ goto error;
+
+ list_add_tail(&ctx->file_list, &file->ctxs);
+ return ctx;
+
+error:
+ kfree(ctx);
+ return NULL;
+}
+
+static int ucma_event_handler(struct rdma_cm_id *cm_id,
+ struct rdma_cm_event *event)
+{
+ struct ucma_event *uevent;
+ struct ucma_context *ctx = cm_id->context;
+ int ret = 0;
+
+ uevent = kzalloc(sizeof(*uevent), GFP_KERNEL);
+ if (!uevent)
+ return event->event == RDMA_CM_EVENT_CONNECT_REQUEST;
+
+ uevent->ctx = ctx;
+ uevent->cm_id = cm_id;
+ uevent->resp.uid = ctx->uid;
+ uevent->resp.id = ctx->id;
+ uevent->resp.event = event->event;
+ uevent->resp.status = event->status;
+ if ((uevent->resp.private_data_len = event->private_data_len))
+ memcpy(uevent->resp.private_data, event->private_data,
+ event->private_data_len);
+
+ mutex_lock(&ctx->file->file_mutex);
+ if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
+ if (!ctx->backlog) {
+ ret = -EDQUOT;
+ goto out;
+ }
+ ctx->backlog--;
+ }
+ list_add_tail(&uevent->file_list, &ctx->file->events);
+ list_add_tail(&uevent->ctx_list, &ctx->events);
+ wake_up_interruptible(&ctx->file->poll_wait);
+out:
+ mutex_unlock(&ctx->file->file_mutex);
+ return ret;
+}
+
+static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct ucma_context *ctx;
+ struct rdma_ucm_get_event cmd;
+ struct ucma_event *uevent;
+ int ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (out_len < sizeof(struct rdma_ucm_event_resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&file->file_mutex);
+ while (list_empty(&file->events)) {
+ if (file->filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
+ mutex_unlock(&file->file_mutex);
+ schedule();
+ mutex_lock(&file->file_mutex);
+ finish_wait(&file->poll_wait, &wait);
+ }
+
+ if (ret)
+ goto done;
+
+ uevent = list_entry(file->events.next, struct ucma_event, file_list);
+
+ if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) {
+ ctx = ucma_alloc_ctx(file);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ uevent->ctx->backlog++;
+ ctx->cm_id = uevent->cm_id;
+ ctx->cm_id->context = ctx;
+ uevent->resp.id = ctx->id;
+ }
+
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &uevent->resp, sizeof(uevent->resp))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ list_del(&uevent->file_list);
+ list_del(&uevent->ctx_list);
+ uevent->ctx->events_reported++;
+ kfree(uevent);
+done:
+ mutex_unlock(&file->file_mutex);
+ return ret;
+}
+
+static ssize_t ucma_create_id(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_create_id cmd;
+ struct rdma_ucm_create_id_resp resp;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&file->file_mutex);
+ ctx = ucma_alloc_ctx(file);
+ mutex_unlock(&file->file_mutex);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->uid = cmd.uid;
+ ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, RDMA_PS_TCP);
+ if (IS_ERR(ctx->cm_id)) {
+ ret = PTR_ERR(ctx->cm_id);
+ goto err1;
+ }
+
+ resp.id = ctx->id;
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err2;
+ }
+ return 0;
+
+err2:
+ rdma_destroy_id(ctx->cm_id);
+err1:
+ mutex_lock(&ctx_mutex);
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&ctx_mutex);
+ kfree(ctx);
+ return ret;
+}
+
+static ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_destroy_id cmd;
+ struct rdma_ucm_destroy_id_resp resp;
+ struct ucma_context *ctx;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ mutex_lock(&ctx_mutex);
+ ctx = idr_find(&ctx_idr, cmd.id);
+ if (!ctx)
+ ctx = ERR_PTR(-ENOENT);
+ else if (ctx->file != file)
+ ctx = ERR_PTR(-EINVAL);
+ else
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&ctx_mutex);
+
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ atomic_dec(&ctx->ref);
+ wait_event(ctx->wait, !atomic_read(&ctx->ref));
+
+ /* No new events will be generated after destroying the id. */
+ rdma_destroy_id(ctx->cm_id);
+ /* Cleanup events not yet reported to the user. */
+ ucma_cleanup_events(ctx);
+
+ resp.events_reported = ctx->events_reported;
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ kfree(ctx);
+ return ret;
+}
+
+static ssize_t ucma_bind_addr(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_bind_addr cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_resolve_addr(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_addr cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr,
+ (struct sockaddr *) &cmd.dst_addr,
+ cmd.timeout_ms);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_resolve_route(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_route cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_resolve_route(ctx->cm_id, cmd.timeout_ms);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp,
+ struct rdma_route *route)
+{
+ struct rdma_dev_addr *dev_addr;
+
+ resp->num_paths = route->num_paths;
+ switch (route->num_paths) {
+ case 0:
+ dev_addr = &route->addr.dev_addr;
+ memcpy(&resp->ib_route[0].dgid, ib_addr_get_dgid(dev_addr),
+ sizeof(union ib_gid));
+ memcpy(&resp->ib_route[0].sgid, ib_addr_get_sgid(dev_addr),
+ sizeof(union ib_gid));
+ resp->ib_route[0].pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr));
+ break;
+ case 2:
+ ib_copy_path_rec_to_user(&resp->ib_route[1],
+ &route->path_rec[1]);
+ /* fall through */
+ case 1:
+ ib_copy_path_rec_to_user(&resp->ib_route[0],
+ &route->path_rec[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+static ssize_t ucma_query_route(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_query_route cmd;
+ struct rdma_ucm_query_route_resp resp;
+ struct ucma_context *ctx;
+ struct sockaddr *addr;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ if (!ctx->cm_id->device) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ addr = &ctx->cm_id->route.addr.src_addr;
+ memcpy(&resp.src_addr, addr, addr->sa_family == AF_INET ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ addr = &ctx->cm_id->route.addr.dst_addr;
+ memcpy(&resp.dst_addr, addr, addr->sa_family == AF_INET ?
+ sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6));
+ resp.node_guid = ctx->cm_id->device->node_guid;
+ resp.port_num = ctx->cm_id->port_num;
+ switch (ctx->cm_id->device->node_type) {
+ case IB_NODE_CA:
+ ucma_copy_ib_route(&resp, &ctx->cm_id->route);
+ default:
+ break;
+ }
+
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+out:
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static void ucma_copy_conn_param(struct rdma_conn_param *dst_conn,
+ struct rdma_ucm_conn_param *src_conn)
+{
+ dst_conn->private_data = src_conn->private_data;
+ dst_conn->private_data_len = src_conn->private_data_len;
+ dst_conn->responder_resources =src_conn->responder_resources;
+ dst_conn->initiator_depth = src_conn->initiator_depth;
+ dst_conn->flow_control = src_conn->flow_control;
+ dst_conn->retry_count = src_conn->retry_count;
+ dst_conn->rnr_retry_count = src_conn->rnr_retry_count;
+ dst_conn->srq = src_conn->srq;
+ dst_conn->qp_num = src_conn->qp_num;
+ dst_conn->qp_type = src_conn->qp_type;
+}
+
+static ssize_t ucma_connect(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_connect cmd;
+ struct rdma_conn_param conn_param;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ if (!cmd.conn_param.valid)
+ return -EINVAL;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ret = rdma_connect(ctx->cm_id, &conn_param);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_listen cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->backlog = cmd.backlog > 0 && cmd.backlog < UCMA_MAX_BACKLOG ?
+ cmd.backlog : UCMA_MAX_BACKLOG;
+ ret = rdma_listen(ctx->cm_id, ctx->backlog);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_accept cmd;
+ struct rdma_conn_param conn_param;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ if (cmd.conn_param.valid) {
+ ctx->uid = cmd.uid;
+ ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ret = rdma_accept(ctx->cm_id, &conn_param);
+ } else
+ ret = rdma_accept(ctx->cm_id, NULL);
+
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_reject(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_reject cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_reject(ctx->cm_id, cmd.private_data, cmd.private_data_len);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_disconnect(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_disconnect cmd;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_disconnect(ctx->cm_id);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_init_qp_attr(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_init_qp_attr cmd;
+ struct ib_uverbs_qp_attr resp;
+ struct ucma_context *ctx;
+ struct ib_qp_attr qp_attr;
+ int ret;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ resp.qp_attr_mask = 0;
+ memset(&qp_attr, 0, sizeof qp_attr);
+ qp_attr.qp_state = cmd.qp_state;
+ ret = rdma_init_qp_attr(ctx->cm_id, &qp_attr, &resp.qp_attr_mask);
+ if (ret)
+ goto out;
+
+ ib_copy_qp_attr_to_user(&resp, &qp_attr);
+ if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+out:
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t (*ucma_cmd_table[])(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len) = {
+ [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id,
+ [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id,
+ [RDMA_USER_CM_CMD_BIND_ADDR] = ucma_bind_addr,
+ [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr,
+ [RDMA_USER_CM_CMD_RESOLVE_ROUTE]= ucma_resolve_route,
+ [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route,
+ [RDMA_USER_CM_CMD_CONNECT] = ucma_connect,
+ [RDMA_USER_CM_CMD_LISTEN] = ucma_listen,
+ [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept,
+ [RDMA_USER_CM_CMD_REJECT] = ucma_reject,
+ [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect,
+ [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr,
+ [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event
+};
+
+static ssize_t ucma_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *pos)
+{
+ struct ucma_file *file = filp->private_data;
+ struct rdma_ucm_cmd_hdr hdr;
+ ssize_t ret;
+
+ if (len < sizeof(hdr))
+ return -EINVAL;
+
+ if (copy_from_user(&hdr, buf, sizeof(hdr)))
+ return -EFAULT;
+
+ if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucma_cmd_table))
+ return -EINVAL;
+
+ if (hdr.in + sizeof(hdr) > len)
+ return -EINVAL;
+
+ ret = ucma_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out);
+ if (!ret)
+ ret = len;
+
+ return ret;
+}
+
+static unsigned int ucma_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct ucma_file *file = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &file->poll_wait, wait);
+
+ mutex_lock(&file->file_mutex);
+ if (!list_empty(&file->events))
+ mask = POLLIN | POLLRDNORM;
+ mutex_unlock(&file->file_mutex);
+
+ return mask;
+}
+
+static int ucma_open(struct inode *inode, struct file *filp)
+{
+ struct ucma_file *file;
+
+ file = kmalloc(sizeof *file, GFP_KERNEL);
+ if (!file)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&file->events);
+ INIT_LIST_HEAD(&file->ctxs);
+ init_waitqueue_head(&file->poll_wait);
+ mutex_init(&file->file_mutex);
+
+ filp->private_data = file;
+ file->filp = filp;
+ return 0;
+}
+
+static int ucma_close(struct inode *inode, struct file *filp)
+{
+ struct ucma_file *file = filp->private_data;
+ struct ucma_context *ctx;
+
+ mutex_lock(&file->file_mutex);
+ while (!list_empty(&file->ctxs)) {
+ ctx = list_entry(file->ctxs.next, struct ucma_context,
+ file_list);
+ mutex_unlock(&file->file_mutex);
+
+ mutex_lock(&ctx_mutex);
+ idr_remove(&ctx_idr, ctx->id);
+ mutex_unlock(&ctx_mutex);
+
+ rdma_destroy_id(ctx->cm_id);
+ ucma_cleanup_events(ctx);
+ kfree(ctx);
+
+ mutex_lock(&file->file_mutex);
+ }
+ mutex_unlock(&file->file_mutex);
+ kfree(file);
+ return 0;
+}
+
+static struct file_operations ucma_fops = {
+ .owner = THIS_MODULE,
+ .open = ucma_open,
+ .release = ucma_close,
+ .write = ucma_write,
+ .poll = ucma_poll,
+};
+
+static struct miscdevice ucma_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "rdma_cm",
+ .fops = &ucma_fops,
+};
+
+static int __init ucma_init(void)
+{
+ return misc_register(&ucma_misc);
+}
+
+static void __exit ucma_cleanup(void)
+{
+ misc_deregister(&ucma_misc);
+ idr_destroy(&ctx_idr);
+}
+
+module_init(ucma_init);
+module_exit(ucma_cleanup);
diff -uprN -X linux-2.6.git/Documentation/dontdiff
linux-2.6.git/include/rdma/rdma_user_cm.h
linux-2.6.ib/include/rdma/rdma_user_cm.h
--- linux-2.6.git/include/rdma/rdma_user_cm.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.ib/include/rdma/rdma_user_cm.h 2006-01-16 16:54:55.000000000 -0800
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef RDMA_USER_CM_H
+#define RDMA_USER_CM_H
+
+#include <linux/types.h>
+#include <linux/in6.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/ib_user_sa.h>
+
+#define RDMA_USER_CM_ABI_VERSION 1
+
+#define RDMA_MAX_PRIVATE_DATA 256
+
+enum {
+ RDMA_USER_CM_CMD_CREATE_ID,
+ RDMA_USER_CM_CMD_DESTROY_ID,
+ RDMA_USER_CM_CMD_BIND_ADDR,
+ RDMA_USER_CM_CMD_RESOLVE_ADDR,
+ RDMA_USER_CM_CMD_RESOLVE_ROUTE,
+ RDMA_USER_CM_CMD_QUERY_ROUTE,
+ RDMA_USER_CM_CMD_CONNECT,
+ RDMA_USER_CM_CMD_LISTEN,
+ RDMA_USER_CM_CMD_ACCEPT,
+ RDMA_USER_CM_CMD_REJECT,
+ RDMA_USER_CM_CMD_DISCONNECT,
+ RDMA_USER_CM_CMD_INIT_QP_ATTR,
+ RDMA_USER_CM_CMD_GET_EVENT
+};
+
+/*
+ * command ABI structures.
+ */
+struct rdma_ucm_cmd_hdr {
+ __u32 cmd;
+ __u16 in;
+ __u16 out;
+};
+
+struct rdma_ucm_create_id {
+ __u64 uid;
+ __u64 response;
+};
+
+struct rdma_ucm_create_id_resp {
+ __u32 id;
+};
+
+struct rdma_ucm_destroy_id {
+ __u64 response;
+ __u32 id;
+ __u32 reserved;
+};
+
+struct rdma_ucm_destroy_id_resp {
+ __u32 events_reported;
+};
+
+struct rdma_ucm_bind_addr {
+ __u64 response;
+ struct sockaddr_in6 addr;
+ __u32 id;
+};
+
+struct rdma_ucm_resolve_addr {
+ struct sockaddr_in6 src_addr;
+ struct sockaddr_in6 dst_addr;
+ __u32 id;
+ __u32 timeout_ms;
+};
+
+struct rdma_ucm_resolve_route {
+ __u32 id;
+ __u32 timeout_ms;
+};
+
+struct rdma_ucm_query_route {
+ __u64 response;
+ __u32 id;
+ __u32 reserved;
+};
+
+struct rdma_ucm_query_route_resp {
+ __u64 node_guid;
+ struct ib_user_path_rec ib_route[2];
+ struct sockaddr_in6 src_addr;
+ struct sockaddr_in6 dst_addr;
+ __u32 num_paths;
+ __u8 port_num;
+ __u8 reserved[3];
+};
+
+struct rdma_ucm_conn_param {
+ __u32 qp_num;
+ __u32 qp_type;
+ __u8 private_data[RDMA_MAX_PRIVATE_DATA];
+ __u8 private_data_len;
+ __u8 srq;
+ __u8 responder_resources;
+ __u8 initiator_depth;
+ __u8 flow_control;
+ __u8 retry_count;
+ __u8 rnr_retry_count;
+ __u8 valid;
+};
+
+struct rdma_ucm_connect {
+ struct rdma_ucm_conn_param conn_param;
+ __u32 id;
+ __u32 reserved;
+};
+
+struct rdma_ucm_listen {
+ __u32 id;
+ __u32 backlog;
+};
+
+struct rdma_ucm_accept {
+ __u64 uid;
+ struct rdma_ucm_conn_param conn_param;
+ __u32 id;
+ __u32 reserved;
+};
+
+struct rdma_ucm_reject {
+ __u32 id;
+ __u8 private_data_len;
+ __u8 reserved[3];
+ __u8 private_data[RDMA_MAX_PRIVATE_DATA];
+};
+
+struct rdma_ucm_disconnect {
+ __u32 id;
+};
+
+struct rdma_ucm_init_qp_attr {
+ __u64 response;
+ __u32 id;
+ __u32 qp_state;
+};
+
+struct rdma_ucm_get_event {
+ __u64 response;
+};
+
+struct rdma_ucm_event_resp {
+ __u64 uid;
+ __u32 id;
+ __u32 event;
+ __u32 status;
+ __u8 private_data_len;
+ __u8 reserved[3];
+ __u8 private_data[RDMA_MAX_PRIVATE_DATA];
+};
+
+#endif /* RDMA_USER_CM_H */
^ permalink raw reply
* [PATCH] gianfar: Fix sparse warnings
From: Kumar Gala @ 2006-02-01 21:18 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linux-kernel, netdev
Fixed sparse warnings mainly due to lack of __iomem.
---
commit 682307be628635abd497a77bf26aa7ba6b5593e9
tree ac3156a394371416519566a8e4aff345575e8f33
parent 690e2cb75eb8fc41413a9e9f85919b275b4164a9
author Kumar Gala <galak@kernel.crashing.org> Wed, 01 Feb 2006 15:17:45 -0600
committer Kumar Gala <galak@kernel.crashing.org> Wed, 01 Feb 2006 15:17:45 -0600
drivers/net/gianfar.c | 24 +++++++++++-------------
drivers/net/gianfar.h | 8 ++++----
drivers/net/gianfar_ethtool.c | 8 ++++----
drivers/net/gianfar_mii.c | 17 ++++++++---------
4 files changed, 27 insertions(+), 30 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 0c18dbd..0e8e3fc 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -199,8 +199,7 @@ static int gfar_probe(struct platform_de
/* get a pointer to the register memory */
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = (struct gfar *)
- ioremap(r->start, sizeof (struct gfar));
+ priv->regs = ioremap(r->start, sizeof (struct gfar));
if (NULL == priv->regs) {
err = -ENOMEM;
@@ -369,7 +368,7 @@ static int gfar_probe(struct platform_de
return 0;
register_fail:
- iounmap((void *) priv->regs);
+ iounmap(priv->regs);
regs_fail:
free_netdev(dev);
return err;
@@ -382,7 +381,7 @@ static int gfar_remove(struct platform_d
platform_set_drvdata(pdev, NULL);
- iounmap((void *) priv->regs);
+ iounmap(priv->regs);
free_netdev(dev);
return 0;
@@ -454,8 +453,7 @@ static void init_registers(struct net_de
/* Zero out the rmon mib registers if it has them */
if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- memset((void *) &(priv->regs->rmon), 0,
- sizeof (struct rmon_mib));
+ memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib));
/* Mask off the CAM interrupts */
gfar_write(&priv->regs->rmon.cam1, 0xffffffff);
@@ -477,7 +475,7 @@ static void init_registers(struct net_de
void gfar_halt(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
u32 tempval;
/* Mask all interrupts */
@@ -507,7 +505,7 @@ void gfar_halt(struct net_device *dev)
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
unsigned long flags;
phy_stop(priv->phydev);
@@ -590,7 +588,7 @@ static void free_skb_resources(struct gf
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
u32 tempval;
/* Enable Rx and Tx in MACCFG1 */
@@ -624,7 +622,7 @@ int startup_gfar(struct net_device *dev)
unsigned long vaddr;
int i;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
int err = 0;
u32 rctrl = 0;
u32 attrs = 0;
@@ -1622,7 +1620,7 @@ static irqreturn_t gfar_interrupt(int ir
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
@@ -1703,7 +1701,7 @@ static void gfar_set_multi(struct net_de
{
struct dev_mc_list *mc_ptr;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar *regs = priv->regs;
+ struct gfar __iomem *regs = priv->regs;
u32 tempval;
if(dev->flags & IFF_PROMISC) {
@@ -1842,7 +1840,7 @@ static void gfar_set_mac_for_addr(struct
int idx;
char tmpbuf[MAC_ADDR_LEN];
u32 tempval;
- u32 *macptr = &priv->regs->macstnaddr1;
+ u32 __iomem *macptr = &priv->regs->macstnaddr1;
macptr += num*2;
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index cb9d66a..d37d540 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -682,8 +682,8 @@ struct gfar_private {
struct rxbd8 *cur_rx; /* Next free rx ring entry */
struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
- struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
- u32 *hash_regs[16];
+ struct gfar __iomem *regs; /* Pointer to the GFAR memory mapped Registers */
+ u32 __iomem *hash_regs[16];
int hash_width;
struct net_device_stats stats; /* linux network statistics */
struct gfar_extra_stats extra_stats;
@@ -718,14 +718,14 @@ struct gfar_private {
uint32_t msg_enable;
};
-static inline u32 gfar_read(volatile unsigned *addr)
+static inline u32 gfar_read(volatile unsigned __iomem *addr)
{
u32 val;
val = in_be32(addr);
return val;
}
-static inline void gfar_write(volatile unsigned *addr, u32 val)
+static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
{
out_be32(addr, val);
}
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 765e810..5de7b2e 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -144,11 +144,11 @@ static void gfar_fill_stats(struct net_d
u64 *extra = (u64 *) & priv->extra_stats;
if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- u32 *rmon = (u32 *) & priv->regs->rmon;
+ u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon;
struct gfar_stats *stats = (struct gfar_stats *) buf;
for (i = 0; i < GFAR_RMON_LEN; i++)
- stats->rmon[i] = (u64) (rmon[i]);
+ stats->rmon[i] = (u64) gfar_read(&rmon[i]);
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
stats->extra[i] = extra[i];
@@ -221,11 +221,11 @@ static void gfar_get_regs(struct net_dev
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- u32 *theregs = (u32 *) priv->regs;
+ u32 __iomem *theregs = (u32 __iomem *) priv->regs;
u32 *buf = (u32 *) regbuf;
for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
- buf[i] = theregs[i];
+ buf[i] = gfar_read(&theregs[i]);
}
/* Convert microseconds to ethernet clock ticks, which changes
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c
index 74e52fc..c6b7255 100644
--- a/drivers/net/gianfar_mii.c
+++ b/drivers/net/gianfar_mii.c
@@ -50,7 +50,7 @@
* All PHY configuration is done through the TSEC1 MIIM regs */
int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
{
- struct gfar_mii *regs = bus->priv;
+ struct gfar_mii __iomem *regs = (void __iomem *)bus->priv;
/* Set the PHY address and the register address we want to write */
gfar_write(®s->miimadd, (mii_id << 8) | regnum);
@@ -70,7 +70,7 @@ int gfar_mdio_write(struct mii_bus *bus,
* configuration has to be done through the TSEC1 MIIM regs */
int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
- struct gfar_mii *regs = bus->priv;
+ struct gfar_mii __iomem *regs = (void __iomem *)bus->priv;
u16 value;
/* Set the PHY address and the register address we want to read */
@@ -94,7 +94,7 @@ int gfar_mdio_read(struct mii_bus *bus,
/* Reset the MIIM registers, and wait for the bus to free */
int gfar_mdio_reset(struct mii_bus *bus)
{
- struct gfar_mii *regs = bus->priv;
+ struct gfar_mii __iomem *regs = (void __iomem *)bus->priv;
unsigned int timeout = PHY_INIT_TIMEOUT;
spin_lock_bh(&bus->mdio_lock);
@@ -126,7 +126,7 @@ int gfar_mdio_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gianfar_mdio_data *pdata;
- struct gfar_mii *regs;
+ struct gfar_mii __iomem *regs;
struct mii_bus *new_bus;
struct resource *r;
int err = 0;
@@ -155,15 +155,14 @@ int gfar_mdio_probe(struct device *dev)
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Set the PHY base address */
- regs = (struct gfar_mii *) ioremap(r->start,
- sizeof (struct gfar_mii));
+ regs = ioremap(r->start, sizeof (struct gfar_mii));
if (NULL == regs) {
err = -ENOMEM;
goto reg_map_fail;
}
- new_bus->priv = regs;
+ new_bus->priv = (void __force *)regs;
new_bus->irq = pdata->irq;
@@ -181,7 +180,7 @@ int gfar_mdio_probe(struct device *dev)
return 0;
bus_register_fail:
- iounmap((void *) regs);
+ iounmap(regs);
reg_map_fail:
kfree(new_bus);
@@ -197,7 +196,7 @@ int gfar_mdio_remove(struct device *dev)
dev_set_drvdata(dev, NULL);
- iounmap((void *) (&bus->priv));
+ iounmap((void __iomem *)bus->priv);
bus->priv = NULL;
kfree(bus);
^ permalink raw reply related
* [patch 1/2] natsemi: NAPI and a bugfix
From: Mark Brown @ 2006-02-02 0:00 UTC (permalink / raw)
To: Jeff Garzik, Tim Hockin; +Cc: netdev, linux-kernel
In-Reply-To: <20060202233909.426660000@lorien.sirena.org.uk>
[-- Attachment #1: natsemi-napi --]
[-- Type: text/plain, Size: 10122 bytes --]
This patch converts the natsemi driver to use NAPI. It was originally
based on one written by Harald Welte, though it has since been modified
quite a bit, most extensively in order to remove the ability to disable
NAPI since none of the other drivers seem to provide that functionality
any more.
Signed-off-by: Mark Brown <broonie@sirena.org.uk>
Index: linux-2.6.15.2/drivers/net/natsemi.c
===================================================================
--- linux-2.6.15.2.orig/drivers/net/natsemi.c 2006-01-31 06:25:07.000000000 +0000
+++ linux-2.6.15.2/drivers/net/natsemi.c 2006-02-01 22:59:29.000000000 +0000
@@ -3,6 +3,7 @@
Written/copyright 1999-2001 by Donald Becker.
Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com)
Portions copyright 2001,2002 Manfred Spraul (manfred@colorfullife.com)
+ Portions copyright 2004 Harald Welte <laforge@gnumonks.org>
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
@@ -135,8 +136,6 @@
TODO:
* big endian support with CFG:BEM instead of cpu_to_le32
- * support for an external PHY
- * NAPI
*/
#include <linux/config.h>
@@ -160,6 +159,7 @@
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/bitops.h>
+#include <linux/prefetch.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/io.h>
#include <asm/irq.h>
@@ -183,8 +183,6 @@
NETIF_MSG_TX_ERR)
static int debug = -1;
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
static int mtu;
/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
@@ -251,14 +249,11 @@ MODULE_AUTHOR("Donald Becker <becker@scy
MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
MODULE_LICENSE("GPL");
-module_param(max_interrupt_work, int, 0);
module_param(mtu, int, 0);
module_param(debug, int, 0);
module_param(rx_copybreak, int, 0);
module_param_array(options, int, NULL, 0);
module_param_array(full_duplex, int, NULL, 0);
-MODULE_PARM_DESC(max_interrupt_work,
- "DP8381x maximum events handled per interrupt");
MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
MODULE_PARM_DESC(debug, "DP8381x default debug level");
MODULE_PARM_DESC(rx_copybreak,
@@ -691,6 +686,8 @@ struct netdev_private {
/* Based on MTU+slack. */
unsigned int rx_buf_sz;
int oom;
+ /* Interrupt status */
+ u32 intr_status;
/* Do not touch the nic registers */
int hands_off;
/* external phy that is used: only valid if dev->if_port != PORT_TP */
@@ -748,7 +745,8 @@ static void init_registers(struct net_de
static int start_tx(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
static void netdev_error(struct net_device *dev, int intr_status);
-static void netdev_rx(struct net_device *dev);
+static int natsemi_poll(struct net_device *dev, int *budget);
+static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);
static void netdev_tx_done(struct net_device *dev);
static int natsemi_change_mtu(struct net_device *dev, int new_mtu);
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -776,6 +774,18 @@ static inline void __iomem *ns_ioaddr(st
return (void __iomem *) dev->base_addr;
}
+static inline void natsemi_irq_enable(struct net_device *dev)
+{
+ writel(1, ns_ioaddr(dev) + IntrEnable);
+ readl(ns_ioaddr(dev) + IntrEnable);
+}
+
+static inline void natsemi_irq_disable(struct net_device *dev)
+{
+ writel(0, ns_ioaddr(dev) + IntrEnable);
+ readl(ns_ioaddr(dev) + IntrEnable);
+}
+
static void move_int_phy(struct net_device *dev, int addr)
{
struct netdev_private *np = netdev_priv(dev);
@@ -879,6 +889,7 @@ static int __devinit natsemi_probe1 (str
spin_lock_init(&np->lock);
np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
np->hands_off = 0;
+ np->intr_status = 0;
/* Initial port:
* - If the nic was configured to use an external phy and if find_mii
@@ -932,6 +943,9 @@ static int __devinit natsemi_probe1 (str
dev->do_ioctl = &netdev_ioctl;
dev->tx_timeout = &tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
+ dev->poll = natsemi_poll;
+ dev->weight = 64;
+
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = &natsemi_poll_controller;
#endif
@@ -2158,68 +2172,92 @@ static void netdev_tx_done(struct net_de
}
}
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
+/* The interrupt handler doesn't actually handle interrupts itself, it
+ * schedules a NAPI poll if there is anything to do. */
static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
struct net_device *dev = dev_instance;
struct netdev_private *np = netdev_priv(dev);
void __iomem * ioaddr = ns_ioaddr(dev);
- int boguscnt = max_interrupt_work;
- unsigned int handled = 0;
if (np->hands_off)
return IRQ_NONE;
- do {
- /* Reading automatically acknowledges all int sources. */
- u32 intr_status = readl(ioaddr + IntrStatus);
+
+ /* Reading automatically acknowledges. */
+ np->intr_status = readl(ioaddr + IntrStatus);
- if (netif_msg_intr(np))
- printk(KERN_DEBUG
- "%s: Interrupt, status %#08x, mask %#08x.\n",
- dev->name, intr_status,
- readl(ioaddr + IntrMask));
+ if (netif_msg_intr(np))
+ printk(KERN_DEBUG
+ "%s: Interrupt, status %#08x, mask %#08x.\n",
+ dev->name, np->intr_status,
+ readl(ioaddr + IntrMask));
- if (intr_status == 0)
- break;
- handled = 1;
+ if (!np->intr_status)
+ return IRQ_NONE;
- if (intr_status &
- (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
- IntrRxErr | IntrRxOverrun)) {
- netdev_rx(dev);
- }
+ prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
+
+ if (netif_rx_schedule_prep(dev)) {
+ /* Disable interrupts and register for poll */
+ natsemi_irq_disable(dev);
+ __netif_rx_schedule(dev);
+ }
+ return IRQ_HANDLED;
+}
- if (intr_status &
- (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
+/* This is the NAPI poll routine. As well as the standard RX handling
+ * it also handles all other interrupts that the chip might raise.
+ */
+static int natsemi_poll(struct net_device *dev, int *budget)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem * ioaddr = ns_ioaddr(dev);
+
+ int work_to_do = min(*budget, dev->quota);
+ int work_done = 0;
+
+ do {
+ if (np->intr_status &
+ (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
spin_lock(&np->lock);
netdev_tx_done(dev);
spin_unlock(&np->lock);
}
/* Abnormal error summary/uncommon events handlers. */
- if (intr_status & IntrAbnormalSummary)
- netdev_error(dev, intr_status);
-
- if (--boguscnt < 0) {
- if (netif_msg_intr(np))
- printk(KERN_WARNING
- "%s: Too much work at interrupt, "
- "status=%#08x.\n",
- dev->name, intr_status);
- break;
+ if (np->intr_status & IntrAbnormalSummary)
+ netdev_error(dev, np->intr_status);
+
+ if (np->intr_status &
+ (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
+ IntrRxErr | IntrRxOverrun)) {
+ netdev_rx(dev, &work_done, work_to_do);
}
- } while (1);
+
+ *budget -= work_done;
+ dev->quota -= work_done;
- if (netif_msg_intr(np))
- printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name);
+ if (work_done >= work_to_do)
+ return 1;
+
+ np->intr_status = readl(ioaddr + IntrStatus);
+ } while (np->intr_status);
- return IRQ_RETVAL(handled);
+ netif_rx_complete(dev);
+
+ /* Reenable interrupts providing nothing is trying to shut
+ * the chip down. */
+ spin_lock(&np->lock);
+ if (!np->hands_off && netif_running(dev))
+ natsemi_irq_enable(dev);
+ spin_unlock(&np->lock);
+
+ return 0;
}
/* This routine is logically part of the interrupt handler, but separated
for clarity and better register allocation. */
-static void netdev_rx(struct net_device *dev)
+static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
{
struct netdev_private *np = netdev_priv(dev);
int entry = np->cur_rx % RX_RING_SIZE;
@@ -2237,6 +2275,12 @@ static void netdev_rx(struct net_device
entry, desc_status);
if (--boguscnt < 0)
break;
+
+ if (*work_done >= work_to_do)
+ break;
+
+ (*work_done)++;
+
pkt_len = (desc_status & DescSizeMask) - 4;
if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
if (desc_status & DescMore) {
@@ -2293,7 +2337,7 @@ static void netdev_rx(struct net_device
np->rx_skbuff[entry] = NULL;
}
skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
+ netif_receive_skb(skb);
dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += pkt_len;
@@ -3074,9 +3118,7 @@ static int netdev_close(struct net_devic
del_timer_sync(&np->timer);
disable_irq(dev->irq);
spin_lock_irq(&np->lock);
- /* Disable interrupts, and flush posted writes */
- writel(0, ioaddr + IntrEnable);
- readl(ioaddr + IntrEnable);
+ natsemi_irq_disable(dev);
np->hands_off = 1;
spin_unlock_irq(&np->lock);
enable_irq(dev->irq);
@@ -3158,6 +3200,9 @@ static void __devexit natsemi_remove1 (s
* * netdev_timer: timer stopped by natsemi_suspend.
* * intr_handler: doesn't acquire the spinlock. suspend calls
* disable_irq() to enforce synchronization.
+ * * natsemi_poll: checks before reenabling interrupts. suspend
+ * sets hands_off, disables interrupts and then waits with
+ * netif_poll_disable().
*
* Interrupts must be disabled, otherwise hands_off can cause irq storms.
*/
@@ -3183,6 +3228,8 @@ static int natsemi_suspend (struct pci_d
spin_unlock_irq(&np->lock);
enable_irq(dev->irq);
+ netif_poll_disable(dev);
+
/* Update the error counts. */
__get_stats(dev);
@@ -3235,6 +3282,7 @@ static int natsemi_resume (struct pci_de
mod_timer(&np->timer, jiffies + 1*HZ);
}
netif_device_attach(dev);
+ netif_poll_enable(dev);
out:
rtnl_unlock();
return 0;
--
"You grabbed my hand and we fell into it, like a daydream - or a fever."
^ permalink raw reply
* [patch 2/2] natsemi: NAPI and a bugfix
From: Mark Brown @ 2006-02-02 0:00 UTC (permalink / raw)
To: Jeff Garzik, Tim Hockin; +Cc: netdev, linux-kernel
In-Reply-To: <20060202233909.426660000@lorien.sirena.org.uk>
[-- Attachment #1: natsemi-an-1287 --]
[-- Type: text/plain, Size: 2345 bytes --]
As documented in National application note 1287 the RX state machine on
the natsemi chip can lock up under some conditions (mostly related to
heavy load). When this happens a series of bogus packets are reported
by the chip including some oversized frames prior to the final lockup.
This patch implements the fix from the application note: when an
oversized packet is reported it resets the RX state machine, dropping
any currently pending packets.
Signed-off-by: Mark Brown <broonie@sirena.org.uk>
Index: linux-2.6.15.2/drivers/net/natsemi.c
===================================================================
--- linux-2.6.15.2.orig/drivers/net/natsemi.c 2006-02-01 22:59:29.000000000 +0000
+++ linux-2.6.15.2/drivers/net/natsemi.c 2006-02-02 00:05:23.000000000 +0000
@@ -1498,6 +1498,31 @@ static void natsemi_reset(struct net_dev
writel(rfcr, ioaddr + RxFilterAddr);
}
+static void reset_rx(struct net_device *dev)
+{
+ int i;
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = ns_ioaddr(dev);
+
+ np->intr_status &= ~RxResetDone;
+
+ writel(RxReset, ioaddr + ChipCmd);
+
+ for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+ np->intr_status |= readl(ioaddr + IntrStatus);
+ if (np->intr_status & RxResetDone)
+ break;
+ udelay(15);
+ }
+ if (i==NATSEMI_HW_TIMEOUT) {
+ printk(KERN_WARNING "%s: RX reset did not complete in %d usec.\n",
+ dev->name, i*15);
+ } else if (netif_msg_hw(np)) {
+ printk(KERN_WARNING "%s: RX reset took %d usec.\n",
+ dev->name, i*15);
+ }
+}
+
static void natsemi_reload_eeprom(struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
@@ -2292,6 +2317,23 @@ static void netdev_rx(struct net_device
"status %#08x.\n", dev->name,
np->cur_rx, desc_status);
np->stats.rx_length_errors++;
+
+ /* The RX state machine has probably
+ * locked up beneath us. Follow the
+ * reset procedure documented in
+ * AN-1287. */
+
+ spin_lock_irq(&np->lock);
+ reset_rx(dev);
+ reinit_rx(dev);
+ writel(np->ring_dma, ioaddr + RxRingPtr);
+ check_link(dev);
+ spin_unlock_irq(&np->lock);
+
+ /* We'll enable RX on exit from this
+ * function. */
+ break;
+
} else {
/* There was an error. */
np->stats.rx_errors++;
--
"You grabbed my hand and we fell into it, like a daydream - or a fever."
^ permalink raw reply
* [PATCH RESEND] net: Move destructor from neigh->ops to neigh_params
From: Roland Dreier @ 2006-02-02 1:28 UTC (permalink / raw)
To: davem; +Cc: netdev, openib-general
In-Reply-To: <adazmlmmy7v.fsf@cisco.com>
Sorry to distract everyone from the VJ channel discussion, but on the
other hand it looks like Dave is back... I'm resending this because
I'd really like to get this problem fixed but I want to make sure
we're doing it the right way. So either an ACK or a NAK with guidance
would be great...
This is a resend of a patch written by Michael S. Tsirkin
<mst@mellanox.co.il>. I'd like to get an ACK or NAK of it from Dave
and other networking people, so that we can either merge it upstream
or try a different approach. There definitely is a problem with
neighbour destructors that IP-over-IB is running into.
It would be good to know what the design was behind putting the
destructor method in neigh->ops in the first place.
Dave, if you want to merge this directly, that's fine. Or I'm fine
with merging this through the IB tree if you'd prefer (if you want me
to do that, let me know if you think it's 2.6.16 material).
Thanks,
Roland
struct neigh_ops currently has a destructor field, which no in-kernel
drivers outside of infiniband use. The infiniband/ulp/ipoib in-tree
driver stashes some info in the neighbour structure (the results of
the second-stage lookup from ARP results to real link-level path), and
it uses neigh->ops->destructor to get a callback so it can clean up
this extra info when a neighbour is freed. We've run into problems
with this: since the destructor is in an ops field that is shared
between neighbours that may belong to different net devices, there's
no way to set/clear it safely.
The following patch moves this field to neigh_parms where it can be
safely set, together with its twin neigh_setup. Two additional
patches in the patch series update ipoib to use this new interface.
Signed-off-by: Michael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
---
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 6fa9ae1..b0666d6 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -68,6 +68,7 @@ struct neigh_parms
struct net_device *dev;
struct neigh_parms *next;
int (*neigh_setup)(struct neighbour *);
+ void (*neigh_destructor)(struct neighbour *);
struct neigh_table *tbl;
void *sysctl_table;
@@ -145,7 +146,6 @@ struct neighbour
struct neigh_ops
{
int family;
- void (*destructor)(struct neighbour *);
void (*solicit)(struct neighbour *, struct sk_buff*);
void (*error_report)(struct neighbour *, struct sk_buff*);
int (*output)(struct sk_buff*);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e68700f..3489e23 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -586,8 +586,8 @@ void neigh_destroy(struct neighbour *nei
kfree(hh);
}
- if (neigh->ops && neigh->ops->destructor)
- (neigh->ops->destructor)(neigh);
+ if (neigh->parms->neigh_destructor)
+ (neigh->parms->neigh_destructor)(neigh);
skb_queue_purge(&neigh->arp_queue);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index fd3f5c8..9588124 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -247,7 +247,6 @@ static void path_free(struct net_device
if (neigh->ah)
ipoib_put_ah(neigh->ah);
*to_ipoib_neigh(neigh->neighbour) = NULL;
- neigh->neighbour->ops->destructor = NULL;
kfree(neigh);
}
@@ -530,7 +529,6 @@ static void neigh_add_path(struct sk_buf
err:
*to_ipoib_neigh(skb->dst->neighbour) = NULL;
list_del(&neigh->list);
- neigh->neighbour->ops->destructor = NULL;
kfree(neigh);
++priv->stats.tx_dropped;
@@ -769,21 +767,9 @@ static void ipoib_neigh_destructor(struc
ipoib_put_ah(ah);
}
-static int ipoib_neigh_setup(struct neighbour *neigh)
-{
- /*
- * Is this kosher? I can't find anybody in the kernel that
- * sets neigh->destructor, so we should be able to set it here
- * without trouble.
- */
- neigh->ops->destructor = ipoib_neigh_destructor;
-
- return 0;
-}
-
static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms)
{
- parms->neigh_setup = ipoib_neigh_setup;
+ parms->neigh_destructor = ipoib_neigh_destructor;
return 0;
}
^ permalink raw reply related
* Re: [PATCH RESEND] net: Move destructor from neigh->ops to neigh_params
From: David S. Miller @ 2006-02-02 1:31 UTC (permalink / raw)
To: rdreier; +Cc: netdev, openib-general
In-Reply-To: <adabqxq1rdh.fsf@cisco.com>
From: Roland Dreier <rdreier@cisco.com>
Date: Wed, 01 Feb 2006 17:28:10 -0800
> Sorry to distract everyone from the VJ channel discussion, but on the
> other hand it looks like Dave is back... I'm resending this because
> I'd really like to get this problem fixed but I want to make sure
> we're doing it the right way. So either an ACK or a NAK with guidance
> would be great...
It's sitting in my backlog, and will be a net-2.6.17 issue not
a net-2.6.16 one as we're in bug fix mode there.
Sorry if you need this in 2.6.16, but that's not really practical.
^ permalink raw reply
* Re: [PATCH RESEND] net: Move destructor from neigh->ops to neigh_params
From: Roland Dreier @ 2006-02-02 1:33 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev, openib-general
In-Reply-To: <20060201.173137.10844554.davem@davemloft.net>
David> It's sitting in my backlog, and will be a net-2.6.17 issue
David> not a net-2.6.16 one as we're in bug fix mode there.
David> Sorry if you need this in 2.6.16, but that's not really
David> practical.
No, that's fine... I was just resending in case you were using RED to
manage your backlog. This is a real issue but I don't think it's
hitting a lot of people.
- R.
^ permalink raw reply
* Re: [PATCH RESEND] net: Move destructor from neigh->ops to neigh_params
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2006-02-02 1:34 UTC (permalink / raw)
To: rdreier; +Cc: yoshfuji, netdev, davem, openib-general
In-Reply-To: <adabqxq1rdh.fsf@cisco.com>
In article <adabqxq1rdh.fsf@cisco.com> (at Wed, 01 Feb 2006 17:28:10 -0800), Roland Dreier <rdreier@cisco.com> says:
> Sorry to distract everyone from the VJ channel discussion, but on the
> other hand it looks like Dave is back... I'm resending this because
> I'd really like to get this problem fixed but I want to make sure
> we're doing it the right way. So either an ACK or a NAK with guidance
> would be great...
Sorry for silence.
Since we have "setup," it'd be natural to have destruct;
I seems sane to me.
--yoshfuji
^ permalink raw reply
* [patch 0/2] natsemi: NAPI and a bugfix
From: Mark Brown @ 2006-02-02 23:27 UTC (permalink / raw)
To: Jeff Garzik, Tim Hockin; +Cc: netdev, linux-kernel
These patches provide a series of updates to the natsemi driver: the
NAPI patch I've submitted before and a workaround for an issue with the
hardware that is easier to provoke at higher data rates.
1/2: Convert the driver to NAPI
2/2: Fix hardware issue with RX state machine lock up
--
"You grabbed my hand and we fell into it, like a daydream - or a fever."
^ permalink raw reply
* Re: [lock validator] inet6_destroy_sock(): soft-safe -> soft-unsafe lock dependency
From: David S. Miller @ 2006-02-03 1:01 UTC (permalink / raw)
To: herbert; +Cc: mingo, davem, linux-kernel, netdev, yoshfuji
In-Reply-To: <20060201104214.GA9085@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Wed, 1 Feb 2006 21:42:14 +1100
> OK this is definitely broken. We should never touch the dst lock in
> softirq context. Since inet6_destroy_sock may be called from that
> context due to the asynchronous nature of sockets, we can't take the
> lock there.
>
> In fact this sk_dst_reset is totally redundant since all IPv6 sockets
> use inet_sock_destruct as their socket destructor which always cleans
> up the dst anyway. So the solution is to simply remove the call.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Looks good, applied, thanks Herbert.
^ permalink raw reply
* Re: FW: Performance evaluation of high speed TCPs
From: Douglas Leith @ 2006-02-03 2:24 UTC (permalink / raw)
To: rhee; +Cc: netdev, end2end-interest
In-Reply-To: <39307.60.44.142.168.1138932213.squirrel@webmail.ncsu.edu>
[-- Attachment #1: Type: text/plain, Size: 742 bytes --]
>Seriously, we can't run the tests for every fix and bug report.
Perhaps best to view it as returning a favour. You may recall that we re-ran all our own experimental tests last year (all data and code available online at www.hamilton.ie/net/eval/) on discovering a previously unreported bug introduced by the linux folks when implementing bic. Something similar has happened with importing htcp into linux.
Seriously, where's the value in comparing buggy implementations - isn't that just a waste of all our time ? If we are genuine about wanting to understand tcp performance then I think we just have to take the hit from issues such as this that are outside all of our control.
Doug
Hamilton Institute
www.hamilton.ie
[-- Attachment #2: Type: text/html, Size: 1200 bytes --]
^ permalink raw reply
* Re: FW: Performance evaluation of high speed TCPs
From: rhee @ 2006-02-03 2:50 UTC (permalink / raw)
To: Douglas Leith; +Cc: netdev, end2end-interest
In-Reply-To: <C0FF12BB5B83E44ABAEAA47149DA9735082675@helios.eee.strath.ac.uk>
Sure. Your comments about running the buggy implementation are well taken.
That is
why this type of reporting is helpful and we are committed to keep this
effort. Just that
it takes time to run the tests, and before we run a new set of tests, we
have to do some
batch of patches to reduce our effort level (but in this case of the HTCP
bug, rest assured
that we are running it now..it is just that there are a lot of other
things going on
that we have to catch a breath a little).
Then again, if we don't do the test and keep the report
up-to-date then it is difficult to find bugs as well...so these reportings
help us find
bugs and also improve TCP algorithms. (I hope our report did the same for
you). Also
sometimes we are not motivated to find the bugs ourselves.
In fact, i contacted your student "Baruch" one month and half before we
posted our
report -- it was CCed in the netdev mailing list as well and we gave him
login and
passwd on our result website (at that time we were just about to write the
report)
and we have not heard from your guys until just one week ago. At least we
did try to
make sure we are running a buggy version.
>>Seriously, we can't run the tests for every fix and bug report.
>
> Perhaps best to view it as returning a favour. You may recall that we
> re-ran all our own experimental tests last year (all data and code
> available online at www.hamilton.ie/net/eval/) on discovering a previously
> unreported bug introduced by the linux folks when implementing bic.
> Something similar has happened with importing htcp into linux.
>
> Seriously, where's the value in comparing buggy implementations - isn't
> that just a waste of all our time ? If we are genuine about wanting to
> understand tcp performance then I think we just have to take the hit from
> issues such as this that are outside all of our control.
>
> Doug
>
> Hamilton Institute
> www.hamilton.ie
>
^ permalink raw reply
* Re: FW: Performance evaluation of high speed TCPs
From: Ian McDonald @ 2006-02-03 2:51 UTC (permalink / raw)
To: Douglas Leith; +Cc: netdev, end2end-interest
In-Reply-To: <C0FF12BB5B83E44ABAEAA47149DA9735082675@helios.eee.strath.ac.uk>
> Seriously, where's the value in comparing buggy implementations - isn't
> that just a waste of all our time ? If we are genuine about wanting to
> understand tcp performance then I think we just have to take the hit from
> issues such as this that are outside all of our control.
>
A real part of the problem here is that the Linux doesn't have a full
TCP testing suite and doesn't have build checking to check for
regressions in TCP variants. As I understand the only thing tested in
nightly builds is throughput for the default TCP.
Stephen Hemminger has done some work on TCP Probes but this is where I
think real progress could be made in improving Linux TCP. I may get
around to doing this myself at some point in my research but would
welcome other people doing it also!
Ian
--
Ian McDonald
http://wand.net.nz/~iam4
WAND Network Research Group
University of Waikato
New Zealand
^ permalink raw reply
* Re: [patch 3/4] net: Percpufy frequently used variables -- proto.sockets_allocated
From: Ravikiran G Thirumalai @ 2006-02-03 3:05 UTC (permalink / raw)
To: Andrew Morton; +Cc: dada1, davem, linux-kernel, shai, netdev, pravins, bcrl
In-Reply-To: <20060127150106.38b9e041.akpm@osdl.org>
On Fri, Jan 27, 2006 at 03:01:06PM -0800, Andrew Morton wrote:
> Ravikiran G Thirumalai <kiran@scalex86.org> wrote:
>
>
> > >
> > > If the benchmarks say that we need to. If we cannot observe any problems
> > > in testing of existing code and if we can't demonstrate any benefit from
> > > the patched code then one option is to go off and do something else ;)
> >
> > We first tried plain per-CPU counters for memory_allocated, found that reads
> > on memory_allocated was causing cacheline transfers, and then
> > switched over to batching. So batching reads is useful. To avoid
> > inaccuracy, we can maybe change percpu_counter_init to:
> >
> > void percpu_counter_init(struct percpu_counter *fbc, int maxdev)
> >
> > the percpu batching limit would then be maxdev/num_possible_cpus. One would
> > use batching counters only when both reads and writes are frequent. With
> > the above scheme, we would go fetch cachelines from other cpus for read
> > often only on large cpu counts, which is not any worse than the global
> > counter alternative, but it would still be beneficial on smaller machines,
> > without sacrificing a pre-set deviation.
> >
> > Comments?
>
> Sounds sane.
>
Here's an implementation which delegates tuning of batching to the user. We
don't really need local_t at all as percpu_counter_mod is not safe against
interrupts and softirqs as it is. If we have a counter which could be
modified in process context and irq/bh context, we just have to use a
wrapper like percpu_counter_mod_bh which will just disable and enable bottom
halves. Reads on the counters are safe as they are atomic_reads, and the
cpu local variables are always accessed by that cpu only.
(PS: the maxerr for ext2/ext3 is just guesstimate)
Comments?
Index: linux-2.6.16-rc1mm4/include/linux/percpu_counter.h
===================================================================
--- linux-2.6.16-rc1mm4.orig/include/linux/percpu_counter.h 2006-02-02 11:18:54.000000000 -0800
+++ linux-2.6.16-rc1mm4/include/linux/percpu_counter.h 2006-02-02 18:29:46.000000000 -0800
@@ -16,24 +16,32 @@
struct percpu_counter {
atomic_long_t count;
+ int percpu_batch;
long *counters;
};
-#if NR_CPUS >= 16
-#define FBC_BATCH (NR_CPUS*2)
-#else
-#define FBC_BATCH (NR_CPUS*4)
-#endif
-static inline void percpu_counter_init(struct percpu_counter *fbc)
+/*
+ * Choose maxerr carefully. maxerr/num_possible_cpus indicates per-cpu batching
+ * Set maximum tolerance for better performance on large systems.
+ */
+static inline void percpu_counter_init(struct percpu_counter *fbc,
+ unsigned int maxerr)
{
atomic_long_set(&fbc->count, 0);
- fbc->counters = alloc_percpu(long);
+ fbc->percpu_batch = maxerr/num_possible_cpus();
+ if (fbc->percpu_batch) {
+ fbc->counters = alloc_percpu(long);
+ if (!fbc->counters)
+ fbc->percpu_batch = 0;
+ }
+
}
static inline void percpu_counter_destroy(struct percpu_counter *fbc)
{
- free_percpu(fbc->counters);
+ if (fbc->percpu_batch)
+ free_percpu(fbc->counters);
}
void percpu_counter_mod(struct percpu_counter *fbc, long amount);
@@ -63,7 +71,8 @@ struct percpu_counter {
long count;
};
-static inline void percpu_counter_init(struct percpu_counter *fbc)
+static inline void percpu_counter_init(struct percpu_counter *fbc,
+ unsigned int maxerr)
{
fbc->count = 0;
}
Index: linux-2.6.16-rc1mm4/mm/swap.c
===================================================================
--- linux-2.6.16-rc1mm4.orig/mm/swap.c 2006-01-29 20:20:20.000000000 -0800
+++ linux-2.6.16-rc1mm4/mm/swap.c 2006-02-02 18:36:21.000000000 -0800
@@ -470,13 +470,20 @@ static int cpu_swap_callback(struct noti
#ifdef CONFIG_SMP
void percpu_counter_mod(struct percpu_counter *fbc, long amount)
{
- long count;
long *pcount;
- int cpu = get_cpu();
+ long count;
+ int cpu;
+ /* Slow mode */
+ if (unlikely(!fbc->percpu_batch)) {
+ atomic_long_add(amount, &fbc->count);
+ return;
+ }
+
+ cpu = get_cpu();
pcount = per_cpu_ptr(fbc->counters, cpu);
count = *pcount + amount;
- if (count >= FBC_BATCH || count <= -FBC_BATCH) {
+ if (count >= fbc->percpu_batch || count <= -fbc->percpu_batch) {
atomic_long_add(count, &fbc->count);
count = 0;
}
Index: linux-2.6.16-rc1mm4/fs/ext2/super.c
===================================================================
--- linux-2.6.16-rc1mm4.orig/fs/ext2/super.c 2006-02-02 18:30:28.000000000 -0800
+++ linux-2.6.16-rc1mm4/fs/ext2/super.c 2006-02-02 18:36:39.000000000 -0800
@@ -610,6 +610,7 @@ static int ext2_fill_super(struct super_
int db_count;
int i, j;
__le32 features;
+ int maxerr;
sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
@@ -835,9 +836,14 @@ static int ext2_fill_super(struct super_
printk ("EXT2-fs: not enough memory\n");
goto failed_mount;
}
- percpu_counter_init(&sbi->s_freeblocks_counter);
- percpu_counter_init(&sbi->s_freeinodes_counter);
- percpu_counter_init(&sbi->s_dirs_counter);
+
+ if (num_possible_cpus() <= 16 )
+ maxerr = 256;
+ else
+ maxerr = 1024;
+ percpu_counter_init(&sbi->s_freeblocks_counter, maxerr);
+ percpu_counter_init(&sbi->s_freeinodes_counter, maxerr);
+ percpu_counter_init(&sbi->s_dirs_counter, maxerr);
bgl_lock_init(&sbi->s_blockgroup_lock);
sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts),
GFP_KERNEL);
Index: linux-2.6.16-rc1mm4/fs/ext3/super.c
===================================================================
--- linux-2.6.16-rc1mm4.orig/fs/ext3/super.c 2006-02-02 18:30:28.000000000 -0800
+++ linux-2.6.16-rc1mm4/fs/ext3/super.c 2006-02-02 18:38:10.000000000 -0800
@@ -1353,6 +1353,7 @@ static int ext3_fill_super (struct super
int i;
int needs_recovery;
__le32 features;
+ int maxerr;
sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
@@ -1578,9 +1579,14 @@ static int ext3_fill_super (struct super
goto failed_mount;
}
- percpu_counter_init(&sbi->s_freeblocks_counter);
- percpu_counter_init(&sbi->s_freeinodes_counter);
- percpu_counter_init(&sbi->s_dirs_counter);
+ if (num_possible_cpus() <= 16)
+ maxerr = 256;
+ else
+ maxerr = 1024;
+
+ percpu_counter_init(&sbi->s_freeblocks_counter, maxerr);
+ percpu_counter_init(&sbi->s_freeinodes_counter, maxerr);
+ percpu_counter_init(&sbi->s_dirs_counter, maxerr);
bgl_lock_init(&sbi->s_blockgroup_lock);
for (i = 0; i < db_count; i++) {
^ permalink raw reply
* Re: [patch 3/4] net: Percpufy frequently used variables -- proto.sockets_allocated
From: Andrew Morton @ 2006-02-03 3:16 UTC (permalink / raw)
To: Ravikiran G Thirumalai
Cc: dada1, davem, linux-kernel, shai, netdev, pravins, bcrl
In-Reply-To: <20060203030547.GB3612@localhost.localdomain>
Ravikiran G Thirumalai <kiran@scalex86.org> wrote:
>
> On Fri, Jan 27, 2006 at 03:01:06PM -0800, Andrew Morton wrote:
> > Ravikiran G Thirumalai <kiran@scalex86.org> wrote:
> >
> >
> > > >
> > > > If the benchmarks say that we need to. If we cannot observe any problems
> > > > in testing of existing code and if we can't demonstrate any benefit from
> > > > the patched code then one option is to go off and do something else ;)
> > >
> > > We first tried plain per-CPU counters for memory_allocated, found that reads
> > > on memory_allocated was causing cacheline transfers, and then
> > > switched over to batching. So batching reads is useful. To avoid
> > > inaccuracy, we can maybe change percpu_counter_init to:
> > >
> > > void percpu_counter_init(struct percpu_counter *fbc, int maxdev)
> > >
> > > the percpu batching limit would then be maxdev/num_possible_cpus. One would
> > > use batching counters only when both reads and writes are frequent. With
> > > the above scheme, we would go fetch cachelines from other cpus for read
> > > often only on large cpu counts, which is not any worse than the global
> > > counter alternative, but it would still be beneficial on smaller machines,
> > > without sacrificing a pre-set deviation.
> > >
> > > Comments?
> >
> > Sounds sane.
> >
>
> Here's an implementation which delegates tuning of batching to the user. We
> don't really need local_t at all as percpu_counter_mod is not safe against
> interrupts and softirqs as it is. If we have a counter which could be
> modified in process context and irq/bh context, we just have to use a
> wrapper like percpu_counter_mod_bh which will just disable and enable bottom
> halves. Reads on the counters are safe as they are atomic_reads, and the
> cpu local variables are always accessed by that cpu only.
>
> (PS: the maxerr for ext2/ext3 is just guesstimate)
Well that's the problem. We need to choose production-quality values for
use in there.
> Comments?
Using num_possible_cpus() in that header file is just asking for build
errors. Probably best to uninline the function rather than adding the
needed include of cpumask.h.
^ permalink raw reply
* [PATCH] acx: make firmware statistics parsing more intelligent
From: Andreas Mohr @ 2006-02-03 10:58 UTC (permalink / raw)
To: netdev; +Cc: acx100-devel
Hi all,
this patch does:
- implement much more flexible firmware statistics parsing
(for /proc/driver/acx_wlanX_diag)
This has the nice effect that we now get output for both the older
TNETW1100 USB and TNETW1450.
Since firmware statistics information has non-stable layout depending on
firmware version, please report if you suspect any parsing mismatch!
This improved version now uses 2kB more driver space, unfortunately.
- use "% 8" modulo instead of more complicated "% 5" calculation
- use
if (++idx >= count)
idx = 0;
instead of more bloaty
idx = (idx + 1) % count;
We might want to add a kernel macro for this *very* common and
performance-critical driver operation, say ring_advance_next or so,
in order to have the most optimized version for each architecture;
Or ($1 million question): Is there already such a beast somewhere!?
- tiny cleanup
Andreas Mohr
diff -urN acx-20060202/acx_func.h acx-20060202_stats/acx_func.h
--- acx-20060202/acx_func.h 2006-02-01 10:49:31.000000000 +0100
+++ acx-20060202_stats/acx_func.h 2006-02-05 06:21:35.000000000 +0100
@@ -257,7 +257,7 @@
** but may be run under lock
**
** A small number of local helpers do not have acx_[eisl]_ prefix.
-** They are always close to caller and are to be revieved locally.
+** They are always close to caller and are to be reviewed locally.
**
** Theory of operation:
**
diff -urN acx-20060202/acx_struct.h acx-20060202_stats/acx_struct.h
--- acx-20060202/acx_struct.h 2006-02-01 10:49:38.000000000 +0100
+++ acx-20060202_stats/acx_struct.h 2006-02-03 23:21:35.000000000 +0100
@@ -582,21 +582,34 @@
/*--- Firmware statistics ----------------------------------------------------*/
-typedef struct fw_stats {
- u32 val0x0 ACX_PACKED; /* hdr; */
+
+/* define a random 100 bytes more to catch firmware versions which
+ * provide a bigger struct */
+#define FW_STATS_FUTURE_EXTENSION 100
+
+typedef struct fw_stats_tx {
u32 tx_desc_of ACX_PACKED;
+} fw_stats_tx_t;
+
+typedef struct fw_stats_rx {
u32 rx_oom ACX_PACKED;
u32 rx_hdr_of ACX_PACKED;
- u32 rx_hdr_use_next ACX_PACKED;
+ u32 rx_hw_stuck ACX_PACKED; /* old: u32 rx_hdr_use_next */
u32 rx_dropped_frame ACX_PACKED;
u32 rx_frame_ptr_err ACX_PACKED;
u32 rx_xfr_hint_trig ACX_PACKED;
+ u32 rx_aci_events ACX_PACKED; /* later versions only */
+ u32 rx_aci_resets ACX_PACKED; /* later versions only */
+} fw_stats_rx_t;
+typedef struct fw_stats_dma {
u32 rx_dma_req ACX_PACKED;
u32 rx_dma_err ACX_PACKED;
u32 tx_dma_req ACX_PACKED;
u32 tx_dma_err ACX_PACKED;
+} fw_stats_dma_t;
+typedef struct fw_stats_irq {
u32 cmd_cplt ACX_PACKED;
u32 fiq ACX_PACKED;
u32 rx_hdrs ACX_PACKED;
@@ -604,23 +617,78 @@
u32 rx_mem_of ACX_PACKED;
u32 rx_rdys ACX_PACKED;
u32 irqs ACX_PACKED;
- u32 acx_trans_procs ACX_PACKED;
+ u32 tx_procs ACX_PACKED;
u32 decrypt_done ACX_PACKED;
u32 dma_0_done ACX_PACKED;
u32 dma_1_done ACX_PACKED;
u32 tx_exch_complet ACX_PACKED;
u32 commands ACX_PACKED;
- u32 acx_rx_procs ACX_PACKED;
+ u32 rx_procs ACX_PACKED;
u32 hw_pm_mode_changes ACX_PACKED;
u32 host_acks ACX_PACKED;
u32 pci_pm ACX_PACKED;
u32 acm_wakeups ACX_PACKED;
+} fw_stats_irq_t;
+typedef struct fw_stats_wep {
u32 wep_key_count ACX_PACKED;
u32 wep_default_key_count ACX_PACKED;
u32 dot11_def_key_mib ACX_PACKED;
u32 wep_key_not_found ACX_PACKED;
u32 wep_decrypt_fail ACX_PACKED;
+ u32 wep_pkt_decrypt ACX_PACKED;
+ u32 wep_decrypt_irqs ACX_PACKED;
+} fw_stats_wep_t;
+
+typedef struct fw_stats_pwr {
+ u32 tx_start_ctr ACX_PACKED;
+ u32 no_ps_tx_too_short ACX_PACKED;
+ u32 rx_start_ctr ACX_PACKED;
+ u32 no_ps_rx_too_short ACX_PACKED;
+ u32 lppd_started ACX_PACKED;
+ u32 no_lppd_too_noisy ACX_PACKED;
+ u32 no_lppd_too_short ACX_PACKED;
+ u32 no_lppd_matching_frame ACX_PACKED;
+} fw_stats_pwr_t;
+
+typedef struct fw_stats_mic {
+ u32 mic_rx_pkts ACX_PACKED;
+ u32 mic_calc_fail ACX_PACKED;
+} fw_stats_mic_t;
+
+typedef struct fw_stats_aes {
+ u32 aes_enc_fail ACX_PACKED;
+ u32 aes_dec_fail ACX_PACKED;
+ u32 aes_enc_pkts ACX_PACKED;
+ u32 aes_dec_pkts ACX_PACKED;
+ u32 aes_enc_irq ACX_PACKED;
+ u32 aes_dec_irq ACX_PACKED;
+} fw_stats_aes_t;
+
+typedef struct fw_stats_event {
+ u32 heartbeat ACX_PACKED;
+ u32 calibration ACX_PACKED;
+ u32 rx_mismatch ACX_PACKED;
+ u32 rx_mem_empty ACX_PACKED;
+ u32 rx_pool ACX_PACKED;
+ u32 oom_late ACX_PACKED;
+ u32 phy_tx_err ACX_PACKED;
+ u32 tx_stuck ACX_PACKED;
+} fw_stats_event_t;
+
+/* mainly for size calculation only */
+typedef struct fw_stats {
+ u16 type;
+ u16 len;
+ fw_stats_tx_t tx;
+ fw_stats_rx_t rx;
+ fw_stats_dma_t dma;
+ fw_stats_irq_t irq;
+ fw_stats_wep_t wep;
+ fw_stats_pwr_t pwr;
+ fw_stats_mic_t mic;
+ fw_stats_aes_t aes;
+ fw_stats_event_t evt;
} fw_stats_t;
/* Firmware version struct */
diff -urN acx-20060202/common.c acx-20060202_stats/common.c
--- acx-20060202/common.c 2006-02-01 10:49:37.000000000 +0100
+++ acx-20060202_stats/common.c 2006-02-05 06:23:21.000000000 +0100
@@ -851,7 +851,7 @@
ACX1xx_IE_RXCONFIG_LEN,
0,
0,
- ACX1xx_IE_FIRMWARE_STATISTICS_LEN,
+ sizeof(fw_stats_t)-4+FW_STATS_FUTURE_EXTENSION,
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
@@ -928,7 +928,7 @@
ACX1xx_IE_RXCONFIG_LEN,
0,
0,
- ACX1xx_IE_FIRMWARE_STATISTICS_LEN,
+ sizeof(fw_stats_t)-4+FW_STATS_FUTURE_EXTENSION,
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
@@ -1148,20 +1148,27 @@
acx_s_proc_diag_output(char *buf, acx_device_t *adev)
{
char *p = buf;
- fw_stats_t *fw_stats;
unsigned long flags;
+ unsigned int len = 0, partlen;
+ u32 temp1, temp2;
+ u8 *st, *st_end;
+#ifdef __BIG_ENDIAN
+ u8 *st2;
+#endif
+ fw_stats_t *fw_stats;
+ char *part_str = NULL;
+ fw_stats_tx_t *tx = NULL;
+ fw_stats_rx_t *rx = NULL;
+ fw_stats_dma_t *dma = NULL;
+ fw_stats_irq_t *irq = NULL;
+ fw_stats_wep_t *wep = NULL;
+ fw_stats_pwr_t *pwr = NULL;
+ fw_stats_mic_t *mic = NULL;
+ fw_stats_aes_t *aes = NULL;
+ fw_stats_event_t *evt = NULL;
FN_ENTER;
- /* TODO: may replace kmalloc/memset with kzalloc once
- * Linux 2.6.14 is widespread */
- fw_stats = kmalloc(sizeof(*fw_stats), GFP_KERNEL);
- if (!fw_stats) {
- FN_EXIT1(0);
- return 0;
- }
- memset(fw_stats, 0, sizeof(*fw_stats));
-
acx_lock(adev, flags);
if (IS_PCI(adev))
@@ -1205,63 +1212,322 @@
acx_unlock(adev, flags);
- if (OK != acx_s_interrogate(adev, fw_stats, ACX1xx_IE_FIRMWARE_STATISTICS))
- p += sprintf(p,
- "\n"
- "** Firmware **\n"
- "QUERY FAILED!!\n");
- else {
- p += sprintf(p,
- "\n"
- "** Firmware **\n"
- "version \"%s\"\n"
- "tx_desc_overfl %u, rx_OutOfMem %u, rx_hdr_overfl %u, rx_hdr_use_next %u\n"
- "rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u, rx_dma_req %u\n"
- "rx_dma_err %u, tx_dma_req %u, tx_dma_err %u, cmd_cplt %u, fiq %u\n"
- "rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u, irqs %u\n"
- "acx_trans_procs %u, decrypt_done %u, dma_0_done %u, dma_1_done %u\n",
- adev->firmware_version,
- le32_to_cpu(fw_stats->tx_desc_of),
- le32_to_cpu(fw_stats->rx_oom),
- le32_to_cpu(fw_stats->rx_hdr_of),
- le32_to_cpu(fw_stats->rx_hdr_use_next),
- le32_to_cpu(fw_stats->rx_dropped_frame),
- le32_to_cpu(fw_stats->rx_frame_ptr_err),
- le32_to_cpu(fw_stats->rx_xfr_hint_trig),
- le32_to_cpu(fw_stats->rx_dma_req),
- le32_to_cpu(fw_stats->rx_dma_err),
- le32_to_cpu(fw_stats->tx_dma_req),
- le32_to_cpu(fw_stats->tx_dma_err),
- le32_to_cpu(fw_stats->cmd_cplt),
- le32_to_cpu(fw_stats->fiq),
- le32_to_cpu(fw_stats->rx_hdrs),
- le32_to_cpu(fw_stats->rx_cmplt),
- le32_to_cpu(fw_stats->rx_mem_of),
- le32_to_cpu(fw_stats->rx_rdys),
- le32_to_cpu(fw_stats->irqs),
- le32_to_cpu(fw_stats->acx_trans_procs),
- le32_to_cpu(fw_stats->decrypt_done),
- le32_to_cpu(fw_stats->dma_0_done),
- le32_to_cpu(fw_stats->dma_1_done));
+ p += sprintf(p,
+ "\n"
+ "** Firmware **\n"
+ "NOTE: version dependent statistics layout, "
+ "please report if you suspect wrong parsing!\n"
+ "\n"
+ "version \"%s\"\n", adev->firmware_version);
+
+ /* TODO: may replace kmalloc/memset with kzalloc once
+ * Linux 2.6.14 is widespread */
+ fw_stats = kmalloc(sizeof(*fw_stats)+FW_STATS_FUTURE_EXTENSION, GFP_KERNEL);
+ if (!fw_stats) {
+ FN_EXIT1(0);
+ return 0;
+ }
+ memset(fw_stats, 0, sizeof(*fw_stats)+FW_STATS_FUTURE_EXTENSION);
+
+ st = (u8 *)fw_stats;
+
+ part_str = "statistics query command";
+
+ if (OK != acx_s_interrogate(adev, st, ACX1xx_IE_FIRMWARE_STATISTICS))
+ goto fw_stats_end;
+
+ st += sizeof(u16);
+ len = *(u16 *)st;
+
+ if (len > sizeof(*fw_stats)) {
p += sprintf(p,
- "tx_exch_complet %u, commands %u, acx_rx_procs %u\n"
- "hw_pm_mode_changes %u, host_acks %u, pci_pm %u, acm_wakeups %u\n"
- "wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
- "wep_key_not_found %u, wep_decrypt_fail %u\n",
- le32_to_cpu(fw_stats->tx_exch_complet),
- le32_to_cpu(fw_stats->commands),
- le32_to_cpu(fw_stats->acx_rx_procs),
- le32_to_cpu(fw_stats->hw_pm_mode_changes),
- le32_to_cpu(fw_stats->host_acks),
- le32_to_cpu(fw_stats->pci_pm),
- le32_to_cpu(fw_stats->acm_wakeups),
- le32_to_cpu(fw_stats->wep_key_count),
- le32_to_cpu(fw_stats->wep_default_key_count),
- le32_to_cpu(fw_stats->dot11_def_key_mib),
- le32_to_cpu(fw_stats->wep_key_not_found),
- le32_to_cpu(fw_stats->wep_decrypt_fail));
+ "firmware version with bigger fw_stats struct detected\n"
+ "(%u vs. %u), PLEASE REPORT!!\n", len, sizeof(fw_stats_t));
+ if (len > sizeof(*fw_stats)+FW_STATS_FUTURE_EXTENSION) {
+ p += sprintf(p, "struct size exceeded allocation!\n");
+ len = sizeof(*fw_stats)+FW_STATS_FUTURE_EXTENSION;
+ }
+ }
+ st += sizeof(u16);
+ st_end = st - 2*sizeof(u16) + len;
+
+#ifdef __BIG_ENDIAN
+ /* let's make one bold assumption here:
+ * (hopefully!) *all* statistics fields are u32 only,
+ * thus if we need to make endianness corrections
+ * we can simply do them in one go, in advance */
+ st2 = (u8 *)fw_stats;
+ for (temp1 = 0; temp1 < len; temp1 += 4, st2 += 4)
+ *(u32 *)st2 = le32_to_cpu(*(u32 *)st2);
+#endif
+
+ part_str = "Rx/Tx";
+
+ /* directly at end of a struct part? --> no error! */
+ if (st == st_end)
+ goto fw_stats_end;
+
+ tx = (fw_stats_tx_t *)st;
+ st += sizeof(fw_stats_tx_t);
+ rx = (fw_stats_rx_t *)st;
+ st += sizeof(fw_stats_rx_t);
+ partlen = sizeof(fw_stats_tx_t) + sizeof(fw_stats_rx_t);
+
+ if (
+ (IS_PCI(adev) && IS_ACX100(adev))
+ || (IS_USB(adev) && IS_ACX100(adev))
+ ) {
+ /* at least ACX100 PCI F/W 1.9.8.b
+ * and ACX100 USB F/W 1.0.7-USB
+ * don't have those two fields... */
+ st -= 2*sizeof(u32);
+
+ /* our parsing doesn't quite match this firmware yet,
+ * log failure */
+ if (st > st_end)
+ goto fw_stats_fail;
+ temp1 = temp2 = 999999999;
+ } else {
+ if (st > st_end)
+ goto fw_stats_fail;
+ temp1 = rx->rx_aci_events;
+ temp2 = rx->rx_aci_resets;
}
+ p += sprintf(p,
+ "%s:\n"
+ " tx_desc_overfl %u\n"
+ " rx_OutOfMem %u, rx_hdr_overfl %u, rx_hw_stuck %u\n"
+ " rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u\n"
+ " rx_aci_events %u, rx_aci_resets %u\n",
+ part_str,
+ tx->tx_desc_of,
+ rx->rx_oom,
+ rx->rx_hdr_of,
+ rx->rx_hw_stuck,
+ rx->rx_dropped_frame,
+ rx->rx_frame_ptr_err,
+ rx->rx_xfr_hint_trig,
+ temp1,
+ temp2);
+
+ part_str = "DMA";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ dma = (fw_stats_dma_t *)st;
+ partlen = sizeof(fw_stats_dma_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " rx_dma_req %u, rx_dma_err %u, tx_dma_req %u, tx_dma_err %u\n",
+ part_str,
+ dma->rx_dma_req,
+ dma->rx_dma_err,
+ dma->tx_dma_req,
+ dma->tx_dma_err);
+
+ part_str = "IRQ";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ irq = (fw_stats_irq_t *)st;
+ partlen = sizeof(fw_stats_irq_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " cmd_cplt %u, fiq %u\n"
+ " rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u\n"
+ " irqs %u, tx_procs %u, decrypt_done %u\n"
+ " dma_0_done %u, dma_1_done %u, tx_exch_complet %u\n"
+ " commands %u, rx_procs %u, hw_pm_mode_changes %u\n"
+ " host_acks %u, pci_pm %u, acm_wakeups %u\n",
+ part_str,
+ irq->cmd_cplt,
+ irq->fiq,
+ irq->rx_hdrs,
+ irq->rx_cmplt,
+ irq->rx_mem_of,
+ irq->rx_rdys,
+ irq->irqs,
+ irq->tx_procs,
+ irq->decrypt_done,
+ irq->dma_0_done,
+ irq->dma_1_done,
+ irq->tx_exch_complet,
+ irq->commands,
+ irq->rx_procs,
+ irq->hw_pm_mode_changes,
+ irq->host_acks,
+ irq->pci_pm,
+ irq->acm_wakeups);
+
+ part_str = "WEP";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ wep = (fw_stats_wep_t *)st;
+ partlen = sizeof(fw_stats_wep_t);
+ st += partlen;
+
+ if (
+ (IS_PCI(adev) && IS_ACX100(adev))
+ || (IS_USB(adev) && IS_ACX100(adev))
+ ) {
+ /* at least ACX100 PCI F/W 1.9.8.b
+ * and ACX100 USB F/W 1.0.7-USB
+ * don't have those two fields... */
+ st -= 2*sizeof(u32);
+ if (st > st_end)
+ goto fw_stats_fail;
+ temp1 = temp2 = 999999999;
+ } else {
+ if (st > st_end)
+ goto fw_stats_fail;
+ temp1 = wep->wep_pkt_decrypt;
+ temp2 = wep->wep_decrypt_irqs;
+ }
+
+ p += sprintf(p,
+ "%s:\n"
+ " wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
+ " wep_key_not_found %u, wep_decrypt_fail %u\n"
+ " wep_pkt_decrypt %u, wep_decrypt_irqs %u\n",
+ part_str,
+ wep->wep_key_count,
+ wep->wep_default_key_count,
+ wep->dot11_def_key_mib,
+ wep->wep_key_not_found,
+ wep->wep_decrypt_fail,
+ temp1,
+ temp2);
+
+ part_str = "power";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ pwr = (fw_stats_pwr_t *)st;
+ partlen = sizeof(fw_stats_pwr_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " tx_start_ctr %u, no_ps_tx_too_short %u\n"
+ " rx_start_ctr %u, no_ps_rx_too_short %u\n"
+ " lppd_started %u\n"
+ " no_lppd_too_noisy %u, no_lppd_too_short %u, no_lppd_matching_frame %u\n",
+ part_str,
+ pwr->tx_start_ctr,
+ pwr->no_ps_tx_too_short,
+ pwr->rx_start_ctr,
+ pwr->no_ps_rx_too_short,
+ pwr->lppd_started,
+ pwr->no_lppd_too_noisy,
+ pwr->no_lppd_too_short,
+ pwr->no_lppd_matching_frame);
+
+ part_str = "MIC";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ mic = (fw_stats_mic_t *)st;
+ partlen = sizeof(fw_stats_mic_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " mic_rx_pkts %u, mic_calc_fail %u\n",
+ part_str,
+ mic->mic_rx_pkts,
+ mic->mic_calc_fail);
+
+ part_str = "AES";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ aes = (fw_stats_aes_t *)st;
+ partlen = sizeof(fw_stats_aes_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " aes_enc_fail %u, aes_dec_fail %u\n"
+ " aes_enc_pkts %u, aes_dec_pkts %u\n"
+ " aes_enc_irq %u, aes_dec_irq %u\n",
+ part_str,
+ aes->aes_enc_fail,
+ aes->aes_dec_fail,
+ aes->aes_enc_pkts,
+ aes->aes_dec_pkts,
+ aes->aes_enc_irq,
+ aes->aes_dec_irq);
+
+ part_str = "event";
+
+ if (st == st_end)
+ goto fw_stats_end;
+
+ evt = (fw_stats_event_t *)st;
+ partlen = sizeof(fw_stats_event_t);
+ st += partlen;
+
+ if (st > st_end)
+ goto fw_stats_fail;
+
+ p += sprintf(p,
+ "%s:\n"
+ " heartbeat %u, calibration %u\n"
+ " rx_mismatch %u, rx_mem_empty %u, rx_pool %u\n"
+ " oom_late %u\n"
+ " phy_tx_err %u, tx_stuck %u\n",
+ part_str,
+ evt->heartbeat,
+ evt->calibration,
+ evt->rx_mismatch,
+ evt->rx_mem_empty,
+ evt->rx_pool,
+ evt->oom_late,
+ evt->phy_tx_err,
+ evt->tx_stuck);
+
+ if (st < st_end)
+ goto fw_stats_bigger;
+
+ goto fw_stats_end;
+
+fw_stats_fail:
+ st -= partlen;
+ p += sprintf(p,
+ "failed at %s part (size %u), offset %u (struct size %u), PLEASE REPORT!\n", part_str, partlen, (int)st - (int)fw_stats, len);
+fw_stats_bigger:
+ for (; st < st_end; st += 4)
+ p += sprintf(p,
+ "UNKN%3d: %u\n", (int)st - (int)fw_stats, *(u32 *)st);
+
+fw_stats_end:
kfree(fw_stats);
FN_EXIT1(p - buf);
diff -urN acx-20060202/pci.c acx-20060202_stats/pci.c
--- acx-20060202/pci.c 2006-02-01 10:49:33.000000000 +0100
+++ acx-20060202_stats/pci.c 2006-02-04 19:30:29.000000000 +0100
@@ -1058,13 +1058,13 @@
/* Test for IDLE state */
if (!cmd_status)
break;
- if (counter % 5 == 0) {
+ if (counter % 8 == 0) {
if (time_after(jiffies, timeout)) {
counter = 0;
break;
}
- /* we waited 5 iterations, no luck. Sleep 5 ms */
- acx_s_msleep(5);
+ /* we waited 8 iterations, no luck. Sleep 8 ms */
+ acx_s_msleep(8);
}
} while (likely(--counter));
@@ -1124,13 +1124,13 @@
break;
}
- if (counter % 5 == 0) {
+ if (counter % 8 == 0) {
if (time_after(jiffies, timeout)) {
counter = 0;
break;
}
- /* we waited 5 iterations, no luck. Sleep 5 ms */
- acx_s_msleep(5);
+ /* we waited 8 iterations, no luck. Sleep 8 ms */
+ acx_s_msleep(8);
}
} while (likely(--counter));
@@ -2307,7 +2307,9 @@
while (1) {
hostdesc = &adev->rxhostdesc_start[tail];
/* advance tail regardless of outcome of the below test */
- tail = (tail + 1) % RX_CNT;
+ /* smaller/less writes than: tail = (tail + 1) % RX_CNT; */
+ if (++tail >= RX_CNT)
+ tail = 0;
if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
&& (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
@@ -2338,7 +2340,9 @@
|| !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
break;
- tail = (tail + 1) % RX_CNT;
+ /* slower: tail = (tail + 1) % RX_CNT; */
+ if (++tail >= RX_CNT)
+ tail = 0;
}
end:
adev->rx_tail = tail;
@@ -3065,7 +3069,10 @@
}
/* returning current descriptor, so advance to next free one */
- adev->tx_head = (head + 1) % TX_CNT;
+ /* slower: adev->tx_head = (head + 1) % TX_CNT; */
+ adev->tx_head = head + 1;
+ if (adev->tx_head >= TX_CNT)
+ adev->tx_head = 0;
end:
FN_EXIT0;
@@ -3497,7 +3504,9 @@
finger, ack_failures, rts_failures, rts_ok, r100);
/* update pointer for descr to be cleaned next */
- finger = (finger + 1) % TX_CNT;
+ /* slower: finger = (finger + 1) % TX_CNT; */
+ if (++finger >= TX_CNT)
+ finger = 0;
}
/* remember last position */
diff -urN acx-20060202/usb.c acx-20060202_stats/usb.c
--- acx-20060202/usb.c 2006-02-01 10:49:34.000000000 +0100
+++ acx-20060202_stats/usb.c 2006-02-03 10:24:07.000000000 +0100
@@ -1576,7 +1576,10 @@
head = adev->tx_head;
do {
- head = (head + 1) % ACX_TX_URB_CNT;
+ /* slower: head = (head + 1) % ACX_TX_URB_CNT; */
+ if (++head >= ACX_TX_URB_CNT)
+ head = 0;
+
if (!adev->usb_tx[head].busy) {
log(L_USBRXTX, "allocated tx %d\n", head);
tx = &adev->usb_tx[head];
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
^ permalink raw reply
* [PATCH] acxsm: merge from acx 0.3.32
From: Denis Vlasenko @ 2006-02-03 12:14 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev, acx100-devel
[-- Attachment #1: Type: text/plain, Size: 298 bytes --]
Standalone acx driver had several fixes since
acxsm fork, this patch merges them:
- initial support for new TNETW1450 USB chip
- support for firmware 2.3.1.31
Also we had one report that acxsm is actually working.
That's quite unexpected.
Signed-off-by: Denis Vlasenko <vda@ilport.com.ua>
--
vda
[-- Attachment #2: acx.diff --]
[-- Type: text/x-diff, Size: 60797 bytes --]
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/Changelog wireless-2.6.git.acx/drivers/net/wireless/tiacx/Changelog
--- wireless-2.6.git/drivers/net/wireless/tiacx/Changelog Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/Changelog Fri Feb 3 13:38:12 2006
@@ -70,6 +70,12 @@ TODO: from Efthym <efthym@gmx.net>:
13:13:32 wlan0: tx error 0x20, buf 05!
13:13:32 wlan0: tx error 0x20, buf 06!
+[20060203] 0.4.3
+* merge from acx 0.3.32
+
+[20060123] 0.4.2
+* conv.c removed
+
[20060117] 0.4.1
* Lots of code commented out
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/acx_config.h wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_config.h
--- wireless-2.6.git/drivers/net/wireless/tiacx/acx_config.h Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_config.h Fri Feb 3 13:38:12 2006
@@ -1,10 +1,10 @@
-#define ACX_RELEASE "v0.4.2"
+#define ACX_RELEASE "v0.4.3"
/* set to 0 if you don't want any debugging code to be compiled in */
/* set to 1 if you want some debugging */
/* set to 2 if you want extensive debug log */
#define ACX_DEBUG 2
-#define ACX_DEFAULT_MSG 0
+#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT)
/* assume 32bit I/O width
* (16bit is also compatible with Compact Flash) */
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/acx_func.h wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_func.h
--- wireless-2.6.git/drivers/net/wireless/tiacx/acx_func.h Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_func.h Fri Feb 3 13:38:12 2006
@@ -565,14 +565,7 @@ acx_l_tx_data(acx_device_t *adev, tx_t *
static inline wlan_hdr_t*
acx_get_wlan_hdr(acx_device_t *adev, const rxbuffer_t *rxbuf)
{
- if (!(adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR))
- return (wlan_hdr_t*)&rxbuf->hdr_a3;
-
- /* take into account phy header in front of packet */
- if (IS_ACX111(adev))
- return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 8);
-
- return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 4);
+ return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + adev->phy_header_len);
}
void acxpci_l_power_led(acx_device_t *adev, int enable);
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/acx_struct.h wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_struct.h
--- wireless-2.6.git/drivers/net/wireless/tiacx/acx_struct.h Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/acx_struct.h Fri Feb 3 13:38:12 2006
@@ -105,6 +105,10 @@ enum { acx_debug = 0 };
#define DEVTYPE_PCI 0
#define DEVTYPE_USB 1
+#if !defined(CONFIG_ACX_PCI) && !defined(CONFIG_ACX_USB)
+#error Driver must include PCI and/or USB support. You selected neither.
+#endif
+
#if defined(CONFIG_ACX_PCI)
#if !defined(CONFIG_ACX_USB)
#define IS_PCI(adev) 1
@@ -148,6 +152,7 @@ enum { acx_debug = 0 };
#define RADIO_UNKNOWN_17 0x17
/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */
#define RADIO_UNKNOWN_19 0x19
+#define RADIO_UNKNOWN_1B 0x1b /* radio in SafeCom SWLUT-54125 USB adapter; entirely unknown!! */
/* Controller Commands */
/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */
@@ -174,13 +179,18 @@ enum { acx_debug = 0 };
#define ACX1xx_CMD_WAKE 0x10
#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */
#define ACX100_CMD_INIT_MEMORY 0x12
+#define ACX1FF_CMD_DISABLE_RADIO 0x12 /* new firmware? TNETW1450? */
#define ACX1xx_CMD_CONFIG_BEACON 0x13
#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14
#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15
#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16
-#define ACX1xx_CMD_TEST 0x17
+#define ACX1xx_CMD_FCC_TEST 0x17
#define ACX1xx_CMD_RADIOINIT 0x18
#define ACX111_CMD_RADIOCALIB 0x19
+#define ACX1FF_CMD_NOISE_HISTOGRAM 0x1c /* new firmware? TNETW1450? */
+#define ACX1FF_CMD_RX_RESET 0x1d /* new firmware? TNETW1450? */
+#define ACX1FF_CMD_LNA_CONTROL 0x20 /* new firmware? TNETW1450? */
+#define ACX1FF_CMD_CONTROL_DBG_TRACE 0x21 /* new firmware? TNETW1450? */
/* 'After Interrupt' Commands */
#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01
@@ -229,18 +239,22 @@ enum { acx_debug = 0 };
/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */
DEF_IE(1xx_IE_UNKNOWN_00 ,0x0000, -1); /* mapped to cfgInvalid in FW150 */
DEF_IE(100_IE_ACX_TIMER ,0x0001, 0x10);
-DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06);
+DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06); /* TNETW1450: length 0x18!! */
DEF_IE(1xx_IE_QUEUE_CONFIG ,0x0003, 0x1c);
DEF_IE(100_IE_BLOCK_SIZE ,0x0004, 0x02);
+DEF_IE(1FF_IE_SLOT_TIME ,0x0004, 0x08); /* later firmware versions only? */
DEF_IE(1xx_IE_MEMORY_CONFIG_OPTIONS ,0x0005, 0x14);
-DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01);
+DEF_IE(1FF_IE_QUEUE_HEAD ,0x0005, 0x14 /* FIXME: length? */);
+DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01); /* TNETW1450: length 2 */
DEF_IE(100_IE_WEP_OPTIONS ,0x0007, 0x03);
DEF_IE(111_IE_RADIO_BAND ,0x0007, -1);
-DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? */
+DEF_IE(1FF_IE_TIMING_CFG ,0x0007, -1); /* later firmware versions; TNETW1450 only? */
DEF_IE(100_IE_SSID ,0x0008, 0x20); /* huh? */
+DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? TNETW1450 has length 0x40!! */
DEF_IE(1xx_IE_SCAN_STATUS ,0x0009, 0x04); /* mapped to cfgInvalid in FW150 */
DEF_IE(1xx_IE_ASSOC_ID ,0x000a, 0x02);
DEF_IE(1xx_IE_UNKNOWN_0B ,0x000b, -1); /* mapped to cfgInvalid in FW150 */
+DEF_IE(1FF_IE_TX_POWER_LEVEL_TABLE ,0x000b, 0x18); /* later firmware versions; TNETW1450 only? */
DEF_IE(100_IE_UNKNOWN_0C ,0x000c, -1); /* very small implementation in FW150! */
/* ACX100 has an equivalent struct in the cmd mailbox directly after reset.
* 0x14c seems extremely large, will trash stack on failure (memset!)
@@ -253,30 +267,47 @@ DEF_IE(1xx_IE_RXCONFIG ,0x0010, 0x04);
DEF_IE(100_IE_UNKNOWN_11 ,0x0011, -1); /* NONBINARY: large implementation in FW150! link quality readings or so? */
DEF_IE(111_IE_QUEUE_THRESH ,0x0011, -1);
DEF_IE(100_IE_UNKNOWN_12 ,0x0012, -1); /* NONBINARY: VERY large implementation in FW150!! */
-DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, -1);
-DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c);
+DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, /* -1 */ 2);
+DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c); /* TNETW1450: length 0x134!! */
+DEF_IE(1FF_IE_RX_INTR_CONFIG ,0x0014, 0x14); /* later firmware versions, TNETW1450 only? */
DEF_IE(1xx_IE_FEATURE_CONFIG ,0x0015, 0x08);
DEF_IE(111_IE_KEY_CHOOSE ,0x0016, 0x04); /* for rekeying. really len=4?? */
+DEF_IE(1FF_IE_MISC_CONFIG_TABLE ,0x0017, 0x04); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_WONE_CONFIG ,0x0018, -1); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_TID_CONFIG ,0x001a, 0x2c); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_CALIB_ASSESSMENT ,0x001e, 0x04); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_BEACON_FILTER_OPTIONS ,0x001f, 0x02); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_LOW_RSSI_THRESH_OPT ,0x0020, 0x04); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_NOISE_HISTOGRAM_RESULTS ,0x0021, 0x30); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_PACKET_DETECT_THRESH ,0x0023, 0x04); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_TX_CONFIG_OPTIONS ,0x0024, 0x04); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_CCA_THRESHOLD ,0x0025, 0x02); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_EVENT_MASK ,0x0026, 0x08); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_DTIM_PERIOD ,0x0027, 0x02); /* later firmware versions, TNETW1450 only? */
+DEF_IE(1FF_IE_ACI_CONFIG_SET ,0x0029, 0x06); /* later firmware versions; maybe TNETW1450 only? */
+DEF_IE(1FF_IE_EEPROM_VER ,0x0030, 0x04); /* later firmware versions; maybe TNETW1450 only? */
DEF_IE(1xx_IE_DOT11_STATION_ID ,0x1001, 0x06);
DEF_IE(100_IE_DOT11_UNKNOWN_1002 ,0x1002, -1); /* mapped to cfgInvalid in FW150 */
-DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150 */
+DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150; TNETW1450 has length 2!! */
DEF_IE(100_IE_DOT11_BEACON_PERIOD ,0x1003, 0x02); /* mapped to cfgInvalid in FW150 */
DEF_IE(1xx_IE_DOT11_DTIM_PERIOD ,0x1004, -1); /* mapped to cfgInvalid in FW150 */
-DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01);
-DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01);
-DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys */
+DEF_IE(1FF_IE_DOT11_MAX_RX_LIFETIME ,0x1004, -1); /* later firmware versions; maybe TNETW1450 only? */
+DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01); /* TNETW1450: length 2 */
+DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01); /* TNETW1450: length 2 */
+DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys; TNETW1450 has length 0x24!! */
DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04);
DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1);
DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02);
//It's harmless to have larger struct. Use USB case always.
DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); /* in fact len=1 for PCI */
DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */
-DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01);
+DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01); /* TNETW1450 has length 2!! */
DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); /* in fact len=1 for PCI */
//USB doesn't return anything - len==0?!
DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04);
-DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID */
+DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID; TNETW1450: length 2 */
DEF_IE(100_IE_DOT11_UNKNOWN_1011 ,0x1011, -1); /* mapped to cfgInvalid in FW150 */
+DEF_IE(1FF_IE_DOT11_CURR_5GHZ_REGDOM ,0x1011, -1); /* later firmware versions; maybe TNETW1450 only? */
DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012, -1); /* mapped to cfgInvalid in FW150 */
DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */
@@ -479,15 +510,15 @@ typedef struct phy_hdr {
* Some seem to have different meanings... */
#define RXBUF_HDRSIZE 12
-#define PHY_HDR(rxbuf) ((phy_hdr_t*)&rxbuf->hdr_a3)
-#define RXBUF_BYTES_RCVD(rxbuf) (le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff)
+#define RXBUF_BYTES_RCVD(adev, rxbuf) \
+ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) - (adev)->phy_header_len)
#define RXBUF_BYTES_USED(rxbuf) \
- ((le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE)
+ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE)
/* USBism */
-#define RXBUF_IS_TXSTAT(rxbuf) (le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0x8000)
+#define RXBUF_IS_TXSTAT(rxbuf) (le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0x8000)
/*
mac_cnt_rcvd:
- 12 bits: length of frame from control field to last byte of FCS
+ 12 bits: length of frame from control field to first byte of FCS
3 bits: reserved
1 bit: 1 = it's a tx status info, not a rx packet (USB only)
@@ -603,31 +634,6 @@ typedef struct fw_ver {
#define FW_ID_SIZE 20
-
-/*--- WEP stuff --------------------------------------------------------------*/
-#define DOT11_MAX_DEFAULT_WEP_KEYS 4
-
-/* non-firmware struct, no packing necessary */
-typedef struct wep_key {
- size_t size; /* most often used member first */
- u8 index;
- u8 key[29];
- u16 strange_filler;
-} wep_key_t; /* size = 264 bytes (33*8) */
-/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key
- * (strange_filler)? */
-
-/* non-firmware struct, no packing necessary */
-typedef struct key_struct {
- u8 addr[ETH_ALEN]; /* 0x00 */
- u16 filler1; /* 0x06 */
- u32 filler2; /* 0x08 */
- u32 index; /* 0x0c */
- u16 len; /* 0x10 */
- u8 key[29]; /* 0x12; is this long enough??? */
-} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */
-
-
/*--- Client (peer) info -----------------------------------------------------*/
/* adev->sta_list[] is used for:
** accumulating and processing of scan results
@@ -1108,6 +1114,9 @@ struct acx_device {
/* most frequent accesses first (dereferencing and cache line!) */
/*** Locking ***/
+ /* FIXME: try to convert semaphore to more efficient mutex according
+ to Ingo Molnar's docs (but not before driver is in mainline or
+ pre-mutex Linux 2.6.10 is very outdated). */
struct semaphore sem;
spinlock_t lock;
#if defined(PARANOID_LOCKING) /* Lock debugging */
@@ -1286,14 +1295,6 @@ struct acx_device {
u8 rate_supported_len;
u8 rate_supported[13];
- /*** Encryption settings (WEP) ***/
- u32 auth_alg; /* used in transmit_authen1 */
- u8 wep_enabled;
- u8 wep_restricted;
- u8 wep_current_index;
- wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */
- key_struct_t wep_key_struct[10];
-
/*** Unknown ***/
u8 dtim_interval;
@@ -1303,6 +1304,7 @@ struct acx_device {
u16 memblocksize;
unsigned int tx_free;
unsigned int tx_head; /* keep as close as possible to Tx stuff below (cache line) */
+ u16 phy_header_len;
/*************************************************************************
*** PCI/USB/... must be last or else hw agnostic code breaks horribly ***
@@ -1380,9 +1382,10 @@ struct acx_device {
static inline acx_device_t*
ndev2adev(struct net_device *ndev)
{
- return ieee80211softmac_priv(ndev);
+ return ieee80211softmac_priv(ndev);
}
+
/* For use with ACX1xx_IE_RXCONFIG */
/* bit description
* 13 include additional header (length etc.) *required*
@@ -1852,7 +1855,7 @@ typedef struct acx_joinbss {
*/
typedef struct mem_read_write {
u16 addr ACX_PACKED;
- u16 type ACX_PACKED; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg. */
+ u16 type ACX_PACKED; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg.; or maybe it's actually 0x30 for MAC? Better verify it by writing and reading back and checking whether the value holds! */
u32 len ACX_PACKED;
u32 data ACX_PACKED;
} mem_read_write_t;
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/common.c wireless-2.6.git.acx/drivers/net/wireless/tiacx/common.c
--- wireless-2.6.git/drivers/net/wireless/tiacx/common.c Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/common.c Fri Feb 3 13:38:12 2006
@@ -631,13 +631,6 @@ acx_s_get_firmware_version(acx_device_t
printk("acx: firmware '%s' is known to be buggy, "
"please upgrade\n", adev->firmware_version);
}
- if (adev->firmware_numver == 0x02030131) {
- /* With this one, all rx packets look mangled
- ** Most probably we simply do not know how to use it
- ** properly */
- printk("acx: firmware '%s' does not work well "
- "with this driver\n", adev->firmware_version);
- }
}
adev->firmware_id = le32_to_cpu(fw.hw_id);
@@ -655,6 +648,9 @@ acx_s_get_firmware_version(acx_device_t
case 0x03010000:
adev->chip_name = "TNETW1130";
break;
+ case 0x04030000: /* 0x04030101 is TNETW1450 */
+ adev->chip_name = "TNETW1450";
+ break;
default:
printk("acx: unknown chip ID 0x%08X, "
"please report\n", adev->firmware_id);
@@ -679,9 +675,6 @@ acx_display_hardware_details(acx_device_
switch (adev->radio_type) {
case RADIO_MAXIM_0D:
- /* hmm, the DWL-650+ seems to have two variants,
- * according to a windows driver changelog comment:
- * RFMD and Maxim. */
radio_str = "Maxim";
break;
case RADIO_RFMD_11:
@@ -701,8 +694,11 @@ acx_display_hardware_details(acx_device_
case RADIO_UNKNOWN_19:
radio_str = "A radio used by Safecom cards?! Please report";
break;
+ case RADIO_UNKNOWN_1B:
+ radio_str = "An unknown radio used by TNETW1450 USB adapters";
+ break;
default:
- radio_str = "UNKNOWN, please report the radio type name!";
+ radio_str = "UNKNOWN, please report radio type name!";
break;
}
@@ -861,6 +857,32 @@ acx100_ie_len[] = {
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
+ ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
+ ACX1FF_IE_WONE_CONFIG_LEN,
+ 0,
+ ACX1FF_IE_TID_CONFIG_LEN,
+ 0,
+ 0,
+ 0,
+ ACX1FF_IE_CALIB_ASSESSMENT_LEN,
+ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
+ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
+ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
+ 0,
+ ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
+ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
+ ACX1FF_IE_CCA_THRESHOLD_LEN,
+ ACX1FF_IE_EVENT_MASK_LEN,
+ ACX1FF_IE_DTIM_PERIOD_LEN,
+ 0,
+ ACX1FF_IE_ACI_CONFIG_SET_LEN,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ACX1FF_IE_EEPROM_VER_LEN,
};
static const u16
@@ -912,6 +934,32 @@ acx111_ie_len[] = {
0,
ACX1xx_IE_FEATURE_CONFIG_LEN,
ACX111_IE_KEY_CHOOSE_LEN,
+ ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
+ ACX1FF_IE_WONE_CONFIG_LEN,
+ 0,
+ ACX1FF_IE_TID_CONFIG_LEN,
+ 0,
+ 0,
+ 0,
+ ACX1FF_IE_CALIB_ASSESSMENT_LEN,
+ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
+ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
+ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
+ 0,
+ ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
+ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
+ ACX1FF_IE_CCA_THRESHOLD_LEN,
+ ACX1FF_IE_EVENT_MASK_LEN,
+ ACX1FF_IE_DTIM_PERIOD_LEN,
+ 0,
+ ACX1FF_IE_ACI_CONFIG_SET_LEN,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ACX1FF_IE_EEPROM_VER_LEN,
};
static const u16
@@ -992,6 +1040,8 @@ acx_s_interrogate_debug(acx_device_t *ad
u16 len;
int res;
+ /* FIXME: no check whether this exceeds the array yet.
+ * We should probably remember the number of entries... */
if (type < 0x1000)
len = adev->ie_len[type];
else
@@ -1137,8 +1187,8 @@ acx_s_proc_diag_output(char *buf, acx_de
"WEP ena %d, restricted %d, idx %d\n",
adev->essid, adev->essid_active, (int)adev->essid_len,
adev->essid_for_assoc, adev->nick,
- adev->wep_enabled, adev->wep_restricted,
- adev->wep_current_index);
+ adev->ieee->sec.enabled, adev->ieee->sec.auth_mode,
+ adev->ieee->sec.active_key);
p += sprintf(p, "dev_addr "MACSTR"\n", MAC(adev->dev_addr));
p += sprintf(p, "bssid "MACSTR"\n", MAC(adev->bssid));
p += sprintf(p, "ap_filter "MACSTR"\n", MAC(adev->ap));
@@ -2087,6 +2137,12 @@ acx_s_initialize_rx_config(acx_device_t
}
adev->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR;
+ if ((adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR)
+ || (adev->firmware_numver >= 0x02000000))
+ adev->phy_header_len = IS_ACX111(adev) ? 8 : 4;
+ else
+ adev->phy_header_len = 0;
+
log(L_INIT, "setting RXconfig to %04X:%04X\n",
adev->rx_config_1, adev->rx_config_2);
cfg.rx_cfg1 = cpu_to_le16(adev->rx_config_1);
@@ -2105,6 +2161,19 @@ acx_s_set_defaults(acx_device_t *adev)
FN_ENTER;
+ /* do it before getting settings, prevent bogus channel 0 warning */
+ adev->channel = 1;
+
+ /* query some settings from the card.
+ * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
+ * query is REQUIRED, otherwise the card won't work correctly! */
+ adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
+ /* Only ACX100 supports ED and CCA */
+ if (IS_ACX100(adev))
+ adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
+
+ acx_s_update_card_settings(adev);
+
acx_lock(adev, flags);
/* set our global interrupt mask */
@@ -2115,8 +2184,11 @@ acx_s_set_defaults(acx_device_t *adev)
adev->brange_max_quality = 60; /* LED blink max quality is 60 */
adev->brange_time_last_state_change = jiffies;
+ /* copy the MAC address we just got from the card
+ * into our MAC address used during current 802.11 session */
MAC_COPY(adev->dev_addr, adev->ndev->dev_addr);
MAC_BCAST(adev->ap);
+
adev->essid_len =
snprintf(adev->essid, sizeof(adev->essid), "STA%02X%02X%02X",
adev->dev_addr[3], adev->dev_addr[4], adev->dev_addr[5]);
@@ -2131,23 +2203,19 @@ acx_s_set_defaults(acx_device_t *adev)
adev->reg_dom_id = adev->cfgopt_domains.list[0];
}
- adev->channel = 1;
/* 0xffff would be better, but then we won't get a "scan complete"
* interrupt, so our current infrastructure will fail: */
adev->scan_count = 1;
adev->scan_mode = ACX_SCAN_OPT_ACTIVE;
-
adev->scan_duration = 100;
adev->scan_probe_delay = 200;
/* reported to break scanning: adev->scan_probe_delay = adev->cfgopt_probe_delay; */
adev->scan_rate = ACX_SCAN_RATE_1;
- adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
- adev->preamble_mode = 2; /* auto */
+ adev->mode = ACX_MODE_2_STA;
+ adev->ieee->sec.auth_mode = WLAN_AUTH_OPEN;
adev->listen_interval = 100;
adev->beacon_interval = DEFAULT_BEACON_INTERVAL;
- adev->mode = ACX_MODE_2_STA;
- adev->monitor_type = ARPHRD_IEEE80211_PRISM;
adev->dtim_interval = DEFAULT_DTIM_INTERVAL;
adev->msdu_lifetime = DEFAULT_MSDU_LIFETIME;
@@ -2159,6 +2227,7 @@ acx_s_set_defaults(acx_device_t *adev)
adev->short_retry = 7; /* max. retries for (short) non-RTS packets */
adev->long_retry = 4; /* max. retries for long (RTS) packets */
+ adev->preamble_mode = 2; /* auto */
adev->fallback_threshold = 3;
adev->stepup_threshold = 10;
adev->rate_bcast = RATE111_1;
@@ -2170,9 +2239,12 @@ acx_s_set_defaults(acx_device_t *adev)
} else {
adev->rate_oper = RATE111_ACX100_COMPAT;
}
- /* build 802.11 style ratevector (802.11 7.3.2.2) */
+
+ /* Supported Rates element - the rates here are given in units of
+ * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */
acx_l_update_ratevector(adev);
+ /* set some more defaults */
if (IS_ACX111(adev)) {
/* 30mW (15dBm) is default, at least in my acx111 card: */
adev->tx_level_dbm = 15;
@@ -2183,7 +2255,6 @@ acx_s_set_defaults(acx_device_t *adev)
adev->tx_level_dbm = 18;
}
/* adev->tx_level_auto = 1; */
-
if (IS_ACX111(adev)) {
/* start with sensitivity level 1 out of 3: */
adev->sensitivity = 1;
@@ -2204,31 +2275,24 @@ acx_s_set_defaults(acx_device_t *adev)
adev->ps_enhanced_transition_time = 0;
#endif
+ /* These settings will be set in fw on ifup */
adev->set_mask = 0
- /* better re-init the antenna value we got */
- | GETSET_ANTENNA
- | GETSET_TXPOWER
- | SET_RXCONFIG
- /* configure card to do rate fallback when in auto rate mode. */
| GETSET_RETRY
| SET_MSDU_LIFETIME
+ /* configure card to do rate fallback when in auto rate mode */
| SET_RATE_FALLBACK
+ | SET_RXCONFIG
+ | GETSET_TXPOWER
+ /* better re-init the antenna value we got above */
+ | GETSET_ANTENNA
#if POWER_SAVE_80211
| GETSET_POWER_80211
#endif
;
- /* query some settings from the card.
- * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
- * query is REQUIRED, otherwise the card won't work correctly!! */
- adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
- /* Only ACX100 supports ED and CCA */
- if (IS_ACX100(adev))
- adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
acx_unlock(adev, flags);
acx_lock_unhold(); /* hold time 844814 CPU ticks @2GHz */
- acx_s_update_card_settings(adev);
acx_s_initialize_rx_config(adev);
FN_EXIT0;
@@ -2576,14 +2640,14 @@ void
acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf)
{
struct wlan_hdr *hdr;
- unsigned int buf_len;
unsigned int qual;
+ int buf_len;
u16 fc;
hdr = acx_get_wlan_hdr(adev, rxbuf);
- /* length of frame from control field to last byte of FCS */
fc = le16_to_cpu(hdr->fc);
- buf_len = RXBUF_BYTES_RCVD(rxbuf);
+ /* length of frame from control field to first byte of FCS */
+ buf_len = RXBUF_BYTES_RCVD(adev, rxbuf);
if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
|| (acx_debug & L_XFER_BEACON)
@@ -2739,16 +2803,14 @@ acx_l_softmac_process_rxbuf(acx_device_t
struct ieee80211_rx_stats stats;
struct wlan_hdr *hdr;
struct sk_buff *skb;
- int buf_len;
int skb_len;
- int payload_offset;
u16 fc;
FN_ENTER;
+ skb_len = RXBUF_BYTES_RCVD(adev, rxbuf);
hdr = acx_get_wlan_hdr(adev, rxbuf);
fc = le16_to_cpu(hdr->fc);
- buf_len = RXBUF_BYTES_RCVD(rxbuf);
if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
|| (acx_debug & L_XFER_BEACON)
@@ -2758,7 +2820,7 @@ acx_l_softmac_process_rxbuf(acx_device_t
"phystat:%02X phyrate:%u status:%u\n",
acx_get_packet_type_string(fc),
le32_to_cpu(rxbuf->time),
- buf_len,
+ skb_len,
acx_signal_to_winlevel(rxbuf->phy_level),
acx_signal_to_winlevel(rxbuf->phy_snr),
rxbuf->mac_status,
@@ -2768,8 +2830,8 @@ acx_l_softmac_process_rxbuf(acx_device_t
}
if (unlikely(acx_debug & L_DATA)) {
- printk("rx: 802.11 buf[%u]: ", buf_len);
- acx_dump_bytes(hdr, buf_len);
+ printk("rx: 802.11 buf[%u]: ", skb_len);
+ acx_dump_bytes(hdr, skb_len);
}
/* FIXME: should check for Rx errors (rxbuf->mac_status?
@@ -2779,8 +2841,6 @@ acx_l_softmac_process_rxbuf(acx_device_t
/* we are in big luck: the acx100 doesn't modify any of the fields */
/* in the 802.11 frame. just pass this packet into the PF_PACKET */
/* subsystem. yeah. */
- payload_offset = ((u8*)acx_get_wlan_hdr(adev, rxbuf) - (u8*)rxbuf);
- skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset;
/* sanity check */
if (unlikely(skb_len > WLAN_A4FR_MAXLEN_WEP)) {
@@ -2798,7 +2858,7 @@ acx_l_softmac_process_rxbuf(acx_device_t
}
skb_put(skb, skb_len);
- memcpy(skb->data, ((unsigned char*)rxbuf)+payload_offset, skb_len);
+ memcpy(skb->data, hdr, skb_len);
memset(&stats, 0, sizeof(stats));
stats.mac_time = le16_to_cpu(rxbuf->time);
@@ -3477,7 +3537,7 @@ acx_i_timer(unsigned long address)
case ACX_STATUS_3_AUTHENTICATED:
/* was set to 0 by set_status() */
if (++adev->auth_or_assoc_retries < 10) {
- log(L_ASSOC, "resend assoc request (attempt %d)\n",
+ log(L_ASSOC, "resend assoc request (attempt %d)\n",
adev->auth_or_assoc_retries + 1);
//sm? acx_l_transmit_assoc_req(adev);
} else {
@@ -4061,10 +4121,10 @@ acx_l_process_data_frame_master(acx_devi
/* To_DS = 0, From_DS = 1 */
hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi;
- len = RXBUF_BYTES_RCVD(rxbuf);
txbuf = acx_l_get_txbuf(adev, tx);
if (txbuf) {
- memcpy(txbuf, &rxbuf->hdr_a3, len);
+ len = RXBUF_BYTES_RCVD(adev, rxbuf);
+ memcpy(txbuf, hdr, len);
acx_l_tx_data(adev, tx, len);
} else {
acx_l_dealloc_tx(adev, tx);
@@ -4219,7 +4279,7 @@ acx_l_process_mgmt_frame(acx_device_t *a
return NOT_OK;
}
- len = RXBUF_BYTES_RCVD(rxbuf);
+ len = RXBUF_BYTES_RCVD(adev, rxbuf);
if (WF_FC_ISWEPi & hdr->fc)
len -= 0x10;
@@ -5434,14 +5494,14 @@ acx100_s_set_wepkey(acx_device_t *adev)
ie_dot11WEPDefaultKey_t dk;
int i;
- for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
- if (adev->wep_keys[i].size != 0) {
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (adev->ieee->sec.key_sizes[i] != 0) {
log(L_INIT, "setting WEP key: %d with "
- "total size: %d\n", i, (int) adev->wep_keys[i].size);
+ "total size: %d\n", i, (int) adev->ieee->sec.key_sizes[i]);
dk.action = 1;
- dk.keySize = adev->wep_keys[i].size;
+ dk.keySize = adev->ieee->sec.key_sizes[i];
dk.defaultKeyNum = i;
- memcpy(dk.key, adev->wep_keys[i].key, dk.keySize);
+ memcpy(dk.key, adev->ieee->sec.keys[i], dk.keySize);
acx_s_configure(adev, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE);
}
}
@@ -5453,20 +5513,20 @@ acx111_s_set_wepkey(acx_device_t *adev)
acx111WEPDefaultKey_t dk;
int i;
- for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
- if (adev->wep_keys[i].size != 0) {
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (adev->ieee->sec.key_sizes[i] != 0) {
log(L_INIT, "setting WEP key: %d with "
- "total size: %d\n", i, (int) adev->wep_keys[i].size);
+ "total size: %d\n", i, (int) adev->ieee->sec.key_sizes[i]);
memset(&dk, 0, sizeof(dk));
dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */
- dk.keySize = adev->wep_keys[i].size;
+ dk.keySize = adev->ieee->sec.key_sizes[i];
/* are these two lines necessary? */
dk.type = 0; /* default WEP key */
dk.index = 0; /* ignored when setting default key */
dk.defaultKeyNum = i;
- memcpy(dk.key, adev->wep_keys[i].key, dk.keySize);
+ memcpy(dk.key, adev->ieee->sec.keys[i], dk.keySize);
acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk));
}
}
@@ -5513,7 +5573,7 @@ acx100_s_init_wep(acx_device_t *adev)
}
/* let's choose maximum setting: 4 default keys, plus 10 other keys: */
- options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
+ options.NumKeys = cpu_to_le16(WEP_KEYS + 10);
options.WEPOption = 0x00;
log(L_ASSOC, "%s: writing WEP options\n", __func__);
@@ -5521,10 +5581,10 @@ acx100_s_init_wep(acx_device_t *adev)
acx100_s_set_wepkey(adev);
- if (adev->wep_keys[adev->wep_current_index].size != 0) {
+ if (adev->ieee->sec.key_sizes[adev->ieee->sec.active_key] != 0) {
log(L_ASSOC, "setting active default WEP key number: %d\n",
- adev->wep_current_index);
- dk.KeyID = adev->wep_current_index;
+ adev->ieee->sec.active_key);
+ dk.KeyID = adev->ieee->sec.active_key;
acx_s_configure(adev, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */
}
/* FIXME!!! wep_key_struct is filled nowhere! But adev
@@ -6580,7 +6640,7 @@ acx_s_update_card_settings(acx_device_t
acx_s_set_wepkey(adev);
- dkey.KeyID = adev->wep_current_index;
+ dkey.KeyID = adev->ieee->sec.active_key;
log(L_INIT, "setting WEP key %u as default\n", dkey.KeyID);
acx_s_configure(adev, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET);
#ifdef DEBUG_WEP
@@ -6601,7 +6661,7 @@ acx_s_update_card_settings(acx_device_t
/* let's choose maximum setting: 4 default keys,
* plus 10 other keys: */
- options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
+ options.NumKeys = cpu_to_le16(WEP_KEYS + 10);
/* don't decrypt default key only,
* don't override decryption: */
options.WEPOption = 0;
@@ -6904,7 +6964,7 @@ acx_update_capabilities(acx_device_t *ad
/* other types of stations do not emit beacons */
}
- if (adev->wep_restricted) {
+ if (adev->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) {
SET_BIT(cap, WF_MGMT_CAP_PRIVACY);
}
if (adev->cfgopt_dot11ShortPreambleOption) {
@@ -7134,10 +7194,74 @@ module_exit(acx_e_cleanup_module)
//SM
void
-acx_e_ieee80211_set_security(struct net_device *dev,
+acx_e_ieee80211_set_security(struct net_device *ndev,
struct ieee80211_security *sec)
{
-//todo
+/* Shamelessly copied from the rt2x00 project. */
+ acx_device_t *adev = ndev2adev(ndev);
+ unsigned long flags;
+ int i;
+
+ acx_sem_lock(adev);
+ acx_lock(adev, flags);
+
+ for (i = 0; i < WEP_KEYS; ++i) {
+ /* This gives us the flag for the 4 WEP keys. */
+ if (sec->flags & (1 << i)) {
+ adev->ieee->sec.encode_alg[i] = sec->encode_alg[i];
+ adev->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+
+ if (sec->key_sizes[i] != 0) {
+ memcpy(adev->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ /* Make sure WEP flag is set. */
+ adev->ieee->sec.flags |= (1 << i);
+ } else if (sec->level != SEC_LEVEL_1)
+ /* Make sure WEP flag isn't set. */
+ adev->ieee->sec.flags &= ~(1 << i);
+ }
+ SET_BIT(adev->set_mask, SET_WEP_OPTIONS);
+ }
+
+ if (sec->flags & SEC_ACTIVE_KEY) {
+ /* Check the key number is valid. */
+ if (sec->active_key < WEP_KEYS) {
+ adev->ieee->sec.active_key = sec->active_key;
+ adev->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ adev->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ } else
+ adev->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if (sec->flags & SEC_AUTH_MODE) {
+ adev->ieee->sec.auth_mode = sec->auth_mode;
+ adev->ieee->sec.flags |= SEC_AUTH_MODE;
+ SET_BIT(adev->set_mask, SET_WEP_OPTIONS);
+ }
+
+ if (sec->flags & SEC_ENCRYPT) {
+ adev->ieee->sec.encrypt = sec->encrypt;
+ adev->ieee->sec.flags |= SEC_ENCRYPT;
+ SET_BIT(adev->set_mask, GETSET_WEP);
+ }
+
+ if (sec->flags & SEC_ENABLED) {
+ adev->ieee->sec.enabled = sec->enabled;
+ adev->ieee->sec.flags |= SEC_ENABLED;
+ SET_BIT(adev->set_mask, GETSET_WEP);
+ }
+
+ if (sec->flags & SEC_LEVEL) {
+ adev->ieee->sec.level = sec->level;
+ adev->ieee->sec.flags |= SEC_LEVEL;
+ SET_BIT(adev->set_mask, GETSET_WEP);
+ }
+
+ acx_unlock(adev, flags);
+ acx_sem_unlock(adev);
+
+ acx_s_update_card_settings(adev);
}
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/ioctl.c wireless-2.6.git.acx/drivers/net/wireless/tiacx/ioctl.c
--- wireless-2.6.git/drivers/net/wireless/tiacx/ioctl.c Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/ioctl.c Fri Feb 3 13:38:12 2006
@@ -1024,7 +1024,7 @@ acx_ioctl_set_encode(
if (dwrq->length > 0) {
/* if index is 0 or invalid, use default key */
if ((index < 0) || (index > 3))
- index = (int)adev->wep_current_index;
+ index = (int)adev->ieee->sec.active_key;
if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) {
if (dwrq->length > 29)
@@ -1032,26 +1032,26 @@ acx_ioctl_set_encode(
if (dwrq->length > 13) {
/* 29*8 == 232, WEP256 */
- adev->wep_keys[index].size = 29;
+ adev->ieee->sec.key_sizes[index] = 29;
} else if (dwrq->length > 5) {
/* 13*8 == 104bit, WEP128 */
- adev->wep_keys[index].size = 13;
+ adev->ieee->sec.key_sizes[index] = 13;
} else if (dwrq->length > 0) {
/* 5*8 == 40bit, WEP64 */
- adev->wep_keys[index].size = 5;
+ adev->ieee->sec.key_sizes[index] = 5;
} else {
/* disable key */
- adev->wep_keys[index].size = 0;
+ adev->ieee->sec.key_sizes[index] = 0;
}
- memset(adev->wep_keys[index].key, 0,
- sizeof(adev->wep_keys[index].key));
- memcpy(adev->wep_keys[index].key, extra, dwrq->length);
+ memset(adev->ieee->sec.keys[index], 0,
+ sizeof(adev->ieee->sec.keys[index]));
+ memcpy(adev->ieee->sec.keys[index], extra, dwrq->length);
}
} else {
/* set transmit key */
if ((index >= 0) && (index <= 3))
- adev->wep_current_index = index;
+ adev->ieee->sec.active_key = index;
else if (0 == (dwrq->flags & IW_ENCODE_MODE)) {
/* complain if we were not just setting
* the key mode */
@@ -1060,15 +1060,13 @@ acx_ioctl_set_encode(
}
}
- adev->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED);
+ adev->ieee->sec.enabled = !(dwrq->flags & IW_ENCODE_DISABLED);
if (dwrq->flags & IW_ENCODE_OPEN) {
- adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
- adev->wep_restricted = 0;
+ adev->ieee->sec.auth_mode = WLAN_AUTH_OPEN;
} else if (dwrq->flags & IW_ENCODE_RESTRICTED) {
- adev->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
- adev->wep_restricted = 1;
+ adev->ieee->sec.auth_mode = WLAN_AUTH_SHARED_KEY;
}
/* set flag to make sure the card WEP settings get updated */
@@ -1078,11 +1076,11 @@ acx_ioctl_set_encode(
dwrq->length, extra, dwrq->flags);
for (index = 0; index <= 3; index++) {
- if (adev->wep_keys[index].size) {
+ if (adev->ieee->sec.key_sizes[index]) {
log(L_IOCTL, "index=%d, size=%d, key at 0x%p\n",
- adev->wep_keys[index].index,
- (int) adev->wep_keys[index].size,
- adev->wep_keys[index].key);
+ adev->ieee->sec.active_key,
+ (int) adev->ieee->sec.key_sizes[index],
+ adev->ieee->sec.keys[index]);
}
}
result = -EINPROGRESS;
@@ -1111,18 +1109,18 @@ acx_ioctl_get_encode(
FN_ENTER;
- if (adev->wep_enabled == 0) {
+ if (adev->ieee->sec.enabled == 0) {
dwrq->flags = IW_ENCODE_DISABLED;
} else {
if ((index < 0) || (index > 3))
- index = (int)adev->wep_current_index;
+ index = (int)adev->ieee->sec.active_key;
- dwrq->flags = (adev->wep_restricted == 1) ?
+ dwrq->flags = (adev->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) ?
IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
- dwrq->length = adev->wep_keys[index].size;
+ dwrq->length = adev->ieee->sec.key_sizes[index];
- memcpy(extra, adev->wep_keys[index].key,
- adev->wep_keys[index].size);
+ memcpy(extra, adev->ieee->sec.keys[index],
+ adev->ieee->sec.key_sizes[index]);
}
/* set the current index */
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/pci.c wireless-2.6.git.acx/drivers/net/wireless/tiacx/pci.c
--- wireless-2.6.git/drivers/net/wireless/tiacx/pci.c Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/pci.c Fri Feb 3 13:38:12 2006
@@ -476,23 +476,23 @@ acxpci_s_write_phy_reg(acx_device_t *ade
**
** Arguments:
** adev wlan device structure
-** apfw_image firmware image.
+** fw_image firmware image.
**
** Returns:
** 1 firmware image corrupted
** 0 success
*/
static int
-acxpci_s_write_fw(acx_device_t *adev, const firmware_image_t *apfw_image, u32 offset)
+acxpci_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset)
{
int len, size;
u32 sum, v32;
/* we skip the first four bytes which contain the control sum */
- const u8 *image = (u8*)apfw_image + 4;
+ const u8 *p = (u8*)fw_image + 4;
/* start the image checksum by adding the image size value */
- sum = image[0]+image[1]+image[2]+image[3];
- image += 4;
+ sum = p[0]+p[1]+p[2]+p[3];
+ p += 4;
write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
@@ -505,12 +505,12 @@ acxpci_s_write_fw(acx_device_t *adev, co
#endif
len = 0;
- size = le32_to_cpu(apfw_image->size) & (~3);
+ size = le32_to_cpu(fw_image->size) & (~3);
while (likely(len < size)) {
- v32 = be32_to_cpu(*(u32*)image);
- sum += image[0]+image[1]+image[2]+image[3];
- image += 4;
+ v32 = be32_to_cpu(*(u32*)p);
+ sum += p[0]+p[1]+p[2]+p[3];
+ p += 4;
len += 4;
#if NO_AUTO_INCREMENT
@@ -521,10 +521,10 @@ acxpci_s_write_fw(acx_device_t *adev, co
}
log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n",
- size, sum, le32_to_cpu(apfw_image->chksum));
+ size, sum, le32_to_cpu(fw_image->chksum));
/* compare our checksum with the stored image checksum */
- return (sum != le32_to_cpu(apfw_image->chksum));
+ return (sum != le32_to_cpu(fw_image->chksum));
}
@@ -536,25 +536,25 @@ acxpci_s_write_fw(acx_device_t *adev, co
**
** Arguments:
** adev wlan device structure
-** apfw_image firmware image.
+** fw_image firmware image.
**
** Returns:
** NOT_OK firmware image corrupted or not correctly written
** OK success
*/
static int
-acxpci_s_validate_fw(acx_device_t *adev, const firmware_image_t *apfw_image,
+acxpci_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image,
u32 offset)
{
u32 sum, v32, w32;
int len, size;
int result = OK;
/* we skip the first four bytes which contain the control sum */
- const u8 *image = (u8*)apfw_image + 4;
+ const u8 *p = (u8*)fw_image + 4;
/* start the image checksum by adding the image size value */
- sum = image[0]+image[1]+image[2]+image[3];
- image += 4;
+ sum = p[0]+p[1]+p[2]+p[3];
+ p += 4;
write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
@@ -566,11 +566,11 @@ acxpci_s_validate_fw(acx_device_t *adev,
#endif
len = 0;
- size = le32_to_cpu(apfw_image->size) & (~3);
+ size = le32_to_cpu(fw_image->size) & (~3);
while (likely(len < size)) {
- v32 = be32_to_cpu(*(u32*)image);
- image += 4;
+ v32 = be32_to_cpu(*(u32*)p);
+ p += 4;
len += 4;
#if NO_AUTO_INCREMENT
@@ -593,7 +593,7 @@ acxpci_s_validate_fw(acx_device_t *adev,
/* sum control verification */
if (result != NOT_OK) {
- if (sum != le32_to_cpu(apfw_image->chksum)) {
+ if (sum != le32_to_cpu(fw_image->chksum)) {
printk("acx: FATAL: firmware upload: "
"checksums don't match!\n");
result = NOT_OK;
@@ -612,10 +612,10 @@ acxpci_s_validate_fw(acx_device_t *adev,
static int
acxpci_s_upload_fw(acx_device_t *adev)
{
- firmware_image_t *apfw_image = NULL;
+ firmware_image_t *fw_image = NULL;
int res = NOT_OK;
int try;
- u32 size;
+ u32 file_size;
char filename[sizeof("tiacx1NNcNN")];
FN_ENTER;
@@ -625,22 +625,22 @@ acxpci_s_upload_fw(acx_device_t *adev)
snprintf(filename, sizeof(filename), "tiacx1%02dc%02X",
IS_ACX111(adev)*11, adev->radio_type);
- apfw_image = acx_s_read_fw(&adev->pdev->dev, filename, &size);
- if (!apfw_image) {
+ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
+ if (!fw_image) {
adev->need_radio_fw = 1;
filename[sizeof("tiacx1NN")-1] = '\0';
- apfw_image = acx_s_read_fw(&adev->pdev->dev, filename, &size);
- if (!apfw_image) {
+ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
+ if (!fw_image) {
FN_EXIT1(NOT_OK);
return NOT_OK;
}
}
for (try = 1; try <= 5; try++) {
- res = acxpci_s_write_fw(adev, apfw_image, 0);
+ res = acxpci_s_write_fw(adev, fw_image, 0);
log(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res);
if (OK == res) {
- res = acxpci_s_validate_fw(adev, apfw_image, 0);
+ res = acxpci_s_validate_fw(adev, fw_image, 0);
log(L_DEBUG|L_INIT, "acx_validate_fw "
"(main/combined):%d\n", res);
}
@@ -654,7 +654,7 @@ acxpci_s_upload_fw(acx_device_t *adev)
acx_s_msleep(1000); /* better wait for a while... */
}
- vfree(apfw_image);
+ vfree(fw_image);
FN_EXIT1(res);
return res;
@@ -930,22 +930,22 @@ acxpci_s_reset_dev(acx_device_t *adev)
acx_unlock(adev, flags);
/* need to know radio type before fw load */
- /* Need to wait for arrival of this information in a loop,
- * most probably since eCPU runs some init code from EEPROM
- * (started burst read in reset_mac()) which also
- * sets the radio type ID */
-
- count = 0xffff;
- do {
- hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION);
- if (!--count) {
- msg = "eCPU didn't indicate radio type";
- goto end_fail;
- }
- cpu_relax();
- } while (!(hardware_info & 0xff00)); /* radio type still zero? */
-
- /* printk("DEBUG: count %d\n", count); */
+ /* Need to wait for arrival of this information in a loop,
+ * most probably since eCPU runs some init code from EEPROM
+ * (started burst read in reset_mac()) which also
+ * sets the radio type ID */
+
+ count = 0xffff;
+ do {
+ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION);
+ if (!--count) {
+ msg = "eCPU didn't indicate radio type";
+ goto end_fail;
+ }
+ cpu_relax();
+ } while (!(hardware_info & 0xff00)); /* radio type still zero? */
+
+ /* printk("DEBUG: count %d\n", count); */
adev->form_factor = hardware_info & 0xff;
adev->radio_type = hardware_info >> 8;
@@ -1641,6 +1641,10 @@ acxpci_e_probe(struct pci_dev *pdev, con
adev->softmac = ieee80211_priv(ndev);
adev->softmac->set_channel = acx_e_ieee80211_set_chan;
+ adev->ieee->sec.encrypt = 0;
+ adev->ieee->sec.enabled = 0;
+ adev->ieee->sec.auth_mode = WLAN_AUTH_OPEN;
+
#ifdef NONESSENTIAL_FEATURES
acx_show_card_eeprom_id(adev);
#endif /* NONESSENTIAL_FEATURES */
@@ -1668,9 +1672,6 @@ acxpci_e_probe(struct pci_dev *pdev, con
if (OK != acxpci_s_reset_dev(adev))
goto fail_reset;
- if (OK != acxpci_read_eeprom_byte(adev, 0x05, &adev->eeprom_version))
- goto fail_read_eeprom_version;
-
if (IS_ACX100(adev)) {
/* ACX100: configopt struct in cmd mailbox - directly after reset */
memcpy_fromio(&co, adev->cmd_area, sizeof(co));
@@ -1728,7 +1729,6 @@ fail_register_netdev:
pci_set_drvdata(pdev, NULL);
fail_init_mac:
-fail_read_eeprom_version:
fail_reset:
acxpci_s_device_chain_remove(ndev);
diff -urpN wireless-2.6.git/drivers/net/wireless/tiacx/usb.c wireless-2.6.git.acx/drivers/net/wireless/tiacx/usb.c
--- wireless-2.6.git/drivers/net/wireless/tiacx/usb.c Thu Feb 2 17:17:50 2006
+++ wireless-2.6.git.acx/drivers/net/wireless/tiacx/usb.c Fri Feb 3 13:54:06 2006
@@ -78,14 +78,25 @@
/***********************************************************************
*/
+/* ACX100 (TNETW1100) USB device: D-Link DWL-120+ */
#define ACX100_VENDOR_ID 0x2001
#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01
#define ACX100_PRODUCT_ID_BOOTED 0x3B00
+/* TNETW1450 USB devices */
+#define VENDOR_ID_DLINK 0x07b8 /* D-Link Corp. */
+#define PRODUCT_ID_WUG2400 0xb21a /* AboCom WUG2400 or SafeCom SWLUT-54125 */
+#define VENDOR_ID_AVM_GMBH 0x057c
+#define PRODUCT_ID_AVM_WLAN_USB 0x5601
+#define VENDOR_ID_ZCOM 0x0cde
+#define PRODUCT_ID_ZCOM_XG750 0x0017 /* not tested yet */
+#define VENDOR_ID_TI 0x0451
+#define PRODUCT_ID_TI_UNKNOWN 0x60c5 /* not tested yet */
+
#define ACX_USB_CTRL_TIMEOUT 5500 /* steps in ms */
-/* Buffer size for fw upload */
-#define ACX_USB_RWMEM_MAXLEN 2048
+/* Buffer size for fw upload, same for both ACX100 USB and TNETW1450 */
+#define USB_RWMEM_MAXLEN 2048
/* The number of bulk URBs to use */
#define ACX_TX_URB_CNT 8
@@ -106,7 +117,7 @@ static void acxusb_i_complete_rx(struct
static int acxusb_e_open(struct net_device *);
static int acxusb_e_close(struct net_device *);
static void acxusb_i_set_rx_mode(struct net_device *);
-static int acxusb_boot(struct usb_device *);
+static int acxusb_boot(struct usb_device *, int is_tnetw1450, int *radio_type);
static void acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx);
@@ -132,6 +143,10 @@ static const struct usb_device_id
acxusb_ids[] = {
{ USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) },
{ USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) },
+ { USB_DEVICE(VENDOR_ID_DLINK, PRODUCT_ID_WUG2400) },
+ { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB) },
+ { USB_DEVICE(VENDOR_ID_ZCOM, PRODUCT_ID_ZCOM_XG750) },
+ { USB_DEVICE(VENDOR_ID_TI, PRODUCT_ID_TI_UNKNOWN) },
{}
};
@@ -443,107 +458,262 @@ bad:
** firmware and transmitting the checksum, the device resets and appears
** as a new device on the USB bus (the device we can finally deal with)
*/
+static inline int
+acxusb_fw_needs_padding(firmware_image_t *fw_image, unsigned int usb_maxlen)
+{
+ unsigned int num_xfers = ((fw_image->size - 1) / usb_maxlen) + 1;
+
+ return ((num_xfers % 2) == 0);
+}
+
static int
-acxusb_boot(struct usb_device *usbdev)
+acxusb_boot(struct usb_device *usbdev, int is_tnetw1450, int *radio_type)
{
- static const char filename[] = "tiacx100usb";
+ char filename[sizeof("tiacx1NNusbcRR")];
- char *firmware = NULL;
+ firmware_image_t *fw_image = NULL;
char *usbbuf;
unsigned int offset;
- unsigned int len, inpipe, outpipe;
- u32 checksum;
- u32 size;
- int result;
+ unsigned int blk_len, inpipe, outpipe;
+ u32 num_processed;
+ u32 img_checksum, sum;
+ u32 file_size;
+ int result = -EIO;
+ int i;
FN_ENTER;
- usbbuf = kmalloc(ACX_USB_RWMEM_MAXLEN, GFP_KERNEL);
+ /* dump_device(usbdev); */
+
+ usbbuf = kmalloc(USB_RWMEM_MAXLEN, GFP_KERNEL);
if (!usbbuf) {
- printk(KERN_ERR "acx: no memory for USB transfer buffer ("
- STRING(ACX_USB_RWMEM_MAXLEN)" bytes)\n");
+ printk(KERN_ERR "acx: no memory for USB transfer buffer (%d bytes)\n", USB_RWMEM_MAXLEN);
result = -ENOMEM;
goto end;
}
- firmware = (char *)acx_s_read_fw(&usbdev->dev, filename, &size);
- if (!firmware) {
+ if (is_tnetw1450) {
+ /* Obtain the I/O pipes */
+ outpipe = usb_sndbulkpipe(usbdev, 1);
+ inpipe = usb_rcvbulkpipe(usbdev, 2);
+
+ printk(KERN_DEBUG "wait for device ready\n");
+ for (i = 0; i <= 2; i++) {
+ result = usb_bulk_msg(usbdev, inpipe,
+ usbbuf,
+ USB_RWMEM_MAXLEN,
+ &num_processed,
+ 2000
+ );
+
+ if ((*(u32 *)&usbbuf[4] == 0x40000001)
+ && (*(u16 *)&usbbuf[2] == 0x1)
+ && ((*(u16 *)usbbuf & 0x3fff) == 0)
+ && ((*(u16 *)usbbuf & 0xc000) == 0xc000))
+ break;
+ msleep(10);
+ }
+ if (i == 2)
+ goto fw_end;
+
+ *radio_type = usbbuf[8];
+ } else {
+ /* Obtain the I/O pipes */
+ outpipe = usb_sndctrlpipe(usbdev, 0);
+ inpipe = usb_rcvctrlpipe(usbdev, 0);
+
+ /* FIXME: shouldn't be hardcoded */
+ *radio_type = RADIO_MAXIM_0D;
+ }
+
+ snprintf(filename, sizeof(filename), "tiacx1%02dusbc%02X",
+ is_tnetw1450 * 11, *radio_type);
+
+ fw_image = acx_s_read_fw(&usbdev->dev, filename, &file_size);
+ if (!fw_image) {
result = -EIO;
goto end;
}
- log(L_INIT, "firmware size: %d bytes\n", size);
+ log(L_INIT, "firmware size: %d bytes\n", file_size);
- /* Obtain the I/O pipes */
- outpipe = usb_sndctrlpipe(usbdev, 0);
- inpipe = usb_rcvctrlpipe(usbdev, 0);
+ img_checksum = le32_to_cpu(fw_image->chksum);
+
+ if (is_tnetw1450) {
+ u8 cmdbuf[20];
+ const u8 *p;
+ u8 need_padding;
+ u32 tmplen, val;
+
+ memset(cmdbuf, 0, 16);
+
+ need_padding = acxusb_fw_needs_padding(fw_image, USB_RWMEM_MAXLEN);
+ tmplen = need_padding ? file_size-4 : file_size-8;
+ *(u16 *)&cmdbuf[0] = 0xc000;
+ *(u16 *)&cmdbuf[2] = 0x000b;
+ *(u32 *)&cmdbuf[4] = tmplen;
+ *(u32 *)&cmdbuf[8] = file_size-8;
+ *(u32 *)&cmdbuf[12] = img_checksum;
+
+ result = usb_bulk_msg(usbdev, outpipe, cmdbuf, 16, &num_processed, HZ);
+ if (result < 0)
+ goto fw_end;
+
+ p = (const u8 *)&fw_image->size;
+
+ /* first calculate checksum for image size part */
+ sum = p[0]+p[1]+p[2]+p[3];
+ p += 4;
+
+ /* now continue checksum for firmware data part */
+ tmplen = le32_to_cpu(fw_image->size);
+ for (i = 0; i < tmplen /* image size */; i++) {
+ sum += *p++;
+ }
+
+ if (sum != le32_to_cpu(fw_image->chksum)) {
+ printk("acx: FATAL: firmware upload: "
+ "checksums don't match! "
+ "(0x%08x vs. 0x%08x)\n",
+ sum, fw_image->chksum);
+ goto fw_end;
+ }
+
+ offset = 8;
+ while (offset < file_size) {
+ blk_len = file_size - offset;
+ if (blk_len > USB_RWMEM_MAXLEN) {
+ blk_len = USB_RWMEM_MAXLEN;
+ }
+
+ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
+ blk_len, offset);
+ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len);
+
+ p = usbbuf;
+ for (i = 0; i < blk_len; i += 4) {
+ *(u32 *)p = be32_to_cpu(*(u32 *)p);
+ p += 4;
+ }
- /* now upload the firmware, slice the data into blocks */
- offset = 8;
- while (offset < size) {
- len = size - offset;
- if (len >= ACX_USB_RWMEM_MAXLEN) {
- len = ACX_USB_RWMEM_MAXLEN;
+ result = usb_bulk_msg(usbdev, outpipe, usbbuf, blk_len, &num_processed, HZ);
+ if ((result < 0) || (num_processed != blk_len))
+ goto fw_end;
+ offset += blk_len;
+ }
+ if (need_padding) {
+ printk(KERN_DEBUG "send padding\n");
+ memset(usbbuf, 0, 4);
+ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ);
+ if ((result < 0) || (num_processed != 4))
+ goto fw_end;
+ }
+ printk(KERN_DEBUG "read firmware upload result\n");
+ memset(cmdbuf, 0, 20); /* additional memset */
+ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000);
+ if (result < 0)
+ goto fw_end;
+ if (*(u32 *)&cmdbuf[4] == 0x40000003)
+ goto fw_end;
+ if (*(u32 *)&cmdbuf[4])
+ goto fw_end;
+ if (*(u16 *)&cmdbuf[16] != 1)
+ goto fw_end;
+
+ val = *(u32 *)&cmdbuf[0];
+ if ((val & 0x3fff)
+ || ((val & 0xc000) != 0xc000))
+ goto fw_end;
+
+ val = *(u32 *)&cmdbuf[8];
+ if (val & 2) {
+ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000);
+ if (result < 0)
+ goto fw_end;
+ val = *(u32 *)&cmdbuf[8];
+ }
+ /* yup, no "else" here! */
+ if (val & 1) {
+ memset(usbbuf, 0, 4);
+ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ);
+ if ((result < 0) || (!num_processed))
+ goto fw_end;
}
- log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
- len, offset);
+
+ printk("TNETW1450 firmware upload successful!\n");
result = 0;
- memcpy(usbbuf, firmware + offset, len);
+ goto end;
+fw_end:
+ result = -EIO;
+ goto end;
+ } else {
+ /* ACX100 USB */
+
+ /* now upload the firmware, slice the data into blocks */
+ offset = 8;
+ while (offset < file_size) {
+ blk_len = file_size - offset;
+ if (blk_len > USB_RWMEM_MAXLEN) {
+ blk_len = USB_RWMEM_MAXLEN;
+ }
+ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
+ blk_len, offset);
+ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len);
+ result = usb_control_msg(usbdev, outpipe,
+ ACX_USB_REQ_UPLOAD_FW,
+ USB_TYPE_VENDOR|USB_DIR_OUT,
+ (file_size - 8) & 0xffff, /* value */
+ (file_size - 8) >> 16, /* index */
+ usbbuf, /* dataptr */
+ blk_len, /* size */
+ 3000 /* timeout in ms */
+ );
+ offset += blk_len;
+ if (result < 0) {
+ printk(KERN_ERR "acx: error %d during upload "
+ "of firmware, aborting\n", result);
+ goto end;
+ }
+ }
+
+ /* finally, send the checksum and reboot the device */
+ /* does this trigger the reboot? */
result = usb_control_msg(usbdev, outpipe,
ACX_USB_REQ_UPLOAD_FW,
USB_TYPE_VENDOR|USB_DIR_OUT,
- size - 8, /* value */
- 0, /* index */
+ img_checksum & 0xffff, /* value */
+ img_checksum >> 16, /* index */
+ NULL, /* dataptr */
+ 0, /* size */
+ 3000 /* timeout in ms */
+ );
+ if (result < 0) {
+ printk(KERN_ERR "acx: error %d during tx of checksum, "
+ "aborting\n", result);
+ goto end;
+ }
+ result = usb_control_msg(usbdev, inpipe,
+ ACX_USB_REQ_ACK_CS,
+ USB_TYPE_VENDOR|USB_DIR_IN,
+ img_checksum & 0xffff, /* value */
+ img_checksum >> 16, /* index */
usbbuf, /* dataptr */
- len, /* size */
+ 8, /* size */
3000 /* timeout in ms */
);
- offset += len;
if (result < 0) {
- printk(KERN_ERR "acx: error %d during upload "
- "of firmware, aborting\n", result);
+ printk(KERN_ERR "acx: error %d during ACK of checksum, "
+ "aborting\n", result);
goto end;
}
+ if (*usbbuf != 0x10) {
+ printk(KERN_ERR "acx: invalid checksum?\n");
+ result = -EINVAL;
+ goto end;
+ }
+ result = 0;
}
- /* finally, send the checksum and reboot the device */
- checksum = le32_to_cpu(*(u32 *)firmware);
- /* is this triggers the reboot? */
- result = usb_control_msg(usbdev, outpipe,
- ACX_USB_REQ_UPLOAD_FW,
- USB_TYPE_VENDOR|USB_DIR_OUT,
- checksum & 0xffff, /* value */
- checksum >> 16, /* index */
- NULL, /* dataptr */
- 0, /* size */
- 3000 /* timeout in ms */
- );
- if (result < 0) {
- printk(KERN_ERR "acx: error %d during tx of checksum, "
- "aborting\n", result);
- goto end;
- }
- result = usb_control_msg(usbdev, inpipe,
- ACX_USB_REQ_ACK_CS,
- USB_TYPE_VENDOR|USB_DIR_IN,
- checksum & 0xffff, /* value */
- checksum >> 16, /* index */
- usbbuf, /* dataptr */
- 8, /* size */
- 3000 /* timeout in ms */
- );
- if (result < 0) {
- printk(KERN_ERR "acx: error %d during ACK of checksum, "
- "aborting\n", result);
- goto end;
- }
- if (*usbbuf != 0x10) {
- kfree(usbbuf);
- printk(KERN_ERR "acx: invalid checksum?\n");
- result = -EINVAL;
- goto end;
- }
- result = 0;
end:
- vfree(firmware);
+ vfree(fw_image);
kfree(usbbuf);
FN_EXIT1(result);
@@ -551,16 +721,28 @@ end:
}
+/* FIXME: maybe merge it with usual eeprom reading, into common code? */
+static void
+acxusb_s_read_eeprom_version(acx_device_t *adev)
+{
+ u8 eeprom_ver[0x8];
+
+ memset(eeprom_ver, 0, sizeof(eeprom_ver));
+ acx_s_interrogate(adev, &eeprom_ver, ACX1FF_IE_EEPROM_VER);
+
+ /* FIXME: which one of those values to take? */
+ adev->eeprom_version = eeprom_ver[5];
+}
+
+
/*
* temporary helper function to at least fill important cfgopt members with
* useful replacement values until we figure out how one manages to fetch
* the configoption struct in the USB device case...
*/
-int
+static int
acxusb_s_fill_configoption(acx_device_t *adev)
{
- printk("FIXME: haven't figured out how to fetch configoption struct "
- "from USB device, passing hardcoded values instead\n");
adev->cfgopt_probe_delay = 200;
adev->cfgopt_dot11CCAModes = 4;
adev->cfgopt_dot11Diversity = 1;
@@ -572,6 +754,7 @@ acxusb_s_fill_configoption(acx_device_t
return OK;
}
+
/***********************************************************************
** acxusb_e_probe()
**
@@ -598,28 +781,41 @@ acxusb_e_probe(struct usb_interface *int
int numconfigs, numfaces, numep;
int result = OK;
int i;
+ int radio_type;
+ /* this one needs to be more precise in case there appears a TNETW1450 from the same vendor */
+ int is_tnetw1450 = (usbdev->descriptor.idVendor != ACX100_VENDOR_ID);
FN_ENTER;
- /* First check if this is the "unbooted" hardware */
- if ((usbdev->descriptor.idVendor == ACX100_VENDOR_ID)
- && (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED)) {
+ if (is_tnetw1450) {
/* Boot the device (i.e. upload the firmware) */
- acxusb_boot(usbdev);
+ acxusb_boot(usbdev, is_tnetw1450, &radio_type);
- /* OK, we are done with booting. Normally, the
- ** ID for the unbooted device should disappear
- ** and it will not need a driver anyway...so
- ** return a NULL
- */
- log(L_INIT, "finished booting, returning from probe()\n");
- result = OK; /* success */
- goto end;
- }
+ /* TNETW1450-based cards will continue right away with
+ * the same USB ID after booting */
+ } else {
+ /* First check if this is the "unbooted" hardware */
+ if (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED) {
- if ((usbdev->descriptor.idVendor != ACX100_VENDOR_ID)
- || (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)) {
- goto end_nodev;
+ /* Boot the device (i.e. upload the firmware) */
+ acxusb_boot(usbdev, is_tnetw1450, &radio_type);
+
+ /* DWL-120+ will first boot the firmware,
+ * then later have a *separate* probe() run
+ * since its USB ID will have changed after
+ * firmware boot!
+ * Since the first probe() run has no
+ * other purpose than booting the firmware,
+ * simply return immediately.
+ */
+ log(L_INIT, "finished booting, returning from probe()\n");
+ result = OK; /* success */
+ goto end;
+ }
+ else
+ /* device not unbooted, but invalid USB ID!? */
+ if (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)
+ goto end_nodev;
}
/* Ok, so it's our device and it has already booted */
@@ -656,10 +852,15 @@ acxusb_e_probe(struct usb_interface *int
adev->ndev = ndev;
adev->dev_type = DEVTYPE_USB;
- adev->chip_type = CHIPTYPE_ACX100;
-
- /* FIXME: should be read from register (via firmware) using standard ACX code */
- adev->radio_type = RADIO_MAXIM_0D;
+ adev->radio_type = radio_type;
+ if (is_tnetw1450) {
+ /* well, actually it's a TNETW1450, but since it
+ * seems to be sufficiently similar to TNETW1130,
+ * I don't want to change large amounts of code now */
+ adev->chip_type = CHIPTYPE_ACX111;
+ } else {
+ adev->chip_type = CHIPTYPE_ACX100;
+ }
adev->usbdev = usbdev;
spin_lock_init(&adev->lock); /* initial state: unlocked */
@@ -677,6 +878,10 @@ acxusb_e_probe(struct usb_interface *int
adev->softmac = ieee80211_priv(ndev);
adev->softmac->set_channel = acx_e_ieee80211_set_chan;
+ adev->ieee->sec.enabled = 0;
+ adev->ieee->sec.encrypt = 0;
+ adev->ieee->sec.auth_mode = WLAN_AUTH_OPEN;
+
/* Check that this is really the hardware we know about.
** If not sure, at least notify the user that he
** may be in trouble...
@@ -698,27 +903,32 @@ acxusb_e_probe(struct usb_interface *int
numep = ifdesc->bNumEndpoints;
log(L_DEBUG, "# of endpoints: %d\n", numep);
- /* obtain information about the endpoint
- ** addresses, begin with some default values
- */
- adev->bulkoutep = 1;
- adev->bulkinep = 1;
- for (i = 0; i < numep; i++) {
+ if (is_tnetw1450) {
+ adev->bulkoutep = 1;
+ adev->bulkinep = 2;
+ } else {
+ /* obtain information about the endpoint
+ ** addresses, begin with some default values
+ */
+ adev->bulkoutep = 1;
+ adev->bulkinep = 1;
+ for (i = 0; i < numep; i++) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
- ep = usbdev->ep_in[i];
- if (!ep)
- continue;
- epdesc = &ep->desc;
+ ep = usbdev->ep_in[i];
+ if (!ep)
+ continue;
+ epdesc = &ep->desc;
#else
- epdesc = usb_epnum_to_ep_desc(usbdev, i);
- if (!epdesc)
- continue;
+ epdesc = usb_epnum_to_ep_desc(usbdev, i);
+ if (!epdesc)
+ continue;
#endif
- if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) {
- if (epdesc->bEndpointAddress & 0x80)
- adev->bulkinep = epdesc->bEndpointAddress & 0xF;
- else
- adev->bulkoutep = epdesc->bEndpointAddress & 0xF;
+ if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) {
+ if (epdesc->bEndpointAddress & 0x80)
+ adev->bulkinep = epdesc->bEndpointAddress & 0xF;
+ else
+ adev->bulkoutep = epdesc->bEndpointAddress & 0xF;
+ }
}
}
log(L_DEBUG, "bulkout ep: 0x%X\n", adev->bulkoutep);
@@ -773,12 +983,13 @@ acxusb_e_probe(struct usb_interface *int
/* put acx out of sleep mode and initialize it */
acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
- acxusb_s_fill_configoption(adev);
-
result = acx_s_init_mac(adev);
if (result)
goto end;
+ /* TODO: see similar code in pci.c */
+ acxusb_s_read_eeprom_version(adev);
+ acxusb_s_fill_configoption(adev);
acx_s_set_defaults(adev);
acx_s_get_firmware_version(adev);
acx_display_hardware_details(adev);
@@ -1622,11 +1833,13 @@ dump_device(struct usb_device *usbdev)
/* This saw a change after 2.6.10 */
printk(" ep_in wMaxPacketSize: ");
for (i = 0; i < 16; ++i)
- printk("%d ", usbdev->ep_in[i]->desc.wMaxPacketSize);
+ if (usbdev->ep_in[i] != NULL)
+ printk("%d:%d ", i, usbdev->ep_in[i]->desc.wMaxPacketSize);
printk("\n");
printk(" ep_out wMaxPacketSize: ");
for (i = 0; i < VEC_SIZE(usbdev->ep_out); ++i)
- printk("%d ", usbdev->ep_out[i]->desc.wMaxPacketSize);
+ if (usbdev->ep_out[i] != NULL)
+ printk("%d:%d ", i, usbdev->ep_out[i]->desc.wMaxPacketSize);
printk("\n");
#else
printk(" epmaxpacketin: ");
^ permalink raw reply
* Re: [PATCH] acx: make firmware statistics parsing more intelligent
From: Denis Vlasenko @ 2006-02-03 12:39 UTC (permalink / raw)
To: acx100-devel; +Cc: Andreas Mohr, netdev
In-Reply-To: <20060203105857.GB11173@rhlx01.fht-esslingen.de>
On Friday 03 February 2006 12:58, Andreas Mohr wrote:
> - adev->tx_head = (head + 1) % TX_CNT;
> + /* slower: adev->tx_head = (head + 1) % TX_CNT; */
> + adev->tx_head = head + 1;
> + if (adev->tx_head >= TX_CNT)
> + adev->tx_head = 0;
struct a {
int tx_head;
};
#define TX_CNT 16
void f(struct a *adev, int head) {
adev->tx_head = (head + 1) % TX_CNT;
}
void g(struct a *adev, int head) {
adev->tx_head = head + 1;
if (adev->tx_head >= TX_CNT)
adev->tx_head = 0;
}
gcc -Os -S t.c -fomit-frame-pointer -falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 -mtune=i386 -march=i386
produces:
f:
movl 8(%esp), %eax
incl %eax
movl $16, %edx
movl %edx, %ecx
cltd
idivl %ecx
movl 4(%esp), %eax
movl %edx, (%eax)
ret
.size f, .-f
.globl g
.type g, @function
g:
movl 4(%esp), %edx
movl 8(%esp), %eax
incl %eax
movl %eax, (%edx)
cmpl $15, %eax
jle .L6
movl $0, (%edx)
.L6:
ret
.size g, .-g
.ident "GCC: (GNU) 4.0.0"
Well, gcc obviously failed to realize that "% 16" == "& 15".
I'll file a bug. Meanwhile, with & 15 f() is better:
f:
movl 8(%esp), %eax
incl %eax
andl $15, %eax
movl 4(%esp), %edx
movl %eax, (%edx)
ret
.size f, .-f
.globl g
.type g, @function
g:
movl 4(%esp), %edx
movl 8(%esp), %eax
incl %eax
movl %eax, (%edx)
cmpl $15, %eax
jle .L6
movl $0, (%edx)
.L6:
ret
.size g, .-g
.ident "GCC: (GNU) 4.0.0"
--
vda
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid\x103432&bid#0486&dat\x121642
^ permalink raw reply
* Re: [PATCH] acx: make firmware statistics parsing more intelligent
From: Denis Vlasenko @ 2006-02-03 13:28 UTC (permalink / raw)
To: acx100-devel; +Cc: Andreas Mohr, netdev
In-Reply-To: <200602031439.14467.vda@ilport.com.ua>
On Friday 03 February 2006 14:39, Denis Vlasenko wrote:
> Well, gcc obviously failed to realize that "% 16" == "& 15".
> I'll file a bug.
-ENOTABUG. It's incorrect for signed integers,
and gcc uses idiv insn instead.
--
vda
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
^ permalink raw reply
* Re: [PATCH] acx: make firmware statistics parsing more intelligent
From: Andreas Mohr @ 2006-02-03 13:56 UTC (permalink / raw)
To: acx100-devel; +Cc: netdev
In-Reply-To: <200602031528.29945.vda@ilport.com.ua>
Hi,
On Fri, Feb 03, 2006 at 03:28:29PM +0200, Denis Vlasenko wrote:
> On Friday 03 February 2006 14:39, Denis Vlasenko wrote:
> > Well, gcc obviously failed to realize that "% 16" == "& 15".
> > I'll file a bug.
>
> -ENOTABUG. It's incorrect for signed integers,
> and gcc uses idiv insn instead.
...which is one of the performance reasons why it may be a good idea
to use unsigned ints wherever signedness isn't required (unsigned int
is said to be faster sometimes, on many platforms).
Andreas Mohr
-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems? Stop! Download the new AJAX search engine that makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
^ permalink raw reply
* Re: FW: Performance evaluation of high speed TCPs
From: Douglas Leith @ 2006-02-03 14:06 UTC (permalink / raw)
To: rhee; +Cc: netdev, end2end-interest
In-Reply-To: <48677.60.44.142.168.1138935007.squirrel@webmail.ncsu.edu>
[-- Attachment #1: Type: text/plain, Size: 1262 bytes --]
Injong,
>In fact, i contacted your student "Baruch" one month and half before we posted our
report -- it was CCed in the netdev mailing list as well and we gave him login and
passwd on our result website (at that time we were just about to write the report)
and we have not heard from your guys until just one week ago. At least we did try to
make sure we are running a buggy version.
We have no record of receiving such an email. Just a mix-up I guess.
Doug
>>Seriously, we can't run the tests for every fix and bug report.
>
> Perhaps best to view it as returning a favour. You may recall that we
> re-ran all our own experimental tests last year (all data and code
> available online at www.hamilton.ie/net/eval/) on discovering a previously
> unreported bug introduced by the linux folks when implementing bic.
> Something similar has happened with importing htcp into linux.
>
> Seriously, where's the value in comparing buggy implementations - isn't
> that just a waste of all our time ? If we are genuine about wanting to
> understand tcp performance then I think we just have to take the hit from
> issues such as this that are outside all of our control.
>
> Doug
>
> Hamilton Institute
> www.hamilton.ie
>
[-- Attachment #2: Type: text/html, Size: 1885 bytes --]
^ permalink raw reply
* Re: Linux 2.6.15.2
From: Andrew Morton @ 2006-02-03 19:14 UTC (permalink / raw)
To: Holger Eitzenberger; +Cc: gregkh, linux-kernel, stable, torvalds, netdev
In-Reply-To: <20060203120925.GA4393@kruemel.my-eitzenberger.de>
Holger Eitzenberger <holger@my-eitzenberger.de> wrote:
>
> On Mon, Jan 30, 2006 at 11:34:27PM -0800, Andrew Morton wrote:
>
> > - A skbuff_head_cache leak causes oom-killings.
> >
> > All of these only seem to affect a small minority of machines.
>
> Hi,
>
> I have searched for a description for the above mentioned bug report,
> but havent found any. Can you tell me?
http://www.mail-archive.com/netdev@vger.kernel.org/msg06355.html
> The reason why I am asking that I am facing a similar problem on
> kernel 2.6.10. During performance tests (Intel XEON, SMP, PCI-X,
> e1000, 2 - 4 Gig RAM) the machine was out of memory.
>
> Tests showed that LowFree went linearly down to a few megabytes, where
> most of the memory was used in skb_head_cache and size-1024 slab
> caches. These two summed up to ~270 MG, which was the reason for
> that.
>
> /proc/net/tcp showed that most of the memory was stuck in the RX
> queues of some processes (two processes with ~1000 sockets each).
>
> A look into /proc/sys/net/ipv4/tcp_mem showed that that the values in
> there were way to high. I hope that a reduction of these values will
> help (not done yet).
>
Sounds different. Please test a more recent kernel and if the problem is
still there, send a report to linux-kernel and cc netdev@vger.kernel.org.
Include the contents of /proc/meminfo and /proc/slabinfo. Thanks.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox