Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH 24/27] HFI: hf network driver
From: Ben Hutchings @ 2011-03-02 22:40 UTC (permalink / raw)
  To: dykmanj
  Cc: netdev, Piyush Chaudhary, Fu-Chung Chang, William S. Cadden,
	Wen C. Chen, Scot Sakolish, Jian Xiao, Carol L. Soto,
	Sarah J. Sheppard
In-Reply-To: <1299100213-8770-24-git-send-email-dykmanj@linux.vnet.ibm.com>

On Wed, 2011-03-02 at 16:10 -0500, dykmanj@linux.vnet.ibm.com wrote:
> From: Jim Dykman <dykmanj@linux.vnet.ibm.com>
> 
> It is a separate binary because it is not strictly necessary to use the HFI.
> This patch includes module load/unload and the window open/setup with the
> hfi device driver.
[...]
> diff --git a/drivers/net/hfi/ip/Kconfig b/drivers/net/hfi/ip/Kconfig
> new file mode 100644
> index 0000000..1a2c21d
> --- /dev/null
> +++ b/drivers/net/hfi/ip/Kconfig
> @@ -0,0 +1,9 @@
> +config HFI_IP
> +	tristate "IP-over-HFI"
> +	depends on NETDEVICES && INET && HFI
> +	---help---
> +	Support for the IP over HFI. It transports IP
> +	packets over HFI.
> +
> +	To compile the driver as a module, choose M here. The module
> +	will be called hf.

You actually call it hf_if!  But why it is not called hfi_ip?

> diff --git a/drivers/net/hfi/ip/Makefile b/drivers/net/hfi/ip/Makefile
> new file mode 100644
> index 0000000..59eff9b
> --- /dev/null
> +++ b/drivers/net/hfi/ip/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the HF IP interface for IBM eServer System p
> +#
> +obj-$(CONFIG_HFI_IP) += hf_if.o
> +
> +hf_if-objs :=	hf_if_main.o
> diff --git a/drivers/net/hfi/ip/hf_if_main.c b/drivers/net/hfi/ip/hf_if_main.c
> new file mode 100644
> index 0000000..329baa1
> --- /dev/null
> +++ b/drivers/net/hfi/ip/hf_if_main.c
[...]
> +static int hf_inet_event(struct notifier_block *this,
> +			 unsigned long event,
> +			 void *ifa)
> +{
> +	struct in_device	*in_dev;
> +	struct net_device	*netdev;
> +
> +	in_dev = ((struct in_ifaddr *)ifa)->ifa_dev;
> +
> +	netdev = in_dev->dev;
> +
> +	if (!net_eq(dev_net(netdev), &init_net))
> +		return NOTIFY_DONE;
> +
> +	if (event == NETDEV_UP) {
> +		struct hf_if	*net_if;
> +
> +		net_if = &(((struct hf_net *)(netdev_priv(netdev)))->hfif);

Try running:

# ifconfig lo down
# ifconfig lo up

and watch the explosion.

You need to check that this is actually one of your devices.  I've done
this by comparing netdev->netdev_ops pointer.

[...]
> +static int hf_alloc_tx_resource(struct hf_if *net_if)
> +{
[...]
> +	if (net_if->tx_fifo.addr == 0) {
> +		printk(KERN_ERR "%s: hf_alloc_tx_resource: "
> +			"tx_fifo fail, size=0x%x\n",
> +			net_if->name, net_if->tx_fifo.size);
[...]

The netdev_err() and netif_err() (etc.) macros are the standard way to
format messages relating to a net device.

[...]
> +static int hf_set_mac_addr(struct net_device *netdev, void *p)
> +{
> +	struct hf_net		*net = netdev_priv(netdev);
> +	struct hf_if		*net_if = &(net->hfif);
> +
> +	/* Mac address format: 02:ClusterID:ISR:ISR:HFI_WIN:WIN */
> +
> +	/* Locally administered MAC address */
> +	netdev->dev_addr[0] = 0x2; /* bit6=1, bit7=0 */
> +
> +	netdev->dev_addr[1] = 0x0; /* cluster id */
> +
> +	*(u16 *)(&(netdev->dev_addr[2])) = (u16)(net_if->isr_id);
> +
> +	*(u16 *)(&(netdev->dev_addr[4])) = (u16)
> +	(((net_if->ai) << HF_MAC_HFI_SHIFT) | (net_if->client.window));

These two assignments should perhaps include an explicit cpu_to_be16().

[...]
> +static int hf_net_close(struct net_device *netdev)
> +{
> +	struct hf_net		*net = netdev_priv(netdev);
> +	struct hf_if		*net_if = &(net->hfif);
> +	struct hfidd_acs	*p_acs = HF_ACS(net_if);
> +
> +	if (net_if->state == HF_NET_CLOSE)
> +		return 0;

I'm a bit puzzled by this.  Do you not trust the networking core to keep
track of your device state?

> +	spin_lock(&(net_if->lock));
> +	if (net_if->state == HF_NET_OPEN) {
> +		hf_close_ip_window(net_if, p_acs);
> +
> +		hf_free_resource(net_if);
> +	}
> +
> +	hf_register_hfi_ready_callback(netdev, p_acs,
> +			HFIDD_REQ_EVENT_UNREGISTER);
> +
> +	net_if->state = HF_NET_CLOSE;
> +	spin_unlock(&(net_if->lock));
> +
> +	return 0;
> +}
> +
> +struct net_device_stats *hf_get_stats(struct net_device *netdev)
> +{
> +	struct hf_net	*net = netdev_priv(netdev);
> +	struct hf_if	*net_if = &(net->hfif);
> +
> +	return &(net_if->net_stats);
> +}

Please use the stats contained in struct net_device instead.

> +static int hf_change_mtu(struct net_device *netdev, int new_mtu)
> +{
> +	if ((new_mtu <= 0) || (new_mtu > HF_NET_MTU))
> +		return -ERANGE;

Since this interface apparently only passes ARP and IPv4, the minimum
MTU should be the minimum for IPv4, which is 68.  (The spec says 576 but
the Linux IPv4 implementation uses this value.)

[...]
> +static void hf_if_setup(struct net_device *netdev)
> +{
> +	netdev->type		= ARPHRD_HFI;
> +	netdev->mtu		= HF_NET_MTU;
> +	netdev->tx_queue_len	= 1000;
> +	netdev->flags		= IFF_BROADCAST;
> +	netdev->hard_header_len	= HF_HLEN;
> +	netdev->addr_len	= HF_ALEN;
> +	netdev->needed_headroom	= 0;
> +
> +	netdev->header_ops	= &hf_header_ops;
> +	netdev->netdev_ops	= &hf_netdev_ops;
> +
> +	netdev->features       |= NETIF_F_SG;

You can't provide NETIF_F_SG without checksum offload.

> +	memcpy(netdev->broadcast, hfi_bcast_addr, HF_ALEN);
> +}
> +
> +static struct hf_net *hf_init_netdev(int idx, int ai)
> +{
> +	struct net_device	*netdev;
> +	struct hf_net		*net;
> +	int			ii;
> +	int			rc;
> +	char			ifname[HF_MAX_NAME_LEN];
> +
> +	ii = (idx * MAX_HFIS) + ai;
> +	sprintf(ifname, "hf%d", ii);
> +	netdev = alloc_netdev(sizeof(struct hf_net), ifname, hf_if_setup);
> +	if (!netdev) {
> +		printk(KERN_ERR "hf_init_netdev: "
> +				"alloc_netdev for hfi%d:hf%d fail\n", ai, idx);
> +		return (struct hf_net *) -ENODEV;

Use ERR_PTR() instead of writing this sort of cast yourself.

[...]
> +static int __init hf_init_module(void)
> +{
> +	u32		idx, ai;
> +	struct hf_net	*net;
> +
> +	memset(&hf_ginfo, 0, sizeof(struct hf_global_info));
> +
> +	for (idx = 0; idx < MAX_HF_PER_HFI; idx++) {
> +		for (ai = 0; ai < MAX_HFIS; ai++) {
> +			net = hf_init_netdev(idx, ai);
> +			if (IS_ERR(net)) {
> +				printk(KERN_ERR "hf_init_module: hf_init_netdev"
> +						" for idx %d ai %d failed rc"
> +						" 0x%016llx\n",
> +						idx, ai, (u64)(PTR_ERR(net)));

Whyever are you formatting the error like this?  Use %ld and remove the
(u64) cast.


> +
> +				goto err_out;
> +			}
> +
> +			hf_ginfo.net[idx][ai] = net;
> +		}
> +	}
> +
> +	register_inetaddr_notifier(&hf_inet_notifier);
> +
> +	printk(KERN_INFO "hf module loaded\n");
> +	return 0;
> +
> +err_out:
> +	for (idx = 0; idx < MAX_HF_PER_HFI; idx++) {
> +		for (ai = 0; ai < MAX_HFIS; ai++) {
> +			net = hf_ginfo.net[idx][ai];
> +			if (net != NULL) {
> +				hf_del_netdev(net);
> +				hf_ginfo.net[idx][ai] = NULL;
> +			}
> +		}
> +	}
> +
> +	return -EINVAL;

Use the error code you were given:

	return PTR_ERR(net);

> +}
> +
> +static void __exit hf_cleanup_module(void)
> +{
> +	u32		idx, ai;
> +	struct hf_net	*net;
> +
> +	unregister_inetaddr_notifier(&hf_inet_notifier);
> +	for (idx = 0; idx < MAX_HF_PER_HFI; idx++) {
> +		for (ai = 0; ai < MAX_HFIS; ai++) {
> +			net = hf_ginfo.net[idx][ai];
> +			if (net != NULL) {
> +				hf_del_netdev(net);
> +				hf_ginfo.net[idx][ai] = NULL;
> +			}
> +		}
> +	}
> +
> +	return;

Redundant statement is redundant.

> +}
[...]
> --- /dev/null
> +++ b/include/linux/hfi/hf_if.h
[...]
> +struct hfi_ip_extended_hdr {            /* 16B */
> +	u32		immediate_len:7;/* In bytes */
> +	u32		num_desc:3;     /* number of descriptors */
> +					/* Logical Port ID: */
> +	u32		lpid_valid:1;   /* set by sending HFI */
> +	u32		lpid:4;         /* set by sending HFI */
> +	/* Ethernet Service Header is 113 bits, which is 14 bytes + 1 bit */
> +	u32		ethernet_svc_hdr_hi:1;    /* Not used by HFI */
> +	char            ethernet_svc_hdr[12];     /* Not used by HFI */
> +	__sum16         bcast_csum;
> +} __packed;

It looks like you're relying on gcc to treat a set of bitfields with
type u32 and only 16 bits assigned as having a size of 2 in a packed
structure.  This might be true now, but I wouldn't want to rely on that
being true for later versions.  Why not define the set of bitfields with
type u16?

Also the above appears to assume big-endian byte and bit order.

[...]
> +#define HF_ALEN				6
> +struct hf_hwhdr {
> +	u8				h_dest[HF_ALEN];
> +	u8				h_source[HF_ALEN];
> +	__be16				h_proto;
> +};
> +
> +#define HF_HLEN				sizeof(struct hf_hwhdr)
[...]

This looks familiar!  Maybe you should just use the existing struct
ethhdr?

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply

* Re: [PATCH 27/27] HFI: hf ethtool support
From: David Miller @ 2011-03-02 22:32 UTC (permalink / raw)
  To: dykmanj
  Cc: bhutchings, netdev, piyushc, fcchang, wscadden, winstonc,
	sakolish, jian, clsoto, sjsheppa
In-Reply-To: <4D6EC49A.9060405@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>
Date: Wed, 02 Mar 2011 17:28:42 -0500

> It is 64-bit only, but we forgot to mention that in Kconfig.

Please do not mark this driver as 64-bit only in the Kconfig if
at all possible, as that will markedly decrease the build test
coverage of this driver.

^ permalink raw reply

* Re: [PATCH 27/27] HFI: hf ethtool support
From: Jim Dykman @ 2011-03-02 22:28 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: netdev, Piyush Chaudhary, Fu-Chung Chang, William S. Cadden,
	Wen C. Chen, Scot Sakolish, Jian Xiao, Carol L. Soto,
	Sarah J. Sheppard
In-Reply-To: <1299102762.4277.4.camel@localhost>

On 3/2/2011 4:52 PM, Ben Hutchings wrote:
> On Wed, 2011-03-02 at 16:10 -0500, dykmanj@linux.vnet.ibm.com wrote:
> [...]
>> +static int hf_get_sset_count(struct net_device *netdev, int sset)
>> +{
>> +	switch (sset) {
>> +	case ETH_SS_STATS:
>> +		return ARRAY_SIZE(hf_ethtool_stats_keys);
>> +	default:
>> +		return -EOPNOTSUPP;
> 
> The error code should be -EINVAL, I think.

ok

> 
>> +	}
>> +}
>> +
>> +static void hf_get_ethtool_stats(struct net_device *netdev,
>> +		struct ethtool_stats *stats, u64 *data)
>> +{
>> +	struct hf_net	*net = netdev_priv(netdev);
>> +	struct hf_if	*net_if = &(net->hfif);
>> +
>> +	memcpy(data, &(net_if->eth_stats), sizeof(struct hf_ethtool_stats));
> [...]
> 
> This may result in word tearing, particularly if this driver can be
> built for a 32-bit system.  Since the stats appear to be updated
> asynchronously in the data path, you may have to declare them as
> unsigned long and then extend them to 64-bit in hf_get_ethtool_stats().
> 
> Ben.
> 

It is 64-bit only, but we forgot to mention that in Kconfig.

Thanks.

Jim Dykman


^ permalink raw reply

* Re: [PATCH 24/27] HFI: hf network driver
From: Stephen Hemminger @ 2011-03-02 22:26 UTC (permalink / raw)
  To: dykmanj
  Cc: netdev, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-24-git-send-email-dykmanj@linux.vnet.ibm.com>

On Wed,  2 Mar 2011 16:10:10 -0500
dykmanj@linux.vnet.ibm.com wrote:

> +struct hf_if {
> +	u32			idx;			/* 0, 1, 2, 3 ...   */
> +	u32			ai;			/* 0=hfi0, 1=hfi1   */
> +	char			name[HF_MAX_NAME_LEN];
> +	u32			isr_id;
> +	u32			ip_addr;
> +	u32			state;			/* CLOSE, OPEN */
> +	spinlock_t		lock;			/* lock for state */
> +	u32			sfifo_fv_polarity;
> +	u32			sfifo_slots_per_blk;
> +	u32			sfifo_packets;
> +	void __iomem		*doorbell;		/* mapped mmio_regs */
> +	struct hf_fifo		tx_fifo;
> +	struct hf_fifo		rx_fifo;
> +	struct hfi_client_info	client;
> +	struct sk_buff		**tx_skb;		/* array to store tx
> +							   2k skb */
> +	void			*sfifo_finishvec;
> +	struct net_device_stats	net_stats;
> +};

You don't need net_stats in this structure if you use
the standard netdev->stats structure instead.

You won't need hf_get_stats then..

-- 

^ permalink raw reply

* Re: [PATCH 02/27] HFI: Add HFI adapter control structure
From: Stephen Hemminger @ 2011-03-02 22:21 UTC (permalink / raw)
  To: dykmanj
  Cc: netdev, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-2-git-send-email-dykmanj@linux.vnet.ibm.com>

On Wed,  2 Mar 2011 16:09:48 -0500
dykmanj@linux.vnet.ibm.com wrote:

> diff --git a/drivers/net/hfi/core/Makefile b/drivers/net/hfi/core/Makefile
> index 80790c6..6fe4e60 100644
> --- a/drivers/net/hfi/core/Makefile
> +++ b/drivers/net/hfi/core/Makefile
> @@ -1,5 +1,6 @@
>  #
>  # Makefile for the HFI device driver for IBM eServer System p
>  #
> -hfi_core-objs:=	hfidd_init.o
> +hfi_core-objs:=	hfidd_adpt.o \
> +		hfidd_init.o
>  obj-$(CONFIG_HFI) += hfi_core.o
> diff --git a/drivers/net/hfi/core/hfidd_adpt.c b/drivers/net/hfi/core/hfidd_adpt.c
> new file mode 100644
> index 0000000..d64fa38
> --- /dev/null
> +++ b/drivers/net/hfi/core/hfidd_adpt.c
> @@ -0,0 +1,60 @@
> +/*
> + * hfidd_adpt.c
> + *
> + * HFI device driver for IBM System p
> + *
> + *  Authors:
> + *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
> + *      William S. Cadden <wscadden@linux.vnet.ibm.com>
> + *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
> + *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
> + *      Jian Xiao <jian@linux.vnet.ibm.com>
> + *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
> + *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
> + *
> + *  (C) Copyright IBM Corp. 2010
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/hfi/hfidd_internal.h>
> +#include "hfidd_proto.h"
> +
> +int hfidd_alloc_adapter(struct hfidd_acs **adpt, dev_t devno, void *uiop)
> +{
> +
> +	struct hfidd_acs	*p_acs = NULL;
> +
> +	p_acs = kzalloc(sizeof(*p_acs), GFP_KERNEL);
> +	if (p_acs == NULL)
> +		return -ENOMEM;
> +
> +	p_acs->dev_num = devno;
> +	p_acs->index  = MINOR(devno);
> +	p_acs->state  = HFI_INVALID;
> +	snprintf(p_acs->name, HFI_DEVICE_NAME_MAX - 1,
> +			"%s%d", HFIDD_DEV_NAME, p_acs->index);
> +
> +	*adpt = p_acs;
> +	return 0;
> +}
> +
> +void hfidd_free_adapter(struct hfidd_acs *p_acs)
> +{
> +	kfree(p_acs);
> +	p_acs = NULL;
> +	return;
> +}

If these were not in a separate file the could be marked as static.

Doing a return; on last line of a void function is considered poor
style since it is unnecessary.

-- 

^ permalink raw reply

* Re: [RFC LOL OMG] pfifo_lat: qdisc that limits dequeueing based on estimated link latency
From: John W. Linville @ 2011-03-02 22:08 UTC (permalink / raw)
  To: netdev; +Cc: bloat-devel
In-Reply-To: <1299102850-2883-1-git-send-email-linville@tuxdriver.com>

On Wed, Mar 02, 2011 at 04:54:10PM -0500, John W. Linville wrote:
> This is a qdisc based on the existing pfifo_fast code.  The difference

Well, it started that way.  This is obviously based on the pfifo
code instead...

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* Re: [GIT PULL nf-2.6] IPVS
From: Simon Horman @ 2011-03-02 22:06 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: lvs-devel, netdev, netfilter-devel, netfilter, Hans Schillstrom,
	Julian Anastasov
In-Reply-To: <4D6E22CA.2020305@trash.net>

On Wed, Mar 02, 2011 at 11:58:18AM +0100, Patrick McHardy wrote:
> Am 01.03.2011 23:59, schrieb Simon Horman:
> > Hi Patrick,
> > 
> > please consider pulling
> > git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-test-2.6.git for-patrick
> > to get the following change from Julian. Please note that it is an nf-2.6
> > (that is 2.6.38-rc) change.
> > 
> > Julian Anastasov (1):
> >       ipvs: fix dst_lock locking on dest update
> 
> Pulled, thanks Simon.

Thanks Patrick.

This change is also needed in nf-next-2.6 but I assume that
will automatically happen when nf-2.6 is merged into nf-next-2.6,
possibly via a merge of net-2.6 into net-next-2.6.

^ permalink raw reply

* [RFC LOL OMG] pfifo_lat: qdisc that limits dequeueing based on estimated link latency
From: John W. Linville @ 2011-03-02 21:54 UTC (permalink / raw)
  To: netdev; +Cc: bloat-devel, John W. Linville
In-Reply-To: <20110228132341.194975v6ojrudl18@hayate.sektori.org>

This is a qdisc based on the existing pfifo_fast code.  The difference
is that this qdisc limits the dequeue rate based on estimates of how
many packets can be in-flight at a given time while maintaining a target
link latency.

This work is based on the eBDP documented in Section IV of "Buffer
Sizing for 802.11 Based Networks" by Tianji Li, et al.

	http://www.hamilton.ie/tianji_li/buffersizing.pdf

This implementation timestamps an skb as it dequeues it, then
computes the service time when the frame is freed by the driver.
An exponentially weighted moving average of per fragment service times
is used to restrict queueing delays in hopes of achieving a target
fragment transmission latency.  The skb->deconstructor mechanism is
abused in order to obtain packet service time estimates.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
I took a whack at reimplementing my eBDP patch at the qdisc level.
Unfortunately, it doesn't seem to work very well and I'm at a loss
as to why... :-( Comments welcome -- maybe I'm doing something really
stupid in the math and just can't see it.

The skb->deconstructor abuse includes adding a union member in the skb
to record the qdisc->handle on the way out so that it can be used for
accounting in the deconstructor -- thanks to Neil Horman for the
suggestion!

The reason I think this is an idea worth exploring is that existing
qdisc code doesn't seem to account for the fact that the devices could
be doing a lot of queueing behind them.  Even Jussi's recent
sch_fifo_ewma post doesn't seem to take into account how long the device
holds-on to packets, which limits his ability to fight latency.

Anyway, all comments appreciated!

 include/linux/skbuff.h  |    2 +
 include/net/pkt_sched.h |    1 +
 net/sched/sch_api.c     |    1 +
 net/sched/sch_fifo.c    |  131 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 135 insertions(+), 0 deletions(-)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index bf221d6..d99861e 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -296,6 +296,7 @@ typedef unsigned char *sk_buff_data_t;
  *	@end: End pointer
  *	@destructor: Destruct function
  *	@mark: Generic packet mark
+ *	@qdhandle: handle of leaf qdisc that handled skb
  *	@nfct: Associated connection, if any
  *	@ipvs_property: skbuff is owned by ipvs
  *	@peeked: this packet has been seen already, so stats have been
@@ -407,6 +408,7 @@ struct sk_buff {
 	union {
 		__u32		mark;
 		__u32		dropcount;
+		__u32		qdhandle;
 	};
 
 	__u16			vlan_tci;
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index d9549af..93189f6 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -72,6 +72,7 @@ extern void qdisc_watchdog_cancel(struct qdisc_watchdog *wd);
 extern struct Qdisc_ops pfifo_qdisc_ops;
 extern struct Qdisc_ops bfifo_qdisc_ops;
 extern struct Qdisc_ops pfifo_head_drop_qdisc_ops;
+extern struct Qdisc_ops pfifo_lat_qdisc_ops;
 
 extern int fifo_set_limit(struct Qdisc *q, unsigned int limit);
 extern struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops,
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index b22ca2d..9c9ba9a 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1769,6 +1769,7 @@ static int __init pktsched_init(void)
 	register_qdisc(&pfifo_qdisc_ops);
 	register_qdisc(&bfifo_qdisc_ops);
 	register_qdisc(&pfifo_head_drop_qdisc_ops);
+	register_qdisc(&pfifo_lat_qdisc_ops);
 	register_qdisc(&mq_qdisc_ops);
 
 	rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL);
diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c
index d468b47..0d2cb48 100644
--- a/net/sched/sch_fifo.c
+++ b/net/sched/sch_fifo.c
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/skbuff.h>
+#include <linux/average.h>
 #include <net/pkt_sched.h>
 
 /* 1 band FIFO pseudo-"scheduler" */
@@ -24,6 +25,20 @@ struct fifo_sched_data
 	u32 limit;
 };
 
+/*
+ * Private data for a pfifo_lat scheduler containing:
+ *	- embedded fifo private data
+ *	- EWMA of average skb service time for each band
+ *	- count of currently in-flight skbs for each band
+ *	- maximum in-flight skbs for each band
+ */
+struct pfifo_lat_data {
+	struct fifo_sched_data q;
+	struct ewma tserv;
+	unsigned int inflight;
+	unsigned int inflight_max;
+};
+
 static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
 {
 	struct fifo_sched_data *q = qdisc_priv(sch);
@@ -59,6 +74,86 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc* sch)
 	return NET_XMIT_CN;
 }
 
+static int pfifo_lat_enqueue(struct sk_buff *skb, struct Qdisc* sch)
+{
+	struct pfifo_lat_data *priv = qdisc_priv(sch);
+
+	/* include inflight count when checking queue length limit */
+	if (skb_queue_len(&sch->q) + priv->inflight < priv->q.limit)
+		return qdisc_enqueue_tail(skb, sch);
+
+	return qdisc_reshape_fail(skb, sch);
+}
+
+static void pfifo_lat_skb_free(struct sk_buff *skb)
+{
+	struct Qdisc *qdisc = qdisc_lookup(skb->dev, skb->qdhandle);
+	struct pfifo_lat_data *priv = qdisc_priv(qdisc);
+	unsigned int tserv_ns, inflight_mult;
+
+	/*
+	 * grab timestamp info for buffer control estimates and factor
+	 * that into service time estimate for this queue
+	 */
+	ewma_add(&priv->tserv,
+		 ktime_to_ns(ktime_sub(ktime_get(), skb->tstamp)));
+	tserv_ns = ewma_read(&priv->tserv);
+	if (tserv_ns) {
+		/* calculate multiplier between tserv and target latency */
+		inflight_mult = 2 * NSEC_PER_MSEC / tserv_ns;
+
+		/*
+		 * use current inflight number as proxy for number of
+		 * packets inflight when this packet was sent to
+		 * hardware queue
+		 */
+		priv->inflight_max =
+			max_t(int, 2, priv->inflight * inflight_mult);
+	}
+
+	priv->inflight--;
+}
+
+static struct sk_buff *pfifo_lat_dequeue(struct Qdisc *qdisc)
+{
+	struct pfifo_lat_data *priv = qdisc_priv(qdisc);
+	struct sk_buff *skb;
+
+	if (priv->inflight >= priv->inflight_max)
+		return NULL;
+
+	skb = qdisc_dequeue_head(qdisc);
+	if (!skb)
+		return NULL;
+
+	priv->inflight++;
+
+	/* take ownership of skb and timestamp it */
+	skb_orphan(skb);
+	skb->qdhandle = qdisc->handle;
+	skb->destructor = pfifo_lat_skb_free;
+	skb->dev = qdisc_dev(qdisc); /* do I need to set this?  */
+	skb->tstamp = ktime_get();
+
+	return skb;
+}
+
+static void pfifo_lat_reset(struct Qdisc* qdisc)
+{
+	struct pfifo_lat_data *priv = qdisc_priv(qdisc);
+
+	/*
+	 * since fifo_sched_data is embedded at head of pfifo_lat_data,
+	 * this should be OK to do...
+	 */
+	qdisc_reset_queue(qdisc);
+
+	/* need to reset priv->tserv somehow? */
+
+	priv->inflight = 0;
+	priv->inflight_max = (typeof(priv->inflight_max))-1;
+}
+
 static int fifo_init(struct Qdisc *sch, struct nlattr *opt)
 {
 	struct fifo_sched_data *q = qdisc_priv(sch);
@@ -82,6 +177,30 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt)
 	return 0;
 }
 
+static int pfifo_lat_init(struct Qdisc *qdisc, struct nlattr *opt)
+{
+	struct pfifo_lat_data *priv = qdisc_priv(qdisc);
+	int rc;
+
+	/*
+	 * since fifo_sched_data is embedded at head of pfifo_lat_data,
+	 * this should be OK to do...
+	 */
+	rc = fifo_init(qdisc, opt);
+	if (rc)
+		return rc;
+
+	/* initialize service time estimate */
+	ewma_init(&priv->tserv, 1, 64);
+
+	priv->inflight = 0; /* necessary to set this explicitly? */
+
+	/* initial inflight_max should be ??? */
+	priv->inflight_max = (typeof(priv->inflight_max))-1;
+
+	return 0;
+}
+
 static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct fifo_sched_data *q = qdisc_priv(sch);
@@ -138,6 +257,18 @@ struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = {
 	.owner		=	THIS_MODULE,
 };
 
+struct Qdisc_ops pfifo_lat_qdisc_ops __read_mostly = {
+	.id		=	"pfifo_lat",
+	.priv_size	=	sizeof(struct pfifo_lat_data),
+	.enqueue	=	pfifo_lat_enqueue,
+	.dequeue	=	pfifo_lat_dequeue,
+	.peek		=	qdisc_peek_head,
+	.init		=	pfifo_lat_init,
+	.reset		=	pfifo_lat_reset,
+	.dump		=	fifo_dump,
+	.owner		=	THIS_MODULE,
+};
+
 /* Pass size change message down to embedded FIFO */
 int fifo_set_limit(struct Qdisc *q, unsigned int limit)
 {
-- 
1.7.4


^ permalink raw reply related

* [PATCH] xfrm: Return dst directly from xfrm_lookup()
From: David Miller @ 2011-03-02 21:55 UTC (permalink / raw)
  To: netdev


Instead of on the stack.

Signed-off-by: David S. Miller <davem@davemloft.net>
---

Ok, this is what I was setting up for with the changes I wrote
and posted yesterday.

Next is getting the ipv4 route lookup interfaces to do this as
well.

 include/net/dst.h                |   14 ++++++++------
 net/decnet/dn_route.c            |   12 ++++++++++--
 net/ipv4/icmp.c                  |   36 ++++++++++++++----------------------
 net/ipv4/netfilter.c             |    6 ++++--
 net/ipv4/route.c                 |    7 ++++++-
 net/ipv6/icmp.c                  |   37 ++++++++++++++++++-------------------
 net/ipv6/ip6_output.c            |   10 ++--------
 net/ipv6/ip6_tunnel.c            |    8 +++++++-
 net/ipv6/mcast.c                 |   13 ++++++++++---
 net/ipv6/ndisc.c                 |    8 ++++----
 net/ipv6/netfilter.c             |    3 ++-
 net/ipv6/netfilter/ip6t_REJECT.c |    3 ++-
 net/netfilter/ipvs/ip_vs_xmit.c  |    9 +++++++--
 net/xfrm/xfrm_policy.c           |   34 +++++++++++++++++-----------------
 14 files changed, 111 insertions(+), 89 deletions(-)

diff --git a/include/net/dst.h b/include/net/dst.h
index 8948452..2a46cba 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -426,15 +426,17 @@ enum {
 
 struct flowi;
 #ifndef CONFIG_XFRM
-static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
-			      const struct flowi *fl, struct sock *sk,
-			      int flags)
+static inline struct dst_entry *xfrm_lookup(struct net *net,
+					    struct dst_entry *dst_orig,
+					    const struct flowi *fl, struct sock *sk,
+					    int flags)
 {
-	return 0;
+	return dst_orig;
 } 
 #else
-extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
-		       const struct flowi *fl, struct sock *sk, int flags);
+extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
+				     const struct flowi *fl, struct sock *sk,
+				     int flags);
 #endif
 #endif
 
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 0877147..484fdbf 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -1222,7 +1222,11 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int f
 
 	err = __dn_route_output_key(pprt, flp, flags);
 	if (err == 0 && flp->proto) {
-		err = xfrm_lookup(&init_net, pprt, flp, NULL, 0);
+		*pprt = xfrm_lookup(&init_net, *pprt, flp, NULL, 0);
+		if (IS_ERR(*pprt)) {
+			err = PTR_ERR(*pprt);
+			*pprt = NULL;
+		}
 	}
 	return err;
 }
@@ -1235,7 +1239,11 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock
 	if (err == 0 && fl->proto) {
 		if (!(flags & MSG_DONTWAIT))
 			fl->flags |= FLOWI_FLAG_CAN_SLEEP;
-		err = xfrm_lookup(&init_net, pprt, fl, sk, 0);
+		*pprt = xfrm_lookup(&init_net, *pprt, fl, sk, 0);
+		if (IS_ERR(*pprt)) {
+			err = PTR_ERR(*pprt);
+			*pprt = NULL;
+		}
 	}
 	return err;
 }
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 2a86c89..c23bd8c 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -398,18 +398,14 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in,
 	if (!fl.fl4_src)
 		fl.fl4_src = rt->rt_src;
 
-	err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0);
-	switch (err) {
-	case 0:
+	rt = (struct rtable *) xfrm_lookup(net, &rt->dst, &fl, NULL, 0);
+	if (!IS_ERR(rt)) {
 		if (rt != rt2)
 			return rt;
-		break;
-	case -EPERM:
+	} else if (PTR_ERR(rt) == -EPERM) {
 		rt = NULL;
-		break;
-	default:
-		return ERR_PTR(err);
-	}
+	} else
+		return rt;
 
 	err = xfrm_decode_session_reverse(skb_in, &fl, AF_INET);
 	if (err)
@@ -438,22 +434,18 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in,
 	if (err)
 		goto relookup_failed;
 
-	err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL,
-			  XFRM_LOOKUP_ICMP);
-	switch (err) {
-	case 0:
+	rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst, &fl, NULL, XFRM_LOOKUP_ICMP);
+	if (!IS_ERR(rt2)) {
 		dst_release(&rt->dst);
 		rt = rt2;
-		break;
-	case -EPERM:
-		return ERR_PTR(err);
-	default:
-		if (!rt)
-			return ERR_PTR(err);
-		break;
+	} else if (PTR_ERR(rt2) == -EPERM) {
+		if (rt)
+			dst_release(&rt->dst);
+		return rt2;
+	} else {
+		err = PTR_ERR(rt2);
+		goto relookup_failed;
 	}
-
-
 	return rt;
 
 relookup_failed:
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 994a1f2..9770bb4 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -69,7 +69,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
 	    xfrm_decode_session(skb, &fl, AF_INET) == 0) {
 		struct dst_entry *dst = skb_dst(skb);
 		skb_dst_set(skb, NULL);
-		if (xfrm_lookup(net, &dst, &fl, skb->sk, 0))
+		dst = xfrm_lookup(net, dst, &fl, skb->sk, 0);
+		if (IS_ERR(dst))
 			return -1;
 		skb_dst_set(skb, dst);
 	}
@@ -102,7 +103,8 @@ int ip_xfrm_me_harder(struct sk_buff *skb)
 		dst = ((struct xfrm_dst *)dst)->route;
 	dst_hold(dst);
 
-	if (xfrm_lookup(dev_net(dst->dev), &dst, &fl, skb->sk, 0) < 0)
+	dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0);
+	if (IS_ERR(dst))
 		return -1;
 
 	skb_dst_drop(skb);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index e24e4cf..63d3700 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2730,7 +2730,12 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp,
 			flp->fl4_src = (*rp)->rt_src;
 		if (!flp->fl4_dst)
 			flp->fl4_dst = (*rp)->rt_dst;
-		return xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, 0);
+		*rp = (struct rtable *) xfrm_lookup(net, &(*rp)->dst, flp, sk, 0);
+		if (IS_ERR(*rp)) {
+			err = PTR_ERR(*rp);
+			*rp = NULL;
+			return err;
+		}
 	}
 
 	return 0;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index e332bae..5566595 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -324,17 +324,15 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *sk
 	/* No need to clone since we're just using its address. */
 	dst2 = dst;
 
-	err = xfrm_lookup(net, &dst, fl, sk, 0);
-	switch (err) {
-	case 0:
+	dst = xfrm_lookup(net, dst, fl, sk, 0);
+	if (!IS_ERR(dst)) {
 		if (dst != dst2)
 			return dst;
-		break;
-	case -EPERM:
-		dst = NULL;
-		break;
-	default:
-		return ERR_PTR(err);
+	} else {
+		if (PTR_ERR(dst) == -EPERM)
+			dst = NULL;
+		else
+			return dst;
 	}
 
 	err = xfrm_decode_session_reverse(skb, &fl2, AF_INET6);
@@ -345,17 +343,17 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *sk
 	if (err)
 		goto relookup_failed;
 
-	err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP);
-	switch (err) {
-	case 0:
+	dst2 = xfrm_lookup(net, dst2, &fl2, sk, XFRM_LOOKUP_ICMP);
+	if (!IS_ERR(dst2)) {
 		dst_release(dst);
 		dst = dst2;
-		break;
-	case -EPERM:
-		dst_release(dst);
-		return ERR_PTR(err);
-	default:
-		goto relookup_failed;
+	} else {
+		err = PTR_ERR(dst2);
+		if (err == -EPERM) {
+			dst_release(dst);
+			return dst2;
+		} else
+			goto relookup_failed;
 	}
 
 relookup_failed:
@@ -560,7 +558,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
 	err = ip6_dst_lookup(sk, &dst, &fl);
 	if (err)
 		goto out;
-	if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0)
+	dst = xfrm_lookup(net, dst, &fl, sk, 0);
+	if (IS_ERR(dst))
 		goto out;
 
 	if (ipv6_addr_is_multicast(&fl.fl6_dst))
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 35a4ad9..adaffaf 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1028,10 +1028,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl,
 	if (can_sleep)
 		fl->flags |= FLOWI_FLAG_CAN_SLEEP;
 
-	err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-	if (err)
-		return ERR_PTR(err);
-	return dst;
+	return xfrm_lookup(sock_net(sk), dst, fl, sk, 0);
 }
 EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow);
 
@@ -1067,10 +1064,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl,
 	if (can_sleep)
 		fl->flags |= FLOWI_FLAG_CAN_SLEEP;
 
-	err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0);
-	if (err)
-		return ERR_PTR(err);
-	return dst;
+	return xfrm_lookup(sock_net(sk), dst, fl, sk, 0);
 }
 EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);
 
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 4f4483e..da43038 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -903,8 +903,14 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
 	else {
 		dst = ip6_route_output(net, NULL, fl);
 
-		if (dst->error || xfrm_lookup(net, &dst, fl, NULL, 0) < 0)
+		if (dst->error)
 			goto tx_err_link_failure;
+		dst = xfrm_lookup(net, dst, fl, NULL, 0);
+		if (IS_ERR(dst)) {
+			err = PTR_ERR(dst);
+			dst = NULL;
+			goto tx_err_link_failure;
+		}
 	}
 
 	tdev = dst->dev;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 49f986d..7b27d08 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1429,7 +1429,12 @@ static void mld_sendpack(struct sk_buff *skb)
 			 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
 			 skb->dev->ifindex);
 
-	err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+	dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+	err = 0;
+	if (IS_ERR(dst)) {
+		err = PTR_ERR(dst);
+		dst = NULL;
+	}
 	skb_dst_set(skb, dst);
 	if (err)
 		goto err_out;
@@ -1796,9 +1801,11 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 			 &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
 			 skb->dev->ifindex);
 
-	err = xfrm_lookup(net, &dst, &fl, NULL, 0);
-	if (err)
+	dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+	if (IS_ERR(dst)) {
+		err = PTR_ERR(dst);
 		goto err_out;
+	}
 
 	skb_dst_set(skb, dst);
 	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 7254ce3..9360d3b 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -529,8 +529,8 @@ void ndisc_send_skb(struct sk_buff *skb,
 		return;
 	}
 
-	err = xfrm_lookup(net, &dst, &fl, NULL, 0);
-	if (err < 0) {
+	dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+	if (IS_ERR(dst)) {
 		kfree_skb(skb);
 		return;
 	}
@@ -1542,8 +1542,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 	if (dst == NULL)
 		return;
 
-	err = xfrm_lookup(net, &dst, &fl, NULL, 0);
-	if (err)
+	dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+	if (IS_ERR(dst))
 		return;
 
 	rt = (struct rt6_info *) dst;
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 35915e8..8d74116 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -39,7 +39,8 @@ int ip6_route_me_harder(struct sk_buff *skb)
 	if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
 	    xfrm_decode_session(skb, &fl, AF_INET6) == 0) {
 		skb_dst_set(skb, NULL);
-		if (xfrm_lookup(net, &dst, &fl, skb->sk, 0))
+		dst = xfrm_lookup(net, dst, &fl, skb->sk, 0);
+		if (IS_ERR(dst))
 			return -1;
 		skb_dst_set(skb, dst);
 	}
diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c
index bf998fe..91f6a61 100644
--- a/net/ipv6/netfilter/ip6t_REJECT.c
+++ b/net/ipv6/netfilter/ip6t_REJECT.c
@@ -101,7 +101,8 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
 		dst_release(dst);
 		return;
 	}
-	if (xfrm_lookup(net, &dst, &fl, NULL, 0))
+	dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+	if (IS_ERR(dst))
 		return;
 
 	hh_len = (dst->dev->hard_header_len + 15)&~15;
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index a48239a..6264219 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -218,8 +218,13 @@ __ip_vs_route_output_v6(struct net *net, struct in6_addr *daddr,
 	    ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev,
 			       &fl.fl6_dst, 0, &fl.fl6_src) < 0)
 		goto out_err;
-	if (do_xfrm && xfrm_lookup(net, &dst, &fl, NULL, 0) < 0)
-		goto out_err;
+	if (do_xfrm) {
+		dst = xfrm_lookup(net, dst, &fl, NULL, 0);
+		if (IS_ERR(dst)) {
+			dst = NULL;
+			goto out_err;
+		}
+	}
 	ipv6_addr_copy(ret_saddr, &fl.fl6_src);
 	return dst;
 
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0248afa..b1932a6 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1757,14 +1757,14 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
  * At the moment we eat a raw IP route. Mostly to speed up lookups
  * on interfaces with disabled IPsec.
  */
-int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
-		const struct flowi *fl,
-		struct sock *sk, int flags)
+struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
+			      const struct flowi *fl,
+			      struct sock *sk, int flags)
 {
 	struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
 	struct flow_cache_object *flo;
 	struct xfrm_dst *xdst;
-	struct dst_entry *dst, *dst_orig = *dst_p, *route;
+	struct dst_entry *dst, *route;
 	u16 family = dst_orig->ops->family;
 	u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
 	int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
@@ -1847,11 +1847,7 @@ restart:
 			xfrm_pols_put(pols, drop_pols);
 			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
 
-			dst = make_blackhole(net, family, dst_orig);
-			if (IS_ERR(dst))
-				return PTR_ERR(dst);
-			*dst_p = dst;
-			return 0;
+			return make_blackhole(net, family, dst_orig);
 		}
 		if (fl->flags & FLOWI_FLAG_CAN_SLEEP) {
 			DECLARE_WAITQUEUE(wait, current);
@@ -1895,27 +1891,28 @@ no_transform:
 		goto error;
 	} else if (num_xfrms > 0) {
 		/* Flow transformed */
-		*dst_p = dst;
 		dst_release(dst_orig);
 	} else {
 		/* Flow passes untransformed */
 		dst_release(dst);
+		dst = dst_orig;
 	}
 ok:
 	xfrm_pols_put(pols, drop_pols);
-	return 0;
+	return dst;
 
 nopol:
-	if (!(flags & XFRM_LOOKUP_ICMP))
+	if (!(flags & XFRM_LOOKUP_ICMP)) {
+		dst = dst_orig;
 		goto ok;
+	}
 	err = -ENOENT;
 error:
 	dst_release(dst);
 dropdst:
 	dst_release(dst_orig);
-	*dst_p = NULL;
 	xfrm_pols_put(pols, drop_pols);
-	return err;
+	return ERR_PTR(err);
 }
 EXPORT_SYMBOL(xfrm_lookup);
 
@@ -2175,7 +2172,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 	struct net *net = dev_net(skb->dev);
 	struct flowi fl;
 	struct dst_entry *dst;
-	int res;
+	int res = 0;
 
 	if (xfrm_decode_session(skb, &fl, family) < 0) {
 		XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR);
@@ -2183,9 +2180,12 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 	}
 
 	skb_dst_force(skb);
-	dst = skb_dst(skb);
 
-	res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0;
+	dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0);
+	if (IS_ERR(dst)) {
+		res = 1;
+		dst = NULL;
+	}
 	skb_dst_set(skb, dst);
 	return res;
 }
-- 
1.7.4.1


^ permalink raw reply related

* Re: [PATCH 0/7] Final BKL removal, take 2
From: Arnd Bergmann @ 2011-03-02 21:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Greg KH, linux-kernel, Andi Kleen, Andrew Hendry,
	Arnaldo Carvalho de Melo, David Miller, Eric Dumazet,
	Evgeniy Dushistov, linux-fsdevel, linux-x25, Max Vozeler,
	Mikulas Patocka, netdev, Nick Bowler, Nick Piggin,
	Palash Bandyopadhyay, Takahiro Hirofuchi
In-Reply-To: <4D6EA965.9050709@redhat.com>

On Wednesday 02 March 2011 21:32:37 Mauro Carvalho Chehab wrote:
> >>
> >> It is probably better to queue the staging/cx25821 patch via my tree, as this is one
> >> of those staging files that it is handled via media tree. So, if it is ok
> >> for you both, I'll get patch 2/7.
> > 
> > Yes, you are right, please take it through your tree.
> 
> Patch applied on my tree, thanks!

I've pushed out everything to my git tree, except for the two staging
patches that go through the other trees.

Once everything is in -next, I'll add the last patch to remove the
infrastructure.

Thanks for the feedback!

	Arnd

^ permalink raw reply

* Re: [patch net-next-2.6] bonding: remove skb_share_check in handle_frame
From: Nicolas de Pesloüan @ 2011-03-02 21:54 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: Andy Gospodarek, netdev, davem, fubar, eric.dumazet
In-Reply-To: <20110302211238.GC3360@psychotron.redhat.com>

Le 02/03/2011 22:12, Jiri Pirko a écrit :
> Wed, Mar 02, 2011 at 09:47:50PM CET, nicolas.2p.debian@gmail.com wrote:
>> Hi Jiri,
>>
>> Do you plan to call the bonding ARP handler from inside bond_handle_frame()?
>
> I do - it's part of patchset I've cooked (going to test that tomorrow).
>
>> A few days ago
>> (http://marc.info/?l=linux-netdev&m=129883949022340&w=2), I noticed
>> that it is not possible to call the bonding ARP handler from inside
>> the bonding rx_handler, because some frame processing may be required
>> after the bonding rx_handler call, to put the frame in a suitable
>> state for the bonding ARP handler.
>
> Do you see another scenario besides the next one?

None that currently work, but eth0 -> bond0 -> br0 -> br0.100 should work too.

>> This is at least true with the following setup, eth0 ->  bond0 ->
>> bond0.100, where the ARP frames are VLAN tagged at the time the
>> bonding rx_handler process them.
>
> Isn't this scenario resolved by vlan_on_bond_hook ?
>
> eth0
>    ->rx_handler ->  another round
> bond0
>    ->vlan_hwaccel_do_receive ->  __netif_receive_skb
> bond0.100
>    ->vlan_on_bond_hook ->  reinject to bond0

Yes, it is, but this hack does not solve the eth0 -> bond0 -> br0 -> br0.100 configuration.

All those handlers that call netif_rx() or __netif_receive_skb() sound horrible to me. Can you 
imagine the global overhead of the above receive path?

The reason why I suggested you introduce the goto another_round is because most - if not all - 
stacking configurations could/should be handled simply by returning the right skb from the 
rx_handler and let __netif_receive_skb() loop. And by having the right orig_dev logic inside 
__netif_receive_skb(), it could be possible to remove the current vlan_on_bond_hook hack.

My question about whether the skb is shared between the protocol handlers (in another thread) also 
target at this idea.

You will probably told me I'm free to propose patchs for all that, and you are right. Just missing 
the time to do so.

	Nicolas.

^ permalink raw reply

* Re: [PATCH 27/27] HFI: hf ethtool support
From: Ben Hutchings @ 2011-03-02 21:52 UTC (permalink / raw)
  To: dykmanj
  Cc: netdev, Piyush Chaudhary, Fu-Chung Chang, William S. Cadden,
	Wen C. Chen, Scot Sakolish, Jian Xiao, Carol L. Soto,
	Sarah J. Sheppard
In-Reply-To: <1299100213-8770-27-git-send-email-dykmanj@linux.vnet.ibm.com>

On Wed, 2011-03-02 at 16:10 -0500, dykmanj@linux.vnet.ibm.com wrote:
[...]
> +static int hf_get_sset_count(struct net_device *netdev, int sset)
> +{
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		return ARRAY_SIZE(hf_ethtool_stats_keys);
> +	default:
> +		return -EOPNOTSUPP;

The error code should be -EINVAL, I think.

> +	}
> +}
> +
> +static void hf_get_ethtool_stats(struct net_device *netdev,
> +		struct ethtool_stats *stats, u64 *data)
> +{
> +	struct hf_net	*net = netdev_priv(netdev);
> +	struct hf_if	*net_if = &(net->hfif);
> +
> +	memcpy(data, &(net_if->eth_stats), sizeof(struct hf_ethtool_stats));
[...]

This may result in word tearing, particularly if this driver can be
built for a 32-bit system.  Since the stats appear to be updated
asynchronously in the data path, you may have to declare them as
unsigned long and then extend them to 64-bit in hf_get_ethtool_stats().

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply

* Re: [question] bond_set_slave_inactive_flags
From: Jay Vosburgh @ 2011-03-02 21:41 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: netdev
In-Reply-To: <20110301074318.GF2855@psychotron.redhat.com>

Jiri Pirko <jpirko@redhat.com> wrote:

>Jay,
>
>Looking at function bond_set_slave_inactive_flags:
>
>static inline void bond_set_slave_inactive_flags(struct slave *slave)
>{
>        struct bonding *bond = netdev_priv(slave->dev->master);
>        if (!bond_is_lb(bond))
>                slave->state = BOND_STATE_BACKUP;
>        if (!bond->params.all_slaves_active)
>                slave->dev->priv_flags |= IFF_SLAVE_INACTIVE;
>        if (slave_do_arp_validate(bond, slave))
>                slave->dev->priv_flags |= IFF_SLAVE_NEEDARP;
>}
>
>Why BOND_STATE_BACKUP is set only for non-lb bonds?
>This was introduced by 8f903c708fcc2b579ebf16542bf6109bad593a1d but
>I do not see why.

	It broke something to have slaves in alb or tlb mode (what
"bond_is_lb" means) set to state == BACKUP; I'm trying to remember what
it was.  I know it would mess up some text messages (calling alb/tlb
slaves "backup", for example), but I think also broke the alb/tlb modes
themselves because SLAVE_IS_OK returns false if state != ACTIVE.

	For alb/tlb, there is an "active" slave, and that's the one that
handles multicast and broadcast.  The others are "inactive," but not
really; they still send and receive regular traffic, but not bcast /
mcast.  The IFF_SLAVE_INACTIVE flag is used to exclude the bcast / mcast
traffic from the "inactive" slaves.

	-J

---
	-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com

^ permalink raw reply

* [PATCH 00/27] HFI minimal device driver/network driver
From: dykmanj @ 2011-03-02 21:19 UTC (permalink / raw)
  To: netdev; +Cc: Jim Dykman

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

The HFI ("Host Fabric Interface") network interface is the internal cluster 
fabric of IBM's PERCS supercomputer. The hardware design is under US export 
control, so we cannot release hardware specs. There is a writeup of 
publically available information about the system available here:
http://sourceforge.net/projects/hfidevicedriver/files/docs/hfi_general_desc_v2.1.txt

hfi_core contains the resource management to set up communications paths for
network traffic. Calls are provided for kernel drivers, and also for setting
up direct user-space access to HFI windows.
hf_if contains the kernel network driver.

The driver has been running in the lab for several months. The full patch is
around 22000 lines, so we've split out a minimal device/network driver that 
can send and receive through the simplest path.  Once that much gets reviewed 
we'll start adding on to it.

Patches are against net-2.6 (Is that the correct tree for us to use?).

Jim Dykman

^ permalink raw reply

* Re: [patch net-next-2.6] bonding: remove skb_share_check in handle_frame
From: Jiri Pirko @ 2011-03-02 21:12 UTC (permalink / raw)
  To: Nicolas de Pesloüan
  Cc: Andy Gospodarek, netdev, davem, fubar, eric.dumazet
In-Reply-To: <4D6EACF6.2040005@gmail.com>

Wed, Mar 02, 2011 at 09:47:50PM CET, nicolas.2p.debian@gmail.com wrote:
>Le 02/03/2011 11:03, Jiri Pirko a écrit :
>>Tue, Mar 01, 2011 at 09:38:43PM CET, andy@greyhouse.net wrote:
>>>On Tue, Mar 01, 2011 at 10:29:07AM +0100, Jiri Pirko wrote:
>>>>Unapplicable, sorry (wrong branch :(). Here's corrected patch:
>>>>
>>>>Subject: [PATCH net-next-2.6 v2] bonding: remove skb_share_check in handle_frame
>>>>
>>>>No need to do share check here.
>>>>
>>>>Signed-off-by: Jiri Pirko<jpirko@redhat.com>
>>>>---
>>>>  drivers/net/bonding/bond_main.c |    3 ---
>>>>  1 files changed, 0 insertions(+), 3 deletions(-)
>>>>
>>>>diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
>>>>index 584f97b..367ea60 100644
>>>>--- a/drivers/net/bonding/bond_main.c
>>>>+++ b/drivers/net/bonding/bond_main.c
>>>>@@ -1498,9 +1498,6 @@ static struct sk_buff *bond_handle_frame(struct sk_buff *skb)
>>>>  	struct net_device *slave_dev;
>>>>  	struct net_device *bond_dev;
>>>>
>>>>-	skb = skb_share_check(skb, GFP_ATOMIC);
>>>>-	if (unlikely(!skb))
>>>>-		return NULL;
>>>>  	slave_dev = skb->dev;
>>>>  	bond_dev = ACCESS_ONCE(slave_dev->master);
>>>>  	if (unlikely(!bond_dev))
>>>>--
>>>>1.7.3.4
>>>>
>>>
>>>Why did you decide to get rid of it here rather than the 3 places in the
>>>bonding driver where it is currently needed?  I think this can cover
>>>those cases since bond_handle_frame will be called after the ptype_all
>>>handlers before any of the ptype handlers.
>>
>>I have already a patch prepared which converts bond ptype handlers into
>>being called from bond_handle_frame. You are propably right that this
>>should probably stay here.
>
>Hi Jiri,
>
>Do you plan to call the bonding ARP handler from inside bond_handle_frame()?

I do - it's part of patchset I've cooked (going to test that tomorrow).

>
>A few days ago
>(http://marc.info/?l=linux-netdev&m=129883949022340&w=2), I noticed
>that it is not possible to call the bonding ARP handler from inside
>the bonding rx_handler, because some frame processing may be required
>after the bonding rx_handler call, to put the frame in a suitable
>state for the bonding ARP handler.

Do you see another scenario besides the next one?

>
>This is at least true with the following setup, eth0 -> bond0 ->
>bond0.100, where the ARP frames are VLAN tagged at the time the
>bonding rx_handler process them.

Isn't this scenario resolved by vlan_on_bond_hook ?

eth0
  ->rx_handler -> another round
bond0
  ->vlan_hwaccel_do_receive -> __netif_receive_skb
bond0.100
  ->vlan_on_bond_hook -> reinject to bond0


>
>	Nicolas.

^ permalink raw reply

* [PATCH 21/27] HFI: Add send and receive interrupts
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Each window has its own interrupt for send interrupts and another for receive
interrupts.

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/Makefile       |    1 +
 drivers/net/hfi/core/hfidd_intr.c   |  127 +++++++++++++++++++++++++++++++++++
 drivers/net/hfi/core/hfidd_proto.h  |    3 +
 drivers/net/hfi/core/hfidd_window.c |   16 ++++-
 include/linux/hfi/hfidd_client.h    |   17 +++++
 include/linux/hfi/hfidd_internal.h  |    2 +
 6 files changed, 165 insertions(+), 1 deletions(-)
 create mode 100644 drivers/net/hfi/core/hfidd_intr.c

diff --git a/drivers/net/hfi/core/Makefile b/drivers/net/hfi/core/Makefile
index 3adf07e..d2ed86f 100644
--- a/drivers/net/hfi/core/Makefile
+++ b/drivers/net/hfi/core/Makefile
@@ -6,5 +6,6 @@ hfi_core-objs:=	hfidd_adpt.o \
 		hfidd_init.o \
 		hfidd_xlat.o \
 		hfidd_map.o \
+		hfidd_intr.o \
 		hfidd_hcalls.o
 obj-$(CONFIG_HFI) += hfi_core.o
diff --git a/drivers/net/hfi/core/hfidd_intr.c b/drivers/net/hfi/core/hfidd_intr.c
new file mode 100644
index 0000000..253de27
--- /dev/null
+++ b/drivers/net/hfi/core/hfidd_intr.c
@@ -0,0 +1,127 @@
+/*
+ * hfidd_intr.c
+ *
+ * HFI device driver for IBM System p
+ *
+ *  Authors:
+ *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *      William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *      Jian Xiao <jian@linux.vnet.ibm.com>
+ *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/hfi/hfidd_internal.h>
+#include "hfidd_proto.h"
+
+static irqreturn_t send_intr_handler(int irq, void *data)
+{
+	struct hfidd_window *win_p = data;
+	struct hfidd_acs *p_acs;
+
+	p_acs = hfidd_global.p_acs[win_p->ai];
+	if (p_acs == NULL)
+		return IRQ_HANDLED;
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t recv_intr_handler(int irq, void *data)
+{
+	struct hfidd_window *win_p = data;
+	struct hfidd_acs *p_acs;
+
+	p_acs = hfidd_global.p_acs[win_p->ai];
+	if (p_acs == NULL)
+		return IRQ_HANDLED;
+
+	return IRQ_HANDLED;
+}
+
+static inline void hfidd_clear_interrupt(unsigned int int_level,
+			struct hfidd_window *win_p)
+{
+	ibmebus_free_irq(int_level, win_p);
+}
+
+static int hfidd_init_interrupt(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p ,
+		irqreturn_t (*handler)(int, void *),
+		const char *name,
+		unsigned int int_level)
+{
+	int rc;
+
+	rc = ibmebus_request_irq(int_level, handler, IRQF_DISABLED, name,
+			win_p);
+	if (rc != 0) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_init_interrupt: request_irq failed for "
+			"int_level 0x%x rc %d\n", int_level, rc);
+		return rc;
+	}
+	return rc;
+}
+
+int hfidd_init_win_interrupt(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p)
+{
+	int rc;
+
+	/* init send interrupt handler */
+	snprintf(win_p->send_name, IRQ_NAME_SIZE - 1, "%s%d-send%d",
+		HFIDD_DEV_NAME, p_acs->index, win_p->index);
+	rc = hfidd_init_interrupt(p_acs, win_p, send_intr_handler,
+		win_p->send_name, win_p->send_intr);
+	if (rc != 0) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_init_win_interrupt: send int failed, "
+			"rc = 0x%x\n", rc);
+		return rc;
+	}
+
+	/* init recv interrupt handler */
+	snprintf(win_p->recv_name, IRQ_NAME_SIZE - 1, "%s%d-recv%d",
+		HFIDD_DEV_NAME, p_acs->index, win_p->index);
+	rc = hfidd_init_interrupt(p_acs, win_p, recv_intr_handler,
+		win_p->recv_name, win_p->recv_intr);
+	if (rc != 0) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_init_win_interrupt: recv int failed, "
+			"rc = 0x%x\n", rc);
+		hfidd_clear_interrupt(win_p->send_intr, win_p);
+		return rc;
+	}
+	return 0;
+}
+
+void hfidd_clear_win_interrupt(struct hfidd_window *win_p)
+{
+	if (win_p->send_intr != 0) {
+		hfidd_clear_interrupt(win_p->send_intr, win_p);
+		win_p->send_intr = 0;
+	}
+	if (win_p->recv_intr != 0) {
+		hfidd_clear_interrupt(win_p->recv_intr, win_p);
+		win_p->recv_intr = 0;
+	}
+}
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index f531dcd..af88f0b 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -73,6 +73,9 @@ int hfidd_query_interface(struct hfidd_acs *p_acs, unsigned int subtype,
 int hfidd_start_nmmu(struct hfidd_acs *p_acs);
 int hfidd_start_interface(struct hfidd_acs *p_acs);
 int hfidd_stop_interface(struct hfidd_acs *p_acs, unsigned int hfi_id);
+int hfidd_init_win_interrupt(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p);
+void hfidd_clear_win_interrupt(struct hfidd_window *win_p);
 long long hfi_start_nmmu(u64 chip_id, void *nmmu_info);
 long long hfi_stop_nmmu(u64 chip_id);
 long long hfi_open_window(u64 unit_id, u64 win_id, u64 flag,
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index ff8b9f0..752cd11 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -1050,6 +1050,15 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 	local_p->local_isrid = p_acs->isr;
 	win_p->client_info.local_isrid = p_acs->isr;
 
+	/* Init the send and recv interrupt handlers */
+	rc = hfidd_init_win_interrupt(p_acs, win_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_open_window_func: hfidd_init_win_interrupt "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_open_window_func_err6;
+	}
+
 	/* Copy out the client info back to user */
 	rc = hfi_copy_to_user((void *)out_p, (void *)local_p,
 			is_userspace, sizeof(struct hfi_client_info));
@@ -1057,7 +1066,7 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		dev_printk(KERN_ERR, p_acs->hfidd_dev,
 			"hfidd_open_window_func: hfi_copy_to_user "
 			"failed, rc = 0x%x\n", rc);
-		goto hfidd_open_window_func_err6;
+		goto hfidd_open_window_func_err7;
 	}
 
 	spin_lock(&(win_p->win_lock));
@@ -1069,6 +1078,8 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 	kfree(local_p);
 	return rc;
 
+hfidd_open_window_func_err7:
+	hfidd_clear_win_interrupt(win_p);
 hfidd_open_window_func_err6:
 	if (is_userspace)
 		hfidd_unmap(local_p->mmio_regs.use.kptr, PAGE_SIZE_64K);
@@ -1135,6 +1146,9 @@ int hfidd_close_window_internal(struct hfidd_acs *p_acs,
 	}
 	spin_unlock(&(win_p->win_lock));
 
+	/* Clear the send and recv interrupt handlers */
+	hfidd_clear_win_interrupt(win_p);
+
 	rc = hfi_unmap_mmio_regs(p_acs, win_p, is_userspace);
 	if (rc) {
 		dev_printk(KERN_ERR, p_acs->hfidd_dev,
diff --git a/include/linux/hfi/hfidd_client.h b/include/linux/hfi/hfidd_client.h
index 11c8973..3b2d032 100644
--- a/include/linux/hfi/hfidd_client.h
+++ b/include/linux/hfi/hfidd_client.h
@@ -121,6 +121,23 @@ struct hfi_window_info {
 	unsigned int		window;
 };
 
+/* Event Notification */
+enum hfi_event_type {
+	HFIDD_SEND		= 0,
+	HFIDD_RECV		= 1,
+	HFIDD_WIN_ERROR		= 2,
+	HFIDD_HFI_ERROR		= 3,
+	HFIDD_TERMINATE		= 4,
+	HFIDD_RELEASE_WINDOW	= 5,
+	HFIDD_CAU_ERROR		= 6,
+	HFIDD_ICS_ERROR		= 7,
+	HFIDD_HFI_READY_REG	= 8,
+	HFIDD_ROUTE_CHANGE	= 9,
+	HFIDD_IP_TRC_LVL	= 10,	/* IP Window only */
+	HFIDD_POOL_SIZE		= 11,	/* IP Window only */
+	HFIDD_NUM_EVENT_TYPES	= 12
+};
+
 #define MAX_TORRENTS            1
 #define MAX_HFI_PER_TORRENT     2
 #define MAX_HFIS                (MAX_TORRENTS * MAX_HFI_PER_TORRENT)
diff --git a/include/linux/hfi/hfidd_internal.h b/include/linux/hfi/hfidd_internal.h
index 0d6b77b..e96142a 100644
--- a/include/linux/hfi/hfidd_internal.h
+++ b/include/linux/hfi/hfidd_internal.h
@@ -145,6 +145,8 @@ struct hfidd_global {
 	struct hfidd_acs	*p_acs[MAX_HFIS];
 };
 
+extern struct hfidd_global hfidd_global;
+
 static inline struct hfidd_window *hfi_window(struct hfidd_acs *p,
 		unsigned int idx)
 {
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 08/27] HFI: DD request framework and first HFI DD request
From: dykmanj @ 2011-03-02 21:09 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

We use an ioctl-ish mechanism similar to the one found in the HEA driver.
Some of our requests have very large parameter lists, this method allows
us to get the parms into the DD quickly.

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/hfidd_init.c  |   94 +++++++++++++++++++++++++++++++++++-
 include/linux/hfi/Kbuild           |    1 +
 include/linux/hfi/hfidd_client.h   |   40 +++++++++++++++
 include/linux/hfi/hfidd_internal.h |   23 ++++++++-
 include/linux/hfi/hfidd_requests.h |   38 ++++++++++++++
 5 files changed, 192 insertions(+), 4 deletions(-)
 create mode 100644 include/linux/hfi/hfidd_requests.h

diff --git a/drivers/net/hfi/core/hfidd_init.c b/drivers/net/hfi/core/hfidd_init.c
index 448349d..a57f247 100644
--- a/drivers/net/hfi/core/hfidd_init.c
+++ b/drivers/net/hfi/core/hfidd_init.c
@@ -37,6 +37,7 @@
 #include <linux/of.h>
 
 #include <linux/hfi/hfidd_internal.h>
+#include <linux/hfi/hfidd_requests.h>
 #include "hfidd_proto.h"
 
 MODULE_VERSION("1.0");
@@ -60,11 +61,102 @@ static ssize_t hfidd_read(struct file *filep, char *buf, size_t count,
 	return 0;
 }
 
+/* Query firmare level and use abi version to users */
+static int hfidd_query_dd_info(struct hfidd_acs *p_acs,
+			struct hfi_query_dd_info *user_p)
+{
+	struct hfi_query_dd_info req;
+	int rc;
+
+	req.fw_ec_level = p_acs->dds.fw_ec_level;
+	req.abi_version = HFIDD_USER_ABI_VERSION;
+
+	rc = copy_to_user(user_p, &req, sizeof(struct hfi_query_dd_info));
+	if (rc)
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_query_dd_info: copy_to_user failed\n");
+
+	return rc;
+}
+
 /* Entry point for user space to do driver requests. */
 static ssize_t hfidd_cmd_write(struct file *filep, const char __user *buf,
 		size_t count, loff_t *pos)
 {
-	return 0;
+	struct hfidd_acs	*p_acs;
+	int			ai;
+	int			cnt = 0;
+	int			rc = 0;
+	struct hfi_req_hdr	cmd;
+	int			is_userspace;
+
+	ai = iminor(filep->f_path.dentry->d_inode);
+	if (ai >= MAX_HFIS) {
+		printk(KERN_ERR "%s: hfidd_cmd_write: wrong ai = %d\n",
+				HFIDD_DEV_NAME, ai);
+		return -ENODEV;
+	}
+
+	p_acs = hfidd_global.p_acs[ai];
+	if (p_acs == NULL) {
+		printk(KERN_ERR "%s: hfidd_cmd_write: p_acs is NULL\n",
+				HFIDD_DEV_NAME);
+		return -EINVAL;
+	}
+
+	if (count < sizeof(cmd)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_cmd_write: Invalid count: 0x%lx expected "
+			"count: 0x%lx\n", count, sizeof(cmd));
+		return -EINVAL;
+	}
+
+	is_userspace = 1;
+	if (segment_eq(get_fs(), KERNEL_DS))
+		is_userspace = 0;
+
+	if (copy_from_user(&cmd, buf, sizeof(cmd))) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_cmd_write: copy_from_user failed\n");
+		return -EINVAL;
+	}
+
+	if (cmd.abi_version != HFIDD_USER_ABI_VERSION) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_cmd_write: wrong abi_version %d, "
+			"should be %d for cmd 0x%x\n",
+			cmd.abi_version, HFIDD_USER_ABI_VERSION, cmd.req);
+		return -EINVAL;
+	}
+
+	switch (cmd.req) {
+	case HFIDD_REQ_QUERY_DD_INFO:
+		if (cmd.req_len != sizeof(struct hfi_query_dd_info)) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_cmd_write: hdr.reqlen 0x%x expected "
+				"0x%x for cmd req 0x%x\n",
+				cmd.req_len, (unsigned int)
+				sizeof(struct hfi_query_dd_info), cmd.req);
+			return -EINVAL;
+		}
+		rc = hfidd_query_dd_info(p_acs, (struct hfi_query_dd_info *)
+			cmd.result.use.kptr);
+		break;
+
+	default:
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_cmd_write: invalid cmd = 0x%x\n", cmd.req);
+		return -EINVAL;
+	}
+
+	if (rc == 0)
+		cnt = count;
+	else
+		cnt = rc;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_cmd_write: Exit cmd = 0x%x rc = 0x%x\n", cmd.req, rc);
+	return cnt;
 }
 
 static const struct file_operations hfidd_fops = {
diff --git a/include/linux/hfi/Kbuild b/include/linux/hfi/Kbuild
index 3a742ce..6637c65 100644
--- a/include/linux/hfi/Kbuild
+++ b/include/linux/hfi/Kbuild
@@ -1 +1,2 @@
 header-y += hfidd_client.h
+header-y += hfidd_requests.h
diff --git a/include/linux/hfi/hfidd_client.h b/include/linux/hfi/hfidd_client.h
index 2714a27..b2ebd01 100644
--- a/include/linux/hfi/hfidd_client.h
+++ b/include/linux/hfi/hfidd_client.h
@@ -33,11 +33,51 @@
 #ifndef _HFIDD_CLIENT_H_
 #define _HFIDD_CLIENT_H_
 
+
+#define HFIDD_USER_ABI_VERSION  1
+
+
+/*
+ * New ioctls are not allowed.  We will use write() calls to pass
+ * in an ioctl-looking request, with struct hfi_req_hdr giving the
+ * information we used to get from the ioctl() parameter list.  The
+ * write() call will copy out the request structure to the buffer pointed
+ * to by result, which is probably the original request.
+ */
+
+struct hfi_64b {
+	union {
+		unsigned long long	allu;	/* APPLICATION Long long
+						   Unsigned 64 bit address
+						   container */
+		void			*kptr;	/* KERNEL Pointer 64 bit
+						   container */
+	} use;
+};
+
+/* Request header: first structure in each of the HFI DD requests */
+struct hfi_req_hdr {
+	unsigned int	req;			/* HFIDD_REQ_* */
+	unsigned int	req_len;		/* length of req, in bytes */
+	unsigned int	abi_version;		/* ABI version */
+	struct hfi_64b	result;			/* user eaddr for output */
+};
+#define HFIDD_REQ_HDR_SIZE			sizeof(struct hfi_req_hdr)
+
 #define MAX_TORRENTS            1
 #define MAX_HFI_PER_TORRENT     2
 #define MAX_HFIS                (MAX_TORRENTS * MAX_HFI_PER_TORRENT)
 #define MAX_WIN_PER_HFI		256
 
+/*
+ * HFIDD_REQ_QUERY_DD_INFO
+ */
+struct hfi_query_dd_info {
+	struct hfi_req_hdr	hdr;
+	unsigned long long	fw_ec_level;	/* Hardware Version */
+	unsigned int	abi_version;	/* ABI Version */
+};
+
 #define HFI_DYN_WINS_DEFAULT	32
 
 #define PAGE_SIZE_4K		0x1000
diff --git a/include/linux/hfi/hfidd_internal.h b/include/linux/hfi/hfidd_internal.h
index 8fe313d..311f906 100644
--- a/include/linux/hfi/hfidd_internal.h
+++ b/include/linux/hfi/hfidd_internal.h
@@ -36,12 +36,29 @@
 #include <linux/fs.h>
 #include <linux/kobject.h>
 #include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/poll.h>
 #include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/device.h>
-
+#include <linux/vermagic.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <asm/cputable.h>
+#include <linux/io.h>
+#include <asm/machdep.h>
+#include <linux/mmu_context.h>
+#include <asm/pgalloc.h>
+#include <asm/ibmebus.h>
+#include <linux/kthread.h>
 #include <linux/hfi/hfidd_client.h>
 #include <linux/hfi/hfidd_adpt.h>
 #include <linux/hfi/hfidd_hcalls.h>
diff --git a/include/linux/hfi/hfidd_requests.h b/include/linux/hfi/hfidd_requests.h
new file mode 100644
index 0000000..b6e255f
--- /dev/null
+++ b/include/linux/hfi/hfidd_requests.h
@@ -0,0 +1,38 @@
+/*
+ * hfidd_requests.h
+ *
+ * HFI device driver for IBM System p
+ *
+ *  Authors:
+ *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *      William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *      Jian Xiao <jian@linux.vnet.ibm.com>
+ *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _HFIDD_REQUESTS_H_
+#define _HFIDD_REQUESTS_H_
+
+#define HFIDD_REQ_QUERY_DD_INFO			0x00001004
+
+#endif /* _HFIDD_REQUESTS_H_ */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 22/27] HFI: Add event notifications
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Interrupts and some error notifications get passed to window users through
events.  User-space applications can register for a signal to be delivered
or can spawn a thread to call into the HFI DD and wait.
Kernel windows can register callbacks.

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/Makefile       |    1 +
 drivers/net/hfi/core/hfidd_adpt.c   |    5 +
 drivers/net/hfi/core/hfidd_events.c | 1106 +++++++++++++++++++++++++++++++++++
 drivers/net/hfi/core/hfidd_init.c   |   35 ++
 drivers/net/hfi/core/hfidd_intr.c   |   40 ++-
 drivers/net/hfi/core/hfidd_proto.h  |   16 +-
 drivers/net/hfi/core/hfidd_window.c |   24 +
 include/linux/hfi/hfidd_client.h    |   64 ++
 include/linux/hfi/hfidd_internal.h  |   69 +++
 include/linux/hfi/hfidd_requests.h  |    3 +
 10 files changed, 1361 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/hfi/core/hfidd_events.c

diff --git a/drivers/net/hfi/core/Makefile b/drivers/net/hfi/core/Makefile
index d2ed86f..da71824 100644
--- a/drivers/net/hfi/core/Makefile
+++ b/drivers/net/hfi/core/Makefile
@@ -3,6 +3,7 @@
 #
 hfi_core-objs:=	hfidd_adpt.o \
 		hfidd_window.o \
+		hfidd_events.o \
 		hfidd_init.o \
 		hfidd_xlat.o \
 		hfidd_map.o \
diff --git a/drivers/net/hfi/core/hfidd_adpt.c b/drivers/net/hfi/core/hfidd_adpt.c
index 372eeec..9ef99c9 100644
--- a/drivers/net/hfi/core/hfidd_adpt.c
+++ b/drivers/net/hfi/core/hfidd_adpt.c
@@ -118,6 +118,7 @@ int hfidd_alloc_windows(struct hfidd_acs *p_acs)
 
 		/* Initialize window fields */
 		spin_lock_init(&(p_acs->win[i]->win_lock));
+		spin_lock_init(&(p_acs->win[i]->event_lock));
 
 		p_acs->win[i]->ai = p_acs->index;
 		p_acs->win[i]->index = p_acs->dds.window_start + i;
@@ -138,6 +139,8 @@ void hfidd_free_windows(struct hfidd_acs *p_acs)
 	int		i;
 
 	for (i = 0; i < p_acs->dds.window_num; i++) {
+		if (p_acs->win[i])
+			hfidd_events_clean(p_acs, p_acs->win[i]);
 		kfree(p_acs->win[i]);
 		p_acs->win[i] = NULL;
 	}
@@ -211,6 +214,8 @@ int hfidd_query_interface(struct hfidd_acs *p_acs, unsigned int subtype,
 			if (p_acs->state != HFI_AVAIL) {
 				p_acs->isr = query_p->local_node_id;
 				p_acs->state = HFI_AVAIL;
+				/* Notify user that adapter is ready */
+				hfidd_notify_hfi_ready(p_acs);
 			}
 		} else {
 			p_acs->state = HFI_UNAVAIL;
diff --git a/drivers/net/hfi/core/hfidd_events.c b/drivers/net/hfi/core/hfidd_events.c
new file mode 100644
index 0000000..fed94f6
--- /dev/null
+++ b/drivers/net/hfi/core/hfidd_events.c
@@ -0,0 +1,1106 @@
+/* hfidd_events.c
+ *
+ * HFI device driver for IBM System p
+ *
+ *  Authors:
+ *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *      William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *      Jian Xiao <jian@linux.vnet.ibm.com>
+ *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/hfi/hfidd_internal.h>
+#include "hfidd_proto.h"
+
+static void rem_events(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		struct hfidd_tid_info *tid_info, unsigned int events,
+		struct hfidd_win_event **event_list);
+static int hfidd_hfi_ready_registration(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *reg);
+static void hfidd_hfi_ready_unregistration(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *reg);
+
+static unsigned int event_mask[HFIDD_NUM_EVENT_TYPES] = {
+	HFIDD_SEND_EVENT,
+	HFIDD_RECV_EVENT,
+	HFIDD_WIN_ERROR_EVENT,
+	HFIDD_HFI_ERROR_EVENT,
+	HFIDD_TERMINATE_EVENT,
+	HFIDD_RELEASE_WINDOW_EVENT,
+	HFIDD_CAU_ERROR_EVENT,
+	HFIDD_ICS_ERROR_EVENT,
+	HFIDD_HFI_READY_REG_EVENT,
+	HFIDD_ROUTE_CHANGE_EVENT,
+	HFIDD_IP_TRC_LVL_EVENT,
+	HFIDD_POOL_SIZE_EVENT};
+
+
+static void hfidd_tid_info_init(struct hfidd_tid_info *tid_info)
+{
+	memset(tid_info, 0, sizeof(*tid_info));
+	sema_init(&(tid_info->tid_sem), 1);
+	INIT_LIST_HEAD(&(tid_info->event_list));
+	init_waitqueue_head(&(tid_info->event_wait));
+	tid_info->th = current;
+	return;
+}
+
+static void hfidd_tid_info_end(struct hfidd_tid_info *tid_info,
+		struct hfidd_q_event **q_event_list)
+{
+	struct list_head	*pos;
+	struct list_head	*q;
+	struct hfidd_q_event	*ev;
+
+	/* Clean up any remaining events. */
+	list_for_each_safe(pos, q, &(tid_info->event_list)) {
+		ev = list_entry(pos, struct hfidd_q_event, list);
+		list_del(pos);
+		ev->next = *q_event_list;
+		*q_event_list = ev;
+	}
+	return;
+}
+
+static inline void hfidd_update_eb(struct hfidd_tid_info *tid_info,
+		struct hfi_reg_events *reg_events)
+{
+	tid_info->eb_xd = current->group_leader;
+	tid_info->eb = (struct hfi_event_buffer *)reg_events->info.eb.use.allu;
+}
+
+/* Post an event.  The win->event_lock must be held before calling. */
+static int hfidd_post_event(struct hfidd_acs *p_acs,
+		struct hfidd_tid_info *tid_info, enum hfi_event_type type,
+		unsigned int event, struct hfidd_q_event **q_event_list)
+{
+	int			rc = 0;
+	struct hfidd_q_event	*ev;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_post_event: event=0x%x tid=0x%llx\n",
+		event, tid_info->tid);
+
+	if (tid_info->type == WAIT_FOR_EVENTS) {
+		/* Allocate and fill in the structure for the event. */
+		if (*q_event_list == NULL) {
+			rc = -EFAULT;
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_post_event: internal error - "
+				"%d\n", rc);
+		} else {
+			ev = *q_event_list;
+			*q_event_list = (*q_event_list)->next;
+			ev->event = event;
+
+			/*
+			 * Add the event to the event list and wake up any
+			 * waiting thread.
+			 */
+			list_add(&(ev->list), &(tid_info->event_list));
+			wake_up_interruptible(&(tid_info->event_wait));
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * Wakeup waiting task if necessary.  The win->event_lock must be held before
+ * calling.
+ */
+static int hfidd_events_wakeup(struct hfidd_acs *p_acs,
+		struct hfidd_tid_info *tid_info,
+		struct hfidd_q_event **q_event_list)
+{
+	int			rc = 0;
+	struct list_head	*pos;
+	struct list_head	*q;
+	struct hfidd_q_event	*ev;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_wakeup: tid=0x%llx\n", tid_info->tid);
+	/*
+	 * A well behaved thread will not be waiting for any events when
+	 * wakeup is called.  This code is to handle misbehaving threads.
+	 */
+
+	/*
+	 * Add an event that will cause any misbehaving waiting thread to
+	 * wake up.  Once it wakes up, it will see that we are cleaning up
+	 * (because win->open_close_count has changed) and will end.
+	 */
+	if (*q_event_list == NULL) {
+		rc = -EFAULT;
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_wakeup: internal error - "
+			"%d\n", rc);
+		goto hfidd_events_wakeup_error1;
+	}
+
+	ev = *q_event_list;
+	*q_event_list = (*q_event_list)->next;
+	ev->event = HFIDD_TERMINATE;
+	list_add(&(ev->list), &(tid_info->event_list));
+	wake_up_interruptible(&(tid_info->event_wait));
+
+	/* By getting this lock, we make sure that we don't delete tid_info */
+	/* until the thread is done using it. */
+	down(&(tid_info->tid_sem));
+
+	list_for_each_safe(pos, q, &(tid_info->event_list)) {
+		ev = list_entry(pos, struct hfidd_q_event, list);
+		list_del(pos);
+		ev->next = *q_event_list;
+		*q_event_list = ev;
+	}
+
+	up(&(tid_info->tid_sem));
+
+
+hfidd_events_wakeup_error1:
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_wakeup: rc=%d\n", rc);
+	return rc;
+}
+
+/*
+ * Preallocate a group of q events.  We must preallocate because we are
+ * not allowed to use kzalloc once we have the event_lock.
+ */
+static struct hfidd_q_event *hfidd_prealloc_q_events(struct hfidd_acs *p_acs,
+		struct hfidd_window *win, int num_events)
+{
+	int i;
+	struct hfidd_q_event *q_event_list = NULL;
+	struct hfidd_q_event *q_event;
+
+	for (i = 0; i < num_events; i++) {
+		q_event = kzalloc(sizeof(*q_event), GFP_KERNEL);
+		if (q_event == NULL) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_prealloc_q_events: kzalloc failed, "
+				"num_events = %d\n", num_events);
+
+			while (q_event_list != NULL) {
+				q_event = q_event_list->next;
+				kfree(q_event_list);
+				q_event_list = q_event;
+			}
+			return NULL;
+		}
+		q_event->next = q_event_list;
+		q_event_list = q_event;
+	}
+
+	return q_event_list;
+}
+
+/* Return any queue events that haven't been used. */
+static void hfidd_return_q_events(struct hfidd_acs *p_acs,
+		struct hfidd_q_event **q_event_list)
+{
+	struct hfidd_q_event *q_event;
+
+	while (*q_event_list != NULL) {
+		q_event = (*q_event_list)->next;
+		kfree(*q_event_list);
+		*q_event_list = q_event;
+	}
+	return;
+}
+/*
+ * Preallocate a tid_info structure.  We must preallocate because we are
+ * not allowed to use kzalloc once we have the event_lock.
+ */
+static struct hfidd_tid_info *prealloc_tid_list(struct hfidd_acs *p_acs)
+{
+	struct hfidd_tid_info *tid_list;
+
+	tid_list = kzalloc(sizeof(*tid_list), GFP_KERNEL);
+	if (tid_list == NULL) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"prealloc_tid_list: kzalloc tid info failed\n");
+	} else {
+		hfidd_tid_info_init(tid_list);
+		tid_list->next = NULL;
+	}
+	return tid_list;
+}
+
+/* Return a list of hfidd_tid_info structures. */
+static void return_tid_list(struct hfidd_acs *p_acs,
+		struct hfidd_tid_info **tid_list,
+		struct hfidd_q_event **q_event_list)
+{
+	struct hfidd_tid_info *tid_info;
+
+	while (*tid_list != NULL) {
+		tid_info = *tid_list;
+		*tid_list = tid_info->next;
+		hfidd_tid_info_end(tid_info, q_event_list);
+		kfree(tid_info);
+	}
+}
+
+/*
+ * Preallocate a list of hfidd_win_event structures.  We must preallocate
+ * because we are not allowed to use kzalloc once we have the event_lock.
+ */
+static struct hfidd_win_event *prealloc_event_list(struct hfidd_acs *p_acs,
+		unsigned int events)
+{
+	int i;
+	unsigned int		test_bit = HFIDD_LOWEST_EVENT;
+	struct hfidd_win_event	*win_event;
+	struct hfidd_win_event	*event_list = NULL;
+
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		if (events & test_bit) {
+			win_event = kzalloc(sizeof(*win_event), GFP_KERNEL);
+			if (win_event == NULL) {
+				dev_printk(KERN_ERR, p_acs->hfidd_dev,
+					"prealloc_event_list: kzalloc failed\n");
+
+				while (event_list != NULL) {
+					win_event = event_list;
+					event_list = event_list->next;
+					kfree(win_event);
+				}
+				return NULL;
+			}
+			win_event->next = event_list;
+			event_list = win_event;
+		}
+		test_bit <<= 1;
+	}
+	return event_list;
+}
+
+/* Return a list of hfidd_win_event structures. */
+static void return_event_list(struct hfidd_acs *p_acs,
+		struct hfidd_win_event **event_list)
+{
+	struct hfidd_win_event *win_event;
+
+	while (*event_list != NULL) {
+		win_event = *event_list;
+		*event_list = (*event_list)->next;
+		kfree(win_event);
+	}
+}
+
+/*
+ * Add a group of events to the event handling structures.  The caller must
+ * hold win->event_lock.
+ */
+static int add_events(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		struct hfidd_tid_info *tid_info, unsigned int events,
+		struct hfidd_win_event **event_list)
+{
+	int			rc = 0;
+	int			i;
+	unsigned int		test_bit = HFIDD_LOWEST_EVENT;
+	struct hfidd_win_event	*win_event;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"add_events: enter win=0x%x events=0x%x\n",
+		win->index, events);
+
+	/* Add individual pointers from the window to the tid_info. */
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		if (events & test_bit) {
+			/* Add a pointer from the window to the events. */
+			if (*event_list == NULL) {
+				rc = -EFAULT;
+				dev_printk(KERN_ERR, p_acs->hfidd_dev,
+					"add_events: internal error - "
+					"%d\n", rc);
+
+				goto add_events_err1;
+			}
+			win_event = *event_list;
+			*event_list = (*event_list)->next;
+			win_event->tid_info = tid_info;
+			win_event->next = win->events[i];
+			win->events[i] = win_event;
+			atomic_inc(&(win->num_events[i]));
+		}
+		test_bit <<= 1;
+	}
+	return rc;
+
+add_events_err1:
+	rem_events(p_acs, win, tid_info, events, event_list);
+	return rc;
+}
+
+/*
+ * Remove a group of events from the event handling structures.	 The caller
+ * must hold win->event_lock.
+ */
+static void rem_events(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		struct hfidd_tid_info *tid_info, unsigned int events,
+		struct hfidd_win_event **event_list)
+{
+	int			i;
+	unsigned int		test_bit = HFIDD_LOWEST_EVENT;
+	struct hfidd_win_event	*prev_win_event;
+	struct hfidd_win_event	*win_event;
+	unsigned int		temp_events = events;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"rem_events: enter win=0x%x events=0x%x\n",
+		win->index, events);
+
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		if (temp_events & test_bit) {
+			/* Remove pointer from the window or p_acs to events. */
+			prev_win_event = NULL;
+			for (win_event = win->events[i]; win_event != NULL;
+					win_event = win_event->next) {
+				/* Search for this tid in list. */
+				if (win_event->tid_info == tid_info)
+					break;
+				else
+					prev_win_event = win_event;
+			}
+			if (win_event != NULL) {
+				/* Found tid.  Remove it. */
+				if (prev_win_event == NULL)
+					win->events[i] = win_event->next;
+				else
+					prev_win_event->next = win_event->next;
+				win_event->next = *event_list;
+				*event_list = win_event;
+				atomic_dec(&(win->num_events[i]));
+			}
+		}
+		test_bit <<= 1;
+	}
+}
+
+/*
+ * Find a tid_info structure for a given tid and window.  The caller must
+ * hold win->event_lock.
+ */
+static struct hfidd_tid_info *get_tid_info(struct hfidd_acs *p_acs,
+		struct hfidd_window *win, unsigned long long tid,
+		enum hfi_event_hndlr_type type,
+		struct hfidd_tid_info **prev_tid_info,
+		struct hfidd_tid_info **tid_list)
+{
+	struct hfidd_tid_info	*tid_info;
+
+	*prev_tid_info = NULL;
+
+	/* See if it exists already. */
+	for (tid_info = win->tid_list; tid_info != NULL;
+			tid_info = tid_info->next) {
+		if (tid_info->tid == tid)
+			break;
+		*prev_tid_info = tid_info;
+	}
+
+	/* Allocate new structure if necessary. */
+	if (tid_info == NULL) {
+		*prev_tid_info = NULL;
+		if (*tid_list == NULL) {
+			tid_info = NULL;
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"get_tid_info: internal error\n");
+		} else {
+			tid_info = *tid_list; /* Set to NULL so caller knows the
+						 preallocated tid_info
+						 structure was used. */
+			*tid_list = (*tid_list)->next;
+			tid_info->tid = tid;
+			tid_info->type = type;
+			atomic_inc(&win->num_tids);
+			tid_info->next = win->tid_list;
+			win->tid_list = tid_info;
+		}
+	}
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"get_tid_info: exit reg_events=0x%x tid_info=%p\n",
+		tid_info->registered_events,
+		(void *) tid_info);
+
+	return tid_info;
+}
+
+/*
+ * Remove a tid_info structure for a given tid and window.  The caller must
+ * hold win->event_lock.
+ */
+static void rem_tid_info(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		struct hfidd_tid_info *prev_tid_info,
+		struct hfidd_tid_info *tid_info,
+		struct hfidd_tid_info **tid_list)
+{
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"rem_tid_info: remove tid_info for tid 0x%llx\n",
+		tid_info->tid);
+
+	if (prev_tid_info == NULL)
+		win->tid_list = tid_info->next;
+	else
+		prev_tid_info->next = tid_info->next;
+	if (atomic_read(&win->num_tids) > 0)
+		atomic_dec(&win->num_tids);
+	tid_info->next = *tid_list;
+	*tid_list = tid_info; /* Set up to free after releasing lock */
+}
+
+
+/* Register events. */
+int hfidd_events_register(struct hfidd_acs *p_acs, struct hfi_reg_events *arg)
+{
+	int			rc = 0;
+	int			got_lock = 0;
+	struct hfi_reg_events	reg_events;
+	unsigned long long	tid;
+	struct hfidd_tid_info	*prev_tid_info;
+	struct hfidd_tid_info	*tid_info = NULL;
+	struct hfidd_tid_info	*tid_list = NULL;
+	unsigned int		new_events;
+	struct hfidd_window	*win = NULL;
+	struct hfidd_win_event	*event_list = NULL;
+	unsigned long		flags = 0;
+	struct hfidd_q_event	*q_event_list = NULL;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_register: enter p_acs=0x%p\n", (void *)p_acs);
+
+	/* Copy in client info from user */
+	rc = copy_from_user(&reg_events, arg, sizeof(reg_events));
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: copy_from_user failed, "
+			"rc=0x%x\n", rc);
+		return rc;
+	}
+
+	/* Verify inputs */
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_register: enter type=0x%x win=0x%x "
+		"events=0x%x\n", reg_events.type, reg_events.window,
+		reg_events.info.events);
+
+	if ((reg_events.type != WAIT_FOR_EVENTS) &&
+	    (reg_events.type != SIGNAL_EVENTS)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: invalid type - "
+			"0x%x\n", reg_events.type);
+		return -EINVAL;
+	}
+	if ((reg_events.window <  min_hfi_windows(p_acs)) ||
+	    (reg_events.window >= max_hfi_windows(p_acs))) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: invalid win - "
+			"0x%x\n", reg_events.window);
+		return -EINVAL;
+	}
+	if ((reg_events.type == WAIT_FOR_EVENTS) &&
+	    (reg_events.info.events & ~HFIDD_ALL_EVENTS)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: invalid events - "
+			"0x%x\n", reg_events.info.events & ~HFIDD_ALL_EVENTS);
+		return -EINVAL;
+	}
+	if ((reg_events.type == SIGNAL_EVENTS) &&
+	    (reg_events.info.eb.use.kptr == NULL)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: invalid signal buffer\n");
+		return -EINVAL;
+	}
+
+	win = hfi_window(p_acs, reg_events.window);
+	/*
+	 * Preallocate data structures.	 We must do this before the
+	 * lock or it will cause errors.
+	 */
+	tid_list = prealloc_tid_list(p_acs);
+	if (tid_list == NULL)
+		return -ENOMEM;
+	if (reg_events.type == WAIT_FOR_EVENTS)
+		event_list = prealloc_event_list(p_acs, reg_events.info.events);
+	else
+		event_list = prealloc_event_list(p_acs, HFIDD_ALL_EVENTS);
+	if (event_list == NULL) {
+		rc = -ENOMEM;
+		goto events_reg_err1;
+	}
+
+	spin_lock_irqsave(&(win->event_lock), flags);
+	got_lock = 1;
+	if (win->state == WIN_AVAILABLE) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: inv state wi=0x%x st=0x%x\n",
+			win->index, win->state);
+		rc = -EINVAL;
+		goto events_reg_err1;
+	}
+
+	/* Get the tid_info structure for this tid. */
+	tid = (current->pid);
+
+	tid_info = get_tid_info(p_acs, win, tid, reg_events.type,
+			&prev_tid_info, &tid_list);
+	if (tid_info == NULL)
+		goto events_reg_err1;
+	if (tid_info->type != reg_events.type) {
+		/* The user can't change types after first registration */
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: inv typ chg old=0x%x new=0x%x\n",
+			tid_info->type, reg_events.type);
+		rc = -EINVAL;
+		goto events_reg_err2;
+	}
+
+	/* Add new event entries. */
+	if (reg_events.type == WAIT_FOR_EVENTS) {
+		new_events = ~tid_info->registered_events &
+			reg_events.info.events;
+	} else {
+		/*
+		 * If signal version is registered more than once, this will
+		 * end up with no events.  Otherwise, all events
+		 */
+		new_events = (~tid_info->registered_events) &
+			HFIDD_ALL_EVENTS;
+		hfidd_update_eb(tid_info, &reg_events);
+	}
+	rc = add_events(p_acs, win, tid_info, new_events, &event_list);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_register: failed to add events, "
+			"rc=%d\n", rc);
+		goto events_reg_err2;
+	}
+	tid_info->registered_events |= new_events;
+
+events_reg_err2:
+	/* Remove tid info if necessary */
+	if (!(tid_info->registered_events))
+		rem_tid_info(p_acs, win, prev_tid_info, tid_info, &tid_list);
+
+events_reg_err1:
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_register: rc=%d events=0x%x\n",
+		rc, (tid_info == NULL) ? 0 : tid_info->registered_events);
+
+	if (got_lock)
+		spin_unlock_irqrestore(&(win->event_lock), flags);
+	return_tid_list(p_acs, &tid_list, &q_event_list);
+	hfidd_return_q_events(p_acs, &q_event_list);
+	return_event_list(p_acs, &event_list);
+	return rc;
+}
+
+int hfidd_events_unregister(struct hfidd_acs *p_acs, struct hfi_reg_events *arg)
+{
+	int			rc = 0;
+	int			got_lock = 0;
+	struct hfi_reg_events	unreg_events;
+	unsigned long long	tid;
+	struct hfidd_tid_info	*prev_tid_info;
+	struct hfidd_tid_info	*tid_info = NULL;
+	struct hfidd_tid_info	*tid_list = NULL;
+	struct hfidd_window	*win = NULL;
+	struct hfidd_win_event	*event_list = NULL;
+	unsigned long		flags = 0;
+	struct hfidd_q_event	*q_event_list = NULL;
+	unsigned int		events_to_rem;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_unregister: enter p_acs=0x%p\n", (void *)p_acs);
+
+	/* Copy in client info from user */
+	rc = copy_from_user(&unreg_events, arg, sizeof(unreg_events));
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_unregister: COPYIN err rc=0x%x\n", rc);
+		return rc;
+	}
+
+	/* Validate input */
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_unregister: enter win=0x%x events=0x%x\n",
+		unreg_events.window, unreg_events.info.events);
+
+	if ((unreg_events.type != WAIT_FOR_EVENTS) &&
+	    (unreg_events.type != SIGNAL_EVENTS)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_unregister: invalid type - "
+			"0x%x\n", unreg_events.type);
+		return -EINVAL;
+	}
+
+	if ((unreg_events.window < min_hfi_windows(p_acs)) ||
+	    (unreg_events.window >= max_hfi_windows(p_acs))) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_unregister: invalid win - "
+			"0x%x\n", unreg_events.window);
+		return -EINVAL;
+	}
+
+	if ((unreg_events.type == WAIT_FOR_EVENTS) &&
+	    (unreg_events.info.events & ~HFIDD_ALL_EVENTS)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_unregister: invalid events - "
+			"0x%x\n", unreg_events.info.events & ~HFIDD_ALL_EVENTS);
+		return -EINVAL;
+	}
+	win = hfi_window(p_acs, unreg_events.window);
+
+	/*
+	 * Preallocate data structures.	 We must do this before the
+	 * lock or it will cause errors.
+	 */
+	tid_list = prealloc_tid_list(p_acs);
+	if (tid_list == NULL)
+		return -ENOMEM;
+	spin_lock_irqsave(&(win->event_lock), flags);
+	got_lock = 1;
+
+	/* Get the tid_info structure for this tid. */
+	tid = (current->pid);
+
+	tid_info = get_tid_info(p_acs, win, tid, unreg_events.type,
+			&prev_tid_info, &tid_list);
+	if (tid_info == NULL)
+		goto events_unreg_err1;
+	if (tid_info->type != unreg_events.type) {
+		/* The user can't change types after first registration */
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_events_unregister: inv typ chg old=0x%x new=0x%x\n",
+			tid_info->type, unreg_events.type);
+		rc = -EINVAL;
+		goto events_unreg_err2;
+	}
+
+	/* Remove entries. */
+	if (unreg_events.type == WAIT_FOR_EVENTS)
+		events_to_rem = unreg_events.info.events;
+	else
+		events_to_rem = HFIDD_ALL_EVENTS;
+	rem_events(p_acs, win, tid_info, events_to_rem, &event_list);
+	tid_info->registered_events &= ~events_to_rem;
+
+events_unreg_err2:
+	/* Remove tid_info if necessary. */
+	if (!(tid_info->registered_events))
+		rem_tid_info(p_acs, win, prev_tid_info, tid_info, &tid_list);
+
+events_unreg_err1:
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_unregister: rc=%d events=0x%x\n",
+		rc, (tid_info == NULL) ? 0 : tid_info->registered_events);
+
+	if (got_lock)
+		spin_unlock_irqrestore(&(win->event_lock), flags);
+	return_tid_list(p_acs, &tid_list, &q_event_list);
+	hfidd_return_q_events(p_acs, &q_event_list);
+	return_event_list(p_acs, &event_list);
+	return rc;
+}
+
+/* Report that an event has occurred. */
+int hfidd_report_event(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		enum hfi_event_type event)
+{
+	int			rc = 0;
+	struct hfidd_win_event	*win_event_p;
+	struct hfidd_q_event	*q_event_list = NULL;
+	int			num_events;
+	unsigned long		flags;
+	int			allocated = 0;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_report_event: enter event=0x%x p_acs=0x%p\n",
+		event, (void *) p_acs);
+
+	/*
+	 * Prealloc queue event entries.  We must do this because we use a
+	 * lock that keeps us from allocating storage.
+	 */
+	while (!allocated) {
+		num_events = atomic_read(&(win->num_events[event]));
+		q_event_list = hfidd_prealloc_q_events(p_acs, win,
+				num_events);
+		if ((q_event_list == NULL) && num_events) {
+			rc = -ENOMEM;
+			return rc;
+		}
+		spin_lock_irqsave(&(win->event_lock), flags);
+		if (num_events == atomic_read(&(win->num_events[event]))) {
+			allocated = 1;
+		} else {
+			/*
+			 * The number of events we allocated for does not
+			 * match the current number of events.	It must
+			 * have changed between the allocation and the lock.
+			 * We must keep trying until we get a match.
+			 */
+			spin_unlock_irqrestore(&(win->event_lock), flags);
+			hfidd_return_q_events(p_acs, &q_event_list);
+		}
+	}
+
+	/* Mark that the event has occurred and awaken each tid. */
+	for (win_event_p = win->events[event]; win_event_p != NULL;
+			win_event_p = win_event_p->next) {
+		hfidd_post_event(p_acs, win_event_p->tid_info,
+				event, event_mask[event], &q_event_list);
+	}
+	spin_unlock_irqrestore(&(win->event_lock), flags);
+	hfidd_return_q_events(p_acs, &q_event_list);
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_report_event: type=0x%x rc=%d\n", event, rc);
+	return rc;
+}
+
+/* Clean up event handling when a window closes. */
+int hfidd_events_clean(struct hfidd_acs *p_acs, struct hfidd_window *win)
+{
+	int			rc = 0;
+	int			i;
+	struct hfidd_win_event	*win_event_p;
+	struct hfidd_win_event	*win_event_list = NULL;
+	struct hfidd_win_event	*next_win_event_p;
+	struct hfidd_tid_info	*tid_info;
+	struct hfidd_tid_info	*next_tid_info;
+	struct hfidd_tid_info	*tid_list = NULL;
+	int			num_events;
+	unsigned long		flags;
+	int			allocated = 0;
+	struct hfidd_q_event	*q_event_list = NULL;
+	int			loop_count = 0;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_clean: enter p_acs=0x%p\n", (void *)p_acs);
+
+	/*
+	 * Prealloc queue event entries.  We must do this because we use a
+	 * ock that keeps us from allocating storage.
+	 */
+	while (!allocated) {
+		num_events = atomic_read(&win->num_tids);
+		q_event_list = hfidd_prealloc_q_events(p_acs, win,
+				num_events);
+		if ((q_event_list == NULL) && num_events) {
+			rc = -ENOMEM;
+			return rc;
+		}
+		spin_lock_irqsave(&(win->event_lock), flags);
+		if (num_events == atomic_read(&win->num_tids)) {
+			allocated = 1;
+		} else {
+			/*
+			 * The number of events we allocated for does not
+			 * match the current number of tids.  It must
+			 * have changed between the allocation and the lock.
+			 * We must keep trying until we get a match.
+			 */
+			spin_unlock_irqrestore(&(win->event_lock), flags);
+			hfidd_return_q_events(p_acs, &q_event_list);
+		}
+	}
+
+	/* Return all of the win_info structures. */
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		for (win_event_p = win->events[i]; win_event_p != NULL;
+				win_event_p = next_win_event_p) {
+			next_win_event_p = win_event_p->next;
+			win_event_p->next = win_event_list;
+			win_event_list = win_event_p;
+		}
+		win->events[i] = NULL;
+		atomic_set(&win->num_events[i], 0);
+	}
+
+	/* Return tid_info structures. */
+	for (tid_info = win->tid_list; tid_info != NULL;
+			tid_info = next_tid_info) {
+		/* Wake up the waiting task if necessary. */
+		hfidd_events_wakeup(p_acs, tid_info, &q_event_list);
+		next_tid_info = tid_info->next;
+		if (atomic_read(&win->num_tids) > 0)
+			atomic_dec(&win->num_tids);
+		tid_info->next = tid_list;
+		tid_list = tid_info;
+	}
+	win->tid_list = NULL;
+	atomic_set(&win->num_tids, 0);
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		win->funcs[i].function_p.use.kptr = NULL;
+		win->funcs[i].parameter.use.kptr = NULL;
+	}
+
+	/* Wait for all threads to finish. */
+	spin_unlock_irqrestore(&(win->event_lock), flags); /* Must disable or
+							will hang */
+	while ((atomic_read(&win->event_wait_count) > 0) &&
+			(loop_count < HFIDD_EVENT_CLEANUP_LOOP_COUNT)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HFIDD_EVENT_CLEANUP_DELAY);
+		loop_count++;
+	}
+	atomic_set(&win->event_wait_count, 0);
+
+	return_event_list(p_acs, &win_event_list);
+	return_tid_list(p_acs, &tid_list, &q_event_list);
+	hfidd_return_q_events(p_acs, &q_event_list);
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_events_clean: rc=%d\n", rc);
+	return rc;
+}
+
+int hfidd_callback_register(struct hfidd_acs *p_acs, struct hfi_reg_events *arg)
+{
+	struct hfi_reg_events	reg_events;
+	struct hfidd_window	*win;
+	int			rc;
+
+	/* Copy in client info from user */
+	memcpy(&reg_events, arg, sizeof(reg_events));
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_callback_register: enter type=0x%x win=0x%x "
+		"events=0x%x\n", reg_events.type, reg_events.window,
+		reg_events.info.func.index);
+
+	/* Verify inputs */
+	if (reg_events.type != FUNCTIONS_FOR_EVENTS) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_register: invalid type = "
+			"0x%x\n", reg_events.type);
+		return -EINVAL;
+	}
+	if (reg_events.info.func.index == HFIDD_HFI_READY_REG) {
+		rc = hfidd_hfi_ready_registration(p_acs, &reg_events);
+		return rc;
+	}
+
+	if ((reg_events.window <  min_hfi_windows(p_acs)) ||
+	    (reg_events.window >= max_hfi_windows(p_acs))) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_register: invalid win = "
+			"0x%x\n", reg_events.window);
+		return -EINVAL;
+	}
+
+	if (reg_events.info.func.index >= HFIDD_NUM_EVENT_TYPES) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_register: invalid events = "
+			"0x%x\n", reg_events.info.func.index);
+		return -EINVAL;
+	}
+
+	win = hfi_window(p_acs, reg_events.window);
+	spin_lock(&(win->win_lock));
+	if (win->state == WIN_AVAILABLE) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_register: inv state "
+			"wi=0x%x st=0x%x\n",
+			win->index, win->state);
+		spin_unlock(&(win->win_lock));
+		return -EINVAL;
+	}
+	spin_unlock(&(win->win_lock));
+
+	/* fill in function pointer and parameter */
+	win->funcs[reg_events.info.func.index].function_p.use.kptr =
+			reg_events.info.func.function_p.use.kptr;
+	win->funcs[reg_events.info.func.index].parameter.use.kptr  =
+			reg_events.info.func.parameter.use.kptr;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hfidd_callback_register);
+
+int hfidd_callback_unregister(struct hfidd_acs *p_acs,
+				struct hfi_reg_events *arg)
+{
+	struct hfi_reg_events	reg_events;
+	struct hfidd_window	*win;
+
+	/* Copy in client info from user */
+	memcpy(&reg_events, arg, sizeof(reg_events));
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_callback_unregister: enter type=0x%x win=0x%x "
+		"events=0x%x\n", reg_events.type, reg_events.window,
+		reg_events.info.func.index);
+
+	/* Verify inputs */
+	if (reg_events.type != FUNCTIONS_FOR_EVENTS) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_unregister: invalid type = "
+			"0x%x\n", reg_events.type);
+		return -EINVAL;
+	}
+	if (reg_events.info.func.index == HFIDD_HFI_READY_REG) {
+		hfidd_hfi_ready_unregistration(p_acs, &reg_events);
+		return 0;
+	}
+
+	if ((reg_events.window <  min_hfi_windows(p_acs)) ||
+	    (reg_events.window >= max_hfi_windows(p_acs))) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_unregister: invalid win = "
+			"0x%x\n", reg_events.window);
+		return -EINVAL;
+	}
+
+	if (reg_events.info.func.index >= HFIDD_NUM_EVENT_TYPES) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_unregister: invalid events = "
+			"0x%x\n", reg_events.info.func.index);
+		return -EINVAL;
+	}
+
+	win = hfi_window(p_acs, reg_events.window);
+	spin_lock(&(win->win_lock));
+	if ((win->state != WIN_OPENED) &&
+	    (win->state != WIN_ERROR)  &&
+	    (win->state != WIN_HERROR)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_unregister: inv state "
+			"wi=0x%x st=0x%x\n",
+			win->index, win->state);
+		spin_unlock(&(win->win_lock));
+		return -EINVAL;
+	}
+	spin_unlock(&(win->win_lock));
+
+	win->funcs[reg_events.info.func.index].function_p.use.kptr = NULL;
+	win->funcs[reg_events.info.func.index].parameter.use.kptr  = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hfidd_callback_unregister);
+
+int hfidd_callback_event(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		enum hfi_event_type event,
+		unsigned int data1,
+		unsigned int *data2_p)
+{
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"hfidd_callback_event: enter event=0x%x, win_p=0x%llx\n",
+		event, (unsigned long long)win);
+
+	if (win->funcs[event].function_p.use.kptr == NULL) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_callback_event: NULL function ptr, "
+			"event=0x%x, win_p=0x%llx\n",
+			event, (unsigned long long)win);
+		return -EINVAL;
+	}
+
+	/* calling function */
+	((hfi_event_func_ptr)win->funcs[event].function_p.use.kptr)
+		(win->funcs[event].parameter.use.kptr,
+		data1, data2_p);
+	return 0;
+}
+
+static int hfidd_hfi_ready_registration(struct hfidd_acs *p_acs,
+				struct hfi_reg_events *reg)
+{
+	struct hfidd_hfi_ready_req *req;
+
+	if (p_acs->state == HFI_AVAIL) {
+		/* notify kernel user */
+		return ((hfi_event_func_ptr)reg->info.func.function_p.use.kptr)
+			(reg->info.func.parameter.use.kptr, 0, 0);
+	} else {
+		/* Alloc entry */
+		req = kzalloc(sizeof(*req), GFP_KERNEL);
+		if (req == NULL) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_hfi_ready_registration: kzalloc failed\n");
+			return -ENOMEM;
+		}
+
+		/* Fill the entry for the list */
+		req->func.index = reg->info.func.index;
+		req->func.function_p.use.kptr =
+				reg->info.func.function_p.use.kptr;
+		req->func.parameter.use.kptr =
+				reg->info.func.parameter.use.kptr;
+		list_add(&(req->list), &(p_acs->hfi_ready_reg_list));
+	}
+
+	return 0;
+}
+
+static void hfidd_hfi_ready_unregistration(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *reg)
+{
+	struct hfidd_hfi_ready_req *req;
+	struct list_head        *q;
+	struct list_head        *pos;
+
+	list_for_each_safe(pos, q, &p_acs->hfi_ready_reg_list) {
+		req = list_entry(pos,
+			struct hfidd_hfi_ready_req, list);
+		if ((req->func.function_p.use.kptr ==
+		     reg->info.func.function_p.use.kptr) &&
+		    (req->func.parameter.use.kptr ==
+		     reg->info.func.parameter.use.kptr)) {
+				list_del(pos);
+				kfree(req);
+				break;
+		}
+	}
+
+	return;
+}
+
+void hfidd_notify_hfi_ready(struct hfidd_acs *p_acs)
+{
+	struct hfidd_hfi_ready_req *req;
+	struct list_head        *q;
+	struct list_head        *pos;
+
+	list_for_each_safe(pos, q, &p_acs->hfi_ready_reg_list) {
+		req = list_entry(pos,
+			struct hfidd_hfi_ready_req, list);
+
+		dev_printk(KERN_INFO, p_acs->hfidd_dev,
+			"hfidd_notify_hfi_ready: Calling Kernel user\n");
+
+		/* Calling IP function */
+		((hfi_event_func_ptr)req->func.function_p.use.kptr)
+			(req->func.parameter.use.kptr, 0, 0);
+		list_del(pos);
+		kfree(req);
+	}
+}
diff --git a/drivers/net/hfi/core/hfidd_init.c b/drivers/net/hfi/core/hfidd_init.c
index 603feb8..12e3857 100644
--- a/drivers/net/hfi/core/hfidd_init.c
+++ b/drivers/net/hfi/core/hfidd_init.c
@@ -180,6 +180,40 @@ static ssize_t hfidd_cmd_write(struct file *filep, const char __user *buf,
 				(struct hfi_window_info *) buf);
 		break;
 
+	case HFIDD_REQ_EVENT_REGISTER:
+		if (cmd.req_len != sizeof(struct hfi_reg_events)) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_cmd_write: hdr.reqlen 0x%x expected "
+				"0x%lx for cmd req 0x%x\n",
+				cmd.req_len,
+				sizeof(struct hfi_reg_events), cmd.req);
+			return -EINVAL;
+		}
+		if (is_userspace)
+			rc = hfidd_events_register(p_acs,
+					(struct hfi_reg_events *) buf);
+		else
+			rc = hfidd_callback_register(p_acs,
+					(struct hfi_reg_events *) buf);
+		break;
+
+	case HFIDD_REQ_EVENT_UNREGISTER:
+		if (cmd.req_len != sizeof(struct hfi_reg_events)) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_cmd_write: hdr.reqlen 0x%x expected "
+				"0x%lx for cmd req 0x%x\n",
+				cmd.req_len,
+				sizeof(struct hfi_reg_events), cmd.req);
+			return -EINVAL;
+		}
+		if (is_userspace)
+			rc = hfidd_events_unregister(p_acs,
+				(struct hfi_reg_events *) buf);
+		else
+			rc = hfidd_callback_unregister(p_acs,
+				(struct hfi_reg_events *) buf);
+		break;
+
 	case HFIDD_REQ_QUERY_DD_INFO:
 		if (cmd.req_len != sizeof(struct hfi_query_dd_info)) {
 			dev_printk(KERN_ERR, p_acs->hfidd_dev,
@@ -349,6 +383,7 @@ int hfidd_init_adapter(struct hfidd_acs *p_acs, void *uiop)
 {
 	int rc = 0;
 
+	INIT_LIST_HEAD(&(p_acs->hfi_ready_reg_list));
 	rc = hfidd_dds_init(p_acs, &(p_acs->dds));
 	p_acs->dds.num_d_windows = HFI_DYN_WINS_DEFAULT;
 	return rc;
diff --git a/drivers/net/hfi/core/hfidd_intr.c b/drivers/net/hfi/core/hfidd_intr.c
index 253de27..38f35f5 100644
--- a/drivers/net/hfi/core/hfidd_intr.c
+++ b/drivers/net/hfi/core/hfidd_intr.c
@@ -33,15 +33,45 @@
 #include <linux/hfi/hfidd_internal.h>
 #include "hfidd_proto.h"
 
+/* Post window event */
+static int hfidd_post_window_event(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p, enum hfi_event_type event)
+{
+	int rc = 0;
+
+	if (win_p->state == WIN_OPENED) {
+		if (win_p->funcs[event].function_p.use.kptr != NULL) {
+			rc = hfidd_callback_event(p_acs, win_p, event,
+					win_p->index, 0);
+		} else {
+			rc = hfidd_report_event(p_acs, win_p, event);
+		}
+		if (rc) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_post_window_event: failed to "
+				"post event %d win 0x%x rc 0x%x\n",
+				event, win_p->index, rc);
+		}
+	}
+	return rc;
+}
+
 static irqreturn_t send_intr_handler(int irq, void *data)
 {
 	struct hfidd_window *win_p = data;
 	struct hfidd_acs *p_acs;
+	int rc;
 
 	p_acs = hfidd_global.p_acs[win_p->ai];
 	if (p_acs == NULL)
 		return IRQ_HANDLED;
-
+	rc = hfidd_post_window_event(p_acs, win_p, HFIDD_SEND);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"send_intr_handler: failed post send event, "
+			"rc %d for win 0x%llx\n",
+			rc, (unsigned long long) win_p);
+	}
 	return IRQ_HANDLED;
 }
 
@@ -49,11 +79,19 @@ static irqreturn_t recv_intr_handler(int irq, void *data)
 {
 	struct hfidd_window *win_p = data;
 	struct hfidd_acs *p_acs;
+	int		rc;
 
 	p_acs = hfidd_global.p_acs[win_p->ai];
 	if (p_acs == NULL)
 		return IRQ_HANDLED;
 
+	rc = hfidd_post_window_event(p_acs, win_p, HFIDD_RECV);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"recv_intr_handler: failed post recv event, "
+			"rc %d for win 0x%llx\n",
+			rc, (unsigned long long) win_p);
+	}
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index af88f0b..89f9639 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -73,6 +73,20 @@ int hfidd_query_interface(struct hfidd_acs *p_acs, unsigned int subtype,
 int hfidd_start_nmmu(struct hfidd_acs *p_acs);
 int hfidd_start_interface(struct hfidd_acs *p_acs);
 int hfidd_stop_interface(struct hfidd_acs *p_acs, unsigned int hfi_id);
+int hfidd_events_register(struct hfidd_acs *p_acs, struct hfi_reg_events *arg);
+int hfidd_events_unregister(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *arg);
+int hfidd_callback_register(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *arg);
+int hfidd_callback_unregister(struct hfidd_acs *p_acs,
+		struct hfi_reg_events *arg);
+int hfidd_report_event(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		enum hfi_event_type event);
+int hfidd_callback_event(struct hfidd_acs *p_acs, struct hfidd_window *win,
+		enum hfi_event_type event, unsigned int data1,
+		unsigned int *data2_p);
+int hfidd_events_clean(struct hfidd_acs *p_acs, struct hfidd_window *win);
+void hfidd_notify_hfi_ready(struct hfidd_acs *p_acs);
 int hfidd_init_win_interrupt(struct hfidd_acs *p_acs,
 		struct hfidd_window *win_p);
 void hfidd_clear_win_interrupt(struct hfidd_window *win_p);
@@ -104,5 +118,5 @@ long long hfi_hquery_interface(u64 unit_id, u64 subtype, u64 query_p,
 		u64 *state);
 long long hfi_start_interface(u64 unit_id);
 long long hfi_stop_interface(u64 unit_id);
-
+long long hfi_query_window(u64 unit_id, u64 win_id, u64 *state);
 #endif
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index 752cd11..ced669b 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -314,6 +314,8 @@ static inline int hfi_validate_window_id(struct hfidd_acs *p_acs,
 static inline void hfi_restore_window_parm(struct hfidd_acs *p_acs,
 		struct hfidd_window *win_p)
 {
+	int i;
+
 	if (win_p->type != HFIDD_RESERVE_WIN) {
 		win_p->type   = HFIDD_DYNAMIC_WIN;
 		win_p->job_id = 0;
@@ -325,6 +327,12 @@ static inline void hfi_restore_window_parm(struct hfidd_acs *p_acs,
 	}
 	win_p->pid   = 0;
 	win_p->is_ip = 0;
+
+
+	for (i = 0; i < HFIDD_NUM_EVENT_TYPES; i++) {
+		win_p->funcs[i].function_p.use.kptr = NULL;
+		win_p->funcs[i].parameter.use.kptr  = NULL;
+	}
 }
 
 /* Validate window number and type for open window request */
@@ -1075,6 +1083,13 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 	win_p->state = WIN_OPENED;
 	spin_unlock(&(win_p->win_lock));
 
+	/*
+	 * Increment so that any waiting threads that wake up realize
+	 * they are dealing with a window that has been reopened
+	 */
+	atomic_inc(&win_p->open_close_count);
+	hfidd_events_clean(p_acs, win_p);
+
 	kfree(local_p);
 	return rc;
 
@@ -1130,6 +1145,12 @@ int hfidd_close_window_internal(struct hfidd_acs *p_acs,
 		goto hfidd_close_window_internal_err0;
 	}
 
+	/* Wake up threads waiting for terminate event. */
+	rc = hfidd_report_event(p_acs, win_p, HFIDD_TERMINATE);
+	if (rc)
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"close_window_internal: report event failed "
+			"rc=0x%x\n", rc);
 	spin_lock(&(win_p->win_lock));
 
 	/* Make sure state is open or error state. */
@@ -1184,6 +1205,9 @@ int hfidd_close_window_internal(struct hfidd_acs *p_acs,
 	hfi_restore_window_parm(p_acs, win_p);
 	spin_unlock(&win_p->win_lock);
 
+	atomic_inc(&win_p->open_close_count);
+	hfidd_events_clean(p_acs, win_p);
+
 	dev_printk(KERN_INFO, p_acs->hfidd_dev,
 		"close_window_internal: type=0x%x state=0x%x JobID=0x%x\n",
 		win_p->type, win_p->state, win_p->job_id);
diff --git a/include/linux/hfi/hfidd_client.h b/include/linux/hfi/hfidd_client.h
index 3b2d032..7f87674 100644
--- a/include/linux/hfi/hfidd_client.h
+++ b/include/linux/hfi/hfidd_client.h
@@ -97,6 +97,9 @@ struct fifo_info {
 #define HFIDD_RESERVE_WIN	3	/* Must be reserved by job scheduler */
 #define HFIDD_DYNAMIC_WIN	4	/* First come, first served. Window# is
 						returned */
+#define HFIDD_DST_BCST_WIN		0
+#define HFIDD_DST_BCST_ISR		0x3FFF
+
 struct hfi_client_info {
 	struct hfi_req_hdr	hdr;
 
@@ -138,6 +141,67 @@ enum hfi_event_type {
 	HFIDD_NUM_EVENT_TYPES	= 12
 };
 
+#define HFIDD_SEND_EVENT			0x00000100
+#define HFIDD_RECV_EVENT			0x00000200
+#define HFIDD_WIN_ERROR_EVENT			0x00000400
+#define HFIDD_HFI_ERROR_EVENT			0x00000800
+#define HFIDD_TERMINATE_EVENT			0x00001000
+#define HFIDD_RELEASE_WINDOW_EVENT		0x00002000
+#define HFIDD_CAU_ERROR_EVENT			0x00004000
+#define HFIDD_ICS_ERROR_EVENT			0x00008000
+#define HFIDD_HFI_READY_REG_EVENT		0x00010000
+#define HFIDD_ROUTE_CHANGE_EVENT		0x00020000
+#define HFIDD_IP_TRC_LVL_EVENT			0x00040000
+#define HFIDD_POOL_SIZE_EVENT			0x00080000
+#define HFIDD_LOWEST_EVENT			HFIDD_SEND_EVENT
+#define HFIDD_ALL_EVENTS			0x000FFF00
+
+enum hfi_event_hndlr_type {
+	WAIT_FOR_EVENTS		= 1,	/* Wait for events */
+	SIGNAL_EVENTS		= 2,	/* Event notification by signal */
+	FUNCTIONS_FOR_EVENTS	= 3	/* Callback functions */
+};
+
+typedef int (*hfi_event_func_ptr)(void *parm, unsigned int win,
+		unsigned int *ext);
+
+struct hfi_callback_func {	/* Callback funcs for kernel windows */
+	enum hfi_event_type	index;		/* index of callback type */
+	unsigned int		pad;
+	struct hfi_64b		function_p;	/* function ptr */
+	struct hfi_64b		parameter;	/* parameter to pass in */
+};
+
+/*
+ * HFIDD_REQ_EVENT_REGISTER/HFIDD_REQ_EVENT_UNREGISTER: event
+ * registration/unregistration
+ */
+#define HFI_MAX_BUF_EVENTS	8	/* # event buffers for signal version */
+struct hfi_event_buffer	{		/* For reporting events with signals */
+	unsigned int		tag;
+	enum hfi_event_type	current_event;
+};
+
+struct hfi_reg_events {
+	struct hfi_req_hdr		hdr;
+	unsigned int			window;
+	enum hfi_event_hndlr_type	type;
+	union {
+		unsigned int		events;
+		struct hfi_callback_func func;
+		struct hfi_64b eb;	/* Pointer to event buffer in
+					user space (signal only) */
+	} info;
+};
+
+/* HFIDD_REQ_EVENT_WAIT: wait on event */
+struct hfi_wait_events {
+	struct hfi_req_hdr	hdr;
+	unsigned int		window;		/* Window for events */
+	unsigned int		events;		/* events to wait for */
+	unsigned int		out_events;	/* events received */
+};
+
 #define MAX_TORRENTS            1
 #define MAX_HFI_PER_TORRENT     2
 #define MAX_HFIS                (MAX_TORRENTS * MAX_HFI_PER_TORRENT)
diff --git a/include/linux/hfi/hfidd_internal.h b/include/linux/hfi/hfidd_internal.h
index e96142a..18f0fb4 100644
--- a/include/linux/hfi/hfidd_internal.h
+++ b/include/linux/hfi/hfidd_internal.h
@@ -72,6 +72,16 @@
 #define HFIDD_DEV_NAME		"hfi"
 #define HFIDD_CLASS_NAME	"hfi"
 
+#define HFIDD_EVENT_CLEANUP_LOOP_COUNT	1000
+#define HFIDD_EVENT_CLEANUP_DELAY	10
+
+/* window event */
+struct hfidd_q_event {
+	struct list_head	list;
+	struct hfidd_q_event	*next;		/* For preallocation list */
+	unsigned int		event;
+};
+
 struct hfidd_dds {
 	unsigned int		version;	/* HFI adapter type */
 	unsigned long long	misc_base_address; /* Misc user base address */
@@ -83,6 +93,33 @@ struct hfidd_dds {
 	unsigned long long	fw_ec_level;	/* Firmware Level */
 };
 
+struct hfidd_tid_info {
+	struct hfidd_tid_info	*next;
+	unsigned long long	tid;		/* Thread id */
+	struct semaphore	tid_sem;
+	unsigned int		registered_events;
+	unsigned int		deferred_events;/* Events that have occurred
+						   but have not yet been
+						   reported */
+	enum hfi_event_hndlr_type type;
+	struct list_head	event_list;	/* List of hfidd_event */
+	wait_queue_head_t	event_wait;	/* Used to wait and post
+						   threads */
+	unsigned int		tag;		/* Used with eb */
+	struct task_struct	*eb_xd;		/* For cross task write */
+	struct hfi_event_buffer	*eb;		/* Pointer to event
+						   buffer location in
+						   user space (for
+						   signal handling) */
+	struct task_struct	*th;		/* task_struct associated with
+						   tid */
+};
+
+struct hfidd_win_event {
+	struct hfidd_win_event	*next;
+	struct hfidd_tid_info	*tid_info;
+};
+
 struct hfidd_fifo {
 	unsigned long long	eaddr;
 	unsigned long long	size;
@@ -92,6 +129,7 @@ struct hfidd_fifo {
 
 struct hfidd_window {
 	spinlock_t		win_lock;	/* lock for window */
+	spinlock_t		event_lock;	/* lock for event handling */
 	int			index;
 	unsigned int		type;		/* dynamic/scheduled */
 	int			state;
@@ -117,10 +155,40 @@ struct hfidd_window {
 							   OPEN_WINDOW hcall */
 	unsigned long long	mmio_regs;		/* logical addr from
 							   OPEN WINDOW hcall */
+	atomic_t		open_close_count;	/*Incremented every time
+						   a window is opened or
+						   closed.  This is used for
+						   event handling to determine
+						   if a close occurred while
+						   waiting. */
+	struct hfi_callback_func funcs[HFIDD_NUM_EVENT_TYPES];	/* Callback
+						   funcs for IP */
+	struct hfidd_win_event	*events[HFIDD_NUM_EVENT_TYPES];	/* Each
+						   array entry points to a
+						   list.  Each list entry
+						   contains a tid that should
+						   be posted when this event
+						   occurs. */
+	atomic_t	num_events[HFIDD_NUM_EVENT_TYPES]; /* Number
+						   of elements in each of the
+						   events lists */
+	struct hfidd_tid_info	*tid_list;	/* List of tids registered for
+						   events on this window */
+	atomic_t		num_tids;	/* Number of tids in
+						   tid_list */
+	atomic_t		event_wait_count;/* Indicates how many
+						    threads are waiting for
+						    events on this window */
 	struct hfidd_vlxmem	*sfifo_x_tab;
 	struct hfidd_vlxmem	*rfifo_x_tab;
 };
 
+struct hfidd_hfi_ready_req {
+	struct list_head		list;
+	struct hfi_callback_func	func;
+};
+
+
 #define HFI_DEVICE_NAME_MAX 64
 /* hfi global */
 struct hfidd_acs {
@@ -131,6 +199,7 @@ struct hfidd_acs {
 	unsigned int		state;
 
 	unsigned int		isr;
+	struct list_head	hfi_ready_reg_list;
 
 	struct hfidd_window	**win;
 	struct device		*hfidd_dev;
diff --git a/include/linux/hfi/hfidd_requests.h b/include/linux/hfi/hfidd_requests.h
index a7a38da..002ae7f 100644
--- a/include/linux/hfi/hfidd_requests.h
+++ b/include/linux/hfi/hfidd_requests.h
@@ -37,4 +37,7 @@
 #define HFIDD_REQ_QUERY_DD_INFO			0x00001004
 #define HFIDD_REQ_CLOSE_WINDOW			0x00000a02
 
+#define HFIDD_REQ_EVENT_REGISTER		0x00000702
+#define HFIDD_REQ_EVENT_UNREGISTER		0x00000703
+
 #endif /* _HFIDD_REQUESTS_H_ */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 20/27] HFI: Close window hypervisor call
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/hfidd_hcalls.c |   22 ++++++++++++++
 drivers/net/hfi/core/hfidd_proto.h  |    1 +
 drivers/net/hfi/core/hfidd_window.c |   53 +++++++++++++++++++++++++++++++++--
 3 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/drivers/net/hfi/core/hfidd_hcalls.c b/drivers/net/hfi/core/hfidd_hcalls.c
index 1915336..4bc6525 100644
--- a/drivers/net/hfi/core/hfidd_hcalls.c
+++ b/drivers/net/hfi/core/hfidd_hcalls.c
@@ -153,6 +153,17 @@ static inline long long h_hfi_open_window(int token,
 	return rc;
 }
 
+static inline long long h_hfi_close_window(int token,
+		u64 HFI_chip_ID,
+		u64 win_num,
+		u64 flag)
+{
+	return plpar_hcall_norets(token,
+		HFI_chip_ID,
+		win_num,
+		flag);
+}
+
 long long hfi_start_nmmu(u64 chip_id, void *nmmu_info)
 {
 	return h_nmmu_start(H_NMMU_START, chip_id, nmmu_info);
@@ -249,6 +260,17 @@ long long hfi_modify_mr(u64 chip_id, u64 request, u64 mr_handle,
 	return hvrc;
 }
 
+long long hfi_close_window(u64 unit_id, u64 win_id, u64 flag)
+{
+	long long hvrc;
+
+	hvrc = h_hfi_close_window(H_HFI_CLOSE_WINDOW,
+			unit_id,
+			win_id,
+			flag);
+			return hvrc;
+}
+
 long long hfi_free_mr(u64 chip_id, u64 res, u64 mr_handle, u64 sub_region_id)
 {
 	long long	hvrc;
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index e065d56..f531dcd 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -94,6 +94,7 @@ long long hfi_modify_mr(u64 chip_id, u64 request, u64 mr_handle,
 		u64 e_addr,
 		u64 l_addr,
 		u64 num_pg_sz);
+long long hfi_close_window(u64 unit_id, u64 win_id, u64 flag);
 long long hfi_free_mr(u64 chip_id, u64 res, u64 mr_handle,
 		u64 sub_region_id);
 long long hfi_hquery_interface(u64 unit_id, u64 subtype, u64 query_p,
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index 1724c30..ff8b9f0 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -460,6 +460,43 @@ static int hfi_hcall_to_open_window(struct hfidd_acs *p_acs,
 	return 0;
 }
 
+/* Call to CLOSE WINDOW hcall */
+static int hfi_hcall_to_close_window(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p)
+{
+	int	rc = 0;
+	long long hvrc = 0;
+	u64	start_time = get_jiffies_64();
+
+	hvrc = hfi_close_window(p_acs->dds.hfi_id,
+			win_p->index,
+			H_CLOSE);
+
+	/*
+	 * Need to call CLOSE WINDOW with flag H_CHECK_CLOSED
+	 * to check when the window is completely closed
+	 */
+	while (hvrc == H_BUSY) {
+		hvrc = hfi_close_window(p_acs->dds.hfi_id,
+				win_p->index,
+				H_CHECK_CLOSED);
+		if (hvrc != H_BUSY)
+			break;
+		if (hfidd_age_hcall(start_time))
+			break;
+	}
+
+	if (hvrc != H_SUCCESS) {
+		win_p->state = WIN_FAIL_CLOSE;
+		rc = -EIO;
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_hcall_to_close_window: CLOSE WINDOW failed, "
+			"hvrc=0x%llx\n", hvrc);
+	}
+
+	return rc;
+}
+
 /*
  * Map the Effective Address pages for Memory Regions.
  * If more than one page, need to setup a page containing
@@ -1006,7 +1043,7 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		dev_printk(KERN_ERR, p_acs->hfidd_dev,
 			"hfidd_open_window_func: hfi_map_mmio_regs "
 			"failed, rc = 0x%x\n", rc);
-		goto hfidd_open_window_func_err4;
+		goto hfidd_open_window_func_err5;
 	}
 
 	/* tell user the local ISR id */
@@ -1020,7 +1057,7 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		dev_printk(KERN_ERR, p_acs->hfidd_dev,
 			"hfidd_open_window_func: hfi_copy_to_user "
 			"failed, rc = 0x%x\n", rc);
-		goto hfidd_open_window_func_err5;
+		goto hfidd_open_window_func_err6;
 	}
 
 	spin_lock(&(win_p->win_lock));
@@ -1032,9 +1069,11 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 	kfree(local_p);
 	return rc;
 
-hfidd_open_window_func_err5:
+hfidd_open_window_func_err6:
 	if (is_userspace)
 		hfidd_unmap(local_p->mmio_regs.use.kptr, PAGE_SIZE_64K);
+hfidd_open_window_func_err5:
+	hfi_hcall_to_close_window(p_acs, win_p);
 hfidd_open_window_func_err4:
 	hfi_destroy_window_parm(p_acs, is_userspace, win_p, local_p);
 hfidd_open_window_func_err3:
@@ -1104,6 +1143,14 @@ int hfidd_close_window_internal(struct hfidd_acs *p_acs,
 		goto hfidd_close_window_internal_err0;
 	}
 
+	rc = hfi_hcall_to_close_window(p_acs, win_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_internal: hfi_hcall_to_close_window "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_close_window_internal_err0;
+	}
+
 	hfi_destroy_window_info(p_acs, win_p);
 
 	/* Call hcall to unregister MR in the MMU */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 19/27] HFI: Add window close request
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/hfidd_init.c   |   36 +++++++++
 drivers/net/hfi/core/hfidd_proto.h  |    4 +
 drivers/net/hfi/core/hfidd_window.c |  148 +++++++++++++++++++++++++++++++++++
 include/linux/hfi/hfidd_client.h    |    8 ++
 include/linux/hfi/hfidd_hcalls.h    |    1 +
 include/linux/hfi/hfidd_requests.h  |    1 +
 6 files changed, 198 insertions(+), 0 deletions(-)

diff --git a/drivers/net/hfi/core/hfidd_init.c b/drivers/net/hfi/core/hfidd_init.c
index 1008260..603feb8 100644
--- a/drivers/net/hfi/core/hfidd_init.c
+++ b/drivers/net/hfi/core/hfidd_init.c
@@ -79,6 +79,20 @@ static int hfidd_query_dd_info(struct hfidd_acs *p_acs,
 	return rc;
 }
 
+/*
+ * This function is to check which command will be allowed after we got
+ * hfi error.
+ */
+static inline int valid_cmd_for_hfi_error(int cmd)
+{
+	switch (cmd) {
+	case HFIDD_REQ_CLOSE_WINDOW:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
 /* Entry point for user space to do driver requests. */
 static ssize_t hfidd_cmd_write(struct file *filep, const char __user *buf,
 		size_t count, loff_t *pos)
@@ -129,6 +143,15 @@ static ssize_t hfidd_cmd_write(struct file *filep, const char __user *buf,
 		return -EINVAL;
 	}
 
+	if (p_acs->state != HFI_AVAIL) {
+		if (valid_cmd_for_hfi_error(cmd.req)) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_write_cmd: hfi%d not avail, "
+				"state 0x%x for cmd 0x%x\n",
+				p_acs->index, p_acs->state, cmd.req);
+			return -EIO;
+		}
+	}
 	switch (cmd.req) {
 	case HFIDD_REQ_OPEN_WINDOW:
 		if (cmd.req_len != sizeof(struct hfi_client_info)) {
@@ -144,6 +167,19 @@ static ssize_t hfidd_cmd_write(struct file *filep, const char __user *buf,
 			(struct hfi_client_info *) cmd.result.use.kptr);
 		break;
 
+	case HFIDD_REQ_CLOSE_WINDOW:
+		if (cmd.req_len != sizeof(struct hfi_window_info)) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_cmd_write: hdr.reqlen 0x%x expected "
+				"0x%lx for cmd req 0x%x\n",
+				cmd.req_len,
+				sizeof(struct hfi_window_info), cmd.req);
+			return -EINVAL;
+		}
+		rc = hfidd_close_window_func(p_acs, is_userspace,
+				(struct hfi_window_info *) buf);
+		break;
+
 	case HFIDD_REQ_QUERY_DD_INFO:
 		if (cmd.req_len != sizeof(struct hfi_query_dd_info)) {
 			dev_printk(KERN_ERR, p_acs->hfidd_dev,
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index 1f7fe80..e065d56 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -54,6 +54,10 @@ int hfidd_get_page_num(struct hfidd_acs *p_acs, void *start_addr,
 int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		struct hfi_client_info *user_p,
 		struct hfi_client_info *out_p);
+int hfidd_close_window_internal(struct hfidd_acs *p_acs,
+		unsigned int is_userspace, unsigned int win_num);
+int hfidd_close_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
+		struct hfi_window_info *user_p);
 int hfi_register_rpages(struct hfidd_acs *p_acs, unsigned long long mr_handle,
 		unsigned int submr, struct hfidd_vlxmem *xtab_p,
 		unsigned int *mapped_pages);
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index 4ee96f8..1724c30 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -875,6 +875,28 @@ setup_window_parm_err1:
 	return rc;
 }
 
+/* Unmap the window mmio registers - only user space window */
+static int hfi_unmap_mmio_regs(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p,
+		unsigned int is_userspace)
+{
+	int	rc = 0;
+
+	if (is_userspace) {
+		rc = hfidd_unmap((void *)
+				(win_p->client_info.mmio_regs.use.kptr),
+				PAGE_SIZE_64K);
+		if (rc) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfi_unmap_mmio_regs: hfidd_unmap failed "
+				"rc = 0x%x\n", rc);
+			return rc;
+		}
+		win_p->client_info.mmio_regs.use.kptr = NULL;
+	}
+	return 0;
+}
+
 /* Map the window mmio registers - only user space window */
 static int hfi_map_mmio_regs(struct hfidd_acs *p_acs,
 		unsigned int is_userspace,
@@ -1024,3 +1046,129 @@ hfidd_open_window_func_err1:
 	return rc;
 }
 EXPORT_SYMBOL_GPL(hfidd_open_window_func);
+
+/*
+ * Close an user/kernel window to stop send/receive network traffic thru
+ * HFI adapter. This function will call PHYP to close the window and
+ * release the system resources allocated during open time. This function
+ * is called by hfidd_close_window_func or by abnormal end handler when
+ * the process goes away.
+ */
+int hfidd_close_window_internal(struct hfidd_acs *p_acs,
+		unsigned int is_userspace, unsigned int win_num)
+{
+	struct hfidd_window	*win_p;
+	int			rc = 0;
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"close_window_internal: win_num=0x%x\n", win_num);
+
+	if ((win_num <  min_hfi_windows(p_acs)) ||
+	    (win_num >= max_hfi_windows(p_acs))) {
+		rc = -EINVAL;
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"close_window_internal: window too large - "
+			"0x%x rc = 0x%x\n", win_num, rc);
+		goto hfidd_close_window_internal_err0;
+	}
+
+	win_p = hfi_window(p_acs, win_num);
+	if (win_p == NULL) {
+		rc = -ENOENT;
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"close_window_internal: win_p is NULL rc=0x%x\n", rc);
+		goto hfidd_close_window_internal_err0;
+	}
+
+	spin_lock(&(win_p->win_lock));
+
+	/* Make sure state is open or error state. */
+	if ((win_p->state != WIN_OPENED) &&
+	    (win_p->state != WIN_SUSPENDED) &&
+	    (win_p->state != WIN_ERROR)  &&
+	    (win_p->state != WIN_HERROR)) {
+		rc = -EFAULT;
+		spin_unlock(&(win_p->win_lock));
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_internal: bad window state=0x%x, "
+			"rc = 0x%x\n",  win_p->state, rc);
+		goto hfidd_close_window_internal_err0;
+	}
+	spin_unlock(&(win_p->win_lock));
+
+	rc = hfi_unmap_mmio_regs(p_acs, win_p, is_userspace);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_internal: hfi_unmap_mmio_regs "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_close_window_internal_err0;
+	}
+
+	hfi_destroy_window_info(p_acs, win_p);
+
+	/* Call hcall to unregister MR in the MMU */
+	rc = hfi_takedown_window_in_MMU(p_acs, is_userspace, win_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_internal: hfi_takedown_window_in_MMU "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_close_window_internal_err0;
+	}
+
+	hfi_free_win_resource(p_acs, is_userspace, win_p,
+		&(win_p->client_info));
+
+	spin_lock(&win_p->win_lock);
+	/* Update the window information */
+	hfi_restore_window_parm(p_acs, win_p);
+	spin_unlock(&win_p->win_lock);
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"close_window_internal: type=0x%x state=0x%x JobID=0x%x\n",
+		win_p->type, win_p->state, win_p->job_id);
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"close_window_internal: rc=0x%x\n", rc);
+	return rc;
+
+hfidd_close_window_internal_err0:
+	return rc;
+}
+
+/*
+ * This function is called by the kernel users directly or a write
+ * system call by the kernel users. It will call hfidd_close_window_internal
+ * to close a specific window.
+ */
+int hfidd_close_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
+	struct hfi_window_info *user_p)
+{
+	unsigned int		win_num;
+	int			rc = 0;
+	struct hfi_window_info	win_info;
+
+	/* Copy in win num from user */
+	rc = hfi_copy_from_user(&win_info, user_p,
+			is_userspace, sizeof(struct hfi_window_info));
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_func: hfi_copy_from_user "
+			"failed, rc = 0x%x\n", rc);
+		return rc;
+	}
+
+	win_num = win_info.window;
+
+	rc = hfidd_close_window_internal(p_acs, is_userspace, win_num);
+	if (rc) {
+		rc = -EINVAL;
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_close_window_func: close_window_internal "
+			"failed, win=0x%x rc=0x%x\n", win_num, rc);
+		return rc;
+	}
+
+	dev_printk(KERN_INFO, p_acs->hfidd_dev,
+		"close_window_func: rc=0x%x\n", rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(hfidd_close_window_func);
diff --git a/include/linux/hfi/hfidd_client.h b/include/linux/hfi/hfidd_client.h
index 7e4c1a7..11c8973 100644
--- a/include/linux/hfi/hfidd_client.h
+++ b/include/linux/hfi/hfidd_client.h
@@ -113,6 +113,14 @@ struct hfi_client_info {
 	struct hfi_64b		mmio_regs;		/* Output	*/
 };
 
+/*
+ * HFIDD_REQ_CLOSE_WINDOW: close window
+ */
+struct hfi_window_info {
+	struct hfi_req_hdr	hdr;
+	unsigned int		window;
+};
+
 #define MAX_TORRENTS            1
 #define MAX_HFI_PER_TORRENT     2
 #define MAX_HFIS                (MAX_TORRENTS * MAX_HFI_PER_TORRENT)
diff --git a/include/linux/hfi/hfidd_hcalls.h b/include/linux/hfi/hfidd_hcalls.h
index 1e007c5..777de8f 100644
--- a/include/linux/hfi/hfidd_hcalls.h
+++ b/include/linux/hfi/hfidd_hcalls.h
@@ -40,6 +40,7 @@
 #define H_HFI_QUERY_INTERFACE		0xF004
 #define H_HFI_STOP_INTERFACE		0xF008
 #define H_HFI_OPEN_WINDOW		0xF00C
+#define H_HFI_CLOSE_WINDOW		0xF014
 #define H_NMMU_START			0xF028
 #define H_NMMU_STOP			0xF02C
 #define H_NMMU_ALLOCATE_RESOURCE	0xF030
diff --git a/include/linux/hfi/hfidd_requests.h b/include/linux/hfi/hfidd_requests.h
index 4f1c74d..a7a38da 100644
--- a/include/linux/hfi/hfidd_requests.h
+++ b/include/linux/hfi/hfidd_requests.h
@@ -35,5 +35,6 @@
 
 #define HFIDD_REQ_OPEN_WINDOW			0x00000a01
 #define HFIDD_REQ_QUERY_DD_INFO			0x00001004
+#define HFIDD_REQ_CLOSE_WINDOW			0x00000a02
 
 #endif /* _HFIDD_REQUESTS_H_ */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 17/27] HFI: Set up and call the open window hypercall
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/hfidd_window.c |  180 +++++++++++++++++++++++++++++++++++
 include/linux/hfi/hfidd_hcalls.h    |    2 +
 2 files changed, 182 insertions(+), 0 deletions(-)

diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index 6d90af6..3a9fea9 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -359,6 +359,106 @@ static int hfi_validate_window_parm(struct hfidd_acs *p_acs,
 	return 0;
 }
 
+/*
+ * Setup a page for phyp with the window parameters needed for
+ * OPEN WINDOW hcall
+ */
+int hfi_build_window_info(struct hfidd_acs *p_acs, struct hfidd_window *win_p)
+{
+	caddr_t		laddr;
+
+#define WIN_INFO	(win_p->win_open_info_p)
+#define CLIENT_INFO	(win_p->client_info)
+
+	/* OPEN WINDOW hcall requires a page to pass arguments */
+	win_p->win_open_info_p = (struct win_open_info *)
+			__get_free_pages(GFP_KERNEL, get_order(PAGE_SIZE_4K));
+	if (win_p->win_open_info_p == NULL) {
+
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_build_window_info: ___get_free_pages failed\n");
+		return -ENOMEM;
+	}
+	memset((void *)(win_p->win_open_info_p), 0, PAGE_SIZE_4K);
+
+	/* Translate virtual adress into logical addres */
+	laddr = (caddr_t)__pa((caddr_t)win_p->win_open_info_p);
+
+	win_p->win_open_info_laddr = laddr;
+	WIN_INFO->job_id = CLIENT_INFO.job_id;
+	WIN_INFO->protection_domain = win_p->job_id;
+
+	WIN_INFO->sfifo_base_eaddr = CLIENT_INFO.sfifo.eaddr.use.allu;
+	WIN_INFO->sfifo_lkey       = win_p->sfifo_x_tab->l_key;
+	WIN_INFO->sfifo_size       = CLIENT_INFO.sfifo.size;
+	WIN_INFO->sfifo_finish_vec = CLIENT_INFO.sfifo_finish_vec.use.allu;
+
+	WIN_INFO->rfifo_base_eaddr = CLIENT_INFO.rfifo.eaddr.use.allu;
+	WIN_INFO->rfifo_lkey       = win_p->rfifo_x_tab->l_key;
+	WIN_INFO->rfifo_size       = CLIENT_INFO.rfifo.size;
+
+	/* Save IP context */
+	WIN_INFO->is_ip_window = win_p->is_ip;
+	if (win_p->is_ip) {
+		WIN_INFO->multicast_enable	   = HFI_MULTICAST_ENABLE;
+		WIN_INFO->disable_src_isr_id_stamp = 0;
+		WIN_INFO->logical_port_id_valid	   = 0;
+		WIN_INFO->logical_port_id	   = 0;
+	}
+
+	return 0;
+}
+
+/* Free the phyp page used at OPEN WINDOW hcall */
+static inline void hfi_destroy_window_info(struct hfidd_acs *p_acs,
+					struct hfidd_window *win_p)
+{
+	free_pages((unsigned long)win_p->win_open_info_p,
+			get_order(PAGE_SIZE_4K));
+	win_p->win_open_info_p = NULL;
+	return;
+}
+
+/* Call to OPEN WINDOW hcall */
+static int hfi_hcall_to_open_window(struct hfidd_acs *p_acs,
+				struct hfidd_window *win_p)
+{
+	int		rc = 0;
+	long long	hvrc = 0;
+	u64		ummio_regs;
+	u64		pmmio_regs;
+	u64		send_intr;
+	u64		recv_intr;
+
+	hvrc = hfi_open_window(p_acs->dds.hfi_id,
+			win_p->index,
+			H_OPEN,
+			(u64)win_p->win_open_info_laddr,
+			&ummio_regs,
+			&pmmio_regs,
+			&send_intr,
+			&recv_intr);
+
+	if (hvrc != H_SUCCESS) {
+		if (hvrc == H_HARDWARE) {
+			win_p->state = WIN_HERROR;
+			rc = -EIO;
+		} else {
+			rc = -EINVAL;
+		}
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_hcall_to_open_window: OPEN WINDOW failed, "
+			"hvrc=0x%llx\n", hvrc);
+		return rc;
+	}
+
+	/* Copy mmio_regs logical page from OPEN_WINDOW hcall */
+	win_p->mmio_regs = ummio_regs;
+	win_p->send_intr  = (unsigned int)send_intr;
+	win_p->recv_intr  = (unsigned int)recv_intr;
+
+	return 0;
+}
 
 /*
  * Map the Effective Address pages for Memory Regions.
@@ -573,6 +673,31 @@ sfifo_err:
 	return rc;
 }
 
+/* Free all the window memory regions */
+static int hfi_takedown_window_in_MMU(struct hfidd_acs *p_acs,
+		unsigned int is_userspace, struct hfidd_window *win_p)
+{
+	int	rc = 0;
+
+	rc = hfi_unregister_MMU(p_acs, win_p->rfifo_x_tab);
+	if (rc != 0) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_takedown_window_in_MMU: rfifo mr failed,"
+			"rc = 0x%x\n", rc);
+		return rc;
+	}
+
+	rc = hfi_unregister_MMU(p_acs, win_p->sfifo_x_tab);
+	if (rc != 0) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_takedown_window_in_MMU: sfifo mr failed,"
+			"rc = 0x%x\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
 static int hfi_xlate_fifos(struct hfidd_acs *p_acs,
 		unsigned int is_userspace,
 		struct hfidd_window *win_p,
@@ -701,6 +826,19 @@ static int hfi_alloc_win_resource(struct hfidd_acs *p_acs,
 	return 0;
 }
 
+static int hfi_destroy_window_parm(struct hfidd_acs *p_acs,
+		unsigned int is_userspace,
+		struct hfidd_window *win_p,
+		struct hfi_client_info *client_p)
+{
+	int	rc = 0;
+
+	hfi_destroy_window_info(p_acs, win_p);
+
+	rc = hfi_takedown_window_in_MMU(p_acs, is_userspace, win_p);
+	return rc;
+}
+
 static int hfi_setup_window_parm(struct hfidd_acs *p_acs,
 			unsigned int is_userspace,
 			struct hfidd_window *win_p,
@@ -719,8 +857,20 @@ static int hfi_setup_window_parm(struct hfidd_acs *p_acs,
 			"failed, rc = 0x%x\n", rc);
 		goto setup_window_parm_err1;
 	}
+
+	/* Build window information for OPEN WINDOW hcall */
+	rc = hfi_build_window_info(p_acs, win_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_setup_window_parm: hfi_build_window_info "
+			"failed, rc = 0x%x\n", rc);
+		goto setup_window_parm_err2;
+	}
+
 	return 0;
 
+setup_window_parm_err2:
+	hfi_takedown_window_in_MMU(p_acs, is_userspace, win_p);
 setup_window_parm_err1:
 	return rc;
 }
@@ -783,9 +933,39 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		goto hfidd_open_window_func_err3;
 	}
 
+	rc = hfi_hcall_to_open_window(p_acs, win_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_open_window_func: hfi_hcall_to_open_window "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_open_window_func_err4;
+	}
+
+	/* tell user the local ISR id */
+	local_p->local_isrid = p_acs->isr;
+	win_p->client_info.local_isrid = p_acs->isr;
+
+	/* Copy out the client info back to user */
+	rc = hfi_copy_to_user((void *)out_p, (void *)local_p,
+			is_userspace, sizeof(struct hfi_client_info));
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_open_window_func: hfi_copy_to_user "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_open_window_func_err4;
+	}
+
+	spin_lock(&(win_p->win_lock));
+	/* Update the window information */
+	win_p->pid = current->tgid;
+	win_p->state = WIN_OPENED;
+	spin_unlock(&(win_p->win_lock));
+
 	kfree(local_p);
 	return rc;
 
+hfidd_open_window_func_err4:
+	hfi_destroy_window_parm(p_acs, is_userspace, win_p, local_p);
 hfidd_open_window_func_err3:
 	hfi_free_win_resource(p_acs, is_userspace, win_p, local_p);
 hfidd_open_window_func_err2:
diff --git a/include/linux/hfi/hfidd_hcalls.h b/include/linux/hfi/hfidd_hcalls.h
index a97bb5e..1e007c5 100644
--- a/include/linux/hfi/hfidd_hcalls.h
+++ b/include/linux/hfi/hfidd_hcalls.h
@@ -70,6 +70,8 @@
 
 #define HFI_ACCESS_CTL_SHIFT	32
 
+#define HFI_MULTICAST_ENABLE	1
+
 struct win_open_info {
 	/* Hyp Feedback */
 	unsigned long long	hypervisor_capabilities;
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 18/27] HFI: Map window registers into user process
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

User-space applications send and receive without kernel involvement, once
the window is open. A page of hardware registers controlling the appropriate
window is mapped into the user's address space.

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/Makefile       |    1 +
 drivers/net/hfi/core/hfidd_map.c    |   99 +++++++++++++++++++++++++++++++++++
 drivers/net/hfi/core/hfidd_proto.h  |    4 ++
 drivers/net/hfi/core/hfidd_window.c |   51 ++++++++++++++++++-
 4 files changed, 154 insertions(+), 1 deletions(-)
 create mode 100644 drivers/net/hfi/core/hfidd_map.c

diff --git a/drivers/net/hfi/core/Makefile b/drivers/net/hfi/core/Makefile
index 8d5558d..3adf07e 100644
--- a/drivers/net/hfi/core/Makefile
+++ b/drivers/net/hfi/core/Makefile
@@ -5,5 +5,6 @@ hfi_core-objs:=	hfidd_adpt.o \
 		hfidd_window.o \
 		hfidd_init.o \
 		hfidd_xlat.o \
+		hfidd_map.o \
 		hfidd_hcalls.o
 obj-$(CONFIG_HFI) += hfi_core.o
diff --git a/drivers/net/hfi/core/hfidd_map.c b/drivers/net/hfi/core/hfidd_map.c
new file mode 100644
index 0000000..816e7ae
--- /dev/null
+++ b/drivers/net/hfi/core/hfidd_map.c
@@ -0,0 +1,99 @@
+/*
+ * hfidd_map.c
+ *
+ * HFI device driver for IBM System p
+ *
+ *  Authors:
+ *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *      William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *      Jian Xiao <jian@linux.vnet.ibm.com>
+ *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/hfi/hfidd_internal.h>
+
+int hfidd_mmap(struct hfidd_acs *p_acs, void **eaddr, int size,
+		unsigned long vm_flag, unsigned long long busaddr,
+		unsigned long long offset)
+{
+	struct vm_area_struct	*vma;
+	unsigned long		vsize;
+	int			rc;
+
+	down_write(&current->mm->mmap_sem);
+	*eaddr = (void *)do_mmap(NULL, (unsigned long)*eaddr, size, PROT_WRITE,
+			MAP_SHARED | MAP_ANONYMOUS,
+			offset);
+	up_write(&current->mm->mmap_sem);
+	if (*eaddr) {
+		vma = find_vma(current->mm, (u64) *eaddr);
+		if (!vma) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfidd_mmap: find_vma failed\n");
+			return -ENOMEM;
+		}
+	} else {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_mmap: do_mmap failed\n");
+		return -ENOMEM;
+	}
+
+	vsize = vma->vm_end - vma->vm_start;
+	if (vsize != size) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_mmap: Wrong sizes: vsize = %ld "
+			"size = %d\n", vsize, size);
+		return -EINVAL;
+	}
+
+	if (vm_flag == VM_RESERVED)
+		vma->vm_page_prot = pgprot_val(vma->vm_page_prot);
+	else
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	vma->vm_flags |= vm_flag;
+
+	rc = remap_pfn_range(vma, vma->vm_start, busaddr >> PAGE_SHIFT, size,
+			vma->vm_page_prot);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_mmap: remap_pfn_range failed\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int hfidd_unmap(void *addr, int size)
+{
+	int rc = 0;
+	struct mm_struct *mm = current->mm;
+
+	if (mm && (addr != NULL)) {
+		down_write(&mm->mmap_sem);
+		rc = do_munmap(mm, (unsigned long)addr, size);
+		up_write(&mm->mmap_sem);
+	}
+
+	return rc;
+}
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index c4ed215..1f7fe80 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -57,6 +57,10 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 int hfi_register_rpages(struct hfidd_acs *p_acs, unsigned long long mr_handle,
 		unsigned int submr, struct hfidd_vlxmem *xtab_p,
 		unsigned int *mapped_pages);
+int hfidd_mmap(struct hfidd_acs *p_acs, void **eaddr, int size,
+		unsigned long vm_flag, unsigned long long busaddr,
+		unsigned long long offset);
+int hfidd_unmap(void *addr, int size);
 int hfidd_get_phyp_page(struct hfidd_acs *p_acs, caddr_t *page,
 	caddr_t *laddr, int size);
 void hfidd_release_phyp_page(caddr_t page, int size);
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index 3a9fea9..4ee96f8 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -875,6 +875,44 @@ setup_window_parm_err1:
 	return rc;
 }
 
+/* Map the window mmio registers - only user space window */
+static int hfi_map_mmio_regs(struct hfidd_acs *p_acs,
+		unsigned int is_userspace,
+		struct hfidd_window *win_p,
+		struct hfi_client_info *client_p)
+{
+	int			rc = 0;
+	unsigned long long	offset;
+	void			*tmp_eaddr;
+
+	if (!is_userspace) {
+		/* No translation, just pass back the logical address */
+		client_p->mmio_regs.use.kptr = (void *)win_p->mmio_regs;
+		win_p->client_info.mmio_regs.use.kptr =
+				(void *)win_p->mmio_regs;
+	} else {
+		/*
+		 * Translate mmio_regs from logical to effective address: 1st
+		 * page
+		 */
+		tmp_eaddr = 0;
+		offset = (client_p->window) << PAGE_SHIFT_64K;
+		rc = hfidd_mmap(p_acs, &tmp_eaddr, PAGE_SIZE_64K,
+				VM_RESERVED | VM_IO,
+				(long long)win_p->mmio_regs, offset);
+		if (rc) {
+			dev_printk(KERN_ERR, p_acs->hfidd_dev,
+				"hfi_map_mmio_regs: hfidd_mmap mmio_regs "
+				"failed, rc = 0x%x, mmio_regs = 0x%llx\n",
+				rc, (unsigned long long)win_p->mmio_regs);
+			return rc;
+		}
+		client_p->mmio_regs.use.kptr = tmp_eaddr;
+		win_p->client_info.mmio_regs.use.kptr = tmp_eaddr;
+	}
+	return 0;
+}
+
 /*
  * Allows an user/kernel window to send/receive network traffic thru HFI
  * adapter. This function will allocate the system resources needed to open
@@ -941,6 +979,14 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		goto hfidd_open_window_func_err4;
 	}
 
+	rc = hfi_map_mmio_regs(p_acs, is_userspace, win_p, local_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_open_window_func: hfi_map_mmio_regs "
+			"failed, rc = 0x%x\n", rc);
+		goto hfidd_open_window_func_err4;
+	}
+
 	/* tell user the local ISR id */
 	local_p->local_isrid = p_acs->isr;
 	win_p->client_info.local_isrid = p_acs->isr;
@@ -952,7 +998,7 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		dev_printk(KERN_ERR, p_acs->hfidd_dev,
 			"hfidd_open_window_func: hfi_copy_to_user "
 			"failed, rc = 0x%x\n", rc);
-		goto hfidd_open_window_func_err4;
+		goto hfidd_open_window_func_err5;
 	}
 
 	spin_lock(&(win_p->win_lock));
@@ -964,6 +1010,9 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 	kfree(local_p);
 	return rc;
 
+hfidd_open_window_func_err5:
+	if (is_userspace)
+		hfidd_unmap(local_p->mmio_regs.use.kptr, PAGE_SIZE_64K);
 hfidd_open_window_func_err4:
 	hfi_destroy_window_parm(p_acs, is_userspace, win_p, local_p);
 hfidd_open_window_func_err3:
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 27/27] HFI: hf ethtool support
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/ip/Makefile     |    2 +-
 drivers/net/hfi/ip/hf_ethtool.c |  136 +++++++++++++++++++++++++++++++++++++++
 drivers/net/hfi/ip/hf_if_main.c |   34 +++++++++-
 drivers/net/hfi/ip/hf_proto.h   |    1 +
 include/linux/hfi/hf_if.h       |   32 +++++++++
 5 files changed, 200 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/hfi/ip/hf_ethtool.c

diff --git a/drivers/net/hfi/ip/Makefile b/drivers/net/hfi/ip/Makefile
index 59eff9b..2909b00 100644
--- a/drivers/net/hfi/ip/Makefile
+++ b/drivers/net/hfi/ip/Makefile
@@ -3,4 +3,4 @@
 #
 obj-$(CONFIG_HFI_IP) += hf_if.o
 
-hf_if-objs :=	hf_if_main.o
+hf_if-objs :=	hf_if_main.o hf_ethtool.o
diff --git a/drivers/net/hfi/ip/hf_ethtool.c b/drivers/net/hfi/ip/hf_ethtool.c
new file mode 100644
index 0000000..8fd48d0
--- /dev/null
+++ b/drivers/net/hfi/ip/hf_ethtool.c
@@ -0,0 +1,136 @@
+/*
+ * hf_ethtool.c
+ *
+ * HF IP driver for IBM System p
+ *
+ *  Authors:
+ *	Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *	William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *	Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *	Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *	Jian Xiao <jian@linux.vnet.ibm.com>
+ *	Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *	Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/ethtool.h>
+
+#include <linux/hfi/hf_if.h>
+
+static char hf_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
+	{"sfifo_packets"},
+	{"rdma_packets"},
+	{"tx_timeout"},
+	{"tx_queue_stop"},
+	{"tx_drop"},
+	{"tx_err_headlen"},
+	{"rx_version_mismatch"},
+	{"rx_err_restore"},
+	{"rx_err_cookie"},
+	{"rx_err_skb"},
+	{"rx_err_hdr_type"},
+	{"rx_err_msg_type"},
+	{"rx_err_status"},
+	{"rx_err_bcast_csum"},
+	{"rx_fslot_debt"},
+	{"mmio_rx_inc_avail"},
+	{"mmio_rx_post_desc"},
+	{"payload_sent"},
+	{"desc_sent"},
+	{"large_bcast_sent"},
+	{"super_sent"},
+	{"payload_recv"},
+	{"desc_recv"},
+	{"rdma_write"},
+	{"rdma_write_fail"},
+	{"rdma_cancel"},
+	{"rdma_cancel_fail"},
+	{"rdma_cancel_already"},
+	{"rdma_rndz_request_sent"},
+	{"rdma_rndz_request_fail"},
+	{"rdma_rndz_reply_recv"},
+	{"rdma_rndz_reply_fail"},
+	{"rdma_rndz_request_recv"},
+	{"rdma_rndz_reply_sent"},
+	{"bad_rdma_notification"},
+	{"bad_rdma_first_notification"},
+	{"rdma_src_completion"},
+	{"rdma_sink_completion"},
+	{"rdma_send_timeout"},
+	{"rdma_recv_timeout"},
+	{"sfifo_send_intr_armed"},
+	{"rdma_send_intr_armed"},
+	{"recv_intr_armed"},
+	{"recv_intr_offset"},
+	{"recv_imm_intr_armed"},
+	{"recv_imm_intr_offset"},
+	{"send_intr_fired"},
+	{"recv_intr_fired"},
+	{"in_poll"},
+	{"max_poll_recv"},
+};
+
+static void hf_get_drvinfo(struct net_device *netdev,
+		struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, HF_DRV_NAME, sizeof(info->driver));
+	strlcpy(info->version, HF_DRV_VERSION, sizeof(info->version));
+}
+
+static void hf_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		memcpy(data, &hf_ethtool_stats_keys,
+				sizeof(hf_ethtool_stats_keys));
+		break;
+	}
+}
+
+static int hf_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return ARRAY_SIZE(hf_ethtool_stats_keys);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void hf_get_ethtool_stats(struct net_device *netdev,
+		struct ethtool_stats *stats, u64 *data)
+{
+	struct hf_net	*net = netdev_priv(netdev);
+	struct hf_if	*net_if = &(net->hfif);
+
+	memcpy(data, &(net_if->eth_stats), sizeof(struct hf_ethtool_stats));
+}
+
+static const struct ethtool_ops hf_ethtool_ops = {
+	.get_drvinfo		= hf_get_drvinfo,
+	.get_strings		= hf_get_strings,
+	.get_sset_count		= hf_get_sset_count,
+	.get_ethtool_stats	= hf_get_ethtool_stats,
+};
+
+void hf_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &hf_ethtool_ops);
+}
diff --git a/drivers/net/hfi/ip/hf_if_main.c b/drivers/net/hfi/ip/hf_if_main.c
index 10dc1da..e45b48f 100644
--- a/drivers/net/hfi/ip/hf_if_main.c
+++ b/drivers/net/hfi/ip/hf_if_main.c
@@ -231,6 +231,7 @@ static int hf_send_intr_callback(void *parm, u32 win, u32 ext)
 	mb();
 
 	netif_wake_queue(net->netdev);
+	net->hfif.eth_stats.send_intr_fired++;
 
 	return 0;
 }
@@ -241,6 +242,7 @@ static int hf_recv_intr_callback(void *parm, u32 win, u32 ext)
 
 	napi_schedule(&(net->napi));
 
+	net->hfif.eth_stats.recv_intr_fired++;
 	return 0;
 }
 
@@ -400,6 +402,9 @@ static void hf_set_recv_intr(struct hf_if *net_if)
 	hf_mmio_regs_write_then_read(net_if, HFI_RFIFO_INTR_REG,
 		(HF_ENA_RECV_INTR + (offset << HF_RECV_INTR_MATCH_SHIFT)));
 
+	net_if->eth_stats.recv_intr_offset = offset;
+	net_if->eth_stats.recv_intr_armed++;
+
 	/* check if there is packet received in the mean time */
 	rx_pkt = net_if->rx_fifo.addr + (offset << HFI_CACHE_LINE_SHIFT);
 
@@ -409,6 +414,9 @@ static void hf_set_recv_intr(struct hf_if *net_if)
 		/* force an immediate recv intr */
 		hf_mmio_regs_write(net_if, HFI_RFIFO_INTR_REG,
 		(HF_IMM_RECV_INTR + (offset << HF_RECV_INTR_MATCH_SHIFT)));
+
+		net_if->eth_stats.recv_imm_intr_offset = offset;
+		net_if->eth_stats.recv_imm_intr_armed++;
 	}
 }
 
@@ -527,7 +535,7 @@ static int hf_net_open(struct net_device *netdev)
 	struct hfidd_acs	*p_acs = HF_ACS(net_if);
 
 	memset(&(net_if->net_stats), 0, sizeof(struct net_device_stats));
-	net_if->sfifo_packets = 0;
+	memset(&(net_if->eth_stats), 0, sizeof(struct hf_ethtool_stats));
 
 	spin_lock(&(net_if->lock));
 	net_if->state = HF_NET_HALF_OPEN;
@@ -637,6 +645,7 @@ static inline int hf_check_hdr_version(struct hf_if *net_if,
 			"%s: hf_check_hdr_version: hdr version 0x%x "
 			"does not match 0x%x\n",
 			net_if->name, hf_hdr->version, HF_PROTO_HDR_VERSION);
+		net_if->eth_stats.rx_version_mismatch++;
 		net_if->net_stats.rx_dropped++;
 
 		return -EINVAL;
@@ -733,6 +742,7 @@ static void hf_recv_ip_with_payload(struct hf_net *net,
 	net_if->net_stats.rx_packets++;
 	net_if->net_stats.rx_bytes += skb->len;
 
+	net_if->eth_stats.payload_recv++;
 	netif_receive_skb(skb);
 }
 
@@ -756,6 +766,7 @@ static void hf_recv_ip_good(struct hf_net *net,
 			rx_curr->type.header_type, pkt_len);
 
 		/* unknown packet, drop it */
+		net_if->eth_stats.rx_err_hdr_type++;
 		net_if->net_stats.rx_dropped++;
 		break;
 	}
@@ -792,6 +803,7 @@ static int hf_rx(struct hf_net *net, int budget)
 				"status = 0x%x, pkt_len = 0x%x\n",
 				status, pkt_len);
 
+			net_if->eth_stats.rx_err_status++;
 			net_if->net_stats.rx_dropped++;
 		}
 
@@ -807,6 +819,7 @@ static int hf_rx(struct hf_net *net, int budget)
 			hf_mmio_regs_write(net_if, HFI_RFIFO_INC_FSLOT_REG,
 					net_if->rx_fslot_debt);
 			net_if->rx_fslot_debt = 0;
+			net_if->eth_stats.mmio_rx_inc_avail++;
 		}
 
 		budget--;
@@ -816,6 +829,7 @@ static int hf_rx(struct hf_net *net, int budget)
 
 	}
 
+	net_if->eth_stats.rx_fslot_debt = net_if->rx_fslot_debt;
 	netdev_dbg(net->netdev, "hf_rx: exit, head = 0x%x, recv 0x%x pkts\n",
 			net_if->rx_fifo.head, num);
 
@@ -885,9 +899,10 @@ int hf_tx_check_avail(struct hf_net *net, u32 xmit_cls)
 			u64		intr_thresh;
 
 			netif_stop_queue(netdev);
+			net_if->eth_stats.tx_queue_stop++;
 
 			/* turn on transmit interrupt */
-			intr_thresh = (net_if->sfifo_packets -
+			intr_thresh = (net_if->eth_stats.sfifo_packets -
 			HF_SFIFO_INTR_WATERMARK) & HF_SFIFO_INTR_MASK;
 
 			intr_cntl = HF_SFIFO_INTR_ENABLE |
@@ -896,6 +911,7 @@ int hf_tx_check_avail(struct hf_net *net, u32 xmit_cls)
 			hf_mmio_regs_write_then_read(net_if,
 					HFI_SFIFO_INTR_CNTL, intr_cntl);
 
+			net_if->eth_stats.sfifo_send_intr_armed++;
 			return -EBUSY;
 		}
 	}
@@ -978,6 +994,7 @@ static char *hf_build_payload_hdr(struct hf_net *net,
 			" not supported\n", hwhdr_p->h_proto);
 
 		dev_kfree_skb_any(skb);
+		net_if->eth_stats.tx_drop++;
 		return NULL;
 	}
 
@@ -1072,7 +1089,8 @@ static int hf_payload_tx(struct sk_buff *skb, struct hf_net *net, u32 is_bcast)
 		(net_if->tx_fifo.tail + xmit_cls) & (net_if->tx_fifo.emax);
 	atomic_sub(xmit_cls, &(net_if->tx_fifo.avail));
 
-	net_if->sfifo_packets++;
+	net_if->eth_stats.sfifo_packets++;
+	net_if->eth_stats.payload_sent++;
 	net_if->net_stats.tx_packets++;
 	net_if->net_stats.tx_bytes += msg_len;
 
@@ -1100,6 +1118,7 @@ static int hf_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 		netdev_err(netdev, "hf_start_xmit: invalid skb->len 0x%x\n",
 						skb->len);
 		dev_kfree_skb_any(skb);
+		net_if->eth_stats.tx_drop++;
 		return NETDEV_TX_OK;
 	}
 
@@ -1140,8 +1159,12 @@ static int hf_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 
 static void hf_tx_timeout(struct net_device *netdev)
 {
+	struct hf_net	*net = netdev_priv(netdev);
+	struct hf_if	*net_if = &(net->hfif);
+
 	netdev_warn(netdev, "hf_tx_timeout: queue_stopped is %d\n",
 			netif_queue_stopped(netdev));
+	net_if->eth_stats.tx_timeout++;
 }
 
 struct net_device_stats *hf_get_stats(struct net_device *netdev)
@@ -1238,6 +1261,7 @@ static int hf_poll(struct napi_struct *napi, int budget)
 	net_if	= &(net->hfif);
 	netdev	= net->netdev;
 
+	net_if->eth_stats.in_poll++;
 	work_done = hf_rx(net, budget);
 
 	/* Always assume we have received all available packets */
@@ -1246,7 +1270,8 @@ static int hf_poll(struct napi_struct *napi, int budget)
 		napi_complete(napi);
 		isync();
 		hf_set_recv_intr(net_if);
-	}
+	} else
+		net_if->eth_stats.max_poll_recv++;
 
 	return work_done;
 }
@@ -1279,6 +1304,7 @@ static struct hf_net *hf_init_netdev(int idx, int ai)
 	net->hfif.state = HF_NET_CLOSE;
 
 	spin_lock_init(&net->hfif.lock);
+	hf_set_ethtool_ops(netdev);
 
 	rc = register_netdev(netdev);
 	if (rc) {
diff --git a/drivers/net/hfi/ip/hf_proto.h b/drivers/net/hfi/ip/hf_proto.h
index b70ecdd..6f53959 100644
--- a/drivers/net/hfi/ip/hf_proto.h
+++ b/drivers/net/hfi/ip/hf_proto.h
@@ -36,6 +36,7 @@
 int hf_tx_check_avail(struct hf_net *net, u32 xmit_cls);
 void hf_construct_hwhdr(struct hf_if *net_if, struct sk_buff *skb,
 			struct base_hdr *b_hdr);
+void hf_set_ethtool_ops(struct net_device *netdev);
 extern int hfidd_open_window_func(struct hfidd_acs *p_acs,
 		u32 is_userspace,
 		struct hfi_client_info *user_p,
diff --git a/include/linux/hfi/hf_if.h b/include/linux/hfi/hf_if.h
index 39bcdae..85aa90f 100644
--- a/include/linux/hfi/hf_if.h
+++ b/include/linux/hfi/hf_if.h
@@ -38,9 +38,11 @@
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
 #include <net/arp.h>
 
 #include <linux/hfi/hfidd_internal.h>
+#include <linux/hfi/hfidd_adpt.h>
 #include <linux/hfi/hfidd_client.h>
 #include <linux/hfi/hfidd_requests.h>
 #include <linux/hfi/hfidd_regs.h>
@@ -157,6 +159,35 @@ struct hf_fifo {
 #define	HF_NET_HALF_OPEN	0xA0
 #define	HF_NET_OPEN		0xA1
 
+struct hf_ethtool_stats {
+	u64		sfifo_packets;	/* total packets send through sfifo */
+	u64		tx_timeout;
+	u64		tx_queue_stop;
+	u64		tx_drop;
+	u64		tx_err_headlen;
+	u64		rx_version_mismatch;
+	u64		rx_err_skb;
+	u64		rx_err_hdr_type;
+	u64		rx_err_msg_type;
+	u64		rx_err_status;
+	u64		rx_err_bcast_csum;
+	u64		rx_fslot_debt;
+	u64		mmio_rx_inc_avail;
+	u64		payload_sent;	/* packets from IP send with payload
+					   mode */
+	u64		payload_recv;	/* packets delivered to IP with payload
+					   mode */
+	u64		sfifo_send_intr_armed;
+	u64		recv_intr_armed;
+	u64		recv_intr_offset;
+	u64		recv_imm_intr_armed;
+	u64		recv_imm_intr_offset;
+	u64		send_intr_fired;
+	u64		recv_intr_fired;
+	u64		in_poll;
+	u64		max_poll_recv;
+};
+
 struct hf_if {
 	u32			idx;			/* 0, 1, 2, 3 ...   */
 	u32			ai;			/* 0=hfi0, 1=hfi1   */
@@ -180,6 +211,7 @@ struct hf_if {
 							   2k skb */
 	void			*sfifo_finishvec;
 	struct net_device_stats	net_stats;
+	struct hf_ethtool_stats eth_stats;
 };
 
 /* Private structure for HF inetrface */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 12/27] HFI:  Sanity check send and receive fifo parameters
From: dykmanj @ 2011-03-02 21:09 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/core/Makefile       |    1 +
 drivers/net/hfi/core/hfidd_proto.h  |    3 +
 drivers/net/hfi/core/hfidd_window.c |  177 +++++++++++++++++++++++++++++++++++
 drivers/net/hfi/core/hfidd_xlat.c   |  131 ++++++++++++++++++++++++++
 include/linux/hfi/hfidd_adpt.h      |   17 ++++
 5 files changed, 329 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/hfi/core/hfidd_xlat.c

diff --git a/drivers/net/hfi/core/Makefile b/drivers/net/hfi/core/Makefile
index 0224a57..8d5558d 100644
--- a/drivers/net/hfi/core/Makefile
+++ b/drivers/net/hfi/core/Makefile
@@ -4,5 +4,6 @@
 hfi_core-objs:=	hfidd_adpt.o \
 		hfidd_window.o \
 		hfidd_init.o \
+		hfidd_xlat.o \
 		hfidd_hcalls.o
 obj-$(CONFIG_HFI) += hfi_core.o
diff --git a/drivers/net/hfi/core/hfidd_proto.h b/drivers/net/hfi/core/hfidd_proto.h
index e7f2901..66ea5da 100644
--- a/drivers/net/hfi/core/hfidd_proto.h
+++ b/drivers/net/hfi/core/hfidd_proto.h
@@ -39,6 +39,9 @@ int hfidd_alloc_windows(struct hfidd_acs *p_acs);
 void hfidd_free_windows(struct hfidd_acs *p_acs);
 int hfidd_init_adapter(struct hfidd_acs *p_acs, void *uiop);
 int hfidd_age_hcall(u64 time_start);
+int hfidd_get_page_size(struct hfidd_acs *p_acs, void *addr,
+		unsigned int is_userspace, unsigned int length,
+		unsigned long long *page_size);
 int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
 		struct hfi_client_info *user_p,
 		struct hfi_client_info *out_p);
diff --git a/drivers/net/hfi/core/hfidd_window.c b/drivers/net/hfi/core/hfidd_window.c
index cc775e3..5a4f395 100644
--- a/drivers/net/hfi/core/hfidd_window.c
+++ b/drivers/net/hfi/core/hfidd_window.c
@@ -35,6 +35,153 @@
 #include "hfidd_proto.h"
 #include <linux/hfi/hfidd_requests.h>
 
+#define FINISH_VECTOR_LENGTH 1
+/* Validate send fifo parameters needed for open window */
+static int hfi_check_sfifo_parm(struct hfidd_acs *p_acs,
+		unsigned int is_userspace,
+		struct hfidd_window *win_p,
+		struct hfi_client_info *client_p)
+{
+	int			rc = 0;
+	unsigned long long	page_sz;
+	unsigned long long	fv_page_sz;
+
+	/* Validate the sfifo size */
+	if ((client_p->sfifo.size < HFI_SFIFO_SIZE_MIN) ||
+	    (client_p->sfifo.size > HFI_SFIFO_SIZE_MAX)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: invalid sfifo "
+			"size = 0x%llx\n",
+			client_p->sfifo.size);
+		return -EINVAL;
+	}
+
+	/*
+	 * Validate the address of sfifo is 4k aligned, and finish vector
+	 * is cache-line aligned
+	 */
+	if ((client_p->sfifo.eaddr.use.allu) & PAGE_MASK_4K) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: not page aligned, "
+			"sfifo_addr = 0x%llx\n",
+			client_p->sfifo.eaddr.use.allu);
+		return -EINVAL;
+	}
+
+	if ((client_p->sfifo_finish_vec.use.allu) & HFI_CACHE_LINE_MASK) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: not cache aligned, "
+			"sfifo_finishvec = 0x%llx\n",
+			client_p->sfifo_finish_vec.use.allu);
+		return -EINVAL;
+	}
+	/*
+	 * Validate the send finish vector are within 4K bytes of end of sfifo
+	 */
+	if (((client_p->sfifo_finish_vec.use.kptr -
+	     (client_p->sfifo.eaddr.use.kptr +
+	      client_p->sfifo.size)) >= PAGE_SIZE_4K)) {
+
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: fv too far away, "
+			"sfifo_addr = 0x%llx\n",
+			client_p->sfifo.eaddr.use.allu);
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: fv too far away, "
+			"sfifo_finishvec = 0x%llx\n",
+			client_p->sfifo_finish_vec.use.allu);
+		return -EINVAL;
+	}
+
+	/* Validate page size of sFifo */
+	rc = hfidd_get_page_size(p_acs, client_p->sfifo.eaddr.use.kptr,
+			is_userspace, client_p->sfifo.size, &page_sz);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: fail in sfifo page size, "
+			"rc=0x%x\n", rc);
+		return rc;
+	}
+
+	/* Find out the page size of send finish vector */
+	rc = hfidd_get_page_size(p_acs, client_p->sfifo_finish_vec.use.kptr,
+			is_userspace, FINISH_VECTOR_LENGTH, &fv_page_sz);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: fail in fv page size, "
+			"rc=0x%x\n", rc);
+		return rc;
+	}
+
+	/* The page size of finish vector must be the same as sfifo */
+	if (page_sz != fv_page_sz) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_sfifo_parm: diff page sz sf=0x%llx, "
+			"fv0=0x%llx\n", page_sz, fv_page_sz);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int hfi_check_rfifo_parm(struct hfidd_acs *p_acs,
+		unsigned int is_userspace,
+		struct hfidd_window *win_p,
+		struct hfi_client_info *client_p)
+{
+	int			rc = 0;
+	unsigned long long	page_sz;
+
+	/* Validate the rfifo size */
+	if ((client_p->rfifo.size < HFI_RFIFO_SIZE_MIN) ||
+	    (client_p->rfifo.size > HFI_RFIFO_SIZE_MAX)) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_rfifo_parm: invalid rfifo size = 0x%llx\n",
+			client_p->rfifo.size);
+		return -EINVAL;
+	}
+
+	/* Validate the address of rfifo is 4K aligned */
+	if ((client_p->rfifo.eaddr.use.allu) & PAGE_MASK_4K) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_rfifo_parm: not cache aligned, "
+			"rfifo_addr = 0x%llx\n",
+			client_p->rfifo.eaddr.use.allu);
+		return -EINVAL;
+	}
+
+	/* Validate page size of rFifo */
+	rc = hfidd_get_page_size(p_acs, client_p->rfifo.eaddr.use.kptr,
+			is_userspace, client_p->rfifo.size, &page_sz);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_check_rfifo_parm: fail in rfifo page size, "
+			"rc=0x%x\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+/* Validate window parameters to setup the fifos and RDMA function */
+static int hfi_validate_window_request(struct hfidd_acs *p_acs,
+	unsigned int is_userspace, struct hfi_client_info *client_p)
+{
+	int			rc = 0;
+	struct hfidd_window	*win_p;
+
+	/* Check every input parameters..... */
+	win_p = hfi_window(p_acs, client_p->window);
+
+	/* Check the request of sFifo */
+	rc = hfi_check_sfifo_parm(p_acs, is_userspace, win_p, client_p);
+	if (rc)
+		return rc;
+	/* Check the request of rFifo */
+	rc = hfi_check_rfifo_parm(p_acs, is_userspace, win_p, client_p);
+	if (rc)
+		return rc;
+	return rc;
+}
+
 /* Validate the type, state and job id for RESERVED window */
 static int hfi_validate_reserve_window_id(struct hfidd_acs *p_acs,
 		struct hfi_client_info *client_p)
@@ -164,12 +311,29 @@ static inline int hfi_validate_window_id(struct hfidd_acs *p_acs,
 	return rc;
 }
 
+static inline void hfi_restore_window_parm(struct hfidd_acs *p_acs,
+		struct hfidd_window *win_p)
+{
+	if (win_p->type != HFIDD_RESERVE_WIN) {
+		win_p->type   = HFIDD_DYNAMIC_WIN;
+		win_p->job_id = 0;
+		if (win_p->state != WIN_HERROR)
+			win_p->state = WIN_AVAILABLE;
+	} else {
+		if (win_p->state != WIN_HERROR)
+			win_p->state = WIN_RESERVED;
+	}
+	win_p->pid   = 0;
+	win_p->is_ip = 0;
+}
+
 /* Validate window number and type for open window request */
 static int hfi_validate_window_parm(struct hfidd_acs *p_acs,
 		unsigned int is_userspace,
 		struct hfi_client_info *client_p)
 {
 	int			rc = 0;
+	struct hfidd_window	*win_p;
 
 	/* Validate the window number */
 	rc = hfi_validate_window_id(p_acs, client_p, is_userspace);
@@ -179,6 +343,19 @@ static int hfi_validate_window_parm(struct hfidd_acs *p_acs,
 			"failed, rc = 0x%x\n", rc);
 		return rc;
 	}
+
+	rc = hfi_validate_window_request(p_acs, is_userspace, client_p);
+	if (rc) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfi_validate_window_parm: "
+			"hfi_validate_window_request failed, rc = 0x%x\n", rc);
+		win_p = hfi_window(p_acs, client_p->window);
+		spin_lock(&(win_p->win_lock));
+		hfi_restore_window_parm(p_acs, win_p);
+		spin_unlock(&(win_p->win_lock));
+		return rc;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/hfi/core/hfidd_xlat.c b/drivers/net/hfi/core/hfidd_xlat.c
new file mode 100644
index 0000000..23236cc
--- /dev/null
+++ b/drivers/net/hfi/core/hfidd_xlat.c
@@ -0,0 +1,131 @@
+/*
+ * hfidd_xlat.c
+ *
+ * HFI device driver for IBM System p
+ *
+ *  Authors:
+ *      Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ *      William S. Cadden <wscadden@linux.vnet.ibm.com>
+ *      Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ *      Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ *      Jian Xiao <jian@linux.vnet.ibm.com>
+ *      Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ *      Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ *  (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/pagemap.h>
+#include <linux/hugetlb.h>
+#include <asm/page.h>
+
+#include <linux/hfi/hfidd_internal.h>
+#include "hfidd_proto.h"
+
+int hfidd_get_page_size(struct hfidd_acs *p_acs, void *addr,
+	unsigned int is_userspace,
+	unsigned int length,
+	unsigned long long *page_size)
+{
+	int			rc = 0;
+	int			i;
+	int			num_pages;
+	struct page		**page_list;
+	struct vm_area_struct	**vma_list;
+	unsigned long long	offset;
+
+	if (!is_userspace) {
+		*page_size = PAGE_SIZE;
+		return 0;
+	}
+
+	offset = (unsigned long long)addr & ~PAGE_MASK;
+	num_pages = PAGE_ALIGN(length + offset) >> PAGE_SHIFT;
+
+	page_list = kzalloc(num_pages * sizeof(struct page *),
+			GFP_KERNEL);
+	if (page_list == NULL) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_get_page_size: kzalloc failed for page_list\n");
+		return -ENOMEM;
+	}
+
+	vma_list = kzalloc(num_pages * sizeof(struct vm_area_struct **),
+			GFP_KERNEL);
+	if (vma_list == NULL) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_get_page_size: kzalloc failed for vma_list\n");
+		rc = -ENOMEM;
+		goto out1;
+	}
+
+	down_read(&current->mm->mmap_sem);
+	rc = get_user_pages(current, current->mm,
+			(unsigned long long)addr,
+			num_pages, 1, 0,	/* yes write, no force */
+			page_list, vma_list);
+	up_read(&current->mm->mmap_sem);
+
+	if (rc < num_pages) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_get_page_size: get_user_pages failed rc = %d "
+			"and numpages %d\n", rc, num_pages);
+		if (rc < 0)
+			goto out2;
+		num_pages = rc;
+		rc = -ENOMEM;
+		goto out3;
+	}
+
+	rc = 0;
+	*page_size = PAGE_SIZE;
+	for (i = 0; i < num_pages; i++) {
+		/* check for huge pages */
+		if (is_vm_hugetlb_page(vma_list[i])) {
+			/* Find huge page size */
+			*page_size = huge_page_size(hstate_vma(vma_list[i]));
+			break;
+		}
+	}
+
+	/* If memory has huge page size, check if all pages are huge pages */
+	if (*page_size != PAGE_SIZE) {
+		for (i = 0; i < num_pages; i++) {
+			/* if not huge page, set to PAGE_SIZE */
+			if (!is_vm_hugetlb_page(vma_list[i])) {
+				*page_size = PAGE_SIZE;
+				break;
+			}
+		}
+	}
+	if (*page_size == PAGE_SIZE_16G) {
+		dev_printk(KERN_ERR, p_acs->hfidd_dev,
+			"hfidd_get_page_size: Large page size "
+			"0x%llx use 4G\n", *page_size);
+		*page_size = PAGE_SIZE_4G;
+	}
+
+out3:
+	for (i = 0; i < num_pages; i++)
+		page_cache_release(page_list[i]);
+out2:
+	kfree(vma_list);
+out1:
+	kfree(page_list);
+	return rc;
+}
diff --git a/include/linux/hfi/hfidd_adpt.h b/include/linux/hfi/hfidd_adpt.h
index babdb14..a41825f 100644
--- a/include/linux/hfi/hfidd_adpt.h
+++ b/include/linux/hfi/hfidd_adpt.h
@@ -36,6 +36,16 @@
 #include <linux/hfi/hfidd_client.h>
 
 
+#define HFI_SFIFO_SIZE_MIN	0x10000		/* min =  64K software limit */
+#define HFI_SFIFO_SIZE_MAX	0x800000	/* max =  8M */
+
+#define HFI_RFIFO_SIZE_MIN	0x1000		/* min =  4K */
+#define HFI_RFIFO_SIZE_MAX	0x8000000	/* max =  128M */
+
+#define HFI_CACHE_LINE_SIZE	0x80
+#define HFI_CACHE_LINE_MASK	(HFI_CACHE_LINE_SIZE - 1)
+#define HFI_CACHE_LINE_SHIFT	7
+
 #define HFI_WNUM_SHIFT		32
 #define HFI_CAUNUM_SHIFT	32
 #define HFI_SHIFT_OCTANT	3
@@ -57,4 +67,11 @@
 #define WIN_PENDING		6
 #define WIN_FAIL_CLOSE		7
 
+#define PAGE_MASK_4K		(PAGE_SIZE_4K - 1)
+#define PAGE_MASK_64K		(PAGE_SIZE_64K - 1)
+#define PAGE_MASK_1M		(PAGE_SIZE_1M - 1)
+#define PAGE_MASK_16M		(PAGE_SIZE_16M - 1)
+#define PAGE_MASK_4G		(PAGE_SIZE_4G - 1)
+#define PAGE_MASK_16G		(PAGE_SIZE_16G - 1)
+
 #endif /* _HFIDD_ADPT_H_ */
-- 
1.7.3.1


^ permalink raw reply related

* [PATCH 26/27] HFI: hf fifo receive path
From: dykmanj @ 2011-03-02 21:10 UTC (permalink / raw)
  To: netdev, netdev
  Cc: Jim Dykman, Piyush Chaudhary, Fu-Chung Chang,  William S. Cadden,
	 Wen C. Chen, Scot Sakolish, Jian Xiao,  Carol L. Soto,
	 Sarah J. Sheppard
In-Reply-To: <1299100213-8770-1-git-send-email-dykmanj@linux.vnet.ibm.com>

From: Jim Dykman <dykmanj@linux.vnet.ibm.com>

Signed-off-by:  Piyush Chaudhary <piyushc@linux.vnet.ibm.com>
Signed-off-by:  Jim Dykman <dykmanj@linux.vnet.ibm.com>
Signed-off-by:  Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
Signed-off-by:  William S. Cadden <wscadden@linux.vnet.ibm.com>
Signed-off-by:  Wen C. Chen <winstonc@linux.vnet.ibm.com>
Signed-off-by:  Scot Sakolish <sakolish@linux.vnet.ibm.com>
Signed-off-by:  Jian Xiao <jian@linux.vnet.ibm.com>
Signed-off-by:  Carol L. Soto <clsoto@linux.vnet.ibm.com>
Signed-off-by:  Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
---
 drivers/net/hfi/ip/hf_if_main.c |  328 ++++++++++++++++++++++++++++++++++++++-
 drivers/net/hfi/ip/hf_proto.h   |    2 +
 include/linux/hfi/hf_if.h       |   25 +++-
 3 files changed, 352 insertions(+), 3 deletions(-)

diff --git a/drivers/net/hfi/ip/hf_if_main.c b/drivers/net/hfi/ip/hf_if_main.c
index ea4579e..10dc1da 100644
--- a/drivers/net/hfi/ip/hf_if_main.c
+++ b/drivers/net/hfi/ip/hf_if_main.c
@@ -177,6 +177,9 @@ static int hf_alloc_rx_resource(struct hf_if *net_if)
 
 	memset(net_if->rx_fifo.addr, 0, net_if->rx_fifo.size);
 
+	net_if->rx_fslot_debt = 0;
+	net_if->rx_pkt_valid = 1;
+
 	return 0;
 }
 
@@ -232,8 +235,18 @@ static int hf_send_intr_callback(void *parm, u32 win, u32 ext)
 	return 0;
 }
 
+static int hf_recv_intr_callback(void *parm, u32 win, u32 ext)
+{
+	struct hf_net	*net = (struct hf_net *)parm;
+
+	napi_schedule(&(net->napi));
+
+	return 0;
+}
+
 struct hf_events_cb hf_events[HF_EVENT_NUM] = {
 	{HFIDD_SEND,		(void *)hf_send_intr_callback},
+	{HFIDD_RECV,		(void *)hf_recv_intr_callback},
 };
 
 static int hf_register_ip_events(struct hf_net *net,
@@ -376,14 +389,50 @@ static int hf_set_mac_addr(struct net_device *netdev, void *p)
 	return 0;
 }
 
+static void hf_set_recv_intr(struct hf_if *net_if)
+{
+	int			offset;
+	struct hfi_hdr		*rx_pkt;
+
+	/* enable recv intr and set threshold to next packet */
+	offset = net_if->rx_fifo.head;
+
+	hf_mmio_regs_write_then_read(net_if, HFI_RFIFO_INTR_REG,
+		(HF_ENA_RECV_INTR + (offset << HF_RECV_INTR_MATCH_SHIFT)));
+
+	/* check if there is packet received in the mean time */
+	rx_pkt = net_if->rx_fifo.addr + (offset << HFI_CACHE_LINE_SHIFT);
+
+	if ((rx_pkt->id.job_id == HF_IP_JOBID) &&
+		(rx_pkt->base_hdr.pkt_valid == net_if->rx_pkt_valid)) {
+
+		/* force an immediate recv intr */
+		hf_mmio_regs_write(net_if, HFI_RFIFO_INTR_REG,
+		(HF_IMM_RECV_INTR + (offset << HF_RECV_INTR_MATCH_SHIFT)));
+	}
+}
+
 static void hf_init_hw_regs(struct hf_if *net_if)
 {
 	/* setup IP with payload threshold in cache line size */
 	hf_mmio_regs_write(net_if, HFI_IP_RECV_SIZE,
 		(HF_PAYLOAD_RX_THRESHOLD << HF_PAYLOAD_RX_THRESH_SHIFT));
 
+	/* setup recv fifo out of order intr control to disable */
+	hf_mmio_regs_write(net_if, HFI_RFIFO_OUT_EVENT_REG,
+			HF_RFIFO_OUT_CNTL_REARM);
+
+	/* setup recv fifo out of order threshold */
+	hf_mmio_regs_write(net_if, HFI_RFIFO_OUT_TH_REG, HF_RFIFO_OUT_THRESH);
+
 	/* initialize SEND INTR STATUS */
 	hf_mmio_regs_write(net_if, HFI_SINTR_STATUS_REG, 0);
+
+	hf_mmio_regs_write(net_if, HFI_RFIFO_INJ_TH_REG,
+			(HF_RFIFO_CACHE_INJ_TH << HF_RFIFO_CACHE_INJ_TH_SHIFT));
+
+	/* enable and set receive intr */
+	hf_set_recv_intr(net_if);
 }
 
 static int hf_net_delayed_open(void *parm, u16 win, u16 ext)
@@ -422,6 +471,7 @@ static int hf_net_delayed_open(void *parm, u16 win, u16 ext)
 	net_if->state = HF_NET_OPEN;
 	spin_unlock(&(net_if->lock));
 
+	napi_enable(&net->napi);
 	netif_carrier_on(netdev);
 	netif_start_queue(netdev);
 
@@ -511,6 +561,7 @@ static int hf_net_close(struct net_device *netdev)
 
 	spin_lock(&(net_if->lock));
 	if (net_if->state == HF_NET_OPEN) {
+		napi_disable(&net->napi);
 		netif_stop_queue(netdev);
 		netif_carrier_off(netdev);
 
@@ -530,6 +581,247 @@ static int hf_net_close(struct net_device *netdev)
 	return 0;
 }
 
+/* Invalidate the jobid field of each cache line before advancing head.
+ * The first cache line is protected by the valid bit, so we skip it. */
+static inline void hf_advance_rx_head(struct hf_if *net_if, u32 len)
+{
+	int		i, h;
+	u32		*cache_p;
+
+	h = (net_if->rx_fifo.head + 1) & (net_if->rx_fifo.emax);
+
+	for (i = 1; i < len; i++) {
+		cache_p = (u32 *)((char *)(net_if->rx_fifo.addr) +
+				(h << HFI_CACHE_LINE_SHIFT));
+		if (*cache_p == HF_IP_JOBID)
+			*cache_p = 0;
+		h = (h + 1) & (net_if->rx_fifo.emax);
+	}
+
+	if (net_if->rx_fifo.head > h)
+		net_if->rx_pkt_valid ^= 0x1;
+
+	net_if->rx_fifo.head = h;
+}
+
+void hf_construct_hwhdr(struct hf_if *net_if,
+			struct sk_buff *skb,
+			struct base_hdr *b_hdr)
+{
+	struct hf_hwhdr		*hwhdr_p;
+
+	hwhdr_p = (struct hf_hwhdr *)(skb->data);
+
+	/* MAC byte 1, bits6 = 1, locally admin MAC */
+	hwhdr_p->h_dest[0] = 0x2;
+	/* MAC byte 2, bits2-7 = cluster id */
+	hwhdr_p->h_dest[1] = 0x0;
+	*(u16 *)(&(hwhdr_p->h_dest[2])) = (u16)(b_hdr->dst_isr);
+	*(u16 *)(&(hwhdr_p->h_dest[4])) =
+			(u16)hf_get_mac(b_hdr->dst_win);
+
+	hwhdr_p->h_source[0] = 0x2;
+	hwhdr_p->h_source[1] = 0x0;
+	*(u16 *)(&(hwhdr_p->h_source[2])) = (u16)(b_hdr->src_isr);
+	*(u16 *)(&(hwhdr_p->h_source[4])) =
+				(u16)hf_get_mac(b_hdr->src_win);
+
+	hwhdr_p->h_proto = skb->protocol;
+}
+
+static inline int hf_check_hdr_version(struct hf_if *net_if,
+				struct hf_if_proto_hdr *hf_hdr)
+{
+	if (hf_hdr->version != HF_PROTO_HDR_VERSION) {
+		printk(KERN_ERR
+			"%s: hf_check_hdr_version: hdr version 0x%x "
+			"does not match 0x%x\n",
+			net_if->name, hf_hdr->version, HF_PROTO_HDR_VERSION);
+		net_if->net_stats.rx_dropped++;
+
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void hf_recv_ip_with_payload(struct hf_net *net,
+				    struct hfi_ip_with_payload_pkt *pkt,
+				    u32 pkt_len)
+{
+	u32			len, resid;
+	struct hf_if		*net_if = &(net->hfif);
+	struct hf_if_proto_hdr	*hf_hdr;
+	struct sk_buff		*skb;
+	void			*src, *dst;
+	u32			cache_ln_num = 0;
+	u16			proto;
+
+	/* retrieve the protocol header pointer */
+	hf_hdr = (struct hf_if_proto_hdr *)(pkt->payload);
+
+	if (hf_check_hdr_version(net_if, hf_hdr) != 0)
+		return;
+
+	switch (hf_hdr->msg_type) {
+	case HF_IF_ARP:
+		proto = htons(ETH_P_ARP);
+		break;
+
+	case HF_IF_FIFO:
+		proto = htons(ETH_P_IP);
+		break;
+
+	default:
+		netdev_err(net->netdev,
+			"hf_recv_ip_with_payload: unknown msg_type 0x%x\n",
+			hf_hdr->msg_type);
+		net_if->net_stats.rx_dropped++;
+		return;
+	}
+
+	len = hf_hdr->msg_len - HF_PROTO_LEN;
+
+	skb = netdev_alloc_skb_ip_align(net->netdev,
+				len + HF_HLEN + HF_ALIGN_PAD);
+	if (!skb) {
+		netdev_err(net->netdev, "hf_recv_ip_with_payload: "
+				"netdev_alloc_skb_ip_align fail\n");
+		net_if->net_stats.rx_dropped++;
+		BUG();
+		return;
+	}
+
+	skb_reserve(skb, HF_ALIGN_PAD);
+	skb->protocol = proto;
+
+	skb_put(skb, len + HF_HLEN);
+
+	/* construct hf_hwhdr from base hdr */
+	hf_construct_hwhdr(net_if, skb, &(pkt->hfi_hdr.base_hdr));
+
+	skb_reset_mac_header(skb);
+
+	skb_pull(skb, HF_HLEN);
+
+	src = (void *)(hf_hdr + 1);
+	dst = (void *)skb->data;
+
+	/* check if the payload wrapped the rx_fifo */
+	if ((net_if->rx_fifo.head + (pkt_len - 1)) > net_if->rx_fifo.emax) {
+		/* Wrapped */
+		cache_ln_num = net_if->rx_fifo.emax - net_if->rx_fifo.head + 1;
+		resid  = cache_ln_num << HFI_CACHE_LINE_SHIFT;
+		resid -= (HF_IP_HDR_LEN + HF_PROTO_LEN);
+
+		/* For netboot, pkt_len maybe larger than len */
+		if (resid > len)
+			resid = len;
+
+		memcpy(dst, src, resid);
+
+		src = (void *)net_if->rx_fifo.addr;
+		dst = (void *)skb->data + resid;
+		len -= resid;
+	}
+
+	/* copy the rest of payload */
+	if (len > 0)
+		memcpy(dst, src, len);
+
+	skb->ip_summed = CHECKSUM_NONE;
+
+	net_if->net_stats.rx_packets++;
+	net_if->net_stats.rx_bytes += skb->len;
+
+	netif_receive_skb(skb);
+}
+
+static void hf_recv_ip_good(struct hf_net *net,
+			    struct hfi_hdr *rx_curr,
+			    u32 pkt_len)
+{
+	struct hf_if	*net_if = &(net->hfif);
+
+	switch (rx_curr->type.header_type) {
+
+	case  HFI_IP_WITH_PAYLOAD:
+	case  HFI_IP_MULTICAST_WITH_PAYLOAD:
+		hf_recv_ip_with_payload(net,
+			(struct hfi_ip_with_payload_pkt *)rx_curr, pkt_len);
+		break;
+
+	default:
+		netdev_err(net->netdev, "hf_rx: receive unknown "
+			"headerType = 0x%x, pkt_len = 0x%x\n",
+			rx_curr->type.header_type, pkt_len);
+
+		/* unknown packet, drop it */
+		net_if->net_stats.rx_dropped++;
+		break;
+	}
+}
+
+static int hf_rx(struct hf_net *net, int budget)
+{
+	int		num = 0;
+	struct hf_if	*net_if = &(net->hfif);
+	u32		pkt_len, status;
+	struct hfi_hdr	*rx_curr;
+	u32		job_id, pkt_valid;
+
+	rx_curr = (struct hfi_hdr *) (net_if->rx_fifo.addr +
+			(net_if->rx_fifo.head << HFI_CACHE_LINE_SHIFT));
+
+	while (budget != 0) {
+		job_id = rx_curr->id.job_id;
+		pkt_valid = rx_curr->base_hdr.pkt_valid;
+
+		isync();
+		if ((job_id != HF_IP_JOBID) ||
+		    (pkt_valid != net_if->rx_pkt_valid))
+			break;
+
+		pkt_len = hfi_pktlen_to_cachelines(rx_curr->base_hdr.pkt_len);
+
+		status = rx_curr->base_hdr.status;
+		if (status == HFI_PKT_STATUS_GOOD) {
+			hf_recv_ip_good(net, rx_curr, pkt_len);
+		} else {
+			/* bad packet */
+			netdev_err(net->netdev, "hf_rx: receive bad "
+				"status = 0x%x, pkt_len = 0x%x\n",
+				status, pkt_len);
+
+			net_if->net_stats.rx_dropped++;
+		}
+
+		net->netdev->last_rx = jiffies;
+
+		hf_advance_rx_head(net_if, pkt_len);
+
+		/* Make sure the jobid is invalidated before posting to hw */
+		wmb();
+
+		net_if->rx_fslot_debt += pkt_len;
+		if (net_if->rx_fslot_debt >= HF_INC_FSLOT_WATERMARK) {
+			hf_mmio_regs_write(net_if, HFI_RFIFO_INC_FSLOT_REG,
+					net_if->rx_fslot_debt);
+			net_if->rx_fslot_debt = 0;
+		}
+
+		budget--;
+		num++;
+		rx_curr = net_if->rx_fifo.addr +
+			(net_if->rx_fifo.head << HFI_CACHE_LINE_SHIFT);
+
+	}
+
+	netdev_dbg(net->netdev, "hf_rx: exit, head = 0x%x, recv 0x%x pkts\n",
+			net_if->rx_fifo.head, num);
+
+	return num;
+}
+
 static void hf_tx_recycle(struct hf_if *net_if)
 {
 	u32		head, head_idx, slots_per_blk;
@@ -935,6 +1227,30 @@ static void hf_if_setup(struct net_device *netdev)
 	memcpy(netdev->broadcast, hfi_bcast_addr, HF_ALEN);
 }
 
+static int hf_poll(struct napi_struct *napi, int budget)
+{
+	int			work_done;
+	struct net_device	*netdev;
+	struct hf_net		*net;
+	struct hf_if		*net_if;
+
+	net	= container_of(napi, struct hf_net, napi);
+	net_if	= &(net->hfif);
+	netdev	= net->netdev;
+
+	work_done = hf_rx(net, budget);
+
+	/* Always assume we have received all available packets */
+	/*  and set recv intr for next packet */
+	if (work_done < budget) {
+		napi_complete(napi);
+		isync();
+		hf_set_recv_intr(net_if);
+	}
+
+	return work_done;
+}
+
 static struct hf_net *hf_init_netdev(int idx, int ai)
 {
 	struct net_device	*netdev;
@@ -953,6 +1269,7 @@ static struct hf_net *hf_init_netdev(int idx, int ai)
 	}
 
 	net = netdev_priv(netdev);
+	netif_napi_add(netdev, &(net->napi), hf_poll, HF_NAPI_WEIGHT);
 	net->netdev = netdev;
 
 	memset(&(net->hfif), 0, sizeof(struct hf_if));
@@ -968,11 +1285,16 @@ static struct hf_net *hf_init_netdev(int idx, int ai)
 		printk(KERN_ERR "hf_init_netdev: "
 				"failed to register netdev=hfi%d:hf%d, "
 				"rc = 0x%x\n", ai, idx, rc);
-		free_netdev(netdev);
-		return (struct hf_net *) -ENODEV;
+		goto err_out1;
 	}
 
 	return net;
+
+err_out1:
+	netif_napi_del(&(net->napi));
+	free_netdev(netdev);
+
+	return (struct hf_net *) -ENODEV;
 }
 
 static void hf_del_netdev(struct hf_net *net)
@@ -981,6 +1303,8 @@ static void hf_del_netdev(struct hf_net *net)
 
 	unregister_netdev(netdev);
 
+	netif_napi_del(&(net->napi));
+
 	free_netdev(netdev);
 }
 
diff --git a/drivers/net/hfi/ip/hf_proto.h b/drivers/net/hfi/ip/hf_proto.h
index b7c6d3c..b70ecdd 100644
--- a/drivers/net/hfi/ip/hf_proto.h
+++ b/drivers/net/hfi/ip/hf_proto.h
@@ -34,6 +34,8 @@
 #define _HF_PROTO_H_
 
 int hf_tx_check_avail(struct hf_net *net, u32 xmit_cls);
+void hf_construct_hwhdr(struct hf_if *net_if, struct sk_buff *skb,
+			struct base_hdr *b_hdr);
 extern int hfidd_open_window_func(struct hfidd_acs *p_acs,
 		u32 is_userspace,
 		struct hfi_client_info *user_p,
diff --git a/include/linux/hfi/hf_if.h b/include/linux/hfi/hf_if.h
index 90b6331..39bcdae 100644
--- a/include/linux/hfi/hf_if.h
+++ b/include/linux/hfi/hf_if.h
@@ -53,8 +53,15 @@
 #define MAX_HF_PER_HFI			2
 #define	HF_IP_JOBID			0xFFFFFFF0
 #define HF_TX_TIMEOUT			(500 * HZ)
+#define HF_NAPI_WEIGHT			256
 #define HF_MAX_NAME_LEN			64
 
+/* rfifo intr */
+#define HF_RFIFO_OUT_CNTL_REARM		0	/* 0 to disable interrupt */
+#define HF_IMM_RECV_INTR		0xf0000000	/* bit 32-35 on */
+#define HF_ENA_RECV_INTR		0xc0000000	/* bit 32-33 on */
+#define HF_RECV_INTR_MATCH_SHIFT	7	/* bit 37-56 */
+
 /* sfifo intr: bit 39-55 is threshold */
 /*             bit 34 enable, bit 35 unmask */
 #define HF_SFIFO_INTR_ENABLE		(0x3 << (63 - 35))
@@ -73,11 +80,17 @@
 #define HF_FV_BIT_MAX			31
 #define HF_SEND_ONE			1
 
+#define HF_RFIFO_CACHE_INJ_TH		7ULL
+#define HF_RFIFO_CACHE_INJ_TH_SHIFT	61
+#define HF_RFIFO_OUT_THRESH		0
+
 #define HF_PAYLOAD_MAX			(2048 - HF_IP_HDR_LEN - HF_PROTO_LEN)
 #define HF_NET_MTU			HF_PAYLOAD_MAX
 #define HF_PAYLOAD_RX_THRESHOLD		0x10ULL
 #define HF_PAYLOAD_RX_THRESH_SHIFT	59
 
+#define HF_INC_FSLOT_WATERMARK		(HF_RFIFO_SLOTS >> 3)
+
 struct hfi_ip_extended_hdr {            /* 16B */
 	u32		immediate_len:7;/* In bytes */
 	u32		num_desc:3;     /* number of descriptors */
@@ -98,6 +111,7 @@ struct hfi_ip_with_payload_pkt {
 
 #define HF_IP_HDR_LEN			((sizeof(struct hfi_hdr) + \
 				sizeof(struct hfi_ip_extended_hdr)))
+
 #define HF_ALEN				6
 struct hf_hwhdr {
 	u8				h_dest[HF_ALEN];
@@ -154,7 +168,10 @@ struct hf_if {
 	u32			sfifo_fv_polarity;
 	u32			sfifo_slots_per_blk;
 	u32			sfifo_packets;
+	u32			rx_pkt_valid;		/* Polarity of recv
+							   packet valid bit */
 	u32			msg_id;
+	u32			rx_fslot_debt;
 	void __iomem		*doorbell;		/* mapped mmio_regs */
 	struct hf_fifo		tx_fifo;
 	struct hf_fifo		rx_fifo;
@@ -168,6 +185,7 @@ struct hf_if {
 /* Private structure for HF inetrface */
 struct hf_net {
 	struct net_device	*netdev;
+	struct napi_struct	napi;
 	struct hf_if		hfif;
 };
 
@@ -181,7 +199,7 @@ struct hf_global_info {
 
 extern struct hf_global_info	hf_ginfo;
 
-#define HF_EVENT_NUM		1
+#define HF_EVENT_NUM		2
 
 struct hf_events_cb {
 	enum hfi_event_type	type;
@@ -191,6 +209,11 @@ struct hf_events_cb {
 #define HF_MAC_HFI_SHIFT	12
 #define HF_HDR_HFI_SHIFT	8
 
+static inline u32 hf_get_mac(u32 w)
+{
+	return ((w >> HF_HDR_HFI_SHIFT) << HF_MAC_HFI_SHIFT) | (w & 0xFF);
+}
+
 static inline u32 hf_get_win(u16 id)
 {
 	return ((id >> HF_MAC_HFI_SHIFT) << HF_HDR_HFI_SHIFT) | (id & 0xFF);
-- 
1.7.3.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox