* Re: [Bugme-new] [Bug 9888] New: tun device without protocol info header fails under IPv6
From: Max Krasnyansky @ 2008-02-08 4:58 UTC (permalink / raw)
To: Andrew Morton; +Cc: steve.zabele, bugme-daemon, netdev
In-Reply-To: <20080204145304.f4921bf4.akpm@linux-foundation.org>
Andrew Morton wrote:
> On Mon, 4 Feb 2008 13:46:13 -0800 (PST)
> bugme-daemon@bugzilla.kernel.org wrote:
>>
>> Open a tun device as type TUN, set the TUN_NO_PI flag, and try sending an IPv6
>> packet. The packet appears at the interface under tcpdumps, but propagates no
>> further. This is because the default protocol info used for tun devices where
>> the TUN_NO_PI flag is set assumes IPv4 as can be seen by the initialization at
>> the top of the tun_get_user function in drivers/net/tun.c file given by
>>
>> struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
>>
>> This can easily be fixed by adding a quick check at the top of tun_get_user.
>> Basically the code that used to read
>>
>> if (!(tun->flags & TUN_NO_PI)) {
>> if ((len -= sizeof(pi)) > count)
>> return -EINVAL;
>>
>> if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>> return -EFAULT;
>> }
>>
>> when changed to read
>>
>> if (!(tun->flags & TUN_NO_PI)) {
>> if ((len -= sizeof(pi)) > count)
>> return -EINVAL;
>>
>> if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi)))
>> return -EFAULT;
>> }
>> else {
>> /* Fixup default pi if IPv6 rather than IPv4 */
>> if (((tun->flags & TUN_TYPE_MASK) == TUN_TUN_DEV) &&
>> (*(char *)(iv->iov_base) == 0x60)) {
>> pi.proto = __constant_htons(ETH_P_IPV6);
>> }
>> }
>>
>> fixes the problem.
>>
>> How do we get this in as part of the maintained codebase??
>>
>
> Please email a tested patch prepared as described in
>
> Documentation/SubmittingPatches
> Documentation/SubmitChecklist
> http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
>
> to
>
> Maxim Krasnyansky <maxk@qualcomm.com>
> "David S. Miller" <davem@davemloft.net>
> Andrew Morton <akpm@linux-foundation.org>
> netdev@vger.kernel.org
btw I'd be ok with this fix. But I guess the questions is why not use
struct tun_pi in the apps instead ?
Max
^ permalink raw reply
* Re: [PATCH] Add IPv6 support to TCP SYN cookies
From: Glenn Griffin @ 2008-02-08 5:32 UTC (permalink / raw)
To: Eric Dumazet
Cc: Evgeniy Polyakov, Glenn Griffin, Alan Cox, Andi Kleen, netdev,
linux-kernel
In-Reply-To: <47AAD203.4030706@cosmosbay.com>
> Or maybe use percpu storage for that...
That seems like a good approach. I'll incorporate it into my v6 patch,
and send out an update. Thanks.
> I am not sure if cookie_hash() is always called with preemption disabled.
> (If not, we have to use get_cpu_var()/put_cpu_var())
cookie_hash is always called within NET_RX_SOFTIRQ context so I believe
preemption will always be disabled by __do_softirq(). So there
shouldn't be a need to use get_cpu_var/put_cpu_var, somebody correct me
if I'm wrong.
--Glenn
^ permalink raw reply
* Re: [PATCH 3/3] Interface to query tun/tap features.
From: Max Krasnyansky @ 2008-02-08 5:07 UTC (permalink / raw)
To: Rusty Russell; +Cc: netdev, Herbert Xu, virtualization
In-Reply-To: <200801240114.18571.rusty@rustcorp.com.au>
Hi Rusty,
Sorry for delay in reply. I totally missed this one. Need to fix my mail filters.
See below.
Rusty Russell wrote:
> (No real change, just updated with new bits)
>
> The problem with introducing IFF_RECV_CSUM and IFF_RECV_GSO is that
> they need to set dev->features to enable GSO and/or checksumming,
> which is supposed to be done before register_netdevice(), ie. as part
> of TUNSETIFF.
>
> Unfortunately, TUNSETIFF has always just ignored flags it doesn't understand,
> so there's no good way of detecting whether the kernel supports IFF_GSO_HDR.
>
> This patch implements a TUNGETFEATURES ioctl which returns all the valid IFF
> flags. It could be extended later to include other features.
>
> Here's an example program which uses it:
>
> #include <linux/if_tun.h>
> #include <sys/types.h>
> #include <sys/ioctl.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <err.h>
> #include <stdio.h>
>
> static struct {
> unsigned int flag;
> const char *name;
> } known_flags[] = {
> { IFF_TUN, "TUN" },
> { IFF_TAP, "TAP" },
> { IFF_NO_PI, "NO_PI" },
> { IFF_ONE_QUEUE, "ONE_QUEUE" },
> { IFF_VIRTIO_HDR, "VIRTIO_HDR" },
> { IFF_RECV_CSUM, "RECV_CSUM" },
> { IFF_RECV_GSO, "RECV_GSO" },
> };
>
> int main()
> {
> unsigned int features, i;
>
> int netfd = open("/dev/net/tun", O_RDWR);
> if (netfd < 0)
> err(1, "Opening /dev/net/tun");
>
> if (ioctl(netfd, TUNGETFEATURES, &features) != 0) {
> printf("Kernel does not support TUNGETFEATURES, guessing\n");
> features = (IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE);
> }
> printf("Available features are: ");
> for (i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) {
> if (features & known_flags[i].flag) {
> features &= ~known_flags[i].flag;
> printf("%s ", known_flags[i].name);
> }
> }
> if (features)
> printf("(UNKNOWN %#x)", features);
> printf("\n");
> return 0;
> }
>
> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
> ---
> drivers/net/tun.c | 9 +++++++++
> include/linux/if_tun.h | 3 +++
> 2 files changed, 12 insertions(+)
>
> diff -r c0e7a8b99325 drivers/net/tun.c
> --- a/drivers/net/tun.c Wed Jan 23 20:12:51 2008 +1100
> +++ b/drivers/net/tun.c Wed Jan 23 20:17:28 2008 +1100
> @@ -790,6 +790,15 @@ static int tun_chr_ioctl(struct inode *i
> return 0;
> }
>
> + if (cmd == TUNGETFEATURES) {
> + /* Currently this just means: "what IFF flags are valid?".
> + * This is needed because we never checked for invalid flags on
> + * TUNSETIFF. This was introduced with IFF_GSO_HDR, so if a
> + * kernel doesn't have this ioctl, it doesn't have GSO header
> + * support. */
> + return put_user(IFF_ALL_FLAGS, (unsigned int __user*)argp);
> + }
> +
> if (!tun)
> return -EBADFD;
>
> diff -r c0e7a8b99325 include/linux/if_tun.h
> --- a/include/linux/if_tun.h Wed Jan 23 20:12:51 2008 +1100
> +++ b/include/linux/if_tun.h Wed Jan 23 20:17:28 2008 +1100
> @@ -82,6 +82,7 @@ struct tun_struct {
> #define TUNSETOWNER _IOW('T', 204, int)
> #define TUNSETLINK _IOW('T', 205, int)
> #define TUNSETGROUP _IOW('T', 206, int)
> +#define TUNGETFEATURES _IOR('T', 207, unsigned int)
>
> /* TUNSETIFF ifr flags */
> #define IFF_TUN 0x0001
> @@ -91,6 +92,8 @@ struct tun_struct {
> #define IFF_VIRTIO_HDR 0x4000
> #define IFF_RECV_CSUM 0x8000
> #define IFF_RECV_GSO 0x0800
> +#define IFF_ALL_FLAGS (IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | \
> + IFF_VIRTIO_HDR | IFF_RECV_CSUM | IFF_RECV_GSO)
>
> struct tun_pi {
> unsigned short flags;
Definitely Ack this one. Query interface makes perfect sense.
I'll reply to the GSO itself shortly.
I was going to create git tree for tun changes for awhile now. Looks like the time has come.
I'll do that asap.
Thanx
Max
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: Paul Moore @ 2008-02-08 2:21 UTC (permalink / raw)
To: David Miller; +Cc: akpm, casey, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <20080207.181519.209912794.davem@davemloft.net>
On Thursday 07 February 2008 9:15:19 pm David Miller wrote:
> From: Paul Moore <paul.moore@hp.com>
> Date: Thu, 7 Feb 2008 20:54:56 -0500
>
> > I have no idea what was causing the mail problem, probably somebody
> > in our IT department playing around with some new knobs, oh well. I
> > resubscribed this afternoon with both fingers crossed.
>
> In the future please contact postmaster@vger.kernel.org when you
> notice you have been unsubscribed so we can work on fixing the
> issue.
>
> Blind resubscriptions are severely frowned upon, we remove you for
> good reason and if the problem isn't solved you'll just soil up my
> inbox further with bounces....
Both points noted for future reference. While the end result is the same, I
can promise you my actions are not maliciously stupid, just ignorantly
stupid ;)
--
paul moore
linux security @ hp
^ permalink raw reply
* Re: [PATCH] [TIPC]: declare proto_ops structures as 'const'.
From: David Miller @ 2008-02-08 2:18 UTC (permalink / raw)
To: fw; +Cc: netdev, allan.stephens, jon.maloy
In-Reply-To: <1201304273-21332-1-git-send-email-fw@strlen.de>
From: Florian Westphal <fw@strlen.de>
Date: Sat, 26 Jan 2008 00:37:53 +0100
> Signed-off-by: Florian Westphal <fw@strlen.de>
Applied, thanks.
^ permalink raw reply
* Re: [PATCH] [TIPC] Kill unused static inline (x5)
From: David Miller @ 2008-02-08 2:17 UTC (permalink / raw)
To: ilpo.jarvinen; +Cc: per.liden, netdev
In-Reply-To: <12013021583862-git-send-email-ilpo.jarvinen@helsinki.fi>
From: "Ilpo_Järvinen" <ilpo.jarvinen@helsinki.fi>
Date: Sat, 26 Jan 2008 01:02:38 +0200
> All these static inlines are unused:
>
> in_own_zone 1 (net/tipc/addr.h)
> msg_dataoctet 1 (net/tipc/msg.h)
> msg_direct 1 (include/net/tipc/tipc_msg.h)
> msg_options 1 (include/net/tipc/tipc_msg.h)
> tipc_nmap_get 1 (net/tipc/bcast.h)
>
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Applied.
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: David Miller @ 2008-02-08 2:15 UTC (permalink / raw)
To: paul.moore; +Cc: akpm, casey, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <200802072054.57284.paul.moore@hp.com>
From: Paul Moore <paul.moore@hp.com>
Date: Thu, 7 Feb 2008 20:54:56 -0500
> I have no idea what was causing the mail problem, probably somebody
> in our IT department playing around with some new knobs, oh well. I
> resubscribed this afternoon with both fingers crossed.
In the future please contact postmaster@vger.kernel.org when you
notice you have been unsubscribed so we can work on fixing the
issue.
Blind resubscriptions are severely frowned upon, we remove you for
good reason and if the problem isn't solved you'll just soil up my
inbox further with bounces....
^ permalink raw reply
* Re: [PATCH] [IPV6] Minor cleanup: remove unused definitions in net/ip6_fib.h
From: David Miller @ 2008-02-08 2:11 UTC (permalink / raw)
To: ramirose; +Cc: netdev, linux-kernel
In-Reply-To: <eb3ff54b0802070823y7038fcedga27cba4a9bb35731@mail.gmail.com>
From: "Rami Rosen" <ramirose@gmail.com>
Date: Thu, 7 Feb 2008 18:23:58 +0200
> Hi,
>
> This patch removes some unused definitions and one method typedef
> declaration (f_pnode)
> in include/net/ip6_fib.h, as they are not used in the kernel.
>
> Signed-off-by: Rami Rosen <ramirose@gmail.com>
Applied, thanks.
^ permalink raw reply
* Re: [PATCH] [IPV6] Minor clenup: remove two unused definitions in net/ip6_route.h
From: David Miller @ 2008-02-08 2:11 UTC (permalink / raw)
To: ramirose; +Cc: netdev, linux-kernel
In-Reply-To: <eb3ff54b0802070758j21299257m19e04fffdedd914@mail.gmail.com>
From: "Rami Rosen" <ramirose@gmail.com>
Date: Thu, 7 Feb 2008 17:58:07 +0200
> Remove IP6_RT_PRIO_FW and IP6_RT_FLOW_MASK definitions in
> include/net/ip6_route.h, as they are not used in the kernel.
>
> Signed-off-by: Rami Rosen <ramirose@gmail.com>
Applied.
If you grep for "fc_metric" in the tree you'll see some
explicit uses of the magic constant "1024" which probably
should be replaced with IP6_RT_PRIO_USER. If you could
verify that and cook up a patch if correct, I'd appreciate
it.
Thanks.
^ permalink raw reply
* Re: [patch 0/3] [IUCV] fixes for net-2.6.25
From: David Miller @ 2008-02-08 2:07 UTC (permalink / raw)
To: braunu; +Cc: netdev, linux-s390
In-Reply-To: <20080207142842.852003000@linux.vnet.ibm.com>
From: Ursula Braun <braunu@de.ibm.com>
Date: Thu, 07 Feb 2008 15:28:42 +0100
> --
> Dave,
>
> the following 3 patches are intended for 2.6.25 and contain:
> - locking changes in iucv.c
> - locking changes in af_iucv.c
> - extra checkings for successful allocations in af_iucv.c
> - secure handling of send_skb_q list in af_iucv.c
Applied, thanks.
^ permalink raw reply
* Re: [patch 0/3] CAN: Some updates for 2.6.25
From: David Miller @ 2008-02-08 2:05 UTC (permalink / raw)
To: urs.thuermann; +Cc: netdev, urs.thuermann, oliver.hartkopp, oliver
In-Reply-To: <20080206220749.20500.0@janus.isnogud.escape.de>
From: urs.thuermann@gmx.de
Date: Wed, 6 Feb 2008 23:07:49 +0100
> The following patches are intended for 2.6.25.
Applied, thanks.
^ permalink raw reply
* Re: [IPV4]: route: fix crash ip_route_input
From: David Miller @ 2008-02-08 1:58 UTC (permalink / raw)
To: kaber; +Cc: netfilter-devel, den, netdev
In-Reply-To: <47A9B7F9.3090808@trash.net>
From: Patrick McHardy <kaber@trash.net>
Date: Wed, 06 Feb 2008 14:36:57 +0100
> [IPV4]: route: fix crash ip_route_input
>
> ip_route_me_harder() may call ip_route_input() with skbs that don't
> have skb->dev set for skbs rerouted in LOCAL_OUT and TCP resets
> generated by the REJECT target, resulting in a crash when dereferencing
> skb->dev->nd_net. Since ip_route_input() has an input device argument,
> it seems correct to use that one anyway.
>
> Bug introduced in b5921910a1 (Routing cache virtualization).
>
> Signed-off-by: Patrick McHardy <kaber@trash.net>
Applied, thanks Patrick.
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: Paul Moore @ 2008-02-08 1:54 UTC (permalink / raw)
To: David Miller; +Cc: akpm, casey, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <20080207.173402.163351740.davem@davemloft.net>
On Thursday 07 February 2008 8:34:02 pm David Miller wrote:
> From: Paul Moore <paul.moore@hp.com>
> Date: Thu, 7 Feb 2008 15:14:34 -0500
>
> > My apologies, those mailing list postings there haven't hit my inbox yet.
>
> I had to remove you a few days ago, see my other reply to
> Andrew.
>
> You are back on the lists now, so I hope that bounce problem
> has been solved.
Yeah, that discussion with Andrew made me look a bit deeper at my mail folders
and I realized the last message I received from any of the vger.kernel.org
mailing lists was late Tuesday night ... I thought Wednesday was awfully
quiet :/
I have no idea what was causing the mail problem, probably somebody in our IT
department playing around with some new knobs, oh well. I resubscribed this
afternoon with both fingers crossed.
--
paul moore
linux security @ hp
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: David Miller @ 2008-02-08 1:34 UTC (permalink / raw)
To: paul.moore; +Cc: akpm, casey, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <200802071514.34770.paul.moore@hp.com>
From: Paul Moore <paul.moore@hp.com>
Date: Thu, 7 Feb 2008 15:14:34 -0500
> My apologies, those mailing list postings there haven't hit my inbox yet.
I had to remove you a few days ago, see my other reply to
Andrew.
You are back on the lists now, so I hope that bounce problem
has been solved.
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: David Miller @ 2008-02-08 1:33 UTC (permalink / raw)
To: akpm; +Cc: paul.moore, casey, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <20080207120459.d4994f44.akpm@linux-foundation.org>
From: Andrew Morton <akpm@linux-foundation.org>
Date: Thu, 7 Feb 2008 12:04:59 -0800
> It was on linux-kernel and netdev. I've restored those cc's.
Perhaps Paul missed it because his email address was bouncing with
"user unknown" errors a few days ago so he got removed from all the
mailing lists @ vger :-)
^ permalink raw reply
* [patch 2/3] ctcm: infrastructure for replaced ctc driver
From: Ursula Braun @ 2008-02-07 23:03 UTC (permalink / raw)
To: jgarzik, netdev, linux-s390; +Cc: Peter Tiedemann
In-Reply-To: <20080207230347.075759000@linux.vnet.ibm.com>
[-- Attachment #1: 702-ctcm-diff --]
[-- Type: text/plain, Size: 231008 bytes --]
From: Peter Tiedemann <ptiedem@de.ibm.com>
ctcm driver supports the channel-to-channel connections of the
old ctc driver plus an additional MPC protocol to provide SNA
connectivity.
This new ctcm driver replaces the existing ctc driver.
Signed-off-by: Peter Tiedemann <ptiedem@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/Kconfig | 12
drivers/s390/net/Makefile | 5
drivers/s390/net/ctcm_dbug.c | 67 +
drivers/s390/net/ctcm_dbug.h | 158 ++
drivers/s390/net/ctcm_fsms.c | 2347 +++++++++++++++++++++++++++++++++++++++
drivers/s390/net/ctcm_fsms.h | 359 ++++++
drivers/s390/net/ctcm_main.c | 1772 ++++++++++++++++++++++++++++++
drivers/s390/net/ctcm_main.h | 287 ++++
drivers/s390/net/ctcm_mpc.c | 2472 ++++++++++++++++++++++++++++++++++++++++++
drivers/s390/net/ctcm_mpc.h | 239 ++++
drivers/s390/net/ctcm_sysfs.c | 210 +++
11 files changed, 7920 insertions(+), 8 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/Makefile
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/Makefile
+++ linux-2.6-uschi/drivers/s390/net/Makefile
@@ -2,11 +2,10 @@
# S/390 network devices
#
-ctc-objs := ctcmain.o ctcdbug.o
-
+ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o
+obj-$(CONFIG_CTCM) += ctcm.o fsm.o cu3088.o
obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
-obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
obj-$(CONFIG_LCS) += lcs.o cu3088.o
obj-$(CONFIG_CLAW) += claw.o cu3088.o
qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o
Index: linux-2.6-uschi/drivers/s390/net/Kconfig
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/Kconfig
+++ linux-2.6-uschi/drivers/s390/net/Kconfig
@@ -11,15 +11,17 @@ config LCS
To compile as a module, choose M. The module name is lcs.ko.
If you do not know what it is, it's safe to choose Y.
-config CTC
- tristate "CTC device support"
+config CTCM
+ tristate "CTC and MPC SNA device support"
depends on CCW && NETDEVICES
help
Select this option if you want to use channel-to-channel
point-to-point networking on IBM System z.
This device driver supports real CTC coupling using ESCON.
It also supports virtual CTCs when running under VM.
- To compile as a module, choose M. The module name is ctc.ko.
+ This driver also supports channel-to-channel MPC SNA devices.
+ MPC is an SNA protocol device used by Communication Server for Linux.
+ To compile as a module, choose M. The module name is ctcm.ko.
To compile into the kernel, choose Y.
If you do not need any channel-to-channel connection, choose N.
@@ -84,7 +86,7 @@ config QETH_VLAN
802.1q VLAN support in the qeth device driver.
config CCWGROUP
- tristate
- default (LCS || CTC || QETH)
+ tristate
+ default (LCS || CTCM || QETH)
endmenu
Index: linux-2.6-uschi/drivers/s390/net/ctcm_dbug.c
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_dbug.c
@@ -0,0 +1,67 @@
+/*
+ * drivers/s390/net/ctcm_dbug.c
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include "ctcm_dbug.h"
+
+/*
+ * Debug Facility Stuff
+ */
+
+DEFINE_PER_CPU(char[256], ctcm_dbf_txt_buf);
+
+struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS] = {
+ [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, 5, NULL},
+ [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, 3, NULL},
+ [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, 3, NULL},
+ [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 64, 5, NULL},
+ [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 64, 3, NULL},
+ [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 64, 3, NULL},
+};
+
+void ctcm_unregister_dbf_views(void)
+{
+ int x;
+ for (x = 0; x < CTCM_DBF_INFOS; x++) {
+ debug_unregister(ctcm_dbf[x].id);
+ ctcm_dbf[x].id = NULL;
+ }
+}
+
+int ctcm_register_dbf_views(void)
+{
+ int x;
+ for (x = 0; x < CTCM_DBF_INFOS; x++) {
+ /* register the areas */
+ ctcm_dbf[x].id = debug_register(ctcm_dbf[x].name,
+ ctcm_dbf[x].pages,
+ ctcm_dbf[x].areas,
+ ctcm_dbf[x].len);
+ if (ctcm_dbf[x].id == NULL) {
+ ctcm_unregister_dbf_views();
+ return -ENOMEM;
+ }
+
+ /* register a view */
+ debug_register_view(ctcm_dbf[x].id, &debug_hex_ascii_view);
+ /* set a passing level */
+ debug_set_level(ctcm_dbf[x].id, ctcm_dbf[x].level);
+ }
+
+ return 0;
+}
+
Index: linux-2.6-uschi/drivers/s390/net/ctcm_dbug.h
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_dbug.h
@@ -0,0 +1,158 @@
+/*
+ * drivers/s390/net/ctcm_dbug.h
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#ifndef _CTCM_DBUG_H_
+#define _CTCM_DBUG_H_
+
+/*
+ * Debug Facility stuff
+ */
+
+#include <asm/debug.h>
+
+#ifdef DEBUG
+ #define do_debug 1
+#else
+ #define do_debug 0
+#endif
+#ifdef DEBUGDATA
+ #define do_debug_data 1
+#else
+ #define do_debug_data 0
+#endif
+#ifdef DEBUGCCW
+ #define do_debug_ccw 1
+#else
+ #define do_debug_ccw 0
+#endif
+
+/* define dbf debug levels similar to kernel msg levels */
+#define CTC_DBF_ALWAYS 0 /* always print this */
+#define CTC_DBF_EMERG 0 /* system is unusable */
+#define CTC_DBF_ALERT 1 /* action must be taken immediately */
+#define CTC_DBF_CRIT 2 /* critical conditions */
+#define CTC_DBF_ERROR 3 /* error conditions */
+#define CTC_DBF_WARN 4 /* warning conditions */
+#define CTC_DBF_NOTICE 5 /* normal but significant condition */
+#define CTC_DBF_INFO 5 /* informational */
+#define CTC_DBF_DEBUG 6 /* debug-level messages */
+
+DECLARE_PER_CPU(char[256], ctcm_dbf_txt_buf);
+
+enum ctcm_dbf_names {
+ CTCM_DBF_SETUP,
+ CTCM_DBF_ERROR,
+ CTCM_DBF_TRACE,
+ CTCM_DBF_MPC_SETUP,
+ CTCM_DBF_MPC_ERROR,
+ CTCM_DBF_MPC_TRACE,
+ CTCM_DBF_INFOS /* must be last element */
+};
+
+struct ctcm_dbf_info {
+ char name[DEBUG_MAX_NAME_LEN];
+ int pages;
+ int areas;
+ int len;
+ int level;
+ debug_info_t *id;
+};
+
+extern struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS];
+
+int ctcm_register_dbf_views(void);
+void ctcm_unregister_dbf_views(void);
+
+static inline const char *strtail(const char *s, int n)
+{
+ int l = strlen(s);
+ return (l > n) ? s + (l - n) : s;
+}
+
+/* sort out levels early to avoid unnecessary sprintfs */
+static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+ return (dbf_grp->level >= level);
+}
+
+#define CTCM_FUNTAIL strtail((char *)__func__, 16)
+
+#define CTCM_DBF_TEXT(name, level, text) \
+ do { \
+ debug_text_event(ctcm_dbf[CTCM_DBF_##name].id, level, text); \
+ } while (0)
+
+#define CTCM_DBF_HEX(name, level, addr, len) \
+ do { \
+ debug_event(ctcm_dbf[CTCM_DBF_##name].id, \
+ level, (void *)(addr), len); \
+ } while (0)
+
+#define CTCM_DBF_TEXT_(name, level, text...) \
+ do { \
+ if (ctcm_dbf_passes(ctcm_dbf[CTCM_DBF_##name].id, level)) { \
+ char *ctcm_dbf_txt_buf = \
+ get_cpu_var(ctcm_dbf_txt_buf); \
+ sprintf(ctcm_dbf_txt_buf, text); \
+ debug_text_event(ctcm_dbf[CTCM_DBF_##name].id, \
+ level, ctcm_dbf_txt_buf); \
+ put_cpu_var(ctcm_dbf_txt_buf); \
+ } \
+ } while (0)
+
+/*
+ * cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}.
+ * dev : netdevice with valid name field.
+ * text: any text string.
+ */
+#define CTCM_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) : %s", \
+ CTCM_FUNTAIL, dev->name, text); \
+ } while (0)
+
+#define MPC_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) : %s", \
+ CTCM_FUNTAIL, dev->name, text); \
+ } while (0)
+
+#define CTCMY_DBF_DEV_NAME(cat, dev, text) \
+ do { \
+ if (IS_MPCDEV(dev)) \
+ MPC_DBF_DEV_NAME(cat, dev, text); \
+ else \
+ CTCM_DBF_DEV_NAME(cat, dev, text); \
+ } while (0)
+
+/*
+ * cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}.
+ * dev : netdevice.
+ * text: any text string.
+ */
+#define CTCM_DBF_DEV(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) : %s", \
+ CTCM_FUNTAIL, dev, text); \
+ } while (0)
+
+#define MPC_DBF_DEV(cat, dev, text) \
+ do { \
+ CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) : %s", \
+ CTCM_FUNTAIL, dev, text); \
+ } while (0)
+
+#define CTCMY_DBF_DEV(cat, dev, text) \
+ do { \
+ if (IS_MPCDEV(dev)) \
+ MPC_DBF_DEV(cat, dev, text); \
+ else \
+ CTCM_DBF_DEV(cat, dev, text); \
+ } while (0)
+
+#endif
Index: linux-2.6-uschi/drivers/s390/net/ctcm_fsms.h
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_fsms.h
@@ -0,0 +1,359 @@
+/*
+ * drivers/s390/net/ctcm_fsms.h
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ * MPC additions :
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+#ifndef _CTCM_FSMS_H_
+#define _CTCM_FSMS_H_
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "fsm.h"
+#include "cu3088.h"
+#include "ctcm_main.h"
+
+/*
+ * Definitions for the channel statemachine(s) for ctc and ctcmpc
+ *
+ * To allow better kerntyping, prefix-less definitions for channel states
+ * and channel events have been replaced :
+ * ch_event... -> ctc_ch_event...
+ * CH_EVENT... -> CTC_EVENT...
+ * ch_state... -> ctc_ch_state...
+ * CH_STATE... -> CTC_STATE...
+ */
+/*
+ * Events of the channel statemachine(s) for ctc and ctcmpc
+ */
+enum ctc_ch_events {
+ /*
+ * Events, representing return code of
+ * I/O operations (ccw_device_start, ccw_device_halt et al.)
+ */
+ CTC_EVENT_IO_SUCCESS,
+ CTC_EVENT_IO_EBUSY,
+ CTC_EVENT_IO_ENODEV,
+ CTC_EVENT_IO_UNKNOWN,
+
+ CTC_EVENT_ATTNBUSY,
+ CTC_EVENT_ATTN,
+ CTC_EVENT_BUSY,
+ /*
+ * Events, representing unit-check
+ */
+ CTC_EVENT_UC_RCRESET,
+ CTC_EVENT_UC_RSRESET,
+ CTC_EVENT_UC_TXTIMEOUT,
+ CTC_EVENT_UC_TXPARITY,
+ CTC_EVENT_UC_HWFAIL,
+ CTC_EVENT_UC_RXPARITY,
+ CTC_EVENT_UC_ZERO,
+ CTC_EVENT_UC_UNKNOWN,
+ /*
+ * Events, representing subchannel-check
+ */
+ CTC_EVENT_SC_UNKNOWN,
+ /*
+ * Events, representing machine checks
+ */
+ CTC_EVENT_MC_FAIL,
+ CTC_EVENT_MC_GOOD,
+ /*
+ * Event, representing normal IRQ
+ */
+ CTC_EVENT_IRQ,
+ CTC_EVENT_FINSTAT,
+ /*
+ * Event, representing timer expiry.
+ */
+ CTC_EVENT_TIMER,
+ /*
+ * Events, representing commands from upper levels.
+ */
+ CTC_EVENT_START,
+ CTC_EVENT_STOP,
+ CTC_NR_EVENTS,
+ /*
+ * additional MPC events
+ */
+ CTC_EVENT_SEND_XID = CTC_NR_EVENTS,
+ CTC_EVENT_RSWEEP_TIMER,
+ /*
+ * MUST be always the last element!!
+ */
+ CTC_MPC_NR_EVENTS,
+};
+
+/*
+ * States of the channel statemachine(s) for ctc and ctcmpc.
+ */
+enum ctc_ch_states {
+ /*
+ * Channel not assigned to any device,
+ * initial state, direction invalid
+ */
+ CTC_STATE_IDLE,
+ /*
+ * Channel assigned but not operating
+ */
+ CTC_STATE_STOPPED,
+ CTC_STATE_STARTWAIT,
+ CTC_STATE_STARTRETRY,
+ CTC_STATE_SETUPWAIT,
+ CTC_STATE_RXINIT,
+ CTC_STATE_TXINIT,
+ CTC_STATE_RX,
+ CTC_STATE_TX,
+ CTC_STATE_RXIDLE,
+ CTC_STATE_TXIDLE,
+ CTC_STATE_RXERR,
+ CTC_STATE_TXERR,
+ CTC_STATE_TERM,
+ CTC_STATE_DTERM,
+ CTC_STATE_NOTOP,
+ CTC_NR_STATES, /* MUST be the last element of non-expanded states */
+ /*
+ * additional MPC states
+ */
+ CH_XID0_PENDING = CTC_NR_STATES,
+ CH_XID0_INPROGRESS,
+ CH_XID7_PENDING,
+ CH_XID7_PENDING1,
+ CH_XID7_PENDING2,
+ CH_XID7_PENDING3,
+ CH_XID7_PENDING4,
+ CTC_MPC_NR_STATES, /* MUST be the last element of expanded mpc states */
+};
+
+extern const char *ctc_ch_event_names[];
+
+extern const char *ctc_ch_state_names[];
+
+void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg);
+void ctcm_purge_skb_queue(struct sk_buff_head *q);
+void fsm_action_nop(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- non-static actions for ctcm channel statemachine -----
+ *
+ */
+void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- FSM (state/event/action) of the ctcm channel statemachine -----
+ */
+extern const fsm_node ch_fsm[];
+extern int ch_fsm_len;
+
+
+/*
+ * ----- non-static actions for ctcmpc channel statemachine ----
+ *
+ */
+/* shared :
+void ctcm_chx_txidle(fsm_instance * fi, int event, void *arg);
+ */
+void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- FSM (state/event/action) of the ctcmpc channel statemachine -----
+ */
+extern const fsm_node ctcmpc_ch_fsm[];
+extern int mpc_ch_fsm_len;
+
+/*
+ * Definitions for the device interface statemachine for ctc and mpc
+ */
+
+/*
+ * States of the device interface statemachine.
+ */
+enum dev_states {
+ DEV_STATE_STOPPED,
+ DEV_STATE_STARTWAIT_RXTX,
+ DEV_STATE_STARTWAIT_RX,
+ DEV_STATE_STARTWAIT_TX,
+ DEV_STATE_STOPWAIT_RXTX,
+ DEV_STATE_STOPWAIT_RX,
+ DEV_STATE_STOPWAIT_TX,
+ DEV_STATE_RUNNING,
+ /*
+ * MUST be always the last element!!
+ */
+ CTCM_NR_DEV_STATES
+};
+
+extern const char *dev_state_names[];
+
+/*
+ * Events of the device interface statemachine.
+ * ctcm and ctcmpc
+ */
+enum dev_events {
+ DEV_EVENT_START,
+ DEV_EVENT_STOP,
+ DEV_EVENT_RXUP,
+ DEV_EVENT_TXUP,
+ DEV_EVENT_RXDOWN,
+ DEV_EVENT_TXDOWN,
+ DEV_EVENT_RESTART,
+ /*
+ * MUST be always the last element!!
+ */
+ CTCM_NR_DEV_EVENTS
+};
+
+extern const char *dev_event_names[];
+
+/*
+ * Actions for the device interface statemachine.
+ * ctc and ctcmpc
+ */
+/*
+static void dev_action_start(fsm_instance * fi, int event, void *arg);
+static void dev_action_stop(fsm_instance * fi, int event, void *arg);
+static void dev_action_restart(fsm_instance *fi, int event, void *arg);
+static void dev_action_chup(fsm_instance * fi, int event, void *arg);
+static void dev_action_chdown(fsm_instance * fi, int event, void *arg);
+*/
+
+/*
+ * The (state/event/action) fsm table of the device interface statemachine.
+ * ctcm and ctcmpc
+ */
+extern const fsm_node dev_fsm[];
+extern int dev_fsm_len;
+
+
+/*
+ * Definitions for the MPC Group statemachine
+ */
+
+/*
+ * MPC Group Station FSM States
+
+State Name When In This State
+====================== =======================================
+MPCG_STATE_RESET Initial State When Driver Loaded
+ We receive and send NOTHING
+
+MPCG_STATE_INOP INOP Received.
+ Group level non-recoverable error
+
+MPCG_STATE_READY XID exchanges for at least 1 write and
+ 1 read channel have completed.
+ Group is ready for data transfer.
+
+States from ctc_mpc_alloc_channel
+==============================================================
+MPCG_STATE_XID2INITW Awaiting XID2(0) Initiation
+ ATTN from other side will start
+ XID negotiations.
+ Y-side protocol only.
+
+MPCG_STATE_XID2INITX XID2(0) negotiations are in progress.
+ At least 1, but not all, XID2(0)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITW XID2(0) complete
+ No XID2(7)'s have yet been received.
+ XID2(7) negotiations pending.
+
+MPCG_STATE_XID7INITX XID2(7) negotiations in progress.
+ At least 1, but not all, XID2(7)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITF XID2(7) negotiations complete.
+ Transitioning to READY.
+
+MPCG_STATE_READY Ready for Data Transfer.
+
+
+States from ctc_mpc_establish_connectivity call
+==============================================================
+MPCG_STATE_XID0IOWAIT Initiating XID2(0) negotiations.
+ X-side protocol only.
+ ATTN-BUSY from other side will convert
+ this to Y-side protocol and the
+ ctc_mpc_alloc_channel flow will begin.
+
+MPCG_STATE_XID0IOWAIX XID2(0) negotiations are in progress.
+ At least 1, but not all, XID2(0)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITI XID2(0) complete
+ No XID2(7)'s have yet been received.
+ XID2(7) negotiations pending.
+
+MPCG_STATE_XID7INITZ XID2(7) negotiations in progress.
+ At least 1, but not all, XID2(7)'s
+ have been received from partner.
+
+MPCG_STATE_XID7INITF XID2(7) negotiations complete.
+ Transitioning to READY.
+
+MPCG_STATE_READY Ready for Data Transfer.
+
+*/
+
+enum mpcg_events {
+ MPCG_EVENT_INOP,
+ MPCG_EVENT_DISCONC,
+ MPCG_EVENT_XID0DO,
+ MPCG_EVENT_XID2,
+ MPCG_EVENT_XID2DONE,
+ MPCG_EVENT_XID7DONE,
+ MPCG_EVENT_TIMER,
+ MPCG_EVENT_DOIO,
+ MPCG_NR_EVENTS,
+};
+
+enum mpcg_states {
+ MPCG_STATE_RESET,
+ MPCG_STATE_INOP,
+ MPCG_STATE_XID2INITW,
+ MPCG_STATE_XID2INITX,
+ MPCG_STATE_XID7INITW,
+ MPCG_STATE_XID7INITX,
+ MPCG_STATE_XID0IOWAIT,
+ MPCG_STATE_XID0IOWAIX,
+ MPCG_STATE_XID7INITI,
+ MPCG_STATE_XID7INITZ,
+ MPCG_STATE_XID7INITF,
+ MPCG_STATE_FLOWC,
+ MPCG_STATE_READY,
+ MPCG_NR_STATES,
+};
+
+#endif
+/* --- This is the END my friend --- */
Index: linux-2.6-uschi/drivers/s390/net/ctcm_main.c
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_main.c
@@ -0,0 +1,1772 @@
+/*
+ * drivers/s390/net/ctcm_main.c
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Author(s):
+ * Original CTC driver(s):
+ * Fritz Elfert (felfert@millenux.com)
+ * Dieter Wellerdiek (wel@de.ibm.com)
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Denis Joseph Barrow (barrow_dj@yahoo.com)
+ * Jochen Roehrig (roehrig@de.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ * MPC additions:
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ * Revived by:
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "cu3088.h"
+#include "ctcm_fsms.h"
+#include "ctcm_main.h"
+
+/* Some common global variables */
+
+/*
+ * Linked list of all detected channels.
+ */
+struct channel *channels;
+
+/**
+ * Unpack a just received skb and hand it over to
+ * upper layers.
+ *
+ * ch The channel where this skb has been received.
+ * pskb The received skb.
+ */
+void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ __u16 len = *((__u16 *) pskb->data);
+
+ skb_put(pskb, 2 + LL_HEADER_LENGTH);
+ skb_pull(pskb, 2);
+ pskb->dev = dev;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ while (len > 0) {
+ struct sk_buff *skb;
+ int skblen;
+ struct ll_header *header = (struct ll_header *)pskb->data;
+
+ skb_pull(pskb, LL_HEADER_LENGTH);
+ if ((ch->protocol == CTCM_PROTO_S390) &&
+ (header->type != ETH_P_IP)) {
+
+ if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
+ /*
+ * Check packet type only if we stick strictly
+ * to S/390's protocol of OS390. This only
+ * supports IP. Otherwise allow any packet
+ * type.
+ */
+ ctcm_pr_warn("%s Illegal packet type 0x%04x "
+ "received, dropping\n",
+ dev->name, header->type);
+ ch->logflags |= LOG_FLAG_ILLEGALPKT;
+ }
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_frame_errors++;
+ return;
+ }
+ pskb->protocol = ntohs(header->type);
+ if (header->length <= LL_HEADER_LENGTH) {
+ if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
+ ctcm_pr_warn(
+ "%s Illegal packet size %d "
+ "received (MTU=%d blocklen=%d), "
+ "dropping\n", dev->name, header->length,
+ dev->mtu, len);
+ ch->logflags |= LOG_FLAG_ILLEGALSIZE;
+ }
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ return;
+ }
+ header->length -= LL_HEADER_LENGTH;
+ len -= LL_HEADER_LENGTH;
+ if ((header->length > skb_tailroom(pskb)) ||
+ (header->length > len)) {
+ if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
+ ctcm_pr_warn(
+ "%s Illegal packet size %d (beyond the"
+ " end of received data), dropping\n",
+ dev->name, header->length);
+ ch->logflags |= LOG_FLAG_OVERRUN;
+ }
+
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ return;
+ }
+ skb_put(pskb, header->length);
+ skb_reset_mac_header(pskb);
+ len -= header->length;
+ skb = dev_alloc_skb(pskb->len);
+ if (!skb) {
+ if (!(ch->logflags & LOG_FLAG_NOMEM)) {
+ ctcm_pr_warn(
+ "%s Out of memory in ctcm_unpack_skb\n",
+ dev->name);
+ ch->logflags |= LOG_FLAG_NOMEM;
+ }
+ priv->stats.rx_dropped++;
+ return;
+ }
+ skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
+ pskb->len);
+ skb_reset_mac_header(skb);
+ skb->dev = pskb->dev;
+ skb->protocol = pskb->protocol;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ skblen = skb->len;
+ /*
+ * reset logflags
+ */
+ ch->logflags = 0;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skblen;
+ netif_rx_ni(skb);
+ dev->last_rx = jiffies;
+ if (len > 0) {
+ skb_pull(pskb, header->length);
+ if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {
+ if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
+ CTCM_DBF_DEV_NAME(TRACE, dev,
+ "Overrun in ctcm_unpack_skb");
+ ch->logflags |= LOG_FLAG_OVERRUN;
+ }
+ return;
+ }
+ skb_put(pskb, LL_HEADER_LENGTH);
+ }
+ }
+}
+
+/**
+ * Release a specific channel in the channel list.
+ *
+ * ch Pointer to channel struct to be released.
+ */
+static void channel_free(struct channel *ch)
+{
+ CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
+ ch->flags &= ~CHANNEL_FLAGS_INUSE;
+ fsm_newstate(ch->fsm, CTC_STATE_IDLE);
+}
+
+/**
+ * Remove a specific channel in the channel list.
+ *
+ * ch Pointer to channel struct to be released.
+ */
+static void channel_remove(struct channel *ch)
+{
+ struct channel **c = &channels;
+ char chid[CTCM_ID_SIZE+1];
+ int ok = 0;
+
+ if (ch == NULL)
+ return;
+ else
+ strncpy(chid, ch->id, CTCM_ID_SIZE);
+
+ channel_free(ch);
+ while (*c) {
+ if (*c == ch) {
+ *c = ch->next;
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ kfree_fsm(ch->fsm);
+ clear_normalized_cda(&ch->ccw[4]);
+ if (ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb_any(ch->trans_skb);
+ }
+ if (IS_MPC(ch)) {
+ tasklet_kill(&ch->ch_tasklet);
+ tasklet_kill(&ch->ch_disc_tasklet);
+ kfree(ch->discontact_th);
+ }
+ kfree(ch->ccw);
+ kfree(ch->irb);
+ kfree(ch);
+ ok = 1;
+ break;
+ }
+ c = &((*c)->next);
+ }
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s) %s", CTCM_FUNTAIL,
+ chid, ok ? "OK" : "failed");
+}
+
+/**
+ * Get a specific channel from the channel list.
+ *
+ * type Type of channel we are interested in.
+ * id Id of channel we are interested in.
+ * direction Direction we want to use this channel for.
+ *
+ * returns Pointer to a channel or NULL if no matching channel available.
+ */
+static struct channel *channel_get(enum channel_types type,
+ char *id, int direction)
+{
+ struct channel *ch = channels;
+
+ if (do_debug) {
+ char buf[64];
+ sprintf(buf, "%s(%d, %s, %d)\n",
+ CTCM_FUNTAIL, type, id, direction);
+ CTCM_DBF_TEXT(TRACE, CTC_DBF_INFO, buf);
+ }
+ while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type)))
+ ch = ch->next;
+ if (!ch) {
+ char buf[64];
+ sprintf(buf, "%s(%d, %s, %d) not found in channel list\n",
+ CTCM_FUNTAIL, type, id, direction);
+ CTCM_DBF_TEXT(ERROR, CTC_DBF_ERROR, buf);
+ } else {
+ if (ch->flags & CHANNEL_FLAGS_INUSE)
+ ch = NULL;
+ else {
+ ch->flags |= CHANNEL_FLAGS_INUSE;
+ ch->flags &= ~CHANNEL_FLAGS_RWMASK;
+ ch->flags |= (direction == WRITE)
+ ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
+ fsm_newstate(ch->fsm, CTC_STATE_STOPPED);
+ }
+ }
+ return ch;
+}
+
+static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
+{
+ if (!IS_ERR(irb))
+ return 0;
+
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, "irb error %ld on device %s\n",
+ PTR_ERR(irb), cdev->dev.bus_id);
+
+ switch (PTR_ERR(irb)) {
+ case -EIO:
+ ctcm_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);
+ break;
+ case -ETIMEDOUT:
+ ctcm_pr_warn("timeout on device %s\n", cdev->dev.bus_id);
+ break;
+ default:
+ ctcm_pr_warn("unknown error %ld on device %s\n",
+ PTR_ERR(irb), cdev->dev.bus_id);
+ }
+ return PTR_ERR(irb);
+}
+
+
+/**
+ * Check sense of a unit check.
+ *
+ * ch The channel, the sense code belongs to.
+ * sense The sense code to inspect.
+ */
+static inline void ccw_unit_check(struct channel *ch, unsigned char sense)
+{
+ CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
+ if (sense & SNS0_INTERVENTION_REQ) {
+ if (sense & 0x01) {
+ ctcm_pr_debug("%s: Interface disc. or Sel. reset "
+ "(remote)\n", ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
+ } else {
+ ctcm_pr_debug("%s: System reset (remote)\n", ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
+ }
+ } else if (sense & SNS0_EQUIPMENT_CHECK) {
+ if (sense & SNS0_BUS_OUT_CHECK) {
+ ctcm_pr_warn("%s: Hardware malfunction (remote)\n",
+ ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch);
+ } else {
+ ctcm_pr_warn("%s: Read-data parity error (remote)\n",
+ ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch);
+ }
+ } else if (sense & SNS0_BUS_OUT_CHECK) {
+ if (sense & 0x04) {
+ ctcm_pr_warn("%s: Data-streaming timeout)\n", ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch);
+ } else {
+ ctcm_pr_warn("%s: Data-transfer parity error\n",
+ ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch);
+ }
+ } else if (sense & SNS0_CMD_REJECT) {
+ ctcm_pr_warn("%s: Command reject\n", ch->id);
+ } else if (sense == 0) {
+ ctcm_pr_debug("%s: Unit check ZERO\n", ch->id);
+ fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch);
+ } else {
+ ctcm_pr_warn("%s: Unit Check with sense code: %02x\n",
+ ch->id, sense);
+ fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch);
+ }
+}
+
+int ctcm_ch_alloc_buffer(struct channel *ch)
+{
+ CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
+
+ clear_normalized_cda(&ch->ccw[1]);
+ ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA);
+ if (ch->trans_skb == NULL) {
+ ctcm_pr_warn("%s: Couldn't alloc %s trans_skb\n",
+ ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ return -ENOMEM;
+ }
+
+ ch->ccw[1].count = ch->max_bufsize;
+ if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
+ dev_kfree_skb(ch->trans_skb);
+ ch->trans_skb = NULL;
+ ctcm_pr_warn("%s: set_normalized_cda for %s "
+ "trans_skb failed, dropping packets\n",
+ ch->id,
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ return -ENOMEM;
+ }
+
+ ch->ccw[1].count = 0;
+ ch->trans_skb_data = ch->trans_skb->data;
+ ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ return 0;
+}
+
+/*
+ * Interface API for upper network layers
+ */
+
+/**
+ * Open an interface.
+ * Called from generic network layer when ifconfig up is run.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+int ctcm_open(struct net_device *dev)
+{
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+ if (!IS_MPC(priv))
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ return 0;
+}
+
+/**
+ * Close an interface.
+ * Called from generic network layer when ifconfig down is run.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+int ctcm_close(struct net_device *dev)
+{
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+ if (!IS_MPC(priv))
+ fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
+ return 0;
+}
+
+
+/**
+ * Transmit a packet.
+ * This is a helper function for ctcm_tx().
+ *
+ * ch Channel to be used for sending.
+ * skb Pointer to struct sk_buff of packet to send.
+ * The linklevel header has already been set up
+ * by ctcm_tx().
+ *
+ * returns 0 on success, -ERRNO on failure. (Never fails.)
+ */
+static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
+{
+ unsigned long saveflags;
+ struct ll_header header;
+ int rc = 0;
+ __u16 block_len;
+ int ccw_idx;
+ struct sk_buff *nskb;
+ unsigned long hi;
+
+ /* we need to acquire the lock for testing the state
+ * otherwise we can have an IRQ changing the state to
+ * TXIDLE after the test but before acquiring the lock.
+ */
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ if (fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) {
+ int l = skb->len + LL_HEADER_LENGTH;
+
+ if (ch->collect_len + l > ch->max_bufsize - 2) {
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ return -EBUSY;
+ } else {
+ atomic_inc(&skb->users);
+ header.length = l;
+ header.type = skb->protocol;
+ header.unused = 0;
+ memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
+ LL_HEADER_LENGTH);
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += l;
+ }
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ goto done;
+ }
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ /*
+ * Protect skb against beeing free'd by upper
+ * layers.
+ */
+ atomic_inc(&skb->users);
+ ch->prof.txlen += skb->len;
+ header.length = skb->len + LL_HEADER_LENGTH;
+ header.type = skb->protocol;
+ header.unused = 0;
+ memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH);
+ block_len = skb->len + 2;
+ *((__u16 *)skb_push(skb, 2)) = block_len;
+
+ /*
+ * IDAL support in CTCM is broken, so we have to
+ * care about skb's above 2G ourselves.
+ */
+ hi = ((unsigned long)skb_tail_pointer(skb) + LL_HEADER_LENGTH) >> 31;
+ if (hi) {
+ nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+ if (!nskb) {
+ atomic_dec(&skb->users);
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctcm_clear_busy(ch->netdev);
+ return -ENOMEM;
+ } else {
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+ atomic_inc(&nskb->users);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ skb = nskb;
+ }
+ }
+
+ ch->ccw[4].count = block_len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ /*
+ * idal allocation failed, try via copying to
+ * trans_skb. trans_skb usually has a pre-allocated
+ * idal.
+ */
+ if (ctcm_checkalloc_buffer(ch)) {
+ /*
+ * Remove our header. It gets added
+ * again on retransmit.
+ */
+ atomic_dec(&skb->users);
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ ctcm_clear_busy(ch->netdev);
+ return -EBUSY;
+ }
+
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = skb->len;
+ skb_copy_from_linear_data(skb,
+ skb_put(ch->trans_skb, skb->len), skb->len);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ ccw_idx = 0;
+ } else {
+ skb_queue_tail(&ch->io_queue, skb);
+ ccw_idx = 3;
+ }
+ ch->retry = 0;
+ fsm_newstate(ch->fsm, CTC_STATE_TX);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (ccw_idx == 3)
+ ch->prof.doios_single++;
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "single skb TX");
+ if (ccw_idx == 3)
+ skb_dequeue_tail(&ch->io_queue);
+ /*
+ * Remove our header. It gets added
+ * again on retransmit.
+ */
+ skb_pull(skb, LL_HEADER_LENGTH + 2);
+ } else if (ccw_idx == 0) {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ }
+done:
+ ctcm_clear_busy(ch->netdev);
+ return rc;
+}
+
+static void ctcmpc_send_sweep_req(struct channel *rch)
+{
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct th_sweep *header;
+ struct sk_buff *sweep_skb;
+ struct channel *ch;
+ int rc = 0;
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ ch = priv->channel[WRITE];
+
+ if (do_debug)
+ MPC_DBF_DEV_NAME(TRACE, dev, ch->id);
+
+ /* sweep processing is not complete until response and request */
+ /* has completed for all read channels in group */
+ if (grp->in_sweep == 0) {
+ grp->in_sweep = 1;
+ grp->sweep_rsp_pend_num = grp->active_channels[READ];
+ grp->sweep_req_pend_num = grp->active_channels[READ];
+ }
+
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
+
+ if (sweep_skb == NULL) {
+ printk(KERN_INFO "Couldn't alloc sweep_skb\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header = kmalloc(TH_SWEEP_LENGTH, gfp_type());
+
+ if (!header) {
+ dev_kfree_skb_any(sweep_skb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header->th.th_seg = 0x00 ;
+ header->th.th_ch_flag = TH_SWEEP_REQ; /* 0x0f */
+ header->th.th_blk_flag = 0x00;
+ header->th.th_is_xid = 0x00;
+ header->th.th_seq_num = 0x00;
+ header->sw.th_last_seq = ch->th_seq_num;
+
+ memcpy(skb_put(sweep_skb, TH_SWEEP_LENGTH), header, TH_SWEEP_LENGTH);
+
+ kfree(header);
+
+ dev->trans_start = jiffies;
+ skb_queue_tail(&ch->sweep_queue, sweep_skb);
+
+ fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
+
+ return;
+
+done:
+ if (rc != 0) {
+ grp->in_sweep = 0;
+ ctcm_clear_busy(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ return;
+}
+
+/*
+ * MPC mode version of transmit_skb
+ */
+static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
+{
+ struct pdu *p_header;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct th_header *header;
+ struct sk_buff *nskb;
+ int rc = 0;
+ int ccw_idx;
+ unsigned long hi;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+ __u16 block_len;
+
+ if (do_debug)
+ ctcm_pr_debug(
+ "ctcm enter: %s(): %s cp=%i ch=0x%p id=%s state=%s\n",
+ __FUNCTION__, dev->name, smp_processor_id(), ch,
+ ch->id, fsm_getstate_str(ch->fsm));
+
+ if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) {
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ atomic_inc(&skb->users);
+ p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
+
+ if (!p_header) {
+ printk(KERN_WARNING "ctcm: OUT OF MEMORY IN %s():"
+ " Data Lost \n", __FUNCTION__);
+
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ p_header->pdu_offset = skb->len;
+ p_header->pdu_proto = 0x01;
+ p_header->pdu_flag = 0x00;
+ if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
+ } else {
+ p_header->pdu_flag |= PDU_FIRST;
+ }
+ p_header->pdu_seq = 0;
+ memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
+ PDU_HEADER_LENGTH);
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcm: %s() Putting on collect_q"
+ " - skb len: %04x \n", __FUNCTION__, skb->len);
+ ctcm_pr_debug("ctcm: %s() pdu header and data"
+ " for up to 32 bytes\n", __FUNCTION__);
+ ctcmpc_dump32((char *)skb->data, skb->len);
+ }
+
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += skb->len;
+ kfree(p_header);
+
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ goto done;
+ }
+
+ /*
+ * Protect skb against beeing free'd by upper
+ * layers.
+ */
+ atomic_inc(&skb->users);
+
+ block_len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ /*
+ * IDAL support in CTCM is broken, so we have to
+ * care about skb's above 2G ourselves.
+ */
+ hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31;
+ if (hi) {
+ nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+ if (!nskb) {
+ printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY"
+ "- Data Lost \n", __FUNCTION__);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ } else {
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+ atomic_inc(&nskb->users);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ skb = nskb;
+ }
+ }
+
+ p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
+
+ if (!p_header) {
+ printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY"
+ ": Data Lost \n", __FUNCTION__);
+
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ p_header->pdu_offset = skb->len;
+ p_header->pdu_proto = 0x01;
+ p_header->pdu_flag = 0x00;
+ p_header->pdu_seq = 0;
+ if (skb->protocol == ntohs(ETH_P_SNAP)) {
+ p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
+ } else {
+ p_header->pdu_flag |= PDU_FIRST;
+ }
+ memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header, PDU_HEADER_LENGTH);
+
+ kfree(p_header);
+
+ if (ch->collect_len > 0) {
+ spin_lock_irqsave(&ch->collect_lock, saveflags);
+ skb_queue_tail(&ch->collect_queue, skb);
+ ch->collect_len += skb->len;
+ skb = skb_dequeue(&ch->collect_queue);
+ ch->collect_len -= skb->len;
+ spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+ }
+
+ p_header = (struct pdu *)skb->data;
+ p_header->pdu_flag |= PDU_LAST;
+
+ ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
+
+ header = kmalloc(TH_HEADER_LENGTH, gfp_type());
+
+ if (!header) {
+ printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY: Data Lost \n",
+ __FUNCTION__);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ header->th_seg = 0x00;
+ header->th_ch_flag = TH_HAS_PDU; /* Normal data */
+ header->th_blk_flag = 0x00;
+ header->th_is_xid = 0x00; /* Just data here */
+ ch->th_seq_num++;
+ header->th_seq_num = ch->th_seq_num;
+
+ if (do_debug_data)
+ ctcm_pr_debug("ctcm: %s() ToVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, ch->th_seq_num);
+
+ /* put the TH on the packet */
+ memcpy(skb_push(skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH);
+
+ kfree(header);
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcm: %s(): skb len: %04x \n",
+ __FUNCTION__, skb->len);
+ ctcm_pr_debug("ctcm: %s(): pdu header and data for up to 32 "
+ "bytes sent to vtam\n", __FUNCTION__);
+ ctcmpc_dump32((char *)skb->data, skb->len);
+ }
+
+ ch->ccw[4].count = skb->len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ /*
+ * idal allocation failed, try via copying to
+ * trans_skb. trans_skb usually has a pre-allocated
+ * idal.
+ */
+ if (ctcm_checkalloc_buffer(ch)) {
+ /*
+ * Remove our header. It gets added
+ * again on retransmit.
+ */
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ printk(KERN_WARNING "ctcm: %s()OUT OF MEMORY:"
+ " Data Lost \n", __FUNCTION__);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = skb->len;
+ memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len);
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ ccw_idx = 0;
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcm: %s() TRANS skb len: %d \n",
+ __FUNCTION__, ch->trans_skb->len);
+ ctcm_pr_debug("ctcm: %s up to 32 bytes of data"
+ " sent to vtam\n", __FUNCTION__);
+ ctcmpc_dump32((char *)ch->trans_skb->data,
+ ch->trans_skb->len);
+ }
+ } else {
+ skb_queue_tail(&ch->io_queue, skb);
+ ccw_idx = 3;
+ }
+ ch->retry = 0;
+ fsm_newstate(ch->fsm, CTC_STATE_TX);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[ccw_idx],
+ sizeof(struct ccw1) * 3);
+
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (ccw_idx == 3)
+ ch->prof.doios_single++;
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "single skb TX");
+ if (ccw_idx == 3)
+ skb_dequeue_tail(&ch->io_queue);
+ } else if (ccw_idx == 0) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
+ }
+ if (ch->th_seq_num > 0xf0000000) /* Chose 4Billion at random. */
+ ctcmpc_send_sweep_req(ch);
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcm exit: %s %s()\n", dev->name, __FUNCTION__);
+ return 0;
+}
+
+/**
+ * Start transmission of a packet.
+ * Called from generic network device layer.
+ *
+ * skb Pointer to buffer containing the packet.
+ * dev Pointer to interface struct.
+ *
+ * returns 0 if packet consumed, !0 if packet rejected.
+ * Note: If we return !0, then the packet is free'd by
+ * the generic network layer.
+ */
+/* first merge version - leaving both functions separated */
+static int ctcm_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int rc = 0;
+ struct ctcm_priv *priv;
+
+ CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
+ priv = dev->priv;
+
+ if (skb == NULL) {
+ ctcm_pr_warn("%s: NULL sk_buff passed\n", dev->name);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
+ ctcm_pr_warn("%s: Got sk_buff with head room < %ld bytes\n",
+ dev->name, LL_HEADER_LENGTH + 2);
+ dev_kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+
+ /*
+ * If channels are not running, try to restart them
+ * and throw away packet.
+ */
+ if (fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) {
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ dev_kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ return 0;
+ }
+
+ if (ctcm_test_and_set_busy(dev))
+ return -EBUSY;
+
+ dev->trans_start = jiffies;
+ if (ctcm_transmit_skb(priv->channel[WRITE], skb) != 0)
+ rc = 1;
+ return rc;
+}
+
+/* unmerged MPC variant of ctcm_tx */
+static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int len = 0;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+ struct sk_buff *newskb = NULL;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): skb:%0lx\n",
+ __FUNCTION__, (unsigned long)skb);
+
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ "ctcmpc enter: %s(): skb:%0lx\n",
+ __FUNCTION__, (unsigned long)skb);
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ /*
+ * Some sanity checks ...
+ */
+ if (skb == NULL) {
+ ctcm_pr_warn("ctcmpc: %s: NULL sk_buff passed\n", dev->name);
+ priv->stats.tx_dropped++;
+ goto done;
+ }
+ if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) {
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_WARN,
+ "%s: Got sk_buff with head room < %ld bytes\n",
+ dev->name, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
+
+ if (do_debug_data)
+ ctcmpc_dump32((char *)skb->data, skb->len);
+
+ len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ newskb = __dev_alloc_skb(len, gfp_type() | GFP_DMA);
+
+ if (!newskb) {
+ printk(KERN_WARNING "ctcmpc: %s() OUT OF MEMORY-"
+ "Data Lost\n",
+ __FUNCTION__);
+
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ newskb->protocol = skb->protocol;
+ skb_reserve(newskb, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
+ memcpy(skb_put(newskb, skb->len), skb->data, skb->len);
+ dev_kfree_skb_any(skb);
+ skb = newskb;
+ }
+
+ /*
+ * If channels are not running,
+ * notify anybody about a link failure and throw
+ * away packet.
+ */
+ if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) ||
+ (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
+ dev_kfree_skb_any(skb);
+ printk(KERN_INFO "ctcmpc: %s() DATA RCVD - MPC GROUP "
+ "NOT ACTIVE - DROPPED\n",
+ __FUNCTION__);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ goto done;
+ }
+
+ if (ctcm_test_and_set_busy(dev)) {
+ printk(KERN_WARNING "%s:DEVICE ERR - UNRECOVERABLE DATA LOSS\n",
+ __FUNCTION__);
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ dev->trans_start = jiffies;
+ if (ctcmpc_transmit_skb(priv->channel[WRITE], skb) != 0) {
+ printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR"
+ ": Data Lost \n",
+ __FUNCTION__);
+ printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR"
+ " - UNRECOVERABLE DATA LOSS\n",
+ __FUNCTION__);
+ dev_kfree_skb_any(skb);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ priv->stats.tx_carrier_errors++;
+ ctcm_clear_busy(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+ ctcm_clear_busy(dev);
+done:
+ if (do_debug)
+ MPC_DBF_DEV_NAME(TRACE, dev, "exit");
+
+ return 0; /* handle freeing of skb here */
+}
+
+
+/**
+ * Sets MTU of an interface.
+ *
+ * dev Pointer to interface struct.
+ * new_mtu The new MTU to use for this interface.
+ *
+ * returns 0 on success, -EINVAL if MTU is out of valid range.
+ * (valid range is 576 .. 65527). If VM is on the
+ * remote side, maximum MTU is 32760, however this is
+ * not checked here.
+ */
+static int ctcm_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct ctcm_priv *priv;
+ int max_bufsize;
+
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
+
+ if (new_mtu < 576 || new_mtu > 65527)
+ return -EINVAL;
+
+ priv = dev->priv;
+ max_bufsize = priv->channel[READ]->max_bufsize;
+
+ if (IS_MPC(priv)) {
+ if (new_mtu > max_bufsize - TH_HEADER_LENGTH)
+ return -EINVAL;
+ dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ } else {
+ if (new_mtu > max_bufsize - LL_HEADER_LENGTH - 2)
+ return -EINVAL;
+ dev->hard_header_len = LL_HEADER_LENGTH + 2;
+ }
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+/**
+ * Returns interface statistics of a device.
+ *
+ * dev Pointer to interface struct.
+ *
+ * returns Pointer to stats struct of this interface.
+ */
+static struct net_device_stats *ctcm_stats(struct net_device *dev)
+{
+ return &((struct ctcm_priv *)dev->priv)->stats;
+}
+
+
+static void ctcm_netdev_unregister(struct net_device *dev)
+{
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
+ if (!dev)
+ return;
+ unregister_netdev(dev);
+}
+
+static int ctcm_netdev_register(struct net_device *dev)
+{
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
+ return register_netdev(dev);
+}
+
+static void ctcm_free_netdevice(struct net_device *dev)
+{
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
+
+ if (!dev)
+ return;
+ priv = dev->priv;
+ if (priv) {
+ grp = priv->mpcg;
+ if (grp) {
+ if (grp->fsm)
+ kfree_fsm(grp->fsm);
+ if (grp->xid_skb)
+ dev_kfree_skb(grp->xid_skb);
+ if (grp->rcvd_xid_skb)
+ dev_kfree_skb(grp->rcvd_xid_skb);
+ tasklet_kill(&grp->mpc_tasklet2);
+ kfree(grp);
+ priv->mpcg = NULL;
+ }
+ if (priv->fsm) {
+ kfree_fsm(priv->fsm);
+ priv->fsm = NULL;
+ }
+ kfree(priv->xid);
+ priv->xid = NULL;
+ /*
+ * Note: kfree(priv); is done in "opposite" function of
+ * allocator function probe_device which is remove_device.
+ */
+ }
+#ifdef MODULE
+ free_netdev(dev);
+#endif
+}
+
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
+
+void static ctcm_dev_setup(struct net_device *dev)
+{
+ dev->open = ctcm_open;
+ dev->stop = ctcm_close;
+ dev->get_stats = ctcm_stats;
+ dev->change_mtu = ctcm_change_mtu;
+ dev->type = ARPHRD_SLIP;
+ dev->tx_queue_len = 100;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+}
+
+/*
+ * Initialize everything of the net device except the name and the
+ * channel structs.
+ */
+static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
+{
+ struct net_device *dev;
+ struct mpc_group *grp;
+ if (!priv)
+ return NULL;
+
+ if (IS_MPC(priv))
+ dev = alloc_netdev(0, MPC_DEVICE_GENE, ctcm_dev_setup);
+ else
+ dev = alloc_netdev(0, CTC_DEVICE_GENE, ctcm_dev_setup);
+
+ if (!dev) {
+ ctcm_pr_err("%s: Out of memory\n", __FUNCTION__);
+ return NULL;
+ }
+ dev->priv = priv;
+ priv->fsm = init_fsm("ctcmdev", dev_state_names, dev_event_names,
+ CTCM_NR_DEV_STATES, CTCM_NR_DEV_EVENTS,
+ dev_fsm, dev_fsm_len, GFP_KERNEL);
+ if (priv->fsm == NULL) {
+ CTCMY_DBF_DEV(SETUP, dev, "init_fsm error");
+ kfree(dev);
+ return NULL;
+ }
+ fsm_newstate(priv->fsm, DEV_STATE_STOPPED);
+ fsm_settimer(priv->fsm, &priv->restart_timer);
+
+ if (IS_MPC(priv)) {
+ /* MPC Group Initializations */
+ grp = ctcmpc_init_mpc_group(priv);
+ if (grp == NULL) {
+ MPC_DBF_DEV(SETUP, dev, "init_mpc_group error");
+ kfree(dev);
+ return NULL;
+ }
+ tasklet_init(&grp->mpc_tasklet2,
+ mpc_group_ready, (unsigned long)dev);
+ dev->mtu = MPC_BUFSIZE_DEFAULT -
+ TH_HEADER_LENGTH - PDU_HEADER_LENGTH;
+
+ dev->hard_start_xmit = ctcmpc_tx;
+ dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+ priv->buffer_size = MPC_BUFSIZE_DEFAULT;
+ } else {
+ dev->mtu = CTCM_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
+ dev->hard_start_xmit = ctcm_tx;
+ dev->hard_header_len = LL_HEADER_LENGTH + 2;
+ }
+
+ CTCMY_DBF_DEV(SETUP, dev, "finished");
+ return dev;
+}
+
+/**
+ * Main IRQ handler.
+ *
+ * cdev The ccw_device the interrupt is for.
+ * intparm interruption parameter.
+ * irb interruption response block.
+ */
+static void ctcm_irq_handler(struct ccw_device *cdev,
+ unsigned long intparm, struct irb *irb)
+{
+ struct channel *ch;
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct ccwgroup_device *cgdev;
+
+ CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __FUNCTION__);
+ if (ctcm_check_irb_error(cdev, irb))
+ return;
+
+ cgdev = dev_get_drvdata(&cdev->dev);
+
+ /* Check for unsolicited interrupts. */
+ if (cgdev == NULL) {
+ ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n",
+ cdev->dev.bus_id, irb->scsw.cstat,
+ irb->scsw.dstat);
+ return;
+ }
+
+ priv = dev_get_drvdata(&cgdev->dev);
+
+ /* Try to extract channel from driver data. */
+ if (priv->channel[READ]->cdev == cdev)
+ ch = priv->channel[READ];
+ else if (priv->channel[WRITE]->cdev == cdev)
+ ch = priv->channel[WRITE];
+ else {
+ ctcm_pr_err("ctcm: Can't determine channel for interrupt, "
+ "device %s\n", cdev->dev.bus_id);
+ return;
+ }
+
+ dev = (struct net_device *)(ch->netdev);
+ if (dev == NULL) {
+ ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n",
+ __FUNCTION__, cdev->dev.bus_id, ch);
+ return;
+ }
+
+ if (do_debug)
+ ctcm_pr_debug("%s: interrupt for device: %s "
+ "received c-%02x d-%02x\n",
+ dev->name,
+ ch->id,
+ irb->scsw.cstat,
+ irb->scsw.dstat);
+
+ /* Copy interruption response block. */
+ memcpy(ch->irb, irb, sizeof(struct irb));
+
+ /* Check for good subchannel return code, otherwise error message */
+ if (irb->scsw.cstat) {
+ fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
+ ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
+ dev->name, ch->id, irb->scsw.cstat,
+ irb->scsw.dstat);
+ return;
+ }
+
+ /* Check the reason-code of a unit check */
+ if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ ccw_unit_check(ch, irb->ecw[0]);
+ return;
+ }
+ if (irb->scsw.dstat & DEV_STAT_BUSY) {
+ if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+ fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
+ else
+ fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
+ return;
+ }
+ if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
+ return;
+ }
+ if ((irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (irb->scsw.stctl ==
+ (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
+ fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
+ else
+ fsm_event(ch->fsm, CTC_EVENT_IRQ, ch);
+
+}
+
+/**
+ * Add ctcm specific attributes.
+ * Add ctcm private data.
+ *
+ * cgdev pointer to ccwgroup_device just added
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_probe_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv;
+ int rc;
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s %p", __FUNCTION__, cgdev);
+
+ if (!get_device(&cgdev->dev))
+ return -ENODEV;
+
+ priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL);
+ if (!priv) {
+ ctcm_pr_err("%s: Out of memory\n", __FUNCTION__);
+ put_device(&cgdev->dev);
+ return -ENOMEM;
+ }
+
+ rc = ctcm_add_files(&cgdev->dev);
+ if (rc) {
+ kfree(priv);
+ put_device(&cgdev->dev);
+ return rc;
+ }
+ priv->buffer_size = CTCM_BUFSIZE_DEFAULT;
+ cgdev->cdev[0]->handler = ctcm_irq_handler;
+ cgdev->cdev[1]->handler = ctcm_irq_handler;
+ dev_set_drvdata(&cgdev->dev, priv);
+
+ return 0;
+}
+
+/**
+ * Add a new channel to the list of channels.
+ * Keeps the channel list sorted.
+ *
+ * cdev The ccw_device to be added.
+ * type The type class of the new channel.
+ * priv Points to the private data of the ccwgroup_device.
+ *
+ * returns 0 on success, !0 on error.
+ */
+static int add_channel(struct ccw_device *cdev, enum channel_types type,
+ struct ctcm_priv *priv)
+{
+ struct channel **c = &channels;
+ struct channel *ch;
+ int ccw_num;
+ int rc = 0;
+
+ CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
+ ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+ if (ch == NULL)
+ goto nomem_return;
+
+ ch->protocol = priv->protocol;
+ if (IS_MPC(priv)) {
+ ch->discontact_th = (struct th_header *)
+ kzalloc(TH_HEADER_LENGTH, gfp_type());
+ if (ch->discontact_th == NULL)
+ goto nomem_return;
+
+ ch->discontact_th->th_blk_flag = TH_DISCONTACT;
+ tasklet_init(&ch->ch_disc_tasklet,
+ mpc_action_send_discontact, (unsigned long)ch);
+
+ tasklet_init(&ch->ch_tasklet, ctcmpc_bh, (unsigned long)ch);
+ ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
+ ccw_num = 17;
+ } else
+ ccw_num = 8;
+
+ ch->ccw = (struct ccw1 *)
+ kzalloc(ccw_num * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
+ if (ch->ccw == NULL)
+ goto nomem_return;
+
+ ch->cdev = cdev;
+ snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", cdev->dev.bus_id);
+ ch->type = type;
+
+ /**
+ * "static" ccws are used in the following way:
+ *
+ * ccw[0..2] (Channel program for generic I/O):
+ * 0: prepare
+ * 1: read or write (depending on direction) with fixed
+ * buffer (idal allocated once when buffer is allocated)
+ * 2: nop
+ * ccw[3..5] (Channel program for direct write of packets)
+ * 3: prepare
+ * 4: write (idal allocated on every write).
+ * 5: nop
+ * ccw[6..7] (Channel program for initial channel setup):
+ * 6: set extended mode
+ * 7: nop
+ *
+ * ch->ccw[0..5] are initialized in ch_action_start because
+ * the channel's direction is yet unknown here.
+ *
+ * ccws used for xid2 negotiations
+ * ch-ccw[8-14] need to be used for the XID exchange either
+ * X side XID2 Processing
+ * 8: write control
+ * 9: write th
+ * 10: write XID
+ * 11: read th from secondary
+ * 12: read XID from secondary
+ * 13: read 4 byte ID
+ * 14: nop
+ * Y side XID Processing
+ * 8: sense
+ * 9: read th
+ * 10: read XID
+ * 11: write th
+ * 12: write XID
+ * 13: write 4 byte ID
+ * 14: nop
+ *
+ * ccws used for double noop due to VM timing issues
+ * which result in unrecoverable Busy on channel
+ * 15: nop
+ * 16: nop
+ */
+ ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
+ ch->ccw[6].flags = CCW_FLAG_SLI;
+
+ ch->ccw[7].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[7].flags = CCW_FLAG_SLI;
+
+ if (IS_MPC(priv)) {
+ ch->ccw[15].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[15].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[15].count = TH_HEADER_LENGTH;
+ ch->ccw[15].cda = virt_to_phys(ch->discontact_th);
+
+ ch->ccw[16].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[16].flags = CCW_FLAG_SLI;
+
+ ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
+ ctc_ch_event_names, CTC_MPC_NR_STATES,
+ CTC_MPC_NR_EVENTS, ctcmpc_ch_fsm,
+ mpc_ch_fsm_len, GFP_KERNEL);
+ } else {
+ ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
+ ctc_ch_event_names, CTC_NR_STATES,
+ CTC_NR_EVENTS, ch_fsm,
+ ch_fsm_len, GFP_KERNEL);
+ }
+ if (ch->fsm == NULL)
+ goto free_return;
+
+ fsm_newstate(ch->fsm, CTC_STATE_IDLE);
+
+ ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
+ if (ch->irb == NULL)
+ goto nomem_return;
+
+ while (*c && ctcm_less_than((*c)->id, ch->id))
+ c = &(*c)->next;
+
+ if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) {
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "%s (%s) already in list, using old entry",
+ __FUNCTION__, (*c)->id);
+
+ goto free_return;
+ }
+
+ spin_lock_init(&ch->collect_lock);
+
+ fsm_settimer(ch->fsm, &ch->timer);
+ skb_queue_head_init(&ch->io_queue);
+ skb_queue_head_init(&ch->collect_queue);
+
+ if (IS_MPC(priv)) {
+ fsm_settimer(ch->fsm, &ch->sweep_timer);
+ skb_queue_head_init(&ch->sweep_queue);
+ }
+ ch->next = *c;
+ *c = ch;
+ return 0;
+
+nomem_return:
+ ctcm_pr_warn("ctcm: Out of memory in %s\n", __FUNCTION__);
+ rc = -ENOMEM;
+
+free_return: /* note that all channel pointers are 0 or valid */
+ kfree(ch->ccw); /* TODO: check that again */
+ kfree(ch->discontact_th);
+ kfree_fsm(ch->fsm);
+ kfree(ch->irb);
+ kfree(ch);
+ return rc;
+}
+
+/*
+ * Return type of a detected device.
+ */
+static enum channel_types get_channel_type(struct ccw_device_id *id)
+{
+ enum channel_types type;
+ type = (enum channel_types)id->driver_info;
+
+ if (type == channel_type_ficon)
+ type = channel_type_escon;
+
+ return type;
+}
+
+/**
+ *
+ * Setup an interface.
+ *
+ * cgdev Device to be setup.
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_new_device(struct ccwgroup_device *cgdev)
+{
+ char read_id[CTCM_ID_SIZE];
+ char write_id[CTCM_ID_SIZE];
+ int direction;
+ enum channel_types type;
+ struct ctcm_priv *priv;
+ struct net_device *dev;
+ int ret;
+
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__);
+
+ priv = dev_get_drvdata(&cgdev->dev);
+ if (!priv)
+ return -ENODEV;
+
+ type = get_channel_type(&cgdev->cdev[0]->id);
+
+ snprintf(read_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
+ snprintf(write_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id);
+
+ ret = add_channel(cgdev->cdev[0], type, priv);
+ if (ret)
+ return ret;
+ ret = add_channel(cgdev->cdev[1], type, priv);
+ if (ret)
+ return ret;
+
+ ret = ccw_device_set_online(cgdev->cdev[0]);
+ if (ret != 0) {
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN,
+ "ccw_device_set_online (cdev[0]) failed ");
+ ctcm_pr_warn("ccw_device_set_online (cdev[0]) failed "
+ "with ret = %d\n", ret);
+ }
+
+ ret = ccw_device_set_online(cgdev->cdev[1]);
+ if (ret != 0) {
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN,
+ "ccw_device_set_online (cdev[1]) failed ");
+ ctcm_pr_warn("ccw_device_set_online (cdev[1]) failed "
+ "with ret = %d\n", ret);
+ }
+
+ dev = ctcm_init_netdevice(priv);
+
+ if (dev == NULL) {
+ ctcm_pr_warn("ctcm_init_netdevice failed\n");
+ goto out;
+ }
+
+ for (direction = READ; direction <= WRITE; direction++) {
+ priv->channel[direction] =
+ channel_get(type, direction == READ ? read_id : write_id,
+ direction);
+ if (priv->channel[direction] == NULL) {
+ if (direction == WRITE)
+ channel_free(priv->channel[READ]);
+ ctcm_free_netdevice(dev);
+ goto out;
+ }
+ priv->channel[direction]->netdev = dev;
+ priv->channel[direction]->protocol = priv->protocol;
+ priv->channel[direction]->max_bufsize = priv->buffer_size;
+ }
+ /* sysfs magic */
+ SET_NETDEV_DEV(dev, &cgdev->dev);
+
+ if (ctcm_netdev_register(dev) != 0) {
+ ctcm_free_netdevice(dev);
+ goto out;
+ }
+
+ if (ctcm_add_attributes(&cgdev->dev)) {
+ ctcm_netdev_unregister(dev);
+/* dev->priv = NULL; why that ???? */
+ ctcm_free_netdevice(dev);
+ goto out;
+ }
+
+ strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
+
+ CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
+ "setup(%s) ok : r/w = %s / %s, proto : %d",
+ dev->name, priv->channel[READ]->id,
+ priv->channel[WRITE]->id, priv->protocol);
+
+ return 0;
+out:
+ ccw_device_set_offline(cgdev->cdev[1]);
+ ccw_device_set_offline(cgdev->cdev[0]);
+
+ return -ENODEV;
+}
+
+/**
+ * Shutdown an interface.
+ *
+ * cgdev Device to be shut down.
+ *
+ * returns 0 on success, !0 on failure.
+ */
+static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv;
+ struct net_device *dev;
+
+ priv = dev_get_drvdata(&cgdev->dev);
+ if (!priv)
+ return -ENODEV;
+
+ if (priv->channel[READ]) {
+ dev = priv->channel[READ]->netdev;
+ CTCM_DBF_DEV(SETUP, dev, "");
+ /* Close the device */
+ ctcm_close(dev);
+ dev->flags &= ~IFF_RUNNING;
+ ctcm_remove_attributes(&cgdev->dev);
+ channel_free(priv->channel[READ]);
+ } else
+ dev = NULL;
+
+ if (priv->channel[WRITE])
+ channel_free(priv->channel[WRITE]);
+
+ if (dev) {
+ ctcm_netdev_unregister(dev);
+/* dev->priv = NULL; why that ??? */
+ ctcm_free_netdevice(dev);
+ }
+
+ if (priv->fsm)
+ kfree_fsm(priv->fsm);
+
+ ccw_device_set_offline(cgdev->cdev[1]);
+ ccw_device_set_offline(cgdev->cdev[0]);
+
+ if (priv->channel[READ])
+ channel_remove(priv->channel[READ]);
+ if (priv->channel[WRITE])
+ channel_remove(priv->channel[WRITE]);
+ priv->channel[READ] = priv->channel[WRITE] = NULL;
+
+ return 0;
+
+}
+
+
+static void ctcm_remove_device(struct ccwgroup_device *cgdev)
+{
+ struct ctcm_priv *priv;
+
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, __FUNCTION__);
+
+ priv = dev_get_drvdata(&cgdev->dev);
+ if (!priv)
+ return;
+ if (cgdev->state == CCWGROUP_ONLINE)
+ ctcm_shutdown_device(cgdev);
+ ctcm_remove_files(&cgdev->dev);
+ dev_set_drvdata(&cgdev->dev, NULL);
+ kfree(priv);
+ put_device(&cgdev->dev);
+}
+
+static struct ccwgroup_driver ctcm_group_driver = {
+ .owner = THIS_MODULE,
+ .name = CTC_DRIVER_NAME,
+ .max_slaves = 2,
+ .driver_id = 0xC3E3C3D4, /* CTCM */
+ .probe = ctcm_probe_device,
+ .remove = ctcm_remove_device,
+ .set_online = ctcm_new_device,
+ .set_offline = ctcm_shutdown_device,
+};
+
+
+/*
+ * Module related routines
+ */
+
+/*
+ * Prepare to be unloaded. Free IRQ's and release all resources.
+ * This is called just before this module is unloaded. It is
+ * not called, if the usage count is !0, so we don't need to check
+ * for that.
+ */
+static void __exit ctcm_exit(void)
+{
+ unregister_cu3088_discipline(&ctcm_group_driver);
+ ctcm_unregister_dbf_views();
+ ctcm_pr_info("CTCM driver unloaded\n");
+}
+
+/*
+ * Print Banner.
+ */
+static void print_banner(void)
+{
+ printk(KERN_INFO "CTCM driver initialized\n");
+}
+
+/**
+ * Initialize module.
+ * This is called just after the module is loaded.
+ *
+ * returns 0 on success, !0 on error.
+ */
+static int __init ctcm_init(void)
+{
+ int ret;
+
+ channels = NULL;
+
+ ret = ctcm_register_dbf_views();
+ if (ret) {
+ ctcm_pr_crit("ctcm_init failed with ctcm_register_dbf_views "
+ "rc = %d\n", ret);
+ return ret;
+ }
+ ret = register_cu3088_discipline(&ctcm_group_driver);
+ if (ret) {
+ ctcm_unregister_dbf_views();
+ ctcm_pr_crit("ctcm_init failed with register_cu3088_discipline "
+ "(rc = %d)\n", ret);
+ return ret;
+ }
+ print_banner();
+ return ret;
+}
+
+module_init(ctcm_init);
+module_exit(ctcm_exit);
+
+MODULE_AUTHOR("Peter Tiedemann <ptiedem@de.ibm.com>");
+MODULE_DESCRIPTION("Network driver for S/390 CTC + CTCMPC (SNA)");
+MODULE_LICENSE("GPL");
+
Index: linux-2.6-uschi/drivers/s390/net/ctcm_main.h
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_main.h
@@ -0,0 +1,287 @@
+/*
+ * drivers/s390/net/ctcm_main.h
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+#ifndef _CTCM_MAIN_H_
+#define _CTCM_MAIN_H_
+
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "fsm.h"
+#include "cu3088.h"
+#include "ctcm_dbug.h"
+#include "ctcm_mpc.h"
+
+#define CTC_DRIVER_NAME "ctcm"
+#define CTC_DEVICE_NAME "ctc"
+#define CTC_DEVICE_GENE "ctc%d"
+#define MPC_DEVICE_NAME "mpc"
+#define MPC_DEVICE_GENE "mpc%d"
+
+#define CHANNEL_FLAGS_READ 0
+#define CHANNEL_FLAGS_WRITE 1
+#define CHANNEL_FLAGS_INUSE 2
+#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
+#define CHANNEL_FLAGS_FAILED 8
+#define CHANNEL_FLAGS_WAITIRQ 16
+#define CHANNEL_FLAGS_RWMASK 1
+#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
+
+#define LOG_FLAG_ILLEGALPKT 1
+#define LOG_FLAG_ILLEGALSIZE 2
+#define LOG_FLAG_OVERRUN 4
+#define LOG_FLAG_NOMEM 8
+
+#define ctcm_pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
+#define ctcm_pr_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
+#define ctcm_pr_notice(fmt, arg...) printk(KERN_NOTICE fmt, ##arg)
+#define ctcm_pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
+#define ctcm_pr_emerg(fmt, arg...) printk(KERN_EMERG fmt, ##arg)
+#define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg)
+#define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg)
+
+/*
+ * CCW commands, used in this driver.
+ */
+#define CCW_CMD_WRITE 0x01
+#define CCW_CMD_READ 0x02
+#define CCW_CMD_NOOP 0x03
+#define CCW_CMD_TIC 0x08
+#define CCW_CMD_SENSE_CMD 0x14
+#define CCW_CMD_WRITE_CTL 0x17
+#define CCW_CMD_SET_EXTENDED 0xc3
+#define CCW_CMD_PREPARE 0xe3
+
+#define CTCM_PROTO_S390 0
+#define CTCM_PROTO_LINUX 1
+#define CTCM_PROTO_LINUX_TTY 2
+#define CTCM_PROTO_OS390 3
+#define CTCM_PROTO_MPC 4
+#define CTCM_PROTO_MAX 4
+
+#define CTCM_BUFSIZE_LIMIT 65535
+#define CTCM_BUFSIZE_DEFAULT 32768
+#define MPC_BUFSIZE_DEFAULT CTCM_BUFSIZE_LIMIT
+
+#define CTCM_TIME_1_SEC 1000
+#define CTCM_TIME_5_SEC 5000
+#define CTCM_TIME_10_SEC 10000
+
+#define CTCM_INITIAL_BLOCKLEN 2
+
+#define READ 0
+#define WRITE 1
+
+#define CTCM_ID_SIZE BUS_ID_SIZE+3
+
+struct ctcm_profile {
+ unsigned long maxmulti;
+ unsigned long maxcqueue;
+ unsigned long doios_single;
+ unsigned long doios_multi;
+ unsigned long txlen;
+ unsigned long tx_time;
+ struct timespec send_stamp;
+};
+
+/*
+ * Definition of one channel
+ */
+struct channel {
+ struct channel *next;
+ char id[CTCM_ID_SIZE];
+ struct ccw_device *cdev;
+ /*
+ * Type of this channel.
+ * CTC/A or Escon for valid channels.
+ */
+ enum channel_types type;
+ /*
+ * Misc. flags. See CHANNEL_FLAGS_... below
+ */
+ __u32 flags;
+ __u16 protocol; /* protocol of this channel (4 = MPC) */
+ /*
+ * I/O and irq related stuff
+ */
+ struct ccw1 *ccw;
+ struct irb *irb;
+ /*
+ * RX/TX buffer size
+ */
+ int max_bufsize;
+ struct sk_buff *trans_skb; /* transmit/receive buffer */
+ struct sk_buff_head io_queue; /* universal I/O queue */
+ struct tasklet_struct ch_tasklet; /* MPC ONLY */
+ /*
+ * TX queue for collecting skb's during busy.
+ */
+ struct sk_buff_head collect_queue;
+ /*
+ * Amount of data in collect_queue.
+ */
+ int collect_len;
+ /*
+ * spinlock for collect_queue and collect_len
+ */
+ spinlock_t collect_lock;
+ /*
+ * Timer for detecting unresposive
+ * I/O operations.
+ */
+ fsm_timer timer;
+ /* MPC ONLY section begin */
+ __u32 th_seq_num; /* SNA TH seq number */
+ __u8 th_seg;
+ __u32 pdu_seq;
+ struct sk_buff *xid_skb;
+ char *xid_skb_data;
+ struct th_header *xid_th;
+ struct xid2 *xid;
+ char *xid_id;
+ struct th_header *rcvd_xid_th;
+ struct xid2 *rcvd_xid;
+ char *rcvd_xid_id;
+ __u8 in_mpcgroup;
+ fsm_timer sweep_timer;
+ struct sk_buff_head sweep_queue;
+ struct th_header *discontact_th;
+ struct tasklet_struct ch_disc_tasklet;
+ /* MPC ONLY section end */
+
+ int retry; /* retry counter for misc. operations */
+ fsm_instance *fsm; /* finite state machine of this channel */
+ struct net_device *netdev; /* corresponding net_device */
+ struct ctcm_profile prof;
+ unsigned char *trans_skb_data;
+ __u16 logflags;
+};
+
+struct ctcm_priv {
+ struct net_device_stats stats;
+ unsigned long tbusy;
+
+ /* The MPC group struct of this interface */
+ struct mpc_group *mpcg; /* MPC only */
+ struct xid2 *xid; /* MPC only */
+
+ /* The finite state machine of this interface */
+ fsm_instance *fsm;
+
+ /* The protocol of this device */
+ __u16 protocol;
+
+ /* Timer for restarting after I/O Errors */
+ fsm_timer restart_timer;
+
+ int buffer_size; /* ctc only */
+
+ struct channel *channel[2];
+};
+
+int ctcm_open(struct net_device *dev);
+int ctcm_close(struct net_device *dev);
+
+/*
+ * prototypes for non-static sysfs functions
+ */
+int ctcm_add_attributes(struct device *dev);
+void ctcm_remove_attributes(struct device *dev);
+int ctcm_add_files(struct device *dev);
+void ctcm_remove_files(struct device *dev);
+
+/*
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+static inline void ctcm_clear_busy_do(struct net_device *dev)
+{
+ clear_bit(0, &(((struct ctcm_priv *)dev->priv)->tbusy));
+ netif_wake_queue(dev);
+}
+
+static inline void ctcm_clear_busy(struct net_device *dev)
+{
+ struct mpc_group *grp;
+ grp = ((struct ctcm_priv *)dev->priv)->mpcg;
+
+ if (!(grp && grp->in_sweep))
+ ctcm_clear_busy_do(dev);
+}
+
+
+static inline int ctcm_test_and_set_busy(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return test_and_set_bit(0, &(((struct ctcm_priv *)dev->priv)->tbusy));
+}
+
+extern int loglevel;
+extern struct channel *channels;
+
+void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb);
+
+/*
+ * Functions related to setup and device detection.
+ */
+
+static inline int ctcm_less_than(char *id1, char *id2)
+{
+ unsigned long dev1, dev2;
+
+ id1 = id1 + 5;
+ id2 = id2 + 5;
+
+ dev1 = simple_strtoul(id1, &id1, 16);
+ dev2 = simple_strtoul(id2, &id2, 16);
+
+ return (dev1 < dev2);
+}
+
+int ctcm_ch_alloc_buffer(struct channel *ch);
+
+static inline int ctcm_checkalloc_buffer(struct channel *ch)
+{
+ if (ch->trans_skb == NULL)
+ return ctcm_ch_alloc_buffer(ch);
+ if (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED) {
+ dev_kfree_skb(ch->trans_skb);
+ return ctcm_ch_alloc_buffer(ch);
+ }
+ return 0;
+}
+
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
+
+/* test if protocol attribute (of struct ctcm_priv or struct channel)
+ * has MPC protocol setting. Type is not checked
+ */
+#define IS_MPC(p) ((p)->protocol == CTCM_PROTO_MPC)
+
+/* test if struct ctcm_priv of struct net_device has MPC protocol setting */
+#define IS_MPCDEV(d) IS_MPC((struct ctcm_priv *)d->priv)
+
+static inline gfp_t gfp_type(void)
+{
+ return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+}
+
+/*
+ * Definition of our link level header.
+ */
+struct ll_header {
+ __u16 length;
+ __u16 type;
+ __u16 unused;
+};
+#define LL_HEADER_LENGTH (sizeof(struct ll_header))
+
+#endif
Index: linux-2.6-uschi/drivers/s390/net/ctcm_mpc.c
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_mpc.c
@@ -0,0 +1,2472 @@
+/*
+ * drivers/s390/net/ctcm_mpc.c
+ *
+ * Copyright IBM Corp. 2004, 2007
+ * Authors: Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+/*
+ This module exports functions to be used by CCS:
+ EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+ EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+ EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+ EXPORT_SYMBOL(ctc_mpc_flow_control);
+*/
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <net/dst.h>
+
+#include <linux/io.h> /* instead of <asm/io.h> ok ? */
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/bitops.h> /* instead of <asm/bitops.h> ok ? */
+#include <linux/uaccess.h> /* instead of <asm/uaccess.h> ok ? */
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <asm/idals.h>
+
+#include "cu3088.h"
+#include "ctcm_mpc.h"
+#include "ctcm_main.h"
+#include "ctcm_fsms.h"
+
+static const struct xid2 init_xid = {
+ .xid2_type_id = XID_FM2,
+ .xid2_len = 0x45,
+ .xid2_adj_id = 0,
+ .xid2_rlen = 0x31,
+ .xid2_resv1 = 0,
+ .xid2_flag1 = 0,
+ .xid2_fmtt = 0,
+ .xid2_flag4 = 0x80,
+ .xid2_resv2 = 0,
+ .xid2_tgnum = 0,
+ .xid2_sender_id = 0,
+ .xid2_flag2 = 0,
+ .xid2_option = XID2_0,
+ .xid2_resv3 = "\x00",
+ .xid2_resv4 = 0,
+ .xid2_dlc_type = XID2_READ_SIDE,
+ .xid2_resv5 = 0,
+ .xid2_mpc_flag = 0,
+ .xid2_resv6 = 0,
+ .xid2_buf_len = (MPC_BUFSIZE_DEFAULT - 35),
+};
+
+static const struct th_header thnorm = {
+ .th_seg = 0x00,
+ .th_ch_flag = TH_IS_XID,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+static const struct th_header thdummy = {
+ .th_seg = 0x00,
+ .th_ch_flag = 0x00,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+/*
+ * Definition of one MPC group
+ */
+
+/*
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+
+static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb);
+
+/*
+ * MPC Group state machine actions (static prototypes)
+ */
+static void mpc_action_nop(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg);
+static void mpc_action_timeout(fsm_instance *fi, int event, void *arg);
+static int mpc_validate_xid(struct mpcg_info *mpcginfo);
+static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg);
+
+#ifdef DEBUGDATA
+/*-------------------------------------------------------------------*
+* Dump buffer format *
+* *
+*--------------------------------------------------------------------*/
+void ctcmpc_dumpit(char *buf, int len)
+{
+ __u32 ct, sw, rm, dup;
+ char *ptr, *rptr;
+ char tbuf[82], tdup[82];
+ #if (UTS_MACHINE == s390x)
+ char addr[22];
+ #else
+ char addr[12];
+ #endif
+ char boff[12];
+ char bhex[82], duphex[82];
+ char basc[40];
+
+ sw = 0;
+ rptr = ptr = buf;
+ rm = 16;
+ duphex[0] = 0x00;
+ dup = 0;
+
+ for (ct = 0; ct < len; ct++, ptr++, rptr++) {
+ if (sw == 0) {
+ #if (UTS_MACHINE == s390x)
+ sprintf(addr, "%16.16lx", (unsigned long)rptr);
+ #else
+ sprintf(addr, "%8.8X", (__u32)rptr);
+ #endif
+
+ sprintf(boff, "%4.4X", (__u32)ct);
+ bhex[0] = '\0';
+ basc[0] = '\0';
+ }
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+
+ #if (UTS_MACHINE == s390x)
+ sprintf(tbuf, "%2.2lX", (unsigned long)*ptr);
+ #else
+ sprintf(tbuf, "%2.2X", (__u32)*ptr);
+ #endif
+
+ tbuf[2] = '\0';
+ strcat(bhex, tbuf);
+ if ((0 != isprint(*ptr)) && (*ptr >= 0x20))
+ basc[sw] = *ptr;
+ else
+ basc[sw] = '.';
+
+ basc[sw+1] = '\0';
+ sw++;
+ rm--;
+ if (sw == 16) {
+ if ((strcmp(duphex, bhex)) != 0) {
+ if (dup != 0) {
+ sprintf(tdup, "Duplicate as above "
+ "to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ dup = 0;
+ strcpy(duphex, bhex);
+ } else
+ dup++;
+
+ sw = 0;
+ rm = 16;
+ }
+ } /* endfor */
+
+ if (sw != 0) {
+ for ( ; rm > 0; rm--, sw++) {
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+ strcat(bhex, " ");
+ strcat(basc, " ");
+ }
+ if (dup != 0) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ } else {
+ if (dup >= 1) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ if (dup != 0) {
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ }
+ }
+
+ return;
+
+} /* end of ctcmpc_dumpit */
+#endif
+
+#ifdef DEBUGDATA
+/*
+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
+ *
+ * skb The sk_buff to dump.
+ * offset Offset relative to skb-data, where to start the dump.
+ */
+void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
+{
+ unsigned char *p = skb->data;
+ struct th_header *header;
+ struct pdu *pheader;
+ int bl = skb->len;
+ int i;
+
+ if (p == NULL)
+ return;
+
+ p += offset;
+ header = (struct th_header *)p;
+
+ printk(KERN_INFO "dump:\n");
+ printk(KERN_INFO "skb len=%d \n", skb->len);
+ if (skb->len > 2) {
+ switch (header->th_ch_flag) {
+ case TH_HAS_PDU:
+ break;
+ case 0x00:
+ case TH_IS_XID:
+ if ((header->th_blk_flag == TH_DATA_IS_XID) &&
+ (header->th_is_xid == 0x01))
+ goto dumpth;
+ case TH_SWEEP_REQ:
+ goto dumpth;
+ case TH_SWEEP_RESP:
+ goto dumpth;
+ default:
+ break;
+ }
+
+ pheader = (struct pdu *)p;
+ printk(KERN_INFO "pdu->offset: %d hex: %04x\n",
+ pheader->pdu_offset, pheader->pdu_offset);
+ printk(KERN_INFO "pdu->flag : %02x\n", pheader->pdu_flag);
+ printk(KERN_INFO "pdu->proto : %02x\n", pheader->pdu_proto);
+ printk(KERN_INFO "pdu->seq : %02x\n", pheader->pdu_seq);
+ goto dumpdata;
+
+dumpth:
+ printk(KERN_INFO "th->seg : %02x\n", header->th_seg);
+ printk(KERN_INFO "th->ch : %02x\n", header->th_ch_flag);
+ printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag);
+ printk(KERN_INFO "th->type : %s\n",
+ (header->th_is_xid) ? "DATA" : "XID");
+ printk(KERN_INFO "th->seqnum : %04x\n", header->th_seq_num);
+
+ }
+dumpdata:
+ if (bl > 32)
+ bl = 32;
+ printk(KERN_INFO "data: ");
+ for (i = 0; i < bl; i++)
+ printk(KERN_INFO "%02x%s", *p++, (i % 16) ? " " : "\n<7>");
+ printk(KERN_INFO "\n");
+}
+#endif
+
+/*
+ * ctc_mpc_alloc_channel
+ * (exported interface)
+ *
+ * Device Initialization :
+ * ACTPATH driven IO operations
+ */
+int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
+{
+ char device[20];
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n", device);
+ return 1;
+ }
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ if (!grp)
+ return 1;
+
+ grp->allochanfunc = callback;
+ grp->port_num = port_num;
+ grp->port_persist = 1;
+
+ ctcm_pr_debug("ctcmpc: %s called for device %s state=%s\n",
+ __FUNCTION__,
+ dev->name,
+ fsm_getstate_str(grp->fsm));
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_INOP:
+ /* Group is in the process of terminating */
+ grp->alloc_called = 1;
+ break;
+ case MPCG_STATE_RESET:
+ /* MPC Group will transition to state */
+ /* MPCG_STATE_XID2INITW iff the minimum number */
+ /* of 1 read and 1 write channel have successfully*/
+ /* activated */
+ /*fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);*/
+ if (callback)
+ grp->send_qllc_disc = 1;
+ case MPCG_STATE_XID0IOWAIT:
+ fsm_deltimer(&grp->timer);
+ grp->outstanding_xid2 = 0;
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if (callback)
+ ctcm_open(dev);
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ break;
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ grp->allocchan_callback_retries++;
+ if (grp->allocchan_callback_retries < 4) {
+ if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num,
+ grp->group_max_buflen);
+ } else {
+ /* there are problems...bail out */
+ /* there may be a state mismatch so restart */
+ grp->port_persist = 1;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->allocchan_callback_retries = 0;
+ }
+ break;
+ default:
+ return 0;
+
+ }
+
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return 0;
+}
+EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+
+/*
+ * ctc_mpc_establish_connectivity
+ * (exported interface)
+ */
+void ctc_mpc_establish_connectivity(int port_num,
+ void (*callback)(int, int, int))
+{
+ char device[20];
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+ struct channel *rch, *wch;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_establish_connectivity "
+ "%s dev=NULL\n", device);
+ return;
+ }
+ priv = dev->priv;
+ rch = priv->channel[READ];
+ wch = priv->channel[WRITE];
+
+ grp = priv->mpcg;
+
+ ctcm_pr_debug("ctcmpc: %s() called for device %s state=%s\n",
+ __FUNCTION__, dev->name,
+ fsm_getstate_str(grp->fsm));
+
+ grp->estconnfunc = callback;
+ grp->port_num = port_num;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ fsm_deltimer(&grp->timer);
+ grp->estconn_callback_retries++;
+ if (grp->estconn_callback_retries < 4) {
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ }
+ } else {
+ /* there are problems...bail out */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->estconn_callback_retries = 0;
+ }
+ break;
+ case MPCG_STATE_INOP:
+ case MPCG_STATE_RESET:
+ /* MPC Group is not ready to start XID - min num of */
+ /* 1 read and 1 write channel have not been acquired*/
+ printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID Req"
+ "uest - Channel Pair is not Active\n", __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ break;
+ case MPCG_STATE_XID2INITW:
+ /* alloc channel was called but no XID exchange */
+ /* has occurred. initiate xside XID exchange */
+ /* make sure yside XID0 processing has not started */
+ if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
+ (fsm_getstate(wch->fsm) > CH_XID0_PENDING)) {
+ printk(KERN_WARNING "mpc: %s() ABORT ACTIVE XID"
+ " Request- PASSIVE XID in process\n"
+ , __FUNCTION__);
+ break;
+ }
+ grp->send_qllc_disc = 1;
+ fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIT);
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if ((rch->in_mpcgroup) &&
+ (fsm_getstate(rch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch);
+ else {
+ printk(KERN_WARNING "mpc: %s() Unable to start"
+ " ACTIVE XID0 on read channel\n",
+ __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ if ((wch->in_mpcgroup) &&
+ (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch);
+ else {
+ printk(KERN_WARNING "mpc: %s() Unable to start"
+ " ACTIVE XID0 on write channel\n",
+ __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ /* already in active XID negotiations */
+ default:
+ break;
+ }
+
+done:
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+
+/*
+ * ctc_mpc_dealloc_ch
+ * (exported interface)
+ */
+void ctc_mpc_dealloc_ch(int port_num)
+{
+ struct net_device *dev;
+ char device[20];
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
+ goto done;
+ }
+
+ ctcm_pr_debug("ctcmpc:%s %s() called for device %s refcount=%d\n",
+ dev->name, __FUNCTION__,
+ dev->name, atomic_read(&dev->refcnt));
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() %s priv=NULL\n",
+ __FUNCTION__, device);
+ goto done;
+ }
+ fsm_deltimer(&priv->restart_timer);
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
+ goto done;
+ }
+ grp->channels_terminating = 0;
+
+ fsm_deltimer(&grp->timer);
+
+ grp->allochanfunc = NULL;
+ grp->estconnfunc = NULL;
+ grp->port_persist = 0;
+ grp->send_qllc_disc = 0;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ ctcm_close(dev);
+done:
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+
+/*
+ * ctc_mpc_flow_control
+ * (exported interface)
+ */
+void ctc_mpc_flow_control(int port_num, int flowc)
+{
+ char device[20];
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct net_device *dev;
+ struct channel *rch;
+ int mpcg_state;
+
+ ctcm_pr_debug("ctcmpc enter: %s() %i\n", __FUNCTION__, flowc);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n", device);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc: %s %s called \n", dev->name, __FUNCTION__);
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "ctcmpc:%s() %s priv=NULL\n",
+ __FUNCTION__, device);
+ return;
+ }
+ grp = priv->mpcg;
+ rch = priv->channel[READ];
+
+ mpcg_state = fsm_getstate(grp->fsm);
+ switch (flowc) {
+ case 1:
+ if (mpcg_state == MPCG_STATE_FLOWC)
+ break;
+ if (mpcg_state == MPCG_STATE_READY) {
+ if (grp->flow_off_called == 1)
+ grp->flow_off_called = 0;
+ else
+ fsm_newstate(grp->fsm, MPCG_STATE_FLOWC);
+ break;
+ }
+ break;
+ case 0:
+ if (mpcg_state == MPCG_STATE_FLOWC) {
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+ /* ensure any data that has accumulated */
+ /* on the io_queue will now be sen t */
+ tasklet_schedule(&rch->ch_tasklet);
+ }
+ /* possible race condition */
+ if (mpcg_state == MPCG_STATE_READY) {
+ grp->flow_off_called = 1;
+ break;
+ }
+ break;
+ }
+
+ ctcm_pr_debug("ctcmpc exit: %s() %i\n", __FUNCTION__, flowc);
+}
+EXPORT_SYMBOL(ctc_mpc_flow_control);
+
+static int mpc_send_qllc_discontact(struct net_device *);
+
+/*
+ * helper function of ctcmpc_unpack_skb
+*/
+static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+
+ if (do_debug_data)
+ ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_rsp_pend_num--;
+
+ if ((grp->sweep_req_pend_num == 0) &&
+ (grp->sweep_rsp_pend_num == 0)) {
+ fsm_deltimer(&ch->sweep_timer);
+ grp->in_sweep = 0;
+ rch->th_seq_num = 0x00;
+ ch->th_seq_num = 0x00;
+ ctcm_clear_busy_do(dev);
+ }
+
+ kfree(mpcginfo);
+
+ return;
+
+}
+
+/*
+ * helper function of mpc_rcvd_sweep_req
+ * which is a helper of ctcmpc_unpack_skb
+ */
+static void ctcmpc_send_sweep_resp(struct channel *rch)
+{
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ int rc = 0;
+ struct th_sweep *header;
+ struct sk_buff *sweep_skb;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, rch, rch->id);
+
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
+ GFP_ATOMIC|GFP_DMA);
+ if (sweep_skb == NULL) {
+ printk(KERN_INFO "Couldn't alloc sweep_skb\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header = (struct th_sweep *)
+ kmalloc(sizeof(struct th_sweep), gfp_type());
+
+ if (!header) {
+ dev_kfree_skb_any(sweep_skb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header->th.th_seg = 0x00 ;
+ header->th.th_ch_flag = TH_SWEEP_RESP;
+ header->th.th_blk_flag = 0x00;
+ header->th.th_is_xid = 0x00;
+ header->th.th_seq_num = 0x00;
+ header->sw.th_last_seq = ch->th_seq_num;
+
+ memcpy(skb_put(sweep_skb, TH_SWEEP_LENGTH), header, TH_SWEEP_LENGTH);
+
+ kfree(header);
+
+ dev->trans_start = jiffies;
+ skb_queue_tail(&ch->sweep_queue, sweep_skb);
+
+ fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
+
+ return;
+
+done:
+ if (rc != 0) {
+ grp->in_sweep = 0;
+ ctcm_clear_busy_do(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ return;
+}
+
+/*
+ * helper function of ctcmpc_unpack_skb
+ */
+static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ " %s(): ch=0x%p id=%s\n", __FUNCTION__, ch, ch->id);
+
+ if (grp->in_sweep == 0) {
+ grp->in_sweep = 1;
+ ctcm_test_and_set_busy(dev);
+ grp->sweep_req_pend_num = grp->active_channels[READ];
+ grp->sweep_rsp_pend_num = grp->active_channels[READ];
+ }
+
+ if (do_debug_data)
+ ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_req_pend_num--;
+ ctcmpc_send_sweep_resp(ch);
+ kfree(mpcginfo);
+ return;
+}
+
+/*
+ * MPC Group Station FSM definitions
+ */
+static const char *mpcg_event_names[] = {
+ [MPCG_EVENT_INOP] = "INOP Condition",
+ [MPCG_EVENT_DISCONC] = "Discontact Received",
+ [MPCG_EVENT_XID0DO] = "Channel Active - Start XID",
+ [MPCG_EVENT_XID2] = "XID2 Received",
+ [MPCG_EVENT_XID2DONE] = "XID0 Complete",
+ [MPCG_EVENT_XID7DONE] = "XID7 Complete",
+ [MPCG_EVENT_TIMER] = "XID Setup Timer",
+ [MPCG_EVENT_DOIO] = "XID DoIO",
+};
+
+static const char *mpcg_state_names[] = {
+ [MPCG_STATE_RESET] = "Reset",
+ [MPCG_STATE_INOP] = "INOP",
+ [MPCG_STATE_XID2INITW] = "Passive XID- XID0 Pending Start",
+ [MPCG_STATE_XID2INITX] = "Passive XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITW] = "Passive XID- XID7 Pending P1 Start",
+ [MPCG_STATE_XID7INITX] = "Passive XID- XID7 Pending P2 Complete",
+ [MPCG_STATE_XID0IOWAIT] = "Active XID- XID0 Pending Start",
+ [MPCG_STATE_XID0IOWAIX] = "Active XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITI] = "Active XID- XID7 Pending Start",
+ [MPCG_STATE_XID7INITZ] = "Active XID- XID7 Pending Complete ",
+ [MPCG_STATE_XID7INITF] = "XID - XID7 Complete ",
+ [MPCG_STATE_FLOWC] = "FLOW CONTROL ON",
+ [MPCG_STATE_READY] = "READY",
+};
+
+/*
+ * The MPC Group Station FSM
+ * 22 events
+ */
+static const fsm_node mpcg_fsm[] = {
+ { MPCG_STATE_RESET, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_INOP, MPCG_EVENT_INOP, mpc_action_nop },
+ { MPCG_STATE_FLOWC, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_READY, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_READY, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_XID7DONE, mpc_action_go_ready },
+};
+
+static int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm);
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ fsm_deltimer(&grp->timer);
+
+ if (grp->saved_xid2->xid2_flag2 == 0x40) {
+ priv->xid->xid2_flag2 = 0x00;
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 1,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else if (grp->allochanfunc)
+ grp->send_qllc_disc = 1;
+ goto done;
+ }
+
+ grp->port_persist = 1;
+ grp->out_of_sequence = 0;
+ grp->estconn_called = 0;
+
+ tasklet_hi_schedule(&grp->mpc_tasklet2);
+
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
+ return;
+
+done:
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+
+ ctcm_pr_info("ctcmpc: %s()failure occurred\n", __FUNCTION__);
+}
+
+/*
+ * helper of ctcm_init_netdevice
+ * CTCM_PROTO_MPC only
+ */
+void mpc_group_ready(unsigned long adev)
+{
+ struct net_device *dev = (struct net_device *)adev;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+ struct channel *ch = NULL;
+
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "ctcmpc:%s() grp=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ printk(KERN_NOTICE "ctcmpc: %s GROUP TRANSITIONED TO READY"
+ " maxbuf:%d\n",
+ dev->name, grp->group_max_buflen);
+
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+
+ /* Put up a read on the channel */
+ ch = priv->channel[READ];
+ ch->pdu_seq = 0;
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
+ __FUNCTION__, ch->pdu_seq);
+
+ ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
+ /* Put the write channel in idle state */
+ ch = priv->channel[WRITE];
+ if (ch->collect_len > 0) {
+ spin_lock(&ch->collect_lock);
+ ctcm_purge_skb_queue(&ch->collect_queue);
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ }
+ ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch);
+
+ ctcm_clear_busy(dev);
+
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else
+ if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num,
+ grp->group_max_buflen);
+
+ grp->send_qllc_disc = 1;
+ grp->changed_side = 0;
+
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+
+}
+
+/*
+ * Increment the MPC Group Active Channel Counts
+ * helper of dev_action (called from channel fsm)
+ */
+int mpc_channel_action(struct channel *ch, int direction, int action)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp = NULL;
+ int rc = 0;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctcmpc_channel_action %i dev=NULL\n",
+ action);
+ rc = 1;
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO
+ "ctcmpc_channel_action%i priv=NULL, dev=%s\n",
+ action, dev->name);
+ rc = 2;
+ goto done;
+ }
+
+ grp = priv->mpcg;
+
+ if (grp == NULL) {
+ printk(KERN_INFO "ctcmpc: %s()%i mpcgroup=NULL, dev=%s\n",
+ __FUNCTION__, action, dev->name);
+ rc = 3;
+ goto done;
+ }
+
+ ctcm_pr_info(
+ "ctcmpc: %s() %i(): Grp:%s total_channel_paths=%i "
+ "active_channels read=%i, write=%i\n",
+ __FUNCTION__,
+ action,
+ fsm_getstate_str(grp->fsm),
+ grp->num_channel_paths,
+ grp->active_channels[READ],
+ grp->active_channels[WRITE]);
+
+ if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
+ grp->num_channel_paths++;
+ grp->active_channels[direction]++;
+ grp->outstanding_xid2++;
+ ch->in_mpcgroup = 1;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+
+ ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
+ GFP_ATOMIC | GFP_DMA);
+ if (ch->xid_skb == NULL) {
+ printk(KERN_INFO "ctcmpc: %s()"
+ "Couldn't alloc ch xid_skb\n", __FUNCTION__);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ return 1;
+ }
+ ch->xid_skb_data = ch->xid_skb->data;
+ ch->xid_th = (struct th_header *)ch->xid_skb->data;
+ skb_put(ch->xid_skb, TH_HEADER_LENGTH);
+ ch->xid = (struct xid2 *)skb_tail_pointer(ch->xid_skb);
+ skb_put(ch->xid_skb, XID2_LENGTH);
+ ch->xid_id = skb_tail_pointer(ch->xid_skb);
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ memcpy(skb_put(ch->xid_skb, grp->xid_skb->len),
+ grp->xid_skb->data,
+ grp->xid_skb->len);
+
+ ch->xid->xid2_dlc_type = ((CHANNEL_DIRECTION(ch->flags) == READ)
+ ? XID2_READ_SIDE : XID2_WRITE_SIDE);
+
+ if (CHANNEL_DIRECTION(ch->flags) == WRITE)
+ ch->xid->xid2_buf_len = 0x00;
+
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ fsm_newstate(ch->fsm, CH_XID0_PENDING);
+
+ if ((grp->active_channels[READ] > 0) &&
+ (grp->active_channels[WRITE] > 0) &&
+ (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
+ printk(KERN_NOTICE "ctcmpc: %s MPC GROUP "
+ "CHANNELS ACTIVE\n", dev->name);
+ }
+ } else if ((action == MPC_CHANNEL_REMOVE) &&
+ (ch->in_mpcgroup == 1)) {
+ ch->in_mpcgroup = 0;
+ grp->num_channel_paths--;
+ grp->active_channels[direction]--;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+ ch->xid_skb = NULL;
+
+ if (grp->channels_terminating)
+ goto done;
+
+ if (((grp->active_channels[READ] == 0) &&
+ (grp->active_channels[WRITE] > 0))
+ || ((grp->active_channels[WRITE] == 0) &&
+ (grp->active_channels[READ] > 0)))
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+done:
+
+ if (do_debug) {
+ ctcm_pr_debug(
+ "ctcmpc: %s() %i Grp:%s ttl_chan_paths=%i "
+ "active_chans read=%i, write=%i\n",
+ __FUNCTION__,
+ action,
+ fsm_getstate_str(grp->fsm),
+ grp->num_channel_paths,
+ grp->active_channels[READ],
+ grp->active_channels[WRITE]);
+
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ }
+ return rc;
+
+}
+
+/**
+ * Unpack a just received skb and hand it over to
+ * upper layers.
+ * special MPC version of unpack_skb.
+ *
+ * ch The channel where this skb has been received.
+ * pskb The received skb.
+ */
+static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct pdu *curr_pdu;
+ struct mpcg_info *mpcginfo;
+ struct th_header *header = NULL;
+ struct th_sweep *sweep = NULL;
+ int pdu_last_seen = 0;
+ __u32 new_len;
+ struct sk_buff *skb;
+ int skblen;
+ int sendrc = 0;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s() %s cp:%i ch:%s\n",
+ __FUNCTION__, dev->name, smp_processor_id(), ch->id);
+
+ header = (struct th_header *)pskb->data;
+ if ((header->th_seg == 0) &&
+ (header->th_ch_flag == 0) &&
+ (header->th_blk_flag == 0) &&
+ (header->th_seq_num == 0))
+ /* nothing for us */ goto done;
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s() th_header\n", __FUNCTION__);
+ ctcmpc_dumpit((char *)header, TH_HEADER_LENGTH);
+ ctcm_pr_debug("ctcmpc: %s() pskb len: %04x \n",
+ __FUNCTION__, pskb->len);
+ }
+
+ pskb->dev = dev;
+ pskb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb_pull(pskb, TH_HEADER_LENGTH);
+
+ if (likely(header->th_ch_flag == TH_HAS_PDU)) {
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() came into th_has_pdu\n",
+ __FUNCTION__);
+ if ((fsm_getstate(grp->fsm) == MPCG_STATE_FLOWC) ||
+ ((fsm_getstate(grp->fsm) == MPCG_STATE_READY) &&
+ (header->th_seq_num != ch->th_seq_num + 1) &&
+ (ch->th_seq_num != 0))) {
+ /* This is NOT the next segment *
+ * we are not the correct race winner *
+ * go away and let someone else win *
+ * BUT..this only applies if xid negot *
+ * is done *
+ */
+ grp->out_of_sequence += 1;
+ __skb_push(pskb, TH_HEADER_LENGTH);
+ skb_queue_tail(&ch->io_queue, pskb);
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() th_seq_num "
+ "expect:%08x got:%08x\n", __FUNCTION__,
+ ch->th_seq_num + 1, header->th_seq_num);
+
+ return;
+ }
+ grp->out_of_sequence = 0;
+ ch->th_seq_num = header->th_seq_num;
+
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
+ __FUNCTION__, ch->th_seq_num);
+
+ if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY))
+ goto done;
+ pdu_last_seen = 0;
+ while ((pskb->len > 0) && !pdu_last_seen) {
+ curr_pdu = (struct pdu *)pskb->data;
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcm: %s() pdu_header\n",
+ __FUNCTION__);
+ ctcmpc_dumpit((char *)pskb->data,
+ PDU_HEADER_LENGTH);
+ ctcm_pr_debug("ctcm: %s() pskb len: %04x \n",
+ __FUNCTION__, pskb->len);
+ }
+ skb_pull(pskb, PDU_HEADER_LENGTH);
+
+ if (curr_pdu->pdu_flag & PDU_LAST)
+ pdu_last_seen = 1;
+ if (curr_pdu->pdu_flag & PDU_CNTL)
+ pskb->protocol = htons(ETH_P_SNAP);
+ else
+ pskb->protocol = htons(ETH_P_SNA_DIX);
+
+ if ((pskb->len <= 0) || (pskb->len > ch->max_bufsize)) {
+ printk(KERN_INFO
+ "%s Illegal packet size %d "
+ "received "
+ "dropping\n", dev->name,
+ pskb->len);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto done;
+ }
+ skb_reset_mac_header(pskb);
+ new_len = curr_pdu->pdu_offset;
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() new_len: %04x \n",
+ __FUNCTION__, new_len);
+ if ((new_len == 0) || (new_len > pskb->len)) {
+ /* should never happen */
+ /* pskb len must be hosed...bail out */
+ printk(KERN_INFO
+ "ctcmpc: %s(): invalid pdu"
+ " offset of %04x - data may be"
+ "lost\n", __FUNCTION__, new_len);
+ goto done;
+ }
+ skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC);
+
+ if (!skb) {
+ printk(KERN_INFO
+ "ctcm: %s Out of memory in "
+ "%s()- request-len:%04x \n",
+ dev->name,
+ __FUNCTION__,
+ new_len+4);
+ priv->stats.rx_dropped++;
+ fsm_event(grp->fsm,
+ MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ memcpy(skb_put(skb, new_len),
+ pskb->data, new_len);
+
+ skb_reset_mac_header(skb);
+ skb->dev = pskb->dev;
+ skb->protocol = pskb->protocol;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ *((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;
+ ch->pdu_seq++;
+
+ if (do_debug_data)
+ ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n",
+ __FUNCTION__, ch->pdu_seq);
+
+ ctcm_pr_debug("ctcm: %s() skb:%0lx "
+ "skb len: %d \n", __FUNCTION__,
+ (unsigned long)skb, skb->len);
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s() up to 32 bytes"
+ " of pdu_data sent\n",
+ __FUNCTION__);
+ ctcmpc_dump32((char *)skb->data, skb->len);
+ }
+
+ skblen = skb->len;
+ sendrc = netif_rx(skb);
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skblen;
+ skb_pull(pskb, new_len); /* point to next PDU */
+ }
+ } else {
+ mpcginfo = (struct mpcg_info *)
+ kmalloc(sizeof(struct mpcg_info), gfp_type());
+ if (mpcginfo == NULL)
+ goto done;
+
+ mpcginfo->ch = ch;
+ mpcginfo->th = header;
+ mpcginfo->skb = pskb;
+ ctcm_pr_debug("ctcmpc: %s() Not PDU - may be control pkt\n",
+ __FUNCTION__);
+ /* it's a sweep? */
+ sweep = (struct th_sweep *)pskb->data;
+ mpcginfo->sweep = sweep;
+ if (header->th_ch_flag == TH_SWEEP_REQ)
+ mpc_rcvd_sweep_req(mpcginfo);
+ else if (header->th_ch_flag == TH_SWEEP_RESP)
+ mpc_rcvd_sweep_resp(mpcginfo);
+ else if (header->th_blk_flag == TH_DATA_IS_XID) {
+ struct xid2 *thisxid = (struct xid2 *)pskb->data;
+ skb_pull(pskb, XID2_LENGTH);
+ mpcginfo->xid = thisxid;
+ fsm_event(grp->fsm, MPCG_EVENT_XID2, mpcginfo);
+ } else if (header->th_blk_flag == TH_DISCONTACT)
+ fsm_event(grp->fsm, MPCG_EVENT_DISCONC, mpcginfo);
+ else if (header->th_seq_num != 0) {
+ printk(KERN_INFO "%s unexpected packet"
+ " expected control pkt\n", dev->name);
+ priv->stats.rx_dropped++;
+ /* mpcginfo only used for non-data transfers */
+ kfree(mpcginfo);
+ if (do_debug_data)
+ ctcmpc_dump_skb(pskb, -8);
+ }
+ }
+done:
+
+ dev_kfree_skb_any(pskb);
+ if (sendrc == NET_RX_DROP) {
+ printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED"
+ " - PACKET DROPPED\n", dev->name, __FUNCTION__);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
+ dev->name, __FUNCTION__, ch, ch->id);
+}
+
+/**
+ * tasklet helper for mpc's skb unpacking.
+ *
+ * ch The channel to work on.
+ * Allow flow control back pressure to occur here.
+ * Throttling back channel can result in excessive
+ * channel inactivity and system deact of channel
+ */
+void ctcmpc_bh(unsigned long thischan)
+{
+ struct channel *ch = (struct channel *)thischan;
+ struct sk_buff *skb;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (do_debug)
+ ctcm_pr_debug("%s cp:%i enter: %s() %s\n",
+ dev->name, smp_processor_id(), __FUNCTION__, ch->id);
+ /* caller has requested driver to throttle back */
+ while ((fsm_getstate(grp->fsm) != MPCG_STATE_FLOWC) &&
+ (skb = skb_dequeue(&ch->io_queue))) {
+ ctcmpc_unpack_skb(ch, skb);
+ if (grp->out_of_sequence > 20) {
+ /* assume data loss has occurred if */
+ /* missing seq_num for extended */
+ /* period of time */
+ grp->out_of_sequence = 0;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ break;
+ }
+ if (skb == skb_peek(&ch->io_queue))
+ break;
+ }
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
+ dev->name, __FUNCTION__, ch, ch->id);
+ return;
+}
+
+/*
+ * MPC Group Initializations
+ */
+struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
+{
+ struct mpc_group *grp;
+
+ CTCM_DBF_TEXT(MPC_SETUP, 3, __FUNCTION__);
+
+ grp = kzalloc(sizeof(struct mpc_group), GFP_KERNEL);
+ if (grp == NULL)
+ return NULL;
+
+ grp->fsm =
+ init_fsm("mpcg", mpcg_state_names, mpcg_event_names,
+ MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm,
+ mpcg_fsm_len, GFP_KERNEL);
+ if (grp->fsm == NULL) {
+ kfree(grp);
+ return NULL;
+ }
+
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ fsm_settimer(grp->fsm, &grp->timer);
+
+ grp->xid_skb =
+ __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
+ if (grp->xid_skb == NULL) {
+ printk(KERN_INFO "Couldn't alloc MPCgroup xid_skb\n");
+ kfree_fsm(grp->fsm);
+ kfree(grp);
+ return NULL;
+ }
+ /* base xid for all channels in group */
+ grp->xid_skb_data = grp->xid_skb->data;
+ grp->xid_th = (struct th_header *)grp->xid_skb->data;
+ memcpy(skb_put(grp->xid_skb, TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+
+ grp->xid = (struct xid2 *) skb_tail_pointer(grp->xid_skb);
+ memcpy(skb_put(grp->xid_skb, XID2_LENGTH), &init_xid, XID2_LENGTH);
+ grp->xid->xid2_adj_id = jiffies | 0xfff00000;
+ grp->xid->xid2_sender_id = jiffies;
+
+ grp->xid_id = skb_tail_pointer(grp->xid_skb);
+ memcpy(skb_put(grp->xid_skb, 4), "VTAM", 4);
+
+ grp->rcvd_xid_skb =
+ __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
+ if (grp->rcvd_xid_skb == NULL) {
+ printk(KERN_INFO "Couldn't alloc MPCgroup rcvd_xid_skb\n");
+ kfree_fsm(grp->fsm);
+ dev_kfree_skb(grp->xid_skb);
+ kfree(grp);
+ return NULL;
+ }
+ grp->rcvd_xid_data = grp->rcvd_xid_skb->data;
+ grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
+ memcpy(skb_put(grp->rcvd_xid_skb, TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ grp->saved_xid2 = NULL;
+ priv->xid = grp->xid;
+ priv->mpcg = grp;
+ return grp;
+}
+
+/*
+ * The MPC Group Station FSM
+ */
+
+/*
+ * MPC Group Station FSM actions
+ * CTCM_PROTO_MPC only
+ */
+
+/**
+ * NOP action for statemachines
+ */
+static void mpc_action_nop(fsm_instance *fi, int event, void *arg)
+{
+}
+
+/*
+ * invoked when the device transitions to dev_stopped
+ * MPC will stop each individual channel if a single XID failure
+ * occurs, or will intitiate all channels be stopped if a GROUP
+ * level failure occurs.
+ */
+static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ int rc = 0;
+ struct channel *wch, *rch;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ grp->flow_off_called = 0;
+
+ fsm_deltimer(&grp->timer);
+
+ if (grp->channels_terminating)
+ goto done;
+
+ grp->channels_terminating = 1;
+
+ grp->saved_state = fsm_getstate(grp->fsm);
+ fsm_newstate(grp->fsm, MPCG_STATE_INOP);
+ if (grp->saved_state > MPCG_STATE_XID7INITF)
+ printk(KERN_NOTICE "%s:MPC GROUP INOPERATIVE\n", dev->name);
+ if ((grp->saved_state != MPCG_STATE_RESET) ||
+ /* dealloc_channel has been called */
+ ((grp->saved_state == MPCG_STATE_RESET) &&
+ (grp->port_persist == 0)))
+ fsm_deltimer(&priv->restart_timer);
+
+ wch = priv->channel[WRITE];
+ rch = priv->channel[READ];
+
+ switch (grp->saved_state) {
+ case MPCG_STATE_RESET:
+ case MPCG_STATE_INOP:
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID7INITF:
+ break;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ default:
+ tasklet_hi_schedule(&wch->ch_disc_tasklet);
+ }
+
+ grp->xid2_tgnum = 0;
+ grp->group_max_buflen = 0; /*min of all received */
+ grp->outstanding_xid2 = 0;
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ grp->xidnogood = 0;
+ grp->changed_side = 0;
+
+ grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
+ skb_reset_tail_pointer(grp->rcvd_xid_skb);
+ grp->rcvd_xid_skb->len = 0;
+ grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
+ memcpy(skb_put(grp->rcvd_xid_skb, TH_HEADER_LENGTH), &thnorm,
+ TH_HEADER_LENGTH);
+
+ if (grp->send_qllc_disc == 1) {
+ grp->send_qllc_disc = 0;
+ rc = mpc_send_qllc_discontact(dev);
+ }
+
+ /* DO NOT issue DEV_EVENT_STOP directly out of this code */
+ /* This can result in INOP of VTAM PU due to halting of */
+ /* outstanding IO which causes a sense to be returned */
+ /* Only about 3 senses are allowed and then IOS/VTAM will*/
+ /* ebcome unreachable without manual intervention */
+ if ((grp->port_persist == 1) || (grp->alloc_called)) {
+ grp->alloc_called = 0;
+ fsm_deltimer(&priv->restart_timer);
+ fsm_addtimer(&priv->restart_timer,
+ 500,
+ DEV_EVENT_RESTART,
+ dev);
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ if (grp->saved_state > MPCG_STATE_XID7INITF)
+ printk(KERN_NOTICE "%s:MPC GROUP RECOVERY SCHEDULED\n",
+ dev->name);
+ } else {
+ fsm_deltimer(&priv->restart_timer);
+ fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_STOP, dev);
+ fsm_newstate(grp->fsm, MPCG_STATE_RESET);
+ printk(KERN_NOTICE "%s:MPC GROUP RECOVERY NOT ATTEMPTED\n",
+ dev->name);
+ }
+
+done:
+ ctcm_pr_debug("ctcmpc exit:%s %s()\n", dev->name, __FUNCTION__);
+ return;
+}
+
+/**
+ * Handle mpc group action timeout.
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ *
+ * fi An instance of an mpc_group fsm.
+ * event The event, just happened.
+ * arg Generic pointer, casted from net_device * upon call.
+ */
+static void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct channel *wch;
+ struct channel *rch;
+
+ CTCM_DBF_TEXT(MPC_TRACE, 6, __FUNCTION__);
+
+ if (dev == NULL) {
+ CTCM_DBF_TEXT_(MPC_ERROR, 4, "%s: dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ wch = priv->channel[WRITE];
+ rch = priv->channel[READ];
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ /* Unless there is outstanding IO on the */
+ /* channel just return and wait for ATTN */
+ /* interrupt to begin XID negotiations */
+ if ((fsm_getstate(rch->fsm) == CH_XID0_PENDING) &&
+ (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
+ break;
+ default:
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ CTCM_DBF_TEXT_(MPC_TRACE, 6, "%s: dev=%s exit",
+ __FUNCTION__, dev->name);
+ return;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+void mpc_action_discontact(fsm_instance *fi, int event, void *arg)
+{
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (ch == NULL) {
+ printk(KERN_INFO "%s() ch=NULL\n", __FUNCTION__);
+ return;
+ }
+ if (ch->netdev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
+
+ grp->send_qllc_disc = 1;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
+ return;
+}
+
+/*
+ * MPC Group Station - not part of FSM
+ * CTCM_PROTO_MPC only
+ * called from add_channel in ctcm_main.c
+ */
+void mpc_action_send_discontact(unsigned long thischan)
+{
+ struct channel *ch;
+ struct net_device *dev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ int rc = 0;
+ unsigned long saveflags;
+
+ ch = (struct channel *)thischan;
+ dev = ch->netdev;
+ priv = dev->priv;
+ grp = priv->mpcg;
+
+ ctcm_pr_info("ctcmpc: %s cp:%i enter: %s() GrpState:%s ChState:%s\n",
+ dev->name,
+ smp_processor_id(),
+ __FUNCTION__,
+ fsm_getstate_str(grp->fsm),
+ fsm_getstate_str(ch->fsm));
+ saveflags = 0; /* avoids compiler warning with
+ spin_unlock_irqrestore */
+
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[15],
+ (unsigned long)ch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+
+ if (rc != 0) {
+ ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n",
+ __FUNCTION__,
+ ch->id);
+ ctcm_ccw_check_rc(ch, rc, "send discontact");
+ /* Not checking return code value here */
+ /* Making best effort to notify partner*/
+ /* that MPC Group is going down */
+ }
+
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
+ return;
+}
+
+
+/*
+ * helper function of mpc FSM
+ * CTCM_PROTO_MPC only
+ * mpc_action_rcvd_xid7
+*/
+static int mpc_validate_xid(struct mpcg_info *mpcginfo)
+{
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct xid2 *xid = mpcginfo->xid;
+ int failed = 0;
+ int rc = 0;
+ __u64 our_id, their_id = 0;
+ int len;
+
+ len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ if (mpcginfo->xid == NULL) {
+ printk(KERN_INFO "%s() xid=NULL\n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ ctcm_pr_debug("ctcmpc : %s xid received()\n", __FUNCTION__);
+ ctcmpc_dumpit((char *)mpcginfo->xid, XID2_LENGTH);
+
+ /*the received direction should be the opposite of ours */
+ if (((CHANNEL_DIRECTION(ch->flags) == READ) ? XID2_WRITE_SIDE :
+ XID2_READ_SIDE) != xid->xid2_dlc_type) {
+ failed = 1;
+ printk(KERN_INFO "ctcmpc:%s() XID REJECTED - READ-WRITE CH "
+ "Pairing Invalid \n", __FUNCTION__);
+ }
+
+ if (xid->xid2_dlc_type == XID2_READ_SIDE) {
+ ctcm_pr_debug("ctcmpc: %s(): grpmaxbuf:%d xid2buflen:%d\n",
+ __FUNCTION__, grp->group_max_buflen,
+ xid->xid2_buf_len);
+
+ if (grp->group_max_buflen == 0 ||
+ grp->group_max_buflen > xid->xid2_buf_len - len)
+ grp->group_max_buflen = xid->xid2_buf_len - len;
+ }
+
+
+ if (grp->saved_xid2 == NULL) {
+ grp->saved_xid2 =
+ (struct xid2 *)skb_tail_pointer(grp->rcvd_xid_skb);
+
+ memcpy(skb_put(grp->rcvd_xid_skb,
+ XID2_LENGTH), xid, XID2_LENGTH);
+ grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
+
+ skb_reset_tail_pointer(grp->rcvd_xid_skb);
+ grp->rcvd_xid_skb->len = 0;
+
+ /* convert two 32 bit numbers into 1 64 bit for id compare */
+ our_id = (__u64)priv->xid->xid2_adj_id;
+ our_id = our_id << 32;
+ our_id = our_id + priv->xid->xid2_sender_id;
+ their_id = (__u64)xid->xid2_adj_id;
+ their_id = their_id << 32;
+ their_id = their_id + xid->xid2_sender_id;
+ /* lower id assume the xside role */
+ if (our_id < their_id) {
+ grp->roll = XSIDE;
+ ctcm_pr_debug("ctcmpc :%s() WE HAVE LOW ID-"
+ "TAKE XSIDE\n", __FUNCTION__);
+ } else {
+ grp->roll = YSIDE;
+ ctcm_pr_debug("ctcmpc :%s() WE HAVE HIGH ID-"
+ "TAKE YSIDE\n", __FUNCTION__);
+ }
+
+ } else {
+ if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) {
+ failed = 1;
+ printk(KERN_INFO "%s XID REJECTED - XID Flag Byte4\n",
+ __FUNCTION__);
+ }
+ if (xid->xid2_flag2 == 0x40) {
+ failed = 1;
+ printk(KERN_INFO "%s XID REJECTED - XID NOGOOD\n",
+ __FUNCTION__);
+ }
+ if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) {
+ failed = 1;
+ printk(KERN_INFO "%s XID REJECTED - "
+ "Adjacent Station ID Mismatch\n",
+ __FUNCTION__);
+ }
+ if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) {
+ failed = 1;
+ printk(KERN_INFO "%s XID REJECTED - "
+ "Sender Address Mismatch\n", __FUNCTION__);
+
+ }
+ }
+
+ if (failed) {
+ ctcm_pr_info("ctcmpc : %s() failed\n", __FUNCTION__);
+ priv->xid->xid2_flag2 = 0x40;
+ grp->saved_xid2->xid2_flag2 = 0x40;
+ rc = 1;
+ }
+
+done:
+
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return rc;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
+{
+ struct channel *ch = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp = NULL;
+ struct net_device *dev = NULL;
+ int rc = 0;
+ int gotlock = 0;
+ unsigned long saveflags = 0; /* avoids compiler warning with
+ spin_unlock_irqrestore */
+
+ if (ch == NULL) {
+ printk(KERN_INFO "%s ch=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+
+ dev = ch->netdev;
+ if (dev == NULL) {
+ printk(KERN_INFO "%s dev=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s priv=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s grp=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+
+ /* skb data-buffer referencing: */
+
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ /* result of the previous 3 statements is NOT always
+ * already set after ctcm_checkalloc_buffer
+ * because of possible reuse of the trans_skb
+ */
+ memset(ch->trans_skb->data, 0, 16);
+ ch->rcvd_xid_th = (struct th_header *)ch->trans_skb_data;
+ /* check is main purpose here: */
+ skb_put(ch->trans_skb, TH_HEADER_LENGTH);
+ ch->rcvd_xid = (struct xid2 *)skb_tail_pointer(ch->trans_skb);
+ /* check is main purpose here: */
+ skb_put(ch->trans_skb, XID2_LENGTH);
+ ch->rcvd_xid_id = skb_tail_pointer(ch->trans_skb);
+ /* cleanup back to startpoint */
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+
+ /* non-checking rewrite of above skb data-buffer referencing: */
+ /*
+ memset(ch->trans_skb->data, 0, 16);
+ ch->rcvd_xid_th = (struct th_header *)ch->trans_skb_data;
+ ch->rcvd_xid = (struct xid2 *)(ch->trans_skb_data + TH_HEADER_LENGTH);
+ ch->rcvd_xid_id = ch->trans_skb_data + TH_HEADER_LENGTH + XID2_LENGTH;
+ */
+
+ ch->ccw[8].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[8].count = 0;
+ ch->ccw[8].cda = 0x00;
+
+ if (side == XSIDE) {
+ /* mpc_action_xside_xid */
+ if (ch->xid_th == NULL) {
+ printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__);
+ goto done;
+ }
+ ch->ccw[9].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[9].count = TH_HEADER_LENGTH;
+ ch->ccw[9].cda = virt_to_phys(ch->xid_th);
+
+ if (ch->xid == NULL) {
+ printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ ch->ccw[10].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[10].count = XID2_LENGTH;
+ ch->ccw[10].cda = virt_to_phys(ch->xid);
+
+ ch->ccw[11].cmd_code = CCW_CMD_READ;
+ ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[11].count = TH_HEADER_LENGTH;
+ ch->ccw[11].cda = virt_to_phys(ch->rcvd_xid_th);
+
+ ch->ccw[12].cmd_code = CCW_CMD_READ;
+ ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[12].count = XID2_LENGTH;
+ ch->ccw[12].cda = virt_to_phys(ch->rcvd_xid);
+
+ ch->ccw[13].cmd_code = CCW_CMD_READ;
+ ch->ccw[13].cda = virt_to_phys(ch->rcvd_xid_id);
+
+ } else { /* side == YSIDE : mpc_action_yside_xid */
+ ch->ccw[9].cmd_code = CCW_CMD_READ;
+ ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[9].count = TH_HEADER_LENGTH;
+ ch->ccw[9].cda = virt_to_phys(ch->rcvd_xid_th);
+
+ ch->ccw[10].cmd_code = CCW_CMD_READ;
+ ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[10].count = XID2_LENGTH;
+ ch->ccw[10].cda = virt_to_phys(ch->rcvd_xid);
+
+ if (ch->xid_th == NULL) {
+ printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__);
+ goto done;
+ }
+ ch->ccw[11].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[11].count = TH_HEADER_LENGTH;
+ ch->ccw[11].cda = virt_to_phys(ch->xid_th);
+
+ if (ch->xid == NULL) {
+ printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__);
+ goto done;
+ }
+ ch->ccw[12].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[12].count = XID2_LENGTH;
+ ch->ccw[12].cda = virt_to_phys(ch->xid);
+
+ if (ch->xid_id == NULL) {
+ printk(KERN_INFO "%s ch->xid_id=NULL\n", __FUNCTION__);
+ goto done;
+ }
+ ch->ccw[13].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[13].cda = virt_to_phys(ch->xid_id);
+
+ }
+ ch->ccw[13].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[13].count = 4;
+
+ ch->ccw[14].cmd_code = CCW_CMD_NOOP;
+ ch->ccw[14].flags = CCW_FLAG_SLI;
+ ch->ccw[14].count = 0;
+ ch->ccw[14].cda = 0;
+
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
+
+ ctcmpc_dumpit((char *)ch->xid_th, TH_HEADER_LENGTH);
+ ctcmpc_dumpit((char *)ch->xid, XID2_LENGTH);
+ ctcmpc_dumpit((char *)ch->xid_id, 4);
+ if (!in_irq()) {
+ /* Such conditional locking is a known problem for
+ * sparse because its static undeterministic.
+ * Warnings should be ignored here. */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ gotlock = 1;
+ }
+
+ fsm_addtimer(&ch->timer, 5000 , CTC_EVENT_TIMER, ch);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[8],
+ (unsigned long)ch, 0xff, 0);
+
+ if (gotlock) /* see remark above about conditional locking */
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+
+ if (rc != 0) {
+ ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n",
+ __FUNCTION__, ch->id);
+ ctcm_ccw_check_rc(ch, rc,
+ (side == XSIDE) ? "x-side XID" : "y-side XID");
+ }
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ return;
+
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg)
+{
+ mpc_action_side_xid(fsm, arg, XSIDE);
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg)
+{
+ mpc_action_side_xid(fsm, arg, YSIDE);
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp = NULL;
+ struct net_device *dev = NULL;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+
+ if (ch == NULL) {
+ printk(KERN_WARNING "%s ch=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ dev = ch->netdev;
+ if (dev == NULL) {
+ printk(KERN_WARNING "%s dev=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_WARNING "%s priv=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_WARNING "%s grp=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ if (ch->xid == NULL) {
+ printk(KERN_WARNING "%s ch-xid=NULL\n", __FUNCTION__);
+ goto done;
+ }
+
+ fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
+
+ ch->xid->xid2_option = XID2_0;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID2INITX:
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ break;
+ }
+
+ fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ return;
+
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+*/
+static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+ int direction;
+ int rc = 0;
+ int send = 0;
+
+ ctcm_pr_debug("ctcmpc enter: %s() \n", __FUNCTION__);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s dev=NULL \n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s priv=NULL \n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s grp=NULL \n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ for (direction = READ; direction <= WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ struct xid2 *thisxid = ch->xid;
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+ thisxid->xid2_option = XID2_7;
+ send = 0;
+
+ /* xid7 phase 1 */
+ if (grp->outstanding_xid7_p2 > 0) {
+ if (grp->roll == YSIDE) {
+ if (fsm_getstate(ch->fsm) == CH_XID7_PENDING1) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING2);
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thdummy, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ } else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING2) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING2);
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ } else {
+ /* xid7 phase 2 */
+ if (grp->roll == YSIDE) {
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING4) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING4);
+ memcpy(skb_put(ch->xid_skb,
+ TH_HEADER_LENGTH),
+ &thnorm, TH_HEADER_LENGTH);
+ ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
+ send = 1;
+ }
+ } else if (fsm_getstate(ch->fsm) == CH_XID7_PENDING3) {
+ fsm_newstate(ch->fsm, CH_XID7_PENDING4);
+ ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
+ memcpy(skb_put(ch->xid_skb, TH_HEADER_LENGTH),
+ &thdummy, TH_HEADER_LENGTH);
+ send = 1;
+ }
+ }
+
+ if (send)
+ fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
+ }
+
+done:
+
+ if (rc != 0)
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ return;
+}
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg)
+{
+
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+
+ ctcm_pr_debug("ctcmpc in:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
+ __FUNCTION__, ch->id,
+ grp->outstanding_xid2,
+ grp->outstanding_xid7,
+ grp->outstanding_xid7_p2);
+
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING)
+ fsm_newstate(ch->fsm, CH_XID7_PENDING);
+
+ grp->outstanding_xid2--;
+ grp->outstanding_xid7++;
+ grp->outstanding_xid7_p2++;
+
+ /* must change state before validating xid to */
+ /* properly handle interim interrupts received*/
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID2INITX:
+ if (grp->outstanding_xid2 == 0) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITW);
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
+ }
+ break;
+ case MPCG_STATE_XID0IOWAIX:
+ if (grp->outstanding_xid2 == 0) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITI);
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
+ }
+ break;
+ }
+ kfree(mpcginfo);
+
+ if (do_debug) {
+ ctcm_pr_debug("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
+ __FUNCTION__, ch->id,
+ grp->outstanding_xid2,
+ grp->outstanding_xid7,
+ grp->outstanding_xid7_p2);
+ ctcm_pr_debug("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
+ __FUNCTION__, ch->id,
+ fsm_getstate_str(grp->fsm),
+ fsm_getstate_str(ch->fsm));
+ }
+ return;
+
+}
+
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
+{
+ struct mpcg_info *mpcginfo = arg;
+ struct channel *ch = mpcginfo->ch;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (do_debug) {
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+
+ ctcm_pr_debug("ctcmpc: outstanding_xid7: %i, "
+ " outstanding_xid7_p2: %i\n",
+ grp->outstanding_xid7,
+ grp->outstanding_xid7_p2);
+ }
+
+ grp->outstanding_xid7--;
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID7INITI:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITZ);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID7INITW:
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITX);
+ mpc_validate_xid(mpcginfo);
+ break;
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID7INITX:
+ if (grp->outstanding_xid7 == 0) {
+ if (grp->outstanding_xid7_p2 > 0) {
+ grp->outstanding_xid7 =
+ grp->outstanding_xid7_p2;
+ grp->outstanding_xid7_p2 = 0;
+ } else
+ fsm_newstate(grp->fsm, MPCG_STATE_XID7INITF);
+
+ mpc_validate_xid(mpcginfo);
+ fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
+ break;
+ }
+ mpc_validate_xid(mpcginfo);
+ break;
+ }
+
+ kfree(mpcginfo);
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+ return;
+
+}
+
+/*
+ * mpc_action helper of an MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static int mpc_send_qllc_discontact(struct net_device *dev)
+{
+ int rc = 0;
+ __u32 new_len = 0;
+ struct sk_buff *skb;
+ struct qllc *qllcptr;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__);
+ rc = 1;
+ goto done;
+ }
+ ctcm_pr_info("ctcmpc: %s() GROUP STATE: %s\n", __FUNCTION__,
+ mpcg_state_names[grp->saved_state]);
+
+ switch (grp->saved_state) {
+ /*
+ * establish conn callback function is
+ * preferred method to report failure
+ */
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ case MPCG_STATE_XID2INITW:
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ break;
+ }
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ grp->send_qllc_disc = 2;
+ new_len = sizeof(struct qllc);
+ qllcptr = kzalloc(new_len, gfp_type() | GFP_DMA);
+ if (qllcptr == NULL) {
+ printk(KERN_INFO
+ "ctcmpc: Out of memory in %s()\n",
+ dev->name);
+ rc = 1;
+ goto done;
+ }
+
+ qllcptr->qllc_address = 0xcc;
+ qllcptr->qllc_commands = 0x03;
+
+ skb = __dev_alloc_skb(new_len, GFP_ATOMIC);
+
+ if (skb == NULL) {
+ printk(KERN_INFO "%s Out of memory in mpc_send_qllc\n",
+ dev->name);
+ priv->stats.rx_dropped++;
+ rc = 1;
+ kfree(qllcptr);
+ goto done;
+ }
+
+ memcpy(skb_put(skb, new_len), qllcptr, new_len);
+ kfree(qllcptr);
+
+ if (skb_headroom(skb) < 4) {
+ printk(KERN_INFO "ctcmpc: %s() Unable to"
+ " build discontact for %s\n",
+ __FUNCTION__, dev->name);
+ rc = 1;
+ dev_kfree_skb_any(skb);
+ goto done;
+ }
+
+ *((__u32 *)skb_push(skb, 4)) = priv->channel[READ]->pdu_seq;
+ priv->channel[READ]->pdu_seq++;
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
+ __FUNCTION__, priv->channel[READ]->pdu_seq);
+
+ /* receipt of CC03 resets anticipated sequence number on
+ receiving side */
+ priv->channel[READ]->pdu_seq = 0x00;
+ skb_reset_mac_header(skb);
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_SNAP);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ ctcmpc_dumpit((char *)skb->data, (sizeof(struct qllc) + 4));
+
+ netif_rx(skb);
+ break;
+ default:
+ break;
+
+ }
+
+done:
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return rc;
+}
+/* --- This is the END my friend --- */
+
Index: linux-2.6-uschi/drivers/s390/net/ctcm_mpc.h
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_mpc.h
@@ -0,0 +1,239 @@
+/*
+ * drivers/s390/net/ctcm_mpc.h
+ *
+ * Copyright IBM Corp. 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ * MPC additions:
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+
+#ifndef _CTC_MPC_H_
+#define _CTC_MPC_H_
+
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+/*
+ * MPC external interface
+ * Note that ctc_mpc_xyz are called with a lock on ................
+ */
+
+/* port_number is the mpc device 0, 1, 2 etc mpc2 is port_number 2 */
+
+/* passive open Just wait for XID2 exchange */
+extern int ctc_mpc_alloc_channel(int port,
+ void (*callback)(int port_num, int max_write_size));
+/* active open Alloc then send XID2 */
+extern void ctc_mpc_establish_connectivity(int port,
+ void (*callback)(int port_num, int rc, int max_write_size));
+
+extern void ctc_mpc_dealloc_ch(int port);
+extern void ctc_mpc_flow_control(int port, int flowc);
+
+/*
+ * other MPC Group prototypes and structures
+ */
+
+#define ETH_P_SNA_DIX 0x80D5
+
+/*
+ * Declaration of an XID2
+ *
+ */
+#define ALLZEROS 0x0000000000000000
+
+#define XID_FM2 0x20
+#define XID2_0 0x00
+#define XID2_7 0x07
+#define XID2_WRITE_SIDE 0x04
+#define XID2_READ_SIDE 0x05
+
+struct xid2 {
+ __u8 xid2_type_id;
+ __u8 xid2_len;
+ __u32 xid2_adj_id;
+ __u8 xid2_rlen;
+ __u8 xid2_resv1;
+ __u8 xid2_flag1;
+ __u8 xid2_fmtt;
+ __u8 xid2_flag4;
+ __u16 xid2_resv2;
+ __u8 xid2_tgnum;
+ __u32 xid2_sender_id;
+ __u8 xid2_flag2;
+ __u8 xid2_option;
+ char xid2_resv3[8];
+ __u16 xid2_resv4;
+ __u8 xid2_dlc_type;
+ __u16 xid2_resv5;
+ __u8 xid2_mpc_flag;
+ __u8 xid2_resv6;
+ __u16 xid2_buf_len;
+ char xid2_buffer[255 - (13 * sizeof(__u8) +
+ 2 * sizeof(__u32) +
+ 4 * sizeof(__u16) +
+ 8 * sizeof(char))];
+} __attribute__ ((packed));
+
+#define XID2_LENGTH (sizeof(struct xid2))
+
+struct th_header {
+ __u8 th_seg;
+ __u8 th_ch_flag;
+#define TH_HAS_PDU 0xf0
+#define TH_IS_XID 0x01
+#define TH_SWEEP_REQ 0xfe
+#define TH_SWEEP_RESP 0xff
+ __u8 th_blk_flag;
+#define TH_DATA_IS_XID 0x80
+#define TH_RETRY 0x40
+#define TH_DISCONTACT 0xc0
+#define TH_SEG_BLK 0x20
+#define TH_LAST_SEG 0x10
+#define TH_PDU_PART 0x08
+ __u8 th_is_xid; /* is 0x01 if this is XID */
+ __u32 th_seq_num;
+} __attribute__ ((packed));
+
+struct th_addon {
+ __u32 th_last_seq;
+ __u32 th_resvd;
+} __attribute__ ((packed));
+
+struct th_sweep {
+ struct th_header th;
+ struct th_addon sw;
+} __attribute__ ((packed));
+
+#define TH_HEADER_LENGTH (sizeof(struct th_header))
+#define TH_SWEEP_LENGTH (sizeof(struct th_sweep))
+
+#define PDU_LAST 0x80
+#define PDU_CNTL 0x40
+#define PDU_FIRST 0x20
+
+struct pdu {
+ __u32 pdu_offset;
+ __u8 pdu_flag;
+ __u8 pdu_proto; /* 0x01 is APPN SNA */
+ __u16 pdu_seq;
+} __attribute__ ((packed));
+
+#define PDU_HEADER_LENGTH (sizeof(struct pdu))
+
+struct qllc {
+ __u8 qllc_address;
+#define QLLC_REQ 0xFF
+#define QLLC_RESP 0x00
+ __u8 qllc_commands;
+#define QLLC_DISCONNECT 0x53
+#define QLLC_UNSEQACK 0x73
+#define QLLC_SETMODE 0x93
+#define QLLC_EXCHID 0xBF
+} __attribute__ ((packed));
+
+
+/*
+ * Definition of one MPC group
+ */
+
+#define MAX_MPCGCHAN 10
+#define MPC_XID_TIMEOUT_VALUE 10000
+#define MPC_CHANNEL_ADD 0
+#define MPC_CHANNEL_REMOVE 1
+#define MPC_CHANNEL_ATTN 2
+#define XSIDE 1
+#define YSIDE 0
+
+struct mpcg_info {
+ struct sk_buff *skb;
+ struct channel *ch;
+ struct xid2 *xid;
+ struct th_sweep *sweep;
+ struct th_header *th;
+};
+
+struct mpc_group {
+ struct tasklet_struct mpc_tasklet;
+ struct tasklet_struct mpc_tasklet2;
+ int changed_side;
+ int saved_state;
+ int channels_terminating;
+ int out_of_sequence;
+ int flow_off_called;
+ int port_num;
+ int port_persist;
+ int alloc_called;
+ __u32 xid2_adj_id;
+ __u8 xid2_tgnum;
+ __u32 xid2_sender_id;
+ int num_channel_paths;
+ int active_channels[2];
+ __u16 group_max_buflen;
+ int outstanding_xid2;
+ int outstanding_xid7;
+ int outstanding_xid7_p2;
+ int sweep_req_pend_num;
+ int sweep_rsp_pend_num;
+ struct sk_buff *xid_skb;
+ char *xid_skb_data;
+ struct th_header *xid_th;
+ struct xid2 *xid;
+ char *xid_id;
+ struct th_header *rcvd_xid_th;
+ struct sk_buff *rcvd_xid_skb;
+ char *rcvd_xid_data;
+ __u8 in_sweep;
+ __u8 roll;
+ struct xid2 *saved_xid2;
+ void (*allochanfunc)(int, int);
+ int allocchan_callback_retries;
+ void (*estconnfunc)(int, int, int);
+ int estconn_callback_retries;
+ int estconn_called;
+ int xidnogood;
+ int send_qllc_disc;
+ fsm_timer timer;
+ fsm_instance *fsm; /* group xid fsm */
+};
+
+#ifdef DEBUGDATA
+void ctcmpc_dumpit(char *buf, int len);
+#else
+static inline void ctcmpc_dumpit(char *buf, int len)
+{
+}
+#endif
+
+#ifdef DEBUGDATA
+/*
+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
+ *
+ * skb The struct sk_buff to dump.
+ * offset Offset relative to skb-data, where to start the dump.
+ */
+void ctcmpc_dump_skb(struct sk_buff *skb, int offset);
+#else
+static inline void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
+{}
+#endif
+
+static inline void ctcmpc_dump32(char *buf, int len)
+{
+ if (len < 32)
+ ctcmpc_dumpit(buf, len);
+ else
+ ctcmpc_dumpit(buf, 32);
+}
+
+int ctcmpc_open(struct net_device *);
+void ctcm_ccw_check_rc(struct channel *, int, char *);
+void mpc_group_ready(unsigned long adev);
+int mpc_channel_action(struct channel *ch, int direction, int action);
+void mpc_action_send_discontact(unsigned long thischan);
+void mpc_action_discontact(fsm_instance *fi, int event, void *arg);
+void ctcmpc_bh(unsigned long thischan);
+#endif
+/* --- This is the END my friend --- */
Index: linux-2.6-uschi/drivers/s390/net/ctcm_sysfs.c
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_sysfs.c
@@ -0,0 +1,210 @@
+/*
+ * drivers/s390/net/ctcm_sysfs.c
+ *
+ * Copyright IBM Corp. 2007, 2007
+ * Authors: Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#include <linux/sysfs.h>
+#include "ctcm_main.h"
+
+/*
+ * sysfs attributes
+ */
+
+static ssize_t ctcm_buffer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+ return sprintf(buf, "%d\n", priv->buffer_size);
+}
+
+static ssize_t ctcm_buffer_write(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct net_device *ndev;
+ int bs1;
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!(priv && priv->channel[READ] &&
+ (ndev = priv->channel[READ]->netdev))) {
+ CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, "bfnondev");
+ return -ENODEV;
+ }
+
+ sscanf(buf, "%u", &bs1);
+ if (bs1 > CTCM_BUFSIZE_LIMIT)
+ goto einval;
+ if (bs1 < (576 + LL_HEADER_LENGTH + 2))
+ goto einval;
+ priv->buffer_size = bs1; /* just to overwrite the default */
+
+ if ((ndev->flags & IFF_RUNNING) &&
+ (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
+ goto einval;
+
+ priv->channel[READ]->max_bufsize = bs1;
+ priv->channel[WRITE]->max_bufsize = bs1;
+ if (!(ndev->flags & IFF_RUNNING))
+ ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
+ priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+ priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
+
+ CTCM_DBF_DEV(SETUP, ndev, buf);
+ return count;
+
+einval:
+ CTCM_DBF_DEV(SETUP, ndev, "buff_err");
+ return -EINVAL;
+}
+
+static void ctcm_print_statistics(struct ctcm_priv *priv)
+{
+ char *sbuf;
+ char *p;
+
+ if (!priv)
+ return;
+ sbuf = kmalloc(2048, GFP_KERNEL);
+ if (sbuf == NULL)
+ return;
+ p = sbuf;
+
+ p += sprintf(p, " Device FSM state: %s\n",
+ fsm_getstate_str(priv->fsm));
+ p += sprintf(p, " RX channel FSM state: %s\n",
+ fsm_getstate_str(priv->channel[READ]->fsm));
+ p += sprintf(p, " TX channel FSM state: %s\n",
+ fsm_getstate_str(priv->channel[WRITE]->fsm));
+ p += sprintf(p, " Max. TX buffer used: %ld\n",
+ priv->channel[WRITE]->prof.maxmulti);
+ p += sprintf(p, " Max. chained SKBs: %ld\n",
+ priv->channel[WRITE]->prof.maxcqueue);
+ p += sprintf(p, " TX single write ops: %ld\n",
+ priv->channel[WRITE]->prof.doios_single);
+ p += sprintf(p, " TX multi write ops: %ld\n",
+ priv->channel[WRITE]->prof.doios_multi);
+ p += sprintf(p, " Netto bytes written: %ld\n",
+ priv->channel[WRITE]->prof.txlen);
+ p += sprintf(p, " Max. TX IO-time: %ld\n",
+ priv->channel[WRITE]->prof.tx_time);
+
+ printk(KERN_INFO "Statistics for %s:\n%s",
+ priv->channel[WRITE]->netdev->name, sbuf);
+ kfree(sbuf);
+ return;
+}
+
+static ssize_t stats_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return -ENODEV;
+ ctcm_print_statistics(priv);
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t stats_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return -ENODEV;
+ /* Reset statistics */
+ memset(&priv->channel[WRITE]->prof, 0,
+ sizeof(priv->channel[WRITE]->prof));
+ return count;
+}
+
+static ssize_t ctcm_proto_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+ if (!priv)
+ return -ENODEV;
+
+ return sprintf(buf, "%d\n", priv->protocol);
+}
+
+static ssize_t ctcm_proto_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value;
+ struct ctcm_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+ sscanf(buf, "%u", &value);
+ if (!((value == CTCM_PROTO_S390) ||
+ (value == CTCM_PROTO_LINUX) ||
+ (value == CTCM_PROTO_MPC) ||
+ (value == CTCM_PROTO_OS390)))
+ return -EINVAL;
+ priv->protocol = value;
+ CTCM_DBF_DEV(SETUP, dev, buf);
+
+ return count;
+}
+
+static ssize_t ctcm_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ccwgroup_device *cgdev;
+
+ cgdev = to_ccwgroupdev(dev);
+ if (!cgdev)
+ return -ENODEV;
+
+ return sprintf(buf, "%s\n",
+ cu3088_type[cgdev->cdev[0]->id.driver_info]);
+}
+
+static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write);
+static DEVICE_ATTR(protocol, 0644, ctcm_proto_show, ctcm_proto_store);
+static DEVICE_ATTR(type, 0444, ctcm_type_show, NULL);
+static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
+
+static struct attribute *ctcm_attr[] = {
+ &dev_attr_protocol.attr,
+ &dev_attr_type.attr,
+ &dev_attr_buffer.attr,
+ NULL,
+};
+
+static struct attribute_group ctcm_attr_group = {
+ .attrs = ctcm_attr,
+};
+
+int ctcm_add_attributes(struct device *dev)
+{
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_stats);
+
+ return rc;
+}
+
+void ctcm_remove_attributes(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_stats);
+}
+
+int ctcm_add_files(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &ctcm_attr_group);
+}
+
+void ctcm_remove_files(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &ctcm_attr_group);
+}
+
Index: linux-2.6-uschi/drivers/s390/net/ctcm_fsms.c
===================================================================
--- /dev/null
+++ linux-2.6-uschi/drivers/s390/net/ctcm_fsms.c
@@ -0,0 +1,2347 @@
+/*
+ * drivers/s390/net/ctcm_fsms.c
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Authors: Fritz Elfert (felfert@millenux.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ * MPC additions :
+ * Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ */
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <net/dst.h>
+
+#include <linux/io.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/uaccess.h>
+
+#include <asm/idals.h>
+
+#include "fsm.h"
+#include "cu3088.h"
+
+#include "ctcm_dbug.h"
+#include "ctcm_main.h"
+#include "ctcm_fsms.h"
+
+const char *dev_state_names[] = {
+ [DEV_STATE_STOPPED] = "Stopped",
+ [DEV_STATE_STARTWAIT_RXTX] = "StartWait RXTX",
+ [DEV_STATE_STARTWAIT_RX] = "StartWait RX",
+ [DEV_STATE_STARTWAIT_TX] = "StartWait TX",
+ [DEV_STATE_STOPWAIT_RXTX] = "StopWait RXTX",
+ [DEV_STATE_STOPWAIT_RX] = "StopWait RX",
+ [DEV_STATE_STOPWAIT_TX] = "StopWait TX",
+ [DEV_STATE_RUNNING] = "Running",
+};
+
+const char *dev_event_names[] = {
+ [DEV_EVENT_START] = "Start",
+ [DEV_EVENT_STOP] = "Stop",
+ [DEV_EVENT_RXUP] = "RX up",
+ [DEV_EVENT_TXUP] = "TX up",
+ [DEV_EVENT_RXDOWN] = "RX down",
+ [DEV_EVENT_TXDOWN] = "TX down",
+ [DEV_EVENT_RESTART] = "Restart",
+};
+
+const char *ctc_ch_event_names[] = {
+ [CTC_EVENT_IO_SUCCESS] = "ccw_device success",
+ [CTC_EVENT_IO_EBUSY] = "ccw_device busy",
+ [CTC_EVENT_IO_ENODEV] = "ccw_device enodev",
+ [CTC_EVENT_IO_UNKNOWN] = "ccw_device unknown",
+ [CTC_EVENT_ATTNBUSY] = "Status ATTN & BUSY",
+ [CTC_EVENT_ATTN] = "Status ATTN",
+ [CTC_EVENT_BUSY] = "Status BUSY",
+ [CTC_EVENT_UC_RCRESET] = "Unit check remote reset",
+ [CTC_EVENT_UC_RSRESET] = "Unit check remote system reset",
+ [CTC_EVENT_UC_TXTIMEOUT] = "Unit check TX timeout",
+ [CTC_EVENT_UC_TXPARITY] = "Unit check TX parity",
+ [CTC_EVENT_UC_HWFAIL] = "Unit check Hardware failure",
+ [CTC_EVENT_UC_RXPARITY] = "Unit check RX parity",
+ [CTC_EVENT_UC_ZERO] = "Unit check ZERO",
+ [CTC_EVENT_UC_UNKNOWN] = "Unit check Unknown",
+ [CTC_EVENT_SC_UNKNOWN] = "SubChannel check Unknown",
+ [CTC_EVENT_MC_FAIL] = "Machine check failure",
+ [CTC_EVENT_MC_GOOD] = "Machine check operational",
+ [CTC_EVENT_IRQ] = "IRQ normal",
+ [CTC_EVENT_FINSTAT] = "IRQ final",
+ [CTC_EVENT_TIMER] = "Timer",
+ [CTC_EVENT_START] = "Start",
+ [CTC_EVENT_STOP] = "Stop",
+ /*
+ * additional MPC events
+ */
+ [CTC_EVENT_SEND_XID] = "XID Exchange",
+ [CTC_EVENT_RSWEEP_TIMER] = "MPC Group Sweep Timer",
+};
+
+const char *ctc_ch_state_names[] = {
+ [CTC_STATE_IDLE] = "Idle",
+ [CTC_STATE_STOPPED] = "Stopped",
+ [CTC_STATE_STARTWAIT] = "StartWait",
+ [CTC_STATE_STARTRETRY] = "StartRetry",
+ [CTC_STATE_SETUPWAIT] = "SetupWait",
+ [CTC_STATE_RXINIT] = "RX init",
+ [CTC_STATE_TXINIT] = "TX init",
+ [CTC_STATE_RX] = "RX",
+ [CTC_STATE_TX] = "TX",
+ [CTC_STATE_RXIDLE] = "RX idle",
+ [CTC_STATE_TXIDLE] = "TX idle",
+ [CTC_STATE_RXERR] = "RX error",
+ [CTC_STATE_TXERR] = "TX error",
+ [CTC_STATE_TERM] = "Terminating",
+ [CTC_STATE_DTERM] = "Restarting",
+ [CTC_STATE_NOTOP] = "Not operational",
+ /*
+ * additional MPC states
+ */
+ [CH_XID0_PENDING] = "Pending XID0 Start",
+ [CH_XID0_INPROGRESS] = "In XID0 Negotiations ",
+ [CH_XID7_PENDING] = "Pending XID7 P1 Start",
+ [CH_XID7_PENDING1] = "Active XID7 P1 Exchange ",
+ [CH_XID7_PENDING2] = "Pending XID7 P2 Start ",
+ [CH_XID7_PENDING3] = "Active XID7 P2 Exchange ",
+ [CH_XID7_PENDING4] = "XID7 Complete - Pending READY ",
+};
+
+static void ctcm_action_nop(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- static ctcm actions for channel statemachine -----
+ *
+*/
+static void chx_txdone(fsm_instance *fi, int event, void *arg);
+static void chx_rx(fsm_instance *fi, int event, void *arg);
+static void chx_rxidle(fsm_instance *fi, int event, void *arg);
+static void chx_firstio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
+
+/*
+ * ----- static ctcmpc actions for ctcmpc channel statemachine -----
+ *
+*/
+static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg);
+static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg);
+static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg);
+/* shared :
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
+*/
+static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg);
+static void ctcmpc_chx_attnbusy(fsm_instance *, int, void *);
+static void ctcmpc_chx_resend(fsm_instance *, int, void *);
+static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
+
+/**
+ * Check return code of a preceeding ccw_device call, halt_IO etc...
+ *
+ * ch : The channel, the error belongs to.
+ * Returns the error code (!= 0) to inspect.
+ */
+void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
+{
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "ccw error %s (%s): %04x\n", ch->id, msg, rc);
+ switch (rc) {
+ case -EBUSY:
+ ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg);
+ fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
+ break;
+ case -ENODEV:
+ ctcm_pr_emerg("%s (%s): Invalid device called for IO\n",
+ ch->id, msg);
+ fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
+ break;
+ default:
+ ctcm_pr_emerg("%s (%s): Unknown error in do_IO %04x\n",
+ ch->id, msg, rc);
+ fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
+ }
+}
+
+void ctcm_purge_skb_queue(struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+
+ CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+
+ while ((skb = skb_dequeue(q))) {
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+/**
+ * NOP action for statemachines
+ */
+static void ctcm_action_nop(fsm_instance *fi, int event, void *arg)
+{
+}
+
+/*
+ * Actions for channel - statemachines.
+ */
+
+/**
+ * Normal data has been send. Free the corresponding
+ * skb (it's in io_queue), reset dev->tbusy and
+ * revert to idle state.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_txdone(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct sk_buff *skb;
+ int first = 1;
+ int i;
+ unsigned long duration;
+ struct timespec done_stamp = current_kernel_time(); /* xtime */
+
+ duration =
+ (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
+ (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
+ if (duration > ch->prof.tx_time)
+ ch->prof.tx_time = duration;
+
+ if (ch->irb->scsw.count != 0)
+ ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
+ dev->name, ch->irb->scsw.count);
+ fsm_deltimer(&ch->timer);
+ while ((skb = skb_dequeue(&ch->io_queue))) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ if (first) {
+ priv->stats.tx_bytes += 2;
+ first = 0;
+ }
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ }
+ spin_lock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[4]);
+ if (ch->collect_len > 0) {
+ int rc;
+
+ if (ctcm_checkalloc_buffer(ch)) {
+ spin_unlock(&ch->collect_lock);
+ return;
+ }
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ if (ch->prof.maxmulti < (ch->collect_len + 2))
+ ch->prof.maxmulti = ch->collect_len + 2;
+ if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
+ ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
+ *((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
+ i = 0;
+ while ((skb = skb_dequeue(&ch->collect_queue))) {
+ skb_copy_from_linear_data(skb,
+ skb_put(ch->trans_skb, skb->len), skb->len);
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ i++;
+ }
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ ch->ccw[1].count = ch->trans_skb->len;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ ch->prof.doios_multi++;
+ if (rc != 0) {
+ priv->stats.tx_dropped += i;
+ priv->stats.tx_errors += i;
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "chained TX");
+ }
+ } else {
+ spin_unlock(&ch->collect_lock);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ }
+ ctcm_clear_busy_do(dev);
+}
+
+/**
+ * Initial data is sent.
+ * Notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev);
+}
+
+/**
+ * Got normal data, check for sanity, queue it up, allocate new buffer
+ * trigger bottom half, and initiate next read.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_rx(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ int len = ch->max_bufsize - ch->irb->scsw.count;
+ struct sk_buff *skb = ch->trans_skb;
+ __u16 block_len = *((__u16 *)skb->data);
+ int check_len;
+ int rc;
+
+ fsm_deltimer(&ch->timer);
+ if (len < 8) {
+ ctcm_pr_debug("%s: got packet with length %d < 8\n",
+ dev->name, len);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+ if (len > ch->max_bufsize) {
+ ctcm_pr_debug("%s: got packet with length %d > %d\n",
+ dev->name, len, ch->max_bufsize);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+
+ /*
+ * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
+ */
+ switch (ch->protocol) {
+ case CTCM_PROTO_S390:
+ case CTCM_PROTO_OS390:
+ check_len = block_len + 2;
+ break;
+ default:
+ check_len = block_len;
+ break;
+ }
+ if ((len < block_len) || (len > check_len)) {
+ ctcm_pr_debug("%s: got block length %d != rx length %d\n",
+ dev->name, block_len, len);
+ if (do_debug)
+ ctcmpc_dump_skb(skb, 0);
+
+ *((__u16 *)skb->data) = len;
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ goto again;
+ }
+ block_len -= 2;
+ if (block_len > 0) {
+ *((__u16 *)skb->data) = block_len;
+ ctcm_unpack_skb(ch, skb);
+ }
+ again:
+ skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ ch->ccw[1].count = ch->max_bufsize;
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc, "normal RX");
+}
+
+/**
+ * Initialize connection by sending a __u16 of value 0.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_firstio(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ int rc;
+
+ CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
+
+ if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
+ ctcm_pr_debug("%s: remote side issued READ?, init.\n", ch->id);
+ fsm_deltimer(&ch->timer);
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
+ (ch->protocol == CTCM_PROTO_OS390)) {
+ /* OS/390 resp. z/OS */
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ *((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC,
+ CTC_EVENT_TIMER, ch);
+ chx_rxidle(fi, event, arg);
+ } else {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
+ }
+ return;
+ }
+
+ /*
+ * Don't setup a timer for receiving the initial RX frame
+ * if in compatibility mode, since VM TCP delays the initial
+ * frame until it has some data to send.
+ */
+ if ((CHANNEL_DIRECTION(ch->flags) == WRITE) ||
+ (ch->protocol != CTCM_PROTO_S390))
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ *((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
+ ch->ccw[1].count = 2; /* Transfer only length */
+
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
+ ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_SETUPWAIT);
+ ctcm_ccw_check_rc(ch, rc, "init IO");
+ }
+ /*
+ * If in compatibility mode since we don't setup a timer, we
+ * also signal RX channel up immediately. This enables us
+ * to send packets early which in turn usually triggers some
+ * reply from VM TCP which brings up the RX channel to it's
+ * final state.
+ */
+ if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
+ (ch->protocol == CTCM_PROTO_S390)) {
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+ }
+}
+
+/**
+ * Got initial data, check it. If OK,
+ * notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void chx_rxidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ __u16 buflen;
+ int rc;
+
+ CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__);
+ fsm_deltimer(&ch->timer);
+ buflen = *((__u16 *)ch->trans_skb->data);
+ if (do_debug)
+ ctcm_pr_debug("%s: Initial RX count %d\n", dev->name, buflen);
+
+ if (buflen >= CTCM_INITIAL_BLOCKLEN) {
+ if (ctcm_checkalloc_buffer(ch))
+ return;
+ ch->ccw[1].count = ch->max_bufsize;
+ fsm_newstate(fi, CTC_STATE_RXIDLE);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (rc != 0) {
+ fsm_newstate(fi, CTC_STATE_RXINIT);
+ ctcm_ccw_check_rc(ch, rc, "initial RX");
+ } else
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+ } else {
+ if (do_debug)
+ ctcm_pr_debug("%s: Initial RX count %d not %d\n",
+ dev->name, buflen, CTCM_INITIAL_BLOCKLEN);
+ chx_firstio(fi, event, arg);
+ }
+}
+
+/**
+ * Set channel into extended mode.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ int rc;
+ unsigned long saveflags = 0;
+ int timeout = CTCM_TIME_5_SEC;
+
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch)) {
+ timeout = 1500;
+ if (do_debug)
+ ctcm_pr_debug("ctcm enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+ }
+ fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
+ fsm_newstate(fi, CTC_STATE_SETUPWAIT);
+ if (do_debug_ccw && IS_MPC(ch))
+ ctcmpc_dumpit((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
+
+ if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is undeterministic in
+ * static view. => ignore sparse warnings here. */
+
+ rc = ccw_device_start(ch->cdev, &ch->ccw[6],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_TIMER) /* see above comments */
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ ctcm_ccw_check_rc(ch, rc, "set Mode");
+ } else
+ ch->retry = 0;
+}
+
+/**
+ * Setup channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ int rc;
+ struct net_device *dev;
+ unsigned long saveflags;
+
+ CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__);
+ if (ch == NULL) {
+ ctcm_pr_warn("chx_start ch=NULL\n");
+ return;
+ }
+ if (ch->netdev == NULL) {
+ ctcm_pr_warn("chx_start dev=NULL, id=%s\n", ch->id);
+ return;
+ }
+ dev = ch->netdev;
+
+ if (do_debug)
+ ctcm_pr_debug("%s: %s channel start\n", dev->name,
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+
+ if (ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb(ch->trans_skb);
+ ch->trans_skb = NULL;
+ }
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ ch->ccw[1].cmd_code = CCW_CMD_READ;
+ ch->ccw[1].flags = CCW_FLAG_SLI;
+ ch->ccw[1].count = 0;
+ } else {
+ ch->ccw[1].cmd_code = CCW_CMD_WRITE;
+ ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[1].count = 0;
+ }
+ if (ctcm_checkalloc_buffer(ch)) {
+ ctcm_pr_notice("%s: %s trans_skb allocation delayed "
+ "until first transfer\n", dev->name,
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ }
+
+ ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
+ ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+ ch->ccw[0].count = 0;
+ ch->ccw[0].cda = 0;
+ ch->ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE + DE */
+ ch->ccw[2].flags = CCW_FLAG_SLI;
+ ch->ccw[2].count = 0;
+ ch->ccw[2].cda = 0;
+ memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(struct ccw1) * 3);
+ ch->ccw[4].cda = 0;
+ ch->ccw[4].flags &= ~CCW_FLAG_IDA;
+
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ if (rc != -EBUSY)
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "initial HaltIO");
+ }
+}
+
+/**
+ * Shutdown a channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ unsigned long saveflags = 0;
+ int rc;
+ int oldstate;
+
+ CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__);
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+
+ if (event == CTC_EVENT_STOP) /* only for STOP not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is undeterministic in
+ * static view. => ignore sparse warnings here. */
+ oldstate = fsm_getstate(fi);
+ fsm_newstate(fi, CTC_STATE_TERM);
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+
+ if (event == CTC_EVENT_STOP)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ /* see remark above about conditional locking */
+
+ if (rc != 0 && rc != -EBUSY) {
+ fsm_deltimer(&ch->timer);
+ if (event != CTC_EVENT_STOP) {
+ fsm_newstate(fi, oldstate);
+ ctcm_ccw_check_rc(ch, rc, (char *)__FUNCTION__);
+ }
+ }
+}
+
+/**
+ * Cleanup helper for chx_fail and chx_stopped
+ * cleanup channels queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * state The next state (depending on caller).
+ * ch The channel to operate on.
+ */
+static void ctcm_chx_cleanup(fsm_instance *fi, int state,
+ struct channel *ch)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+
+ fsm_deltimer(&ch->timer);
+ if (IS_MPC(ch))
+ fsm_deltimer(&ch->sweep_timer);
+
+ fsm_newstate(fi, state);
+ if (state == CTC_STATE_STOPPED && ch->trans_skb != NULL) {
+ clear_normalized_cda(&ch->ccw[1]);
+ dev_kfree_skb_any(ch->trans_skb);
+ ch->trans_skb = NULL;
+ }
+
+ ch->th_seg = 0x00;
+ ch->th_seq_num = 0x00;
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ skb_queue_purge(&ch->io_queue);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ ctcm_purge_skb_queue(&ch->io_queue);
+ if (IS_MPC(ch))
+ ctcm_purge_skb_queue(&ch->sweep_queue);
+ spin_lock(&ch->collect_lock);
+ ctcm_purge_skb_queue(&ch->collect_queue);
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/**
+ * A channel has successfully been halted.
+ * Cleanup it's queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg)
+{
+ CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+ ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg);
+}
+
+/**
+ * A stop command from device statemachine arrived and we are in
+ * not operational mode. Set state to stopped.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg)
+{
+ fsm_newstate(fi, CTC_STATE_STOPPED);
+}
+
+/**
+ * A machine check for no path, not operational status or gone device has
+ * happened.
+ * Cleanup queue and notify interface statemachine.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg)
+{
+ CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+ ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg);
+}
+
+/**
+ * Handle error during setup of channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ /*
+ * Special case: Got UC_RCRESET on setmode.
+ * This means that remote side isn't setup. In this case
+ * simply retry after some 10 secs...
+ */
+ if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
+ ((event == CTC_EVENT_UC_RCRESET) ||
+ (event == CTC_EVENT_UC_RSRESET))) {
+ fsm_newstate(fi, CTC_STATE_STARTRETRY);
+ fsm_deltimer(&ch->timer);
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ if (!IS_MPC(ch) && (CHANNEL_DIRECTION(ch->flags) == READ)) {
+ int rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc,
+ "HaltIO in chx_setuperr");
+ }
+ return;
+ }
+
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
+ "%s : %s error during %s channel setup state=%s\n",
+ dev->name, ctc_ch_event_names[event],
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
+ fsm_getstate_str(fi));
+
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/**
+ * Restart a channel after an error.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ unsigned long saveflags = 0;
+ int oldstate;
+ int rc;
+
+ CTCM_DBF_TEXT(TRACE, CTC_DBF_NOTICE, __FUNCTION__);
+ fsm_deltimer(&ch->timer);
+ ctcm_pr_debug("%s: %s channel restart\n", dev->name,
+ (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ oldstate = fsm_getstate(fi);
+ fsm_newstate(fi, CTC_STATE_STARTWAIT);
+ if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is a known problem for
+ * sparse because its undeterministic in static view.
+ * Warnings should be ignored here. */
+ rc = ccw_device_halt(ch->cdev, (unsigned long)ch);
+ if (event == CTC_EVENT_TIMER)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ if (rc != -EBUSY) {
+ fsm_deltimer(&ch->timer);
+ fsm_newstate(fi, oldstate);
+ }
+ ctcm_ccw_check_rc(ch, rc, "HaltIO in ctcm_chx_restart");
+ }
+}
+
+/**
+ * Handle error during RX initial handshake (exchange of
+ * 0-length block header)
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__);
+ if (event == CTC_EVENT_TIMER) {
+ if (!IS_MPCDEV(dev))
+ /* TODO : check if MPC deletes timer somewhere */
+ fsm_deltimer(&ch->timer);
+ ctcm_pr_debug("%s: Timeout during RX init handshake\n",
+ dev->name);
+ if (ch->retry++ < 3)
+ ctcm_chx_restart(fi, event, arg);
+ else {
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ }
+ } else
+ ctcm_pr_warn("%s: Error during RX init handshake\n", dev->name);
+}
+
+/**
+ * Notify device statemachine if we gave up initialization
+ * of RX channel.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__);
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ ctcm_pr_warn("%s: RX busy. Initialization failed\n", dev->name);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+}
+
+/**
+ * Handle RX Unit check remote reset (remote disconnected)
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct channel *ch2;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_DEV_NAME(TRACE, dev, "Got remote disconnect, re-initializing");
+ fsm_deltimer(&ch->timer);
+ if (do_debug)
+ ctcm_pr_debug("%s: Got remote disconnect, "
+ "re-initializing ...\n", dev->name);
+ /*
+ * Notify device statemachine
+ */
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+
+ fsm_newstate(fi, CTC_STATE_DTERM);
+ ch2 = priv->channel[WRITE];
+ fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
+
+ ccw_device_halt(ch->cdev, (unsigned long)ch);
+ ccw_device_halt(ch2->cdev, (unsigned long)ch2);
+}
+
+/**
+ * Handle error during TX channel initialization.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ if (event == CTC_EVENT_TIMER) {
+ fsm_deltimer(&ch->timer);
+ CTCM_DBF_DEV_NAME(ERROR, dev,
+ "Timeout during TX init handshake");
+ if (ch->retry++ < 3)
+ ctcm_chx_restart(fi, event, arg);
+ else {
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+ } else {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s : %s error during channel setup state=%s",
+ dev->name, ctc_ch_event_names[event],
+ fsm_getstate_str(fi));
+
+ ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name);
+ }
+}
+
+/**
+ * Handle TX timeout by retrying operation.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct sk_buff *skb;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+
+ fsm_deltimer(&ch->timer);
+ if (ch->retry++ > 3) {
+ struct mpc_group *gptr = priv->mpcg;
+ ctcm_pr_debug("%s: TX retry failed, restarting channel\n",
+ dev->name);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ /* call restart if not MPC or if MPC and mpcg fsm is ready.
+ use gptr as mpc indicator */
+ if (!(gptr && (fsm_getstate(gptr->fsm) != MPCG_STATE_READY)))
+ ctcm_chx_restart(fi, event, arg);
+ goto done;
+ }
+
+ ctcm_pr_debug("%s: TX retry %d\n", dev->name, ch->retry);
+ skb = skb_peek(&ch->io_queue);
+ if (skb) {
+ int rc = 0;
+ unsigned long saveflags = 0;
+ clear_normalized_cda(&ch->ccw[4]);
+ ch->ccw[4].count = skb->len;
+ if (set_normalized_cda(&ch->ccw[4], skb->data)) {
+ ctcm_pr_debug("%s: IDAL alloc failed, chan restart\n",
+ dev->name);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ ctcm_chx_restart(fi, event, arg);
+ goto done;
+ }
+ fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
+ if (event == CTC_EVENT_TIMER) /* for TIMER not yet locked */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ /* Such conditional locking is a known problem for
+ * sparse because its undeterministic in static view.
+ * Warnings should be ignored here. */
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[3],
+ sizeof(struct ccw1) * 3);
+
+ rc = ccw_device_start(ch->cdev, &ch->ccw[3],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_TIMER)
+ spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
+ saveflags);
+ if (rc != 0) {
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "TX in chx_txretry");
+ ctcm_purge_skb_queue(&ch->io_queue);
+ }
+ }
+done:
+ return;
+}
+
+/**
+ * Handle fatal errors during an I/O command.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__);
+ fsm_deltimer(&ch->timer);
+ ctcm_pr_warn("%s %s : unrecoverable channel error\n",
+ CTC_DRIVER_NAME, dev->name);
+ if (IS_MPC(ch)) {
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ }
+
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ ctcm_pr_debug("%s: RX I/O error\n", dev->name);
+ fsm_newstate(fi, CTC_STATE_RXERR);
+ fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
+ } else {
+ ctcm_pr_debug("%s: TX I/O error\n", dev->name);
+ fsm_newstate(fi, CTC_STATE_TXERR);
+ fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
+ }
+}
+
+/*
+ * The ctcm statemachine for a channel.
+ */
+const fsm_node ch_fsm[] = {
+ { CTC_STATE_STOPPED, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_START, ctcm_chx_start },
+ { CTC_STATE_STOPPED, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+
+ { CTC_STATE_NOTOP, CTC_EVENT_STOP, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_GOOD, ctcm_chx_start },
+
+ { CTC_STATE_STARTWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_TIMER, ctcm_chx_setuperr },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_STARTRETRY, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_FINSTAT, chx_firstio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXINIT, CTC_EVENT_FINSTAT, chx_rxidle },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_TIMER, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_ATTNBUSY, ctcm_chx_rxinitfail },
+ { CTC_STATE_RXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_ZERO, chx_firstio },
+ { CTC_STATE_RXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXIDLE, CTC_EVENT_FINSTAT, chx_rx },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_rxdisc },
+ { CTC_STATE_RXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_ZERO, chx_rx },
+
+ { CTC_STATE_TXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXINIT, CTC_EVENT_FINSTAT, ctcm_chx_txidle },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_TIMER, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_FINSTAT, chx_firstio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TERM, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_TERM, CTC_EVENT_FINSTAT, ctcm_chx_stopped },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_DTERM, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_DTERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_DTERM, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_TX, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TX, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TX, CTC_EVENT_FINSTAT, chx_txdone },
+ { CTC_STATE_TX, CTC_EVENT_UC_RCRESET, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_UC_RSRESET, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_TIMER, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TX, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+};
+
+int ch_fsm_len = ARRAY_SIZE(ch_fsm);
+
+/*
+ * MPC actions for mpc channel statemachine
+ * handling of MPC protocol requires extra
+ * statemachine and actions which are prefixed ctcmpc_ .
+ * The ctc_ch_states and ctc_ch_state_names,
+ * ctc_ch_events and ctc_ch_event_names share the ctcm definitions
+ * which are expanded by some elements.
+ */
+
+/*
+ * Actions for mpc channel statemachine.
+ */
+
+/**
+ * Normal data has been send. Free the corresponding
+ * skb (it's in io_queue), reset dev->tbusy and
+ * revert to idle state.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct sk_buff *skb;
+ int first = 1;
+ int i;
+ struct timespec done_stamp;
+ __u32 data_space;
+ unsigned long duration;
+ struct sk_buff *peekskb;
+ int rc;
+ struct th_header *header;
+ struct pdu *p_header;
+
+ if (do_debug)
+ ctcm_pr_debug("%s cp:%i enter: %s()\n",
+ dev->name, smp_processor_id(), __FUNCTION__);
+
+ done_stamp = current_kernel_time(); /* xtime */
+ duration = (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000
+ + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
+ if (duration > ch->prof.tx_time)
+ ch->prof.tx_time = duration;
+
+ if (ch->irb->scsw.count != 0)
+ ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
+ dev->name, ch->irb->scsw.count);
+ fsm_deltimer(&ch->timer);
+ while ((skb = skb_dequeue(&ch->io_queue))) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
+ if (first) {
+ priv->stats.tx_bytes += 2;
+ first = 0;
+ }
+ atomic_dec(&skb->users);
+ dev_kfree_skb_irq(skb);
+ }
+ spin_lock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[4]);
+
+ if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) {
+ spin_unlock(&ch->collect_lock);
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ goto done;
+ }
+
+ if (ctcm_checkalloc_buffer(ch)) {
+ spin_unlock(&ch->collect_lock);
+ goto done;
+ }
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ if (ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
+ ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
+ if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
+ ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
+ i = 0;
+
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() building "
+ "trans_skb from collect_q \n", __FUNCTION__);
+
+ data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
+
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() building trans_skb from collect_q"
+ " data_space:%04x\n", __FUNCTION__, data_space);
+ p_header = NULL;
+ while ((skb = skb_dequeue(&ch->collect_queue))) {
+ memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len);
+ p_header = (struct pdu *)
+ (skb_tail_pointer(ch->trans_skb) - skb->len);
+ p_header->pdu_flag = 0x00;
+ if (skb->protocol == ntohs(ETH_P_SNAP))
+ p_header->pdu_flag |= 0x60;
+ else
+ p_header->pdu_flag |= 0x20;
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n",
+ __FUNCTION__, ch->trans_skb->len);
+ ctcm_pr_debug("ctcmpc: %s() pdu header and data"
+ " for up to 32 bytes sent to vtam\n",
+ __FUNCTION__);
+ ctcmpc_dumpit((char *)p_header,
+ min_t(int, skb->len, 32));
+ }
+ ch->collect_len -= skb->len;
+ data_space -= skb->len;
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ atomic_dec(&skb->users);
+ dev_kfree_skb_any(skb);
+ peekskb = skb_peek(&ch->collect_queue);
+ if (peekskb->len > data_space)
+ break;
+ i++;
+ }
+ /* p_header points to the last one we handled */
+ if (p_header)
+ p_header->pdu_flag |= PDU_LAST; /*Say it's the last one*/
+ header = kzalloc(TH_HEADER_LENGTH, gfp_type());
+
+ if (!header) {
+ printk(KERN_WARNING "ctcmpc: OUT OF MEMORY IN %s()"
+ ": Data Lost \n", __FUNCTION__);
+ spin_unlock(&ch->collect_lock);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ header->th_ch_flag = TH_HAS_PDU; /* Normal data */
+ ch->th_seq_num++;
+ header->th_seq_num = ch->th_seq_num;
+
+ if (do_debug_data)
+ ctcm_pr_debug("%s: ToVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, ch->th_seq_num);
+
+ memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header,
+ TH_HEADER_LENGTH); /* put the TH on the packet */
+
+ kfree(header);
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n",
+ __FUNCTION__, ch->trans_skb->len);
+
+ ctcm_pr_debug("ctcmpc: %s() up-to-50 bytes of trans_skb "
+ "data to vtam from collect_q\n", __FUNCTION__);
+ ctcmpc_dumpit((char *)ch->trans_skb->data,
+ min_t(int, ch->trans_skb->len, 50));
+ }
+
+ spin_unlock(&ch->collect_lock);
+ clear_normalized_cda(&ch->ccw[1]);
+ if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
+ dev_kfree_skb_any(ch->trans_skb);
+ ch->trans_skb = NULL;
+ printk(KERN_WARNING
+ "ctcmpc: %s()CCW failure - data lost\n",
+ __FUNCTION__);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ return;
+ }
+ ch->ccw[1].count = ch->trans_skb->len;
+ fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
+ ch->prof.send_stamp = current_kernel_time(); /* xtime */
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ ch->prof.doios_multi++;
+ if (rc != 0) {
+ priv->stats.tx_dropped += i;
+ priv->stats.tx_errors += i;
+ fsm_deltimer(&ch->timer);
+ ctcm_ccw_check_rc(ch, rc, "chained TX");
+ }
+done:
+ ctcm_clear_busy(dev);
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
+ return;
+}
+
+/**
+ * Got normal data, check for sanity, queue it up, allocate new buffer
+ * trigger bottom half, and initiate next read.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct sk_buff *skb = ch->trans_skb;
+ struct sk_buff *new_skb;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+ int len = ch->max_bufsize - ch->irb->scsw.count;
+
+ if (do_debug_data) {
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n",
+ dev->name, smp_processor_id(), ch->id);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx: maxbuf: %04x "
+ "len: %04x\n", ch->max_bufsize, len);
+ }
+ fsm_deltimer(&ch->timer);
+
+ if (skb == NULL) {
+ ctcm_pr_debug("ctcmpc exit: %s() TRANS_SKB = NULL \n",
+ __FUNCTION__);
+ goto again;
+ }
+
+ if (len < TH_HEADER_LENGTH) {
+ ctcm_pr_info("%s: got packet with invalid length %d\n",
+ dev->name, len);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_length_errors++;
+ } else {
+ /* must have valid th header or game over */
+ __u32 block_len = len;
+ len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
+ new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC);
+
+ if (new_skb == NULL) {
+ printk(KERN_INFO "ctcmpc:%s() NEW_SKB = NULL\n",
+ __FUNCTION__);
+ printk(KERN_WARNING "ctcmpc: %s() MEMORY ALLOC FAILED"
+ " - DATA LOST - MPC FAILED\n",
+ __FUNCTION__);
+ fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
+ goto again;
+ }
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_RESET:
+ case MPCG_STATE_INOP:
+ dev_kfree_skb_any(new_skb);
+ break;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ memcpy(skb_put(new_skb, block_len),
+ skb->data, block_len);
+ skb_queue_tail(&ch->io_queue, new_skb);
+ tasklet_schedule(&ch->ch_tasklet);
+ break;
+ default:
+ memcpy(skb_put(new_skb, len), skb->data, len);
+ skb_queue_tail(&ch->io_queue, new_skb);
+ tasklet_hi_schedule(&ch->ch_tasklet);
+ break;
+ }
+ }
+
+again:
+ switch (fsm_getstate(grp->fsm)) {
+ int rc, dolock;
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ if (ctcm_checkalloc_buffer(ch))
+ break;
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = ch->max_bufsize;
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[0],
+ sizeof(struct ccw1) * 3);
+ dolock = !in_irq();
+ if (dolock)
+ spin_lock_irqsave(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (dolock) /* see remark about conditional locking */
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0)
+ ctcm_ccw_check_rc(ch, rc, "normal RX");
+ default:
+ break;
+ }
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n",
+ dev->name, __FUNCTION__, ch, ch->id);
+
+}
+
+/**
+ * Initialize connection by sending a __u16 of value 0.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+
+ if (do_debug) {
+ struct mpc_group *gptr = priv->mpcg;
+ ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ ctcm_pr_debug("%s() %s chstate:%i grpstate:%i chprotocol:%i\n",
+ __FUNCTION__, ch->id, fsm_getstate(fi),
+ fsm_getstate(gptr->fsm), ch->protocol);
+ }
+ if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
+ MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? ");
+
+ fsm_deltimer(&ch->timer);
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+
+ switch (fsm_getstate(fi)) {
+ case CTC_STATE_STARTRETRY:
+ case CTC_STATE_SETUPWAIT:
+ if (CHANNEL_DIRECTION(ch->flags) == READ) {
+ ctcmpc_chx_rxidle(fi, event, arg);
+ } else {
+ fsm_newstate(fi, CTC_STATE_TXIDLE);
+ fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
+ }
+ goto done;
+ default:
+ break;
+ };
+
+ fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
+ ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ return;
+}
+
+/**
+ * Got initial data, check it. If OK,
+ * notify device statemachine that we are up and
+ * running.
+ *
+ * fi An instance of a channel statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from channel * upon call.
+ */
+void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ int rc;
+ unsigned long saveflags = 0; /* avoids compiler warning */
+
+ fsm_deltimer(&ch->timer);
+ ctcm_pr_debug("%s cp:%i enter: %s()\n",
+ dev->name, smp_processor_id(), __FUNCTION__);
+ if (do_debug)
+ ctcm_pr_debug("%s() %s chstate:%i grpstate:%i\n",
+ __FUNCTION__, ch->id,
+ fsm_getstate(fi), fsm_getstate(grp->fsm));
+
+ fsm_newstate(fi, CTC_STATE_RXIDLE);
+ /* XID processing complete */
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_FLOWC:
+ case MPCG_STATE_READY:
+ if (ctcm_checkalloc_buffer(ch))
+ goto done;
+ ch->trans_skb->data = ch->trans_skb_data;
+ skb_reset_tail_pointer(ch->trans_skb);
+ ch->trans_skb->len = 0;
+ ch->ccw[1].count = ch->max_bufsize;
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&ch->ccw[0],
+ sizeof(struct ccw1) * 3);
+ if (event == CTC_EVENT_START)
+ /* see remark about conditional locking */
+ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
+ rc = ccw_device_start(ch->cdev, &ch->ccw[0],
+ (unsigned long)ch, 0xff, 0);
+ if (event == CTC_EVENT_START)
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(ch->cdev), saveflags);
+ if (rc != 0) {
+ fsm_newstate(fi, CTC_STATE_RXINIT);
+ ctcm_ccw_check_rc(ch, rc, "initial RX");
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+
+ fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n",
+ dev->name, __FUNCTION__);
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ if (do_debug) {
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s"
+ "GrpState:%s ChState:%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id,
+ fsm_getstate_str(grp->fsm),
+ fsm_getstate_str(ch->fsm));
+ }
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID2INITW:
+ /* ok..start yside xid exchanges */
+ if (!ch->in_mpcgroup)
+ break;
+ if (fsm_getstate(ch->fsm) == CH_XID0_PENDING) {
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer,
+ MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+
+ } else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
+ /* attn rcvd before xid0 processed via bh */
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case MPCG_STATE_XID2INITX:
+ case MPCG_STATE_XID0IOWAIT:
+ case MPCG_STATE_XID0IOWAIX:
+ /* attn rcvd before xid0 processed on ch
+ but mid-xid0 processing for group */
+ if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ switch (fsm_getstate(ch->fsm)) {
+ case CH_XID7_PENDING:
+ fsm_newstate(ch->fsm, CH_XID7_PENDING1);
+ break;
+ case CH_XID7_PENDING2:
+ fsm_newstate(ch->fsm, CH_XID7_PENDING3);
+ break;
+ }
+ fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
+ break;
+ }
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ch, ch->id);
+ return;
+
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from one point in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n",
+ dev->name,
+ __FUNCTION__, ch->id,
+ fsm_getstate_str(grp->fsm),
+ fsm_getstate_str(ch->fsm));
+
+ fsm_deltimer(&ch->timer);
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_XID0IOWAIT:
+ /* vtam wants to be primary.start yside xid exchanges*/
+ /* only receive one attn-busy at a time so must not */
+ /* change state each time */
+ grp->changed_side = 1;
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
+ break;
+ case MPCG_STATE_XID2INITW:
+ if (grp->changed_side == 1) {
+ grp->changed_side = 2;
+ break;
+ }
+ /* process began via call to establish_conn */
+ /* so must report failure instead of reverting */
+ /* back to ready-for-xid passive state */
+ if (grp->estconnfunc)
+ goto done;
+ /* this attnbusy is NOT the result of xside xid */
+ /* collisions so yside must have been triggered */
+ /* by an ATTN that was not intended to start XID */
+ /* processing. Revert back to ready-for-xid and */
+ /* wait for ATTN interrupt to signal xid start */
+ if (fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS) {
+ fsm_newstate(ch->fsm, CH_XID0_PENDING) ;
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ case MPCG_STATE_XID2INITX:
+ /* XID2 was received before ATTN Busy for second
+ channel.Send yside xid for second channel.
+ */
+ if (grp->changed_side == 1) {
+ grp->changed_side = 2;
+ break;
+ }
+ case MPCG_STATE_XID0IOWAIX:
+ case MPCG_STATE_XID7INITW:
+ case MPCG_STATE_XID7INITX:
+ case MPCG_STATE_XID7INITI:
+ case MPCG_STATE_XID7INITZ:
+ default:
+ /* multiple attn-busy indicates too out-of-sync */
+ /* and they are certainly not being received as part */
+ /* of valid mpc group negotiations.. */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ }
+
+ if (grp->changed_side == 1) {
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ }
+ if (ch->in_mpcgroup)
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+ else
+ printk(KERN_WARNING "ctcmpc: %s() Not all channels have"
+ " been added to group\n", __FUNCTION__);
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s()%s ch=0x%p id=%s\n",
+ __FUNCTION__, dev->name, ch, ch->id);
+
+ return;
+
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ch = arg;
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+
+ ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n",
+ dev->name, __FUNCTION__, ch->id,
+ fsm_getstate_str(grp->fsm),
+ fsm_getstate_str(ch->fsm));
+
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
+
+ return;
+}
+
+/*
+ * ctcmpc channel FSM action
+ * called from several points in ctcmpc_ch_fsm
+ * ctcmpc only
+ */
+static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
+{
+ struct channel *ach = arg;
+ struct net_device *dev = ach->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *wch = priv->channel[WRITE];
+ struct channel *rch = priv->channel[READ];
+ struct sk_buff *skb;
+ struct th_sweep *header;
+ int rc = 0;
+ unsigned long saveflags = 0;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
+ __FUNCTION__, smp_processor_id(), ach, ach->id);
+
+ if (grp->in_sweep == 0)
+ goto done;
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s() 1: ToVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, wch->th_seq_num);
+ ctcm_pr_debug("ctcmpc: %s() 1: FromVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, rch->th_seq_num);
+ }
+
+ if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) {
+ /* give the previous IO time to complete */
+ fsm_addtimer(&wch->sweep_timer,
+ 200, CTC_EVENT_RSWEEP_TIMER, wch);
+ goto done;
+ }
+
+ skb = skb_dequeue(&wch->sweep_queue);
+ if (!skb)
+ goto done;
+
+ if (set_normalized_cda(&wch->ccw[4], skb->data)) {
+ grp->in_sweep = 0;
+ ctcm_clear_busy_do(dev);
+ dev_kfree_skb_any(skb);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ goto done;
+ } else {
+ atomic_inc(&skb->users);
+ skb_queue_tail(&wch->io_queue, skb);
+ }
+
+ /* send out the sweep */
+ wch->ccw[4].count = skb->len;
+
+ header = (struct th_sweep *)skb->data;
+ switch (header->th.th_ch_flag) {
+ case TH_SWEEP_REQ:
+ grp->sweep_req_pend_num--;
+ break;
+ case TH_SWEEP_RESP:
+ grp->sweep_rsp_pend_num--;
+ break;
+ }
+
+ header->sw.th_last_seq = wch->th_seq_num;
+
+ if (do_debug_ccw)
+ ctcmpc_dumpit((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
+
+ ctcm_pr_debug("ctcmpc: %s() sweep packet\n", __FUNCTION__);
+ ctcmpc_dumpit((char *)header, TH_SWEEP_LENGTH);
+
+ fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch);
+ fsm_newstate(wch->fsm, CTC_STATE_TX);
+
+ spin_lock_irqsave(get_ccwdev_lock(wch->cdev), saveflags);
+ wch->prof.send_stamp = current_kernel_time(); /* xtime */
+ rc = ccw_device_start(wch->cdev, &wch->ccw[3],
+ (unsigned long) wch, 0xff, 0);
+ spin_unlock_irqrestore(get_ccwdev_lock(wch->cdev), saveflags);
+
+ if ((grp->sweep_req_pend_num == 0) &&
+ (grp->sweep_rsp_pend_num == 0)) {
+ grp->in_sweep = 0;
+ rch->th_seq_num = 0x00;
+ wch->th_seq_num = 0x00;
+ ctcm_clear_busy_do(dev);
+ }
+
+ if (do_debug_data) {
+ ctcm_pr_debug("ctcmpc: %s()2: ToVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, wch->th_seq_num);
+ ctcm_pr_debug("ctcmpc: %s()2: FromVTAM_th_seq= %08x\n" ,
+ __FUNCTION__, rch->th_seq_num);
+ }
+
+ if (rc != 0)
+ ctcm_ccw_check_rc(wch, rc, "send sweep");
+
+done:
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit: %s() %s\n", __FUNCTION__, ach->id);
+ return;
+}
+
+
+/*
+ * The ctcmpc statemachine for a channel.
+ */
+
+const fsm_node ctcmpc_ch_fsm[] = {
+ { CTC_STATE_STOPPED, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_START, ctcm_chx_start },
+ { CTC_STATE_STOPPED, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STOPPED, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_STOPPED, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+
+ { CTC_STATE_NOTOP, CTC_EVENT_STOP, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_FAIL, ctcm_action_nop },
+ { CTC_STATE_NOTOP, CTC_EVENT_MC_GOOD, ctcm_chx_start },
+ { CTC_STATE_NOTOP, CTC_EVENT_UC_RCRESET, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_UC_RSRESET, ctcm_chx_stop },
+ { CTC_STATE_NOTOP, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_STARTWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_TIMER, ctcm_chx_setuperr },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_STARTWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_STARTRETRY, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_STARTRETRY, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_FINSTAT, ctcmpc_chx_firstio },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_TIMER, ctcm_chx_setmode },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_SETUPWAIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CTC_STATE_RXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXINIT, CTC_EVENT_FINSTAT, ctcmpc_chx_rxidle },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_TIMER, ctcm_chx_rxiniterr },
+ { CTC_STATE_RXINIT, CTC_EVENT_ATTNBUSY, ctcm_chx_rxinitfail },
+ { CTC_STATE_RXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXINIT, CTC_EVENT_UC_ZERO, ctcmpc_chx_firstio },
+ { CTC_STATE_RXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+
+ { CH_XID0_PENDING, CTC_EVENT_FINSTAT, ctcm_action_nop },
+ { CH_XID0_PENDING, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID0_PENDING, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID0_PENDING, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID0_PENDING, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID0_PENDING, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID0_PENDING, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+
+ { CH_XID0_INPROGRESS, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID0_INPROGRESS, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID0_INPROGRESS, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID0_INPROGRESS, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID0_INPROGRESS, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID0_INPROGRESS, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID0_INPROGRESS, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID0_INPROGRESS, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID0_INPROGRESS, CTC_EVENT_ATTNBUSY, ctcmpc_chx_attnbusy },
+ { CH_XID0_INPROGRESS, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID0_INPROGRESS, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING1, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING1, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING1, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING1, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING1, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING1, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING1, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING1, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING1, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING1, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING2, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING2, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING2, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING2, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING2, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING2, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING2, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING2, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING2, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING2, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING3, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING3, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING3, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING3, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING3, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING3, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING3, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING3, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING3, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING3, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CH_XID7_PENDING4, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CH_XID7_PENDING4, CTC_EVENT_ATTN, ctcmpc_chx_attn },
+ { CH_XID7_PENDING4, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CH_XID7_PENDING4, CTC_EVENT_START, ctcm_action_nop },
+ { CH_XID7_PENDING4, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CH_XID7_PENDING4, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_RCRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING4, CTC_EVENT_UC_RSRESET, ctcm_chx_setuperr },
+ { CH_XID7_PENDING4, CTC_EVENT_ATTNBUSY, ctcm_chx_iofatal },
+ { CH_XID7_PENDING4, CTC_EVENT_TIMER, ctcmpc_chx_resend },
+ { CH_XID7_PENDING4, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CTC_STATE_RXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_RXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_RXIDLE, CTC_EVENT_FINSTAT, ctcmpc_chx_rx },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_rxdisc },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_RXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXIDLE, CTC_EVENT_UC_ZERO, ctcmpc_chx_rx },
+
+ { CTC_STATE_TXINIT, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXINIT, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXINIT, CTC_EVENT_FINSTAT, ctcm_chx_txidle },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RCRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_UC_RSRESET, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_TIMER, ctcm_chx_txiniterr },
+ { CTC_STATE_TXINIT, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXINIT, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TXINIT, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+
+ { CTC_STATE_TXIDLE, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TXIDLE, CTC_EVENT_FINSTAT, ctcmpc_chx_firstio },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RCRESET, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXIDLE, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TXIDLE, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+
+ { CTC_STATE_TERM, CTC_EVENT_STOP, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_TERM, CTC_EVENT_FINSTAT, ctcm_chx_stopped },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_TERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TERM, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+ { CTC_STATE_TERM, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_DTERM, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_DTERM, CTC_EVENT_START, ctcm_chx_restart },
+ { CTC_STATE_DTERM, CTC_EVENT_FINSTAT, ctcm_chx_setmode },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RCRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_UC_RSRESET, ctcm_action_nop },
+ { CTC_STATE_DTERM, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_DTERM, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+
+ { CTC_STATE_TX, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TX, CTC_EVENT_START, ctcm_action_nop },
+ { CTC_STATE_TX, CTC_EVENT_FINSTAT, ctcmpc_chx_txdone },
+ { CTC_STATE_TX, CTC_EVENT_UC_RCRESET, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_UC_RSRESET, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_TIMER, ctcm_chx_txretry },
+ { CTC_STATE_TX, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TX, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_TX, CTC_EVENT_RSWEEP_TIMER, ctcmpc_chx_send_sweep },
+ { CTC_STATE_TX, CTC_EVENT_IO_EBUSY, ctcm_chx_fail },
+
+ { CTC_STATE_RXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_STOP, ctcm_chx_haltio },
+ { CTC_STATE_TXERR, CTC_EVENT_IO_ENODEV, ctcm_chx_iofatal },
+ { CTC_STATE_TXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+ { CTC_STATE_RXERR, CTC_EVENT_MC_FAIL, ctcm_chx_fail },
+};
+
+int mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm);
+
+/*
+ * Actions for interface - statemachine.
+ */
+
+/**
+ * Startup channels by sending CTC_EVENT_START to each channel.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_start(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->priv;
+ int direction;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ fsm_deltimer(&priv->restart_timer);
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ if (IS_MPC(priv))
+ priv->mpcg->channels_terminating = 0;
+ for (direction = READ; direction <= WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ fsm_event(ch->fsm, CTC_EVENT_START, ch);
+ }
+}
+
+/**
+ * Shutdown channels by sending CTC_EVENT_STOP to each channel.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_stop(fsm_instance *fi, int event, void *arg)
+{
+ int direction;
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ for (direction = READ; direction <= WRITE; direction++) {
+ struct channel *ch = priv->channel[direction];
+ fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
+ ch->th_seq_num = 0x00;
+ if (do_debug)
+ ctcm_pr_debug("ctcm: %s() CH_th_seq= %08x\n",
+ __FUNCTION__, ch->th_seq_num);
+ }
+ if (IS_MPC(priv))
+ fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
+}
+
+static void dev_action_restart(fsm_instance *fi, int event, void *arg)
+{
+ int restart_timer;
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(TRACE, dev, "");
+
+ if (IS_MPC(priv)) {
+ ctcm_pr_info("ctcm: %s Restarting Device and "
+ "MPC Group in 5 seconds\n",
+ dev->name);
+ restart_timer = CTCM_TIME_1_SEC;
+ } else {
+ ctcm_pr_info("%s: Restarting\n", dev->name);
+ restart_timer = CTCM_TIME_5_SEC;
+ }
+
+ dev_action_stop(fi, event, arg);
+ fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
+ if (IS_MPC(priv))
+ fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
+
+ /* going back into start sequence too quickly can */
+ /* result in the other side becoming unreachable due */
+ /* to sense reported when IO is aborted */
+ fsm_addtimer(&priv->restart_timer, restart_timer,
+ DEV_EVENT_START, dev);
+}
+
+/**
+ * Called from channel statemachine
+ * when a channel is up and running.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_chup(fsm_instance *fi, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ switch (fsm_getstate(fi)) {
+ case DEV_STATE_STARTWAIT_RXTX:
+ if (event == DEV_EVENT_RXUP)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
+ else
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
+ break;
+ case DEV_STATE_STARTWAIT_RX:
+ if (event == DEV_EVENT_RXUP) {
+ fsm_newstate(fi, DEV_STATE_RUNNING);
+ ctcm_pr_info("%s: connected with remote side\n",
+ dev->name);
+ ctcm_clear_busy(dev);
+ }
+ break;
+ case DEV_STATE_STARTWAIT_TX:
+ if (event == DEV_EVENT_TXUP) {
+ fsm_newstate(fi, DEV_STATE_RUNNING);
+ ctcm_pr_info("%s: connected with remote side\n",
+ dev->name);
+ ctcm_clear_busy(dev);
+ }
+ break;
+ case DEV_STATE_STOPWAIT_TX:
+ if (event == DEV_EVENT_RXUP)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ break;
+ case DEV_STATE_STOPWAIT_RX:
+ if (event == DEV_EVENT_TXUP)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
+ break;
+ }
+
+ if (IS_MPC(priv)) {
+ if (event == DEV_EVENT_RXUP)
+ mpc_channel_action(priv->channel[READ],
+ READ, MPC_CHANNEL_ADD);
+ else
+ mpc_channel_action(priv->channel[WRITE],
+ WRITE, MPC_CHANNEL_ADD);
+ }
+}
+
+/**
+ * Called from device statemachine
+ * when a channel has been shutdown.
+ *
+ * fi An instance of an interface statemachine.
+ * event The event, just happened.
+ * arg Generic pointer, casted from struct net_device * upon call.
+ */
+static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
+{
+
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = dev->priv;
+
+ CTCMY_DBF_DEV_NAME(SETUP, dev, "");
+
+ switch (fsm_getstate(fi)) {
+ case DEV_STATE_RUNNING:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
+ else
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
+ break;
+ case DEV_STATE_STARTWAIT_RX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ break;
+ case DEV_STATE_STARTWAIT_TX:
+ if (event == DEV_EVENT_RXDOWN)
+ fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
+ break;
+ case DEV_STATE_STOPWAIT_RXTX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
+ else
+ fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
+ break;
+ case DEV_STATE_STOPWAIT_RX:
+ if (event == DEV_EVENT_RXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPPED);
+ break;
+ case DEV_STATE_STOPWAIT_TX:
+ if (event == DEV_EVENT_TXDOWN)
+ fsm_newstate(fi, DEV_STATE_STOPPED);
+ break;
+ }
+ if (IS_MPC(priv)) {
+ if (event == DEV_EVENT_RXDOWN)
+ mpc_channel_action(priv->channel[READ],
+ READ, MPC_CHANNEL_REMOVE);
+ else
+ mpc_channel_action(priv->channel[WRITE],
+ WRITE, MPC_CHANNEL_REMOVE);
+ }
+}
+
+const fsm_node dev_fsm[] = {
+ { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
+ { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown },
+ { DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown },
+ { DEV_STATE_RUNNING, DEV_EVENT_TXUP, ctcm_action_nop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RXUP, ctcm_action_nop },
+ { DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart },
+};
+
+int dev_fsm_len = ARRAY_SIZE(dev_fsm);
+
+/* --- This is the END my friend --- */
+
--
^ permalink raw reply
* [patch 3/3] ctc: removal of the old ctc driver
From: Ursula Braun @ 2008-02-07 23:03 UTC (permalink / raw)
To: jgarzik, netdev, linux-s390; +Cc: Peter Tiedemann
In-Reply-To: <20080207230347.075759000@linux.vnet.ibm.com>
[-- Attachment #1: 703-ctc_removal.diff --]
[-- Type: TEXT/PLAIN, Size: 103955 bytes --]
From: Peter Tiedemann <ptiedem@de.ibm.com>
ctc driver is replaced by a new ctcm driver.
The ctcm driver supports the channel-to-channel connections of the
old ctc driver plus an additional MPC protocol to provide SNA
connectivity.
This patch removes the functions of the old ctc driver.
Signed-off-by: Peter Tiedemann <ptiedem@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/ctcdbug.c | 80 -
drivers/s390/net/ctcdbug.h | 125 -
drivers/s390/net/ctcmain.c | 3062 ---------------------------------------------
drivers/s390/net/ctcmain.h | 270 ---
4 files changed, 3537 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/ctcdbug.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/ctcdbug.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/ctcdbug.c
- *
- * CTC / ESCON network driver - s390 dbf exploit.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Original Code written by
- * Peter Tiedemann (ptiedem@de.ibm.com)
- *
- * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "ctcdbug.h"
-
-/**
- * Debug Facility Stuff
- */
-debug_info_t *ctc_dbf_setup = NULL;
-debug_info_t *ctc_dbf_data = NULL;
-debug_info_t *ctc_dbf_trace = NULL;
-
-DEFINE_PER_CPU(char[256], ctc_dbf_txt_buf);
-
-void
-ctc_unregister_dbf_views(void)
-{
- if (ctc_dbf_setup)
- debug_unregister(ctc_dbf_setup);
- if (ctc_dbf_data)
- debug_unregister(ctc_dbf_data);
- if (ctc_dbf_trace)
- debug_unregister(ctc_dbf_trace);
-}
-int
-ctc_register_dbf_views(void)
-{
- ctc_dbf_setup = debug_register(CTC_DBF_SETUP_NAME,
- CTC_DBF_SETUP_PAGES,
- CTC_DBF_SETUP_NR_AREAS,
- CTC_DBF_SETUP_LEN);
- ctc_dbf_data = debug_register(CTC_DBF_DATA_NAME,
- CTC_DBF_DATA_PAGES,
- CTC_DBF_DATA_NR_AREAS,
- CTC_DBF_DATA_LEN);
- ctc_dbf_trace = debug_register(CTC_DBF_TRACE_NAME,
- CTC_DBF_TRACE_PAGES,
- CTC_DBF_TRACE_NR_AREAS,
- CTC_DBF_TRACE_LEN);
-
- if ((ctc_dbf_setup == NULL) || (ctc_dbf_data == NULL) ||
- (ctc_dbf_trace == NULL)) {
- ctc_unregister_dbf_views();
- return -ENOMEM;
- }
- debug_register_view(ctc_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_setup, CTC_DBF_SETUP_LEVEL);
-
- debug_register_view(ctc_dbf_data, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_data, CTC_DBF_DATA_LEVEL);
-
- debug_register_view(ctc_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(ctc_dbf_trace, CTC_DBF_TRACE_LEVEL);
-
- return 0;
-}
-
Index: linux-2.6-uschi/drivers/s390/net/ctcdbug.h
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/ctcdbug.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- *
- * linux/drivers/s390/net/ctcdbug.h
- *
- * CTC / ESCON network driver - s390 dbf exploit.
- *
- * Copyright 2000,2003 IBM Corporation
- *
- * Author(s): Original Code written by
- * Peter Tiedemann (ptiedem@de.ibm.com)
- *
- * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef _CTCDBUG_H_
-#define _CTCDBUG_H_
-
-#include <asm/debug.h>
-#include "ctcmain.h"
-/**
- * Debug Facility stuff
- */
-#define CTC_DBF_SETUP_NAME "ctc_setup"
-#define CTC_DBF_SETUP_LEN 16
-#define CTC_DBF_SETUP_PAGES 8
-#define CTC_DBF_SETUP_NR_AREAS 1
-#define CTC_DBF_SETUP_LEVEL 3
-
-#define CTC_DBF_DATA_NAME "ctc_data"
-#define CTC_DBF_DATA_LEN 128
-#define CTC_DBF_DATA_PAGES 8
-#define CTC_DBF_DATA_NR_AREAS 1
-#define CTC_DBF_DATA_LEVEL 3
-
-#define CTC_DBF_TRACE_NAME "ctc_trace"
-#define CTC_DBF_TRACE_LEN 16
-#define CTC_DBF_TRACE_PAGES 4
-#define CTC_DBF_TRACE_NR_AREAS 2
-#define CTC_DBF_TRACE_LEVEL 3
-
-#define DBF_TEXT(name,level,text) \
- do { \
- debug_text_event(ctc_dbf_##name,level,text); \
- } while (0)
-
-#define DBF_HEX(name,level,addr,len) \
- do { \
- debug_event(ctc_dbf_##name,level,(void*)(addr),len); \
- } while (0)
-
-DECLARE_PER_CPU(char[256], ctc_dbf_txt_buf);
-extern debug_info_t *ctc_dbf_setup;
-extern debug_info_t *ctc_dbf_data;
-extern debug_info_t *ctc_dbf_trace;
-
-
-#define DBF_TEXT_(name,level,text...) \
- do { \
- char* ctc_dbf_txt_buf = get_cpu_var(ctc_dbf_txt_buf); \
- sprintf(ctc_dbf_txt_buf, text); \
- debug_text_event(ctc_dbf_##name,level,ctc_dbf_txt_buf); \
- put_cpu_var(ctc_dbf_txt_buf); \
- } while (0)
-
-#define DBF_SPRINTF(name,level,text...) \
- do { \
- debug_sprintf_event(ctc_dbf_trace, level, ##text ); \
- debug_sprintf_event(ctc_dbf_trace, level, text ); \
- } while (0)
-
-
-int ctc_register_dbf_views(void);
-
-void ctc_unregister_dbf_views(void);
-
-/**
- * some more debug stuff
- */
-
-#define HEXDUMP16(importance,header,ptr) \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
- *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
- *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
- *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
- *(((char*)ptr)+12),*(((char*)ptr)+13), \
- *(((char*)ptr)+14),*(((char*)ptr)+15)); \
-PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
- *(((char*)ptr)+16),*(((char*)ptr)+17), \
- *(((char*)ptr)+18),*(((char*)ptr)+19), \
- *(((char*)ptr)+20),*(((char*)ptr)+21), \
- *(((char*)ptr)+22),*(((char*)ptr)+23), \
- *(((char*)ptr)+24),*(((char*)ptr)+25), \
- *(((char*)ptr)+26),*(((char*)ptr)+27), \
- *(((char*)ptr)+28),*(((char*)ptr)+29), \
- *(((char*)ptr)+30),*(((char*)ptr)+31));
-
-static inline void
-hex_dump(unsigned char *buf, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len; i++) {
- if (i && !(i % 16))
- printk("\n");
- printk("%02x ", *(buf + i));
- }
- printk("\n");
-}
-
-
-#endif
Index: linux-2.6-uschi/drivers/s390/net/ctcmain.c
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/ctcmain.c
+++ /dev/null
@@ -1,3062 +0,0 @@
-/*
- * CTC / ESCON network driver
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- * Fixes by : Jochen Röhrig (roehrig@de.ibm.com)
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- Peter Tiedemann (ptiedem@de.ibm.com)
- * Driver Model stuff by : Cornelia Huck <cornelia.huck@de.ibm.com>
- *
- * Documentation used:
- * - Principles of Operation (IBM doc#: SA22-7201-06)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
- * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
- * - ESCON I/O Interface (IBM doc#: SA22-7202-029
- *
- * and the source of the original CTC driver by:
- * Dieter Wellerdiek (wel@de.ibm.com)
- * Martin Schwidefsky (schwidefsky@de.ibm.com)
- * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Jochen Röhrig (roehrig@de.ibm.com)
- *
- * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#undef DEBUG
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/bitops.h>
-
-#include <linux/signal.h>
-#include <linux/string.h>
-
-#include <linux/ip.h>
-#include <linux/if_arp.h>
-#include <linux/tcp.h>
-#include <linux/skbuff.h>
-#include <linux/ctype.h>
-#include <net/dst.h>
-
-#include <asm/io.h>
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-#include <asm/uaccess.h>
-
-#include <asm/idals.h>
-
-#include "fsm.h"
-#include "cu3088.h"
-
-#include "ctcdbug.h"
-#include "ctcmain.h"
-
-MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
-MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
-MODULE_LICENSE("GPL");
-/**
- * States of the interface statemachine.
- */
-enum dev_states {
- DEV_STATE_STOPPED,
- DEV_STATE_STARTWAIT_RXTX,
- DEV_STATE_STARTWAIT_RX,
- DEV_STATE_STARTWAIT_TX,
- DEV_STATE_STOPWAIT_RXTX,
- DEV_STATE_STOPWAIT_RX,
- DEV_STATE_STOPWAIT_TX,
- DEV_STATE_RUNNING,
- /**
- * MUST be always the last element!!
- */
- CTC_NR_DEV_STATES
-};
-
-static const char *dev_state_names[] = {
- "Stopped",
- "StartWait RXTX",
- "StartWait RX",
- "StartWait TX",
- "StopWait RXTX",
- "StopWait RX",
- "StopWait TX",
- "Running",
-};
-
-/**
- * Events of the interface statemachine.
- */
-enum dev_events {
- DEV_EVENT_START,
- DEV_EVENT_STOP,
- DEV_EVENT_RXUP,
- DEV_EVENT_TXUP,
- DEV_EVENT_RXDOWN,
- DEV_EVENT_TXDOWN,
- DEV_EVENT_RESTART,
- /**
- * MUST be always the last element!!
- */
- CTC_NR_DEV_EVENTS
-};
-
-static const char *dev_event_names[] = {
- "Start",
- "Stop",
- "RX up",
- "TX up",
- "RX down",
- "TX down",
- "Restart",
-};
-
-/**
- * Events of the channel statemachine
- */
-enum ch_events {
- /**
- * Events, representing return code of
- * I/O operations (ccw_device_start, ccw_device_halt et al.)
- */
- CH_EVENT_IO_SUCCESS,
- CH_EVENT_IO_EBUSY,
- CH_EVENT_IO_ENODEV,
- CH_EVENT_IO_EIO,
- CH_EVENT_IO_UNKNOWN,
-
- CH_EVENT_ATTNBUSY,
- CH_EVENT_ATTN,
- CH_EVENT_BUSY,
-
- /**
- * Events, representing unit-check
- */
- CH_EVENT_UC_RCRESET,
- CH_EVENT_UC_RSRESET,
- CH_EVENT_UC_TXTIMEOUT,
- CH_EVENT_UC_TXPARITY,
- CH_EVENT_UC_HWFAIL,
- CH_EVENT_UC_RXPARITY,
- CH_EVENT_UC_ZERO,
- CH_EVENT_UC_UNKNOWN,
-
- /**
- * Events, representing subchannel-check
- */
- CH_EVENT_SC_UNKNOWN,
-
- /**
- * Events, representing machine checks
- */
- CH_EVENT_MC_FAIL,
- CH_EVENT_MC_GOOD,
-
- /**
- * Event, representing normal IRQ
- */
- CH_EVENT_IRQ,
- CH_EVENT_FINSTAT,
-
- /**
- * Event, representing timer expiry.
- */
- CH_EVENT_TIMER,
-
- /**
- * Events, representing commands from upper levels.
- */
- CH_EVENT_START,
- CH_EVENT_STOP,
-
- /**
- * MUST be always the last element!!
- */
- NR_CH_EVENTS,
-};
-
-/**
- * States of the channel statemachine.
- */
-enum ch_states {
- /**
- * Channel not assigned to any device,
- * initial state, direction invalid
- */
- CH_STATE_IDLE,
-
- /**
- * Channel assigned but not operating
- */
- CH_STATE_STOPPED,
- CH_STATE_STARTWAIT,
- CH_STATE_STARTRETRY,
- CH_STATE_SETUPWAIT,
- CH_STATE_RXINIT,
- CH_STATE_TXINIT,
- CH_STATE_RX,
- CH_STATE_TX,
- CH_STATE_RXIDLE,
- CH_STATE_TXIDLE,
- CH_STATE_RXERR,
- CH_STATE_TXERR,
- CH_STATE_TERM,
- CH_STATE_DTERM,
- CH_STATE_NOTOP,
-
- /**
- * MUST be always the last element!!
- */
- NR_CH_STATES,
-};
-
-static int loglevel = CTC_LOGLEVEL_DEFAULT;
-
-/**
- * Linked list of all detected channels.
- */
-static struct channel *channels = NULL;
-
-/**
- * Print Banner.
- */
-static void
-print_banner(void)
-{
- static int printed = 0;
-
- if (printed)
- return;
-
- printk(KERN_INFO "CTC driver initialized\n");
- printed = 1;
-}
-
-/**
- * Return type of a detected device.
- */
-static enum channel_types
-get_channel_type(struct ccw_device_id *id)
-{
- enum channel_types type = (enum channel_types) id->driver_info;
-
- if (type == channel_type_ficon)
- type = channel_type_escon;
-
- return type;
-}
-
-static const char *ch_event_names[] = {
- "ccw_device success",
- "ccw_device busy",
- "ccw_device enodev",
- "ccw_device ioerr",
- "ccw_device unknown",
-
- "Status ATTN & BUSY",
- "Status ATTN",
- "Status BUSY",
-
- "Unit check remote reset",
- "Unit check remote system reset",
- "Unit check TX timeout",
- "Unit check TX parity",
- "Unit check Hardware failure",
- "Unit check RX parity",
- "Unit check ZERO",
- "Unit check Unknown",
-
- "SubChannel check Unknown",
-
- "Machine check failure",
- "Machine check operational",
-
- "IRQ normal",
- "IRQ final",
-
- "Timer",
-
- "Start",
- "Stop",
-};
-
-static const char *ch_state_names[] = {
- "Idle",
- "Stopped",
- "StartWait",
- "StartRetry",
- "SetupWait",
- "RX init",
- "TX init",
- "RX",
- "TX",
- "RX idle",
- "TX idle",
- "RX error",
- "TX error",
- "Terminating",
- "Restarting",
- "Not operational",
-};
-
-#ifdef DEBUG
-/**
- * Dump header and first 16 bytes of an sk_buff for debugging purposes.
- *
- * @param skb The sk_buff to dump.
- * @param offset Offset relative to skb-data, where to start the dump.
- */
-static void
-ctc_dump_skb(struct sk_buff *skb, int offset)
-{
- unsigned char *p = skb->data;
- __u16 bl;
- struct ll_header *header;
- int i;
-
- if (!(loglevel & CTC_LOGLEVEL_DEBUG))
- return;
- p += offset;
- bl = *((__u16 *) p);
- p += 2;
- header = (struct ll_header *) p;
- p -= 2;
-
- printk(KERN_DEBUG "dump:\n");
- printk(KERN_DEBUG "blocklen=%d %04x\n", bl, bl);
-
- printk(KERN_DEBUG "h->length=%d %04x\n", header->length,
- header->length);
- printk(KERN_DEBUG "h->type=%04x\n", header->type);
- printk(KERN_DEBUG "h->unused=%04x\n", header->unused);
- if (bl > 16)
- bl = 16;
- printk(KERN_DEBUG "data: ");
- for (i = 0; i < bl; i++)
- printk("%02x%s", *p++, (i % 16) ? " " : "\n<7>");
- printk("\n");
-}
-#else
-static inline void
-ctc_dump_skb(struct sk_buff *skb, int offset)
-{
-}
-#endif
-
-/**
- * Unpack a just received skb and hand it over to
- * upper layers.
- *
- * @param ch The channel where this skb has been received.
- * @param pskb The received skb.
- */
-static void
-ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
-{
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
- __u16 len = *((__u16 *) pskb->data);
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- skb_put(pskb, 2 + LL_HEADER_LENGTH);
- skb_pull(pskb, 2);
- pskb->dev = dev;
- pskb->ip_summed = CHECKSUM_UNNECESSARY;
- while (len > 0) {
- struct sk_buff *skb;
- struct ll_header *header = (struct ll_header *) pskb->data;
-
- skb_pull(pskb, LL_HEADER_LENGTH);
- if ((ch->protocol == CTC_PROTO_S390) &&
- (header->type != ETH_P_IP)) {
-
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
-#endif
- /**
- * Check packet type only if we stick strictly
- * to S/390's protocol of OS390. This only
- * supports IP. Otherwise allow any packet
- * type.
- */
- ctc_pr_warn(
- "%s Illegal packet type 0x%04x received, dropping\n",
- dev->name, header->type);
- ch->logflags |= LOG_FLAG_ILLEGALPKT;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_frame_errors++;
- return;
- }
- pskb->protocol = ntohs(header->type);
- if (header->length <= LL_HEADER_LENGTH) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
-#endif
- ctc_pr_warn(
- "%s Illegal packet size %d "
- "received (MTU=%d blocklen=%d), "
- "dropping\n", dev->name, header->length,
- dev->mtu, len);
- ch->logflags |= LOG_FLAG_ILLEGALSIZE;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- header->length -= LL_HEADER_LENGTH;
- len -= LL_HEADER_LENGTH;
- if ((header->length > skb_tailroom(pskb)) ||
- (header->length > len)) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
-#endif
- ctc_pr_warn(
- "%s Illegal packet size %d "
- "(beyond the end of received data), "
- "dropping\n", dev->name, header->length);
- ch->logflags |= LOG_FLAG_OVERRUN;
-#ifndef DEBUG
- }
-#endif
-#ifdef DEBUG
- ctc_dump_skb(pskb, -6);
-#endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- skb_put(pskb, header->length);
- skb_reset_mac_header(pskb);
- len -= header->length;
- skb = dev_alloc_skb(pskb->len);
- if (!skb) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_NOMEM)) {
-#endif
- ctc_pr_warn(
- "%s Out of memory in ctc_unpack_skb\n",
- dev->name);
- ch->logflags |= LOG_FLAG_NOMEM;
-#ifndef DEBUG
- }
-#endif
- privptr->stats.rx_dropped++;
- return;
- }
- skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
- pskb->len);
- skb_reset_mac_header(skb);
- skb->dev = pskb->dev;
- skb->protocol = pskb->protocol;
- pskb->ip_summed = CHECKSUM_UNNECESSARY;
- /**
- * reset logflags
- */
- ch->logflags = 0;
- privptr->stats.rx_packets++;
- privptr->stats.rx_bytes += skb->len;
- netif_rx_ni(skb);
- dev->last_rx = jiffies;
- if (len > 0) {
- skb_pull(pskb, header->length);
- if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {
-#ifndef DEBUG
- if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
-#endif
- ctc_pr_warn(
- "%s Overrun in ctc_unpack_skb\n",
- dev->name);
- ch->logflags |= LOG_FLAG_OVERRUN;
-#ifndef DEBUG
- }
-#endif
- return;
- }
- skb_put(pskb, LL_HEADER_LENGTH);
- }
- }
-}
-
-/**
- * Check return code of a preceeding ccw_device call, halt_IO etc...
- *
- * @param ch The channel, the error belongs to.
- * @param return_code The error code to inspect.
- */
-static void
-ccw_check_return_code(struct channel *ch, int return_code, char *msg)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- switch (return_code) {
- case 0:
- fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch);
- break;
- case -EBUSY:
- ctc_pr_warn("%s (%s): Busy !\n", ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch);
- break;
- case -ENODEV:
- ctc_pr_emerg("%s (%s): Invalid device called for IO\n",
- ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch);
- break;
- case -EIO:
- ctc_pr_emerg("%s (%s): Status pending... \n",
- ch->id, msg);
- fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch);
- break;
- default:
- ctc_pr_emerg("%s (%s): Unknown error in do_IO %04x\n",
- ch->id, msg, return_code);
- fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch);
- }
-}
-
-/**
- * Check sense of a unit check.
- *
- * @param ch The channel, the sense code belongs to.
- * @param sense The sense code to inspect.
- */
-static void
-ccw_unit_check(struct channel *ch, unsigned char sense)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (sense & SNS0_INTERVENTION_REQ) {
- if (sense & 0x01) {
- ctc_pr_debug("%s: Interface disc. or Sel. reset "
- "(remote)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
- } else {
- ctc_pr_debug("%s: System reset (remote)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch);
- }
- } else if (sense & SNS0_EQUIPMENT_CHECK) {
- if (sense & SNS0_BUS_OUT_CHECK) {
- ctc_pr_warn("%s: Hardware malfunction (remote)\n",
- ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch);
- } else {
- ctc_pr_warn("%s: Read-data parity error (remote)\n",
- ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch);
- }
- } else if (sense & SNS0_BUS_OUT_CHECK) {
- if (sense & 0x04) {
- ctc_pr_warn("%s: Data-streaming timeout)\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch);
- } else {
- ctc_pr_warn("%s: Data-transfer parity error\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch);
- }
- } else if (sense & SNS0_CMD_REJECT) {
- ctc_pr_warn("%s: Command reject\n", ch->id);
- } else if (sense == 0) {
- ctc_pr_debug("%s: Unit check ZERO\n", ch->id);
- fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch);
- } else {
- ctc_pr_warn("%s: Unit Check with sense code: %02x\n",
- ch->id, sense);
- fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch);
- }
-}
-
-static void
-ctc_purge_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
-
- while ((skb = skb_dequeue(q))) {
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- }
-}
-
-static int
-ctc_checkalloc_buffer(struct channel *ch, int warn)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- if ((ch->trans_skb == NULL) ||
- (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED)) {
- if (ch->trans_skb != NULL)
- dev_kfree_skb(ch->trans_skb);
- clear_normalized_cda(&ch->ccw[1]);
- ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
- GFP_ATOMIC | GFP_DMA);
- if (ch->trans_skb == NULL) {
- if (warn)
- ctc_pr_warn(
- "%s: Couldn't alloc %s trans_skb\n",
- ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ?
- "RX" : "TX");
- return -ENOMEM;
- }
- ch->ccw[1].count = ch->max_bufsize;
- if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- if (warn)
- ctc_pr_warn(
- "%s: set_normalized_cda for %s "
- "trans_skb failed, dropping packets\n",
- ch->id,
- (CHANNEL_DIRECTION(ch->flags) == READ) ?
- "RX" : "TX");
- return -ENOMEM;
- }
- ch->ccw[1].count = 0;
- ch->trans_skb_data = ch->trans_skb->data;
- ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
- }
- return 0;
-}
-
-/**
- * Dummy NOP action for statemachines
- */
-static void
-fsm_action_nop(fsm_instance * fi, int event, void *arg)
-{
-}
-
-/**
- * Actions for channel - statemachines.
- *****************************************************************************/
-
-/**
- * Normal data has been send. Free the corresponding
- * skb (it's in io_queue), reset dev->tbusy and
- * revert to idle state.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txdone(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- struct sk_buff *skb;
- int first = 1;
- int i;
- unsigned long duration;
- struct timespec done_stamp = current_kernel_time();
-
- DBF_TEXT(trace, 4, __FUNCTION__);
-
- duration =
- (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
- (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
- if (duration > ch->prof.tx_time)
- ch->prof.tx_time = duration;
-
- if (ch->irb->scsw.count != 0)
- ctc_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
- fsm_deltimer(&ch->timer);
- while ((skb = skb_dequeue(&ch->io_queue))) {
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
- if (first) {
- privptr->stats.tx_bytes += 2;
- first = 0;
- }
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- }
- spin_lock(&ch->collect_lock);
- clear_normalized_cda(&ch->ccw[4]);
- if (ch->collect_len > 0) {
- int rc;
-
- if (ctc_checkalloc_buffer(ch, 1)) {
- spin_unlock(&ch->collect_lock);
- return;
- }
- ch->trans_skb->data = ch->trans_skb_data;
- skb_reset_tail_pointer(ch->trans_skb);
- ch->trans_skb->len = 0;
- if (ch->prof.maxmulti < (ch->collect_len + 2))
- ch->prof.maxmulti = ch->collect_len + 2;
- if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
- ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
- *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
- i = 0;
- while ((skb = skb_dequeue(&ch->collect_queue))) {
- skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
- skb->len),
- skb->len);
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- i++;
- }
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- ch->ccw[1].count = ch->trans_skb->len;
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- ch->prof.send_stamp = current_kernel_time();
- rc = ccw_device_start(ch->cdev, &ch->ccw[0],
- (unsigned long) ch, 0xff, 0);
- ch->prof.doios_multi++;
- if (rc != 0) {
- privptr->stats.tx_dropped += i;
- privptr->stats.tx_errors += i;
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "chained TX");
- }
- } else {
- spin_unlock(&ch->collect_lock);
- fsm_newstate(fi, CH_STATE_TXIDLE);
- }
- ctc_clear_busy(dev);
-}
-
-/**
- * Initial data is sent.
- * Notify device statemachine that we are up and
- * running.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txidle(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((struct ctc_priv *) ch->netdev->priv)->fsm, DEV_EVENT_TXUP,
- ch->netdev);
-}
-
-/**
- * Got normal data, check for sanity, queue it up, allocate new buffer
- * trigger bottom half, and initiate next read.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rx(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- int len = ch->max_bufsize - ch->irb->scsw.count;
- struct sk_buff *skb = ch->trans_skb;
- __u16 block_len = *((__u16 *) skb->data);
- int check_len;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (len < 8) {
- ctc_pr_debug("%s: got packet with length %d < 8\n",
- dev->name, len);
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
- if (len > ch->max_bufsize) {
- ctc_pr_debug("%s: got packet with length %d > %d\n",
- dev->name, len, ch->max_bufsize);
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
-
- /**
- * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
- */
- switch (ch->protocol) {
- case CTC_PROTO_S390:
- case CTC_PROTO_OS390:
- check_len = block_len + 2;
- break;
- default:
- check_len = block_len;
- break;
- }
- if ((len < block_len) || (len > check_len)) {
- ctc_pr_debug("%s: got block length %d != rx length %d\n",
- dev->name, block_len, len);
-#ifdef DEBUG
- ctc_dump_skb(skb, 0);
-#endif
- *((__u16 *) skb->data) = len;
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
- block_len -= 2;
- if (block_len > 0) {
- *((__u16 *) skb->data) = block_len;
- ctc_unpack_skb(ch, skb);
- }
- again:
- skb->data = ch->trans_skb_data;
- skb_reset_tail_pointer(skb);
- skb->len = 0;
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- ch->ccw[1].count = ch->max_bufsize;
- rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0);
- if (rc != 0)
- ccw_check_return_code(ch, rc, "normal RX");
-}
-
-static void ch_action_rxidle(fsm_instance * fi, int event, void *arg);
-
-/**
- * Initialize connection by sending a __u16 of value 0.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_firstio(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
-
- if (fsm_getstate(fi) == CH_STATE_TXIDLE)
- ctc_pr_debug("%s: remote side issued READ?, init ...\n", ch->id);
- fsm_deltimer(&ch->timer);
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
- (ch->protocol == CTC_PROTO_OS390)) {
- /* OS/390 resp. z/OS */
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
- CH_EVENT_TIMER, ch);
- ch_action_rxidle(fi, event, arg);
- } else {
- struct net_device *dev = ch->netdev;
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXUP, dev);
- }
- return;
- }
-
- /**
- * Don't setup a timer for receiving the initial RX frame
- * if in compatibility mode, since VM TCP delays the initial
- * frame until it has some data to send.
- */
- if ((CHANNEL_DIRECTION(ch->flags) == WRITE) ||
- (ch->protocol != CTC_PROTO_S390))
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-
- *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
- ch->ccw[1].count = 2; /* Transfer only length */
-
- fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
- ? CH_STATE_RXINIT : CH_STATE_TXINIT);
- rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- ccw_check_return_code(ch, rc, "init IO");
- }
- /**
- * If in compatibility mode since we don't setup a timer, we
- * also signal RX channel up immediately. This enables us
- * to send packets early which in turn usually triggers some
- * reply from VM TCP which brings up the RX channel to it's
- * final state.
- */
- if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
- (ch->protocol == CTC_PROTO_S390)) {
- struct net_device *dev = ch->netdev;
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXUP,
- dev);
- }
-}
-
-/**
- * Got initial data, check it. If OK,
- * notify device statemachine that we are up and
- * running.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxidle(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- __u16 buflen;
- int rc;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- buflen = *((__u16 *) ch->trans_skb->data);
-#ifdef DEBUG
- ctc_pr_debug("%s: Initial RX count %d\n", dev->name, buflen);
-#endif
- if (buflen >= CTC_INITIAL_BLOCKLEN) {
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- ch->ccw[1].count = ch->max_bufsize;
- fsm_newstate(fi, CH_STATE_RXIDLE);
- rc = ccw_device_start(ch->cdev, &ch->ccw[0],
- (unsigned long) ch, 0xff, 0);
- if (rc != 0) {
- fsm_newstate(fi, CH_STATE_RXINIT);
- ccw_check_return_code(ch, rc, "initial RX");
- } else
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXUP, dev);
- } else {
- ctc_pr_debug("%s: Initial RX count %d not %d\n",
- dev->name, buflen, CTC_INITIAL_BLOCKLEN);
- ch_action_firstio(fi, event, arg);
- }
-}
-
-/**
- * Set channel into extended mode.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_setmode(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- int rc;
- unsigned long saveflags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for timer not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_start(ch->cdev, &ch->ccw[6], (unsigned long) ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- ccw_check_return_code(ch, rc, "set Mode");
- } else
- ch->retry = 0;
-}
-
-/**
- * Setup channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_start(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- unsigned long saveflags;
- int rc;
- struct net_device *dev;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (ch == NULL) {
- ctc_pr_warn("ch_action_start ch=NULL\n");
- return;
- }
- if (ch->netdev == NULL) {
- ctc_pr_warn("ch_action_start dev=NULL, id=%s\n", ch->id);
- return;
- }
- dev = ch->netdev;
-
-#ifdef DEBUG
- ctc_pr_debug("%s: %s channel start\n", dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
-#endif
-
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- }
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- ch->ccw[1].cmd_code = CCW_CMD_READ;
- ch->ccw[1].flags = CCW_FLAG_SLI;
- ch->ccw[1].count = 0;
- } else {
- ch->ccw[1].cmd_code = CCW_CMD_WRITE;
- ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
- ch->ccw[1].count = 0;
- }
- if (ctc_checkalloc_buffer(ch, 0)) {
- ctc_pr_notice(
- "%s: Could not allocate %s trans_skb, delaying "
- "allocation until first transfer\n",
- dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
- }
-
- ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
- ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
- ch->ccw[0].count = 0;
- ch->ccw[0].cda = 0;
- ch->ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE + DE */
- ch->ccw[2].flags = CCW_FLAG_SLI;
- ch->ccw[2].count = 0;
- ch->ccw[2].cda = 0;
- memcpy(&ch->ccw[3], &ch->ccw[0], sizeof (struct ccw1) * 3);
- ch->ccw[4].cda = 0;
- ch->ccw[4].flags &= ~CCW_FLAG_IDA;
-
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY)
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "initial HaltIO");
- }
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): leaving\n", __func__);
-#endif
-}
-
-/**
- * Shutdown a channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_haltio(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- unsigned long saveflags;
- int rc;
- int oldstate;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- saveflags = 0; /* avoids comp warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_STOP) // only for STOP not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- oldstate = fsm_getstate(fi);
- fsm_newstate(fi, CH_STATE_TERM);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (event == CH_EVENT_STOP)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- }
- ccw_check_return_code(ch, rc, "HaltIO in ch_action_haltio");
- }
-}
-
-/**
- * A channel has successfully been halted.
- * Cleanup it's queue and notify interface statemachine.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_stopped(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_STOPPED);
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- }
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- skb_queue_purge(&ch->io_queue);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_purge_skb_queue(&ch->io_queue);
- spin_lock(&ch->collect_lock);
- ctc_purge_skb_queue(&ch->collect_queue);
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * A stop command from device statemachine arrived and we are in
- * not operational mode. Set state to stopped.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_stop(fsm_instance * fi, int event, void *arg)
-{
- fsm_newstate(fi, CH_STATE_STOPPED);
-}
-
-/**
- * A machine check for no path, not operational status or gone device has
- * happened.
- * Cleanup queue and notify interface statemachine.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_fail(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_NOTOP);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- skb_queue_purge(&ch->io_queue);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_purge_skb_queue(&ch->io_queue);
- spin_lock(&ch->collect_lock);
- ctc_purge_skb_queue(&ch->collect_queue);
- ch->collect_len = 0;
- spin_unlock(&ch->collect_lock);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * Handle error during setup of channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_setuperr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- /**
- * Special case: Got UC_RCRESET on setmode.
- * This means that remote side isn't setup. In this case
- * simply retry after some 10 secs...
- */
- if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
- ((event == CH_EVENT_UC_RCRESET) ||
- (event == CH_EVENT_UC_RSRESET))) {
- fsm_newstate(fi, CH_STATE_STARTRETRY);
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- int rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (rc != 0)
- ccw_check_return_code(
- ch, rc, "HaltIO in ch_action_setuperr");
- }
- return;
- }
-
- ctc_pr_debug("%s: Error %s during %s channel setup state=%s\n",
- dev->name, ch_event_names[event],
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
- fsm_getstate_str(fi));
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-/**
- * Restart a channel after an error.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_restart(fsm_instance * fi, int event, void *arg)
-{
- unsigned long saveflags;
- int oldstate;
- int rc;
-
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: %s channel restart\n", dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- oldstate = fsm_getstate(fi);
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for timer not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- rc = ccw_device_halt(ch->cdev, (unsigned long) ch);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (rc != 0) {
- if (rc != -EBUSY) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- }
- ccw_check_return_code(ch, rc, "HaltIO in ch_action_restart");
- }
-}
-
-/**
- * Handle error during RX initial handshake (exchange of
- * 0-length block header)
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxiniterr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Timeout during RX init handshake\n", dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- }
- } else
- ctc_pr_warn("%s: Error during RX init handshake\n", dev->name);
-}
-
-/**
- * Notify device statemachine if we gave up initialization
- * of RX channel.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxinitfail(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- fsm_newstate(fi, CH_STATE_RXERR);
- ctc_pr_warn("%s: RX initialization failed\n", dev->name);
- ctc_pr_warn("%s: RX <-> RX connection detected\n", dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-}
-
-/**
- * Handle RX Unit check remote reset (remote disconnected)
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_rxdisc(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct channel *ch2;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Got remote disconnect, re-initializing ...\n",
- dev->name);
-
- /**
- * Notify device statemachine
- */
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-
- fsm_newstate(fi, CH_STATE_DTERM);
- ch2 = ((struct ctc_priv *) dev->priv)->channel[WRITE];
- fsm_newstate(ch2->fsm, CH_STATE_DTERM);
-
- ccw_device_halt(ch->cdev, (unsigned long) ch);
- ccw_device_halt(ch2->cdev, (unsigned long) ch2);
-}
-
-/**
- * Handle error during TX channel initialization.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txiniterr(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(setup, 2, __FUNCTION__);
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- ctc_pr_debug("%s: Timeout during TX init handshake\n", dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
- } else
- ctc_pr_warn("%s: Error during TX init handshake\n", dev->name);
-}
-
-/**
- * Handle TX timeout by retrying operation.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_txretry(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
- unsigned long saveflags;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (ch->retry++ > 3) {
- ctc_pr_debug("%s: TX retry failed, restarting channel\n",
- dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- ch_action_restart(fi, event, arg);
- } else {
- struct sk_buff *skb;
-
- ctc_pr_debug("%s: TX retry %d\n", dev->name, ch->retry);
- if ((skb = skb_peek(&ch->io_queue))) {
- int rc = 0;
-
- clear_normalized_cda(&ch->ccw[4]);
- ch->ccw[4].count = skb->len;
- if (set_normalized_cda(&ch->ccw[4], skb->data)) {
- ctc_pr_debug(
- "%s: IDAL alloc failed, chan restart\n",
- dev->name);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- ch_action_restart(fi, event, arg);
- return;
- }
- fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
- saveflags = 0; /* avoids compiler warning with
- spin_unlock_irqrestore */
- if (event == CH_EVENT_TIMER) // only for TIMER not yet locked
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev),
- saveflags);
- rc = ccw_device_start(ch->cdev, &ch->ccw[3],
- (unsigned long) ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
- saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "TX in ch_action_txretry");
- ctc_purge_skb_queue(&ch->io_queue);
- }
- }
- }
-
-}
-
-/**
- * Handle fatal errors during an I/O command.
- *
- * @param fi An instance of a channel statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from channel * upon call.
- */
-static void
-ch_action_iofatal(fsm_instance * fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *) arg;
- struct net_device *dev = ch->netdev;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_deltimer(&ch->timer);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- ctc_pr_debug("%s: RX I/O error\n", dev->name);
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- } else {
- ctc_pr_debug("%s: TX I/O error\n", dev->name);
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
-}
-
-static void
-ch_action_reinit(fsm_instance *fi, int event, void *arg)
-{
- struct channel *ch = (struct channel *)arg;
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- ch_action_iofatal(fi, event, arg);
- fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev);
-}
-
-/**
- * The statemachine for a channel.
- */
-static const fsm_node ch_fsm[] = {
- {CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop },
- {CH_STATE_STOPPED, CH_EVENT_START, ch_action_start },
- {CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop },
-
- {CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop },
- {CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop },
- {CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start },
-
- {CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode },
- {CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr },
- {CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode },
- {CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop },
- {CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio },
- {CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr },
- {CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr },
- {CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode },
- {CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle },
- {CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr },
- {CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail },
- {CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio },
- {CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx },
- {CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc },
-// {CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry },
- {CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail },
- {CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx },
-
- {CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle },
- {CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr },
- {CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio },
- {CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_START, ch_action_restart },
- {CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped },
- {CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_DTERM, CH_EVENT_START, ch_action_restart },
- {CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode },
- {CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop },
- {CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop },
- {CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TX, CH_EVENT_START, fsm_action_nop },
- {CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone },
- {CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry },
- {CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal },
- {CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit },
- {CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail },
-
- {CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio },
- {CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail },
- {CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail },
-};
-
-static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node);
-
-/**
- * Functions related to setup and device detection.
- *****************************************************************************/
-
-static inline int
-less_than(char *id1, char *id2)
-{
- int dev1, dev2, i;
-
- for (i = 0; i < 5; i++) {
- id1++;
- id2++;
- }
- dev1 = simple_strtoul(id1, &id1, 16);
- dev2 = simple_strtoul(id2, &id2, 16);
-
- return (dev1 < dev2);
-}
-
-/**
- * Add a new channel to the list of channels.
- * Keeps the channel list sorted.
- *
- * @param cdev The ccw_device to be added.
- * @param type The type class of the new channel.
- *
- * @return 0 on success, !0 on error.
- */
-static int
-add_channel(struct ccw_device *cdev, enum channel_types type)
-{
- struct channel **c = &channels;
- struct channel *ch;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
- if (!ch) {
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- return -1;
- }
- /* assure all flags and counters are reset */
- ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
- if (!ch->ccw) {
- kfree(ch);
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- return -1;
- }
-
-
- /**
- * "static" ccws are used in the following way:
- *
- * ccw[0..2] (Channel program for generic I/O):
- * 0: prepare
- * 1: read or write (depending on direction) with fixed
- * buffer (idal allocated once when buffer is allocated)
- * 2: nop
- * ccw[3..5] (Channel program for direct write of packets)
- * 3: prepare
- * 4: write (idal allocated on every write).
- * 5: nop
- * ccw[6..7] (Channel program for initial channel setup):
- * 6: set extended mode
- * 7: nop
- *
- * ch->ccw[0..5] are initialized in ch_action_start because
- * the channel's direction is yet unknown here.
- */
- ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
- ch->ccw[6].flags = CCW_FLAG_SLI;
-
- ch->ccw[7].cmd_code = CCW_CMD_NOOP;
- ch->ccw[7].flags = CCW_FLAG_SLI;
-
- ch->cdev = cdev;
- snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id);
- ch->type = type;
- ch->fsm = init_fsm(ch->id, ch_state_names,
- ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
- ch_fsm, CH_FSM_LEN, GFP_KERNEL);
- if (ch->fsm == NULL) {
- ctc_pr_warn("ctc: Could not create FSM in add_channel\n");
- kfree(ch->ccw);
- kfree(ch);
- return -1;
- }
- fsm_newstate(ch->fsm, CH_STATE_IDLE);
- ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
- if (!ch->irb) {
- ctc_pr_warn("ctc: Out of memory in add_channel\n");
- kfree_fsm(ch->fsm);
- kfree(ch->ccw);
- kfree(ch);
- return -1;
- }
- while (*c && less_than((*c)->id, ch->id))
- c = &(*c)->next;
- if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) {
- ctc_pr_debug(
- "ctc: add_channel: device %s already in list, "
- "using old entry\n", (*c)->id);
- kfree(ch->irb);
- kfree_fsm(ch->fsm);
- kfree(ch->ccw);
- kfree(ch);
- return 0;
- }
-
- spin_lock_init(&ch->collect_lock);
-
- fsm_settimer(ch->fsm, &ch->timer);
- skb_queue_head_init(&ch->io_queue);
- skb_queue_head_init(&ch->collect_queue);
- ch->next = *c;
- *c = ch;
- return 0;
-}
-
-/**
- * Release a specific channel in the channel list.
- *
- * @param ch Pointer to channel struct to be released.
- */
-static void
-channel_free(struct channel *ch)
-{
- ch->flags &= ~CHANNEL_FLAGS_INUSE;
- fsm_newstate(ch->fsm, CH_STATE_IDLE);
-}
-
-/**
- * Remove a specific channel in the channel list.
- *
- * @param ch Pointer to channel struct to be released.
- */
-static void
-channel_remove(struct channel *ch)
-{
- struct channel **c = &channels;
-
- DBF_TEXT(trace, 2, __FUNCTION__);
- if (ch == NULL)
- return;
-
- channel_free(ch);
- while (*c) {
- if (*c == ch) {
- *c = ch->next;
- fsm_deltimer(&ch->timer);
- kfree_fsm(ch->fsm);
- clear_normalized_cda(&ch->ccw[4]);
- if (ch->trans_skb != NULL) {
- clear_normalized_cda(&ch->ccw[1]);
- dev_kfree_skb(ch->trans_skb);
- }
- kfree(ch->ccw);
- kfree(ch->irb);
- kfree(ch);
- return;
- }
- c = &((*c)->next);
- }
-}
-
-/**
- * Get a specific channel from the channel list.
- *
- * @param type Type of channel we are interested in.
- * @param id Id of channel we are interested in.
- * @param direction Direction we want to use this channel for.
- *
- * @return Pointer to a channel or NULL if no matching channel available.
- */
-static struct channel
-*
-channel_get(enum channel_types type, char *id, int direction)
-{
- struct channel *ch = channels;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): searching for ch with id %s and type %d\n",
- __func__, id, type);
-#endif
-
- while (ch && ((strncmp(ch->id, id, CTC_ID_SIZE)) || (ch->type != type))) {
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n",
- __func__, ch, ch->id, ch->type);
-#endif
- ch = ch->next;
- }
-#ifdef DEBUG
- ctc_pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n",
- __func__, ch, ch->id, ch->type);
-#endif
- if (!ch) {
- ctc_pr_warn("ctc: %s(): channel with id %s "
- "and type %d not found in channel list\n",
- __func__, id, type);
- } else {
- if (ch->flags & CHANNEL_FLAGS_INUSE)
- ch = NULL;
- else {
- ch->flags |= CHANNEL_FLAGS_INUSE;
- ch->flags &= ~CHANNEL_FLAGS_RWMASK;
- ch->flags |= (direction == WRITE)
- ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
- fsm_newstate(ch->fsm, CH_STATE_STOPPED);
- }
- }
- return ch;
-}
-
-/**
- * Return the channel type by name.
- *
- * @param name Name of network interface.
- *
- * @return Type class of channel to be used for that interface.
- */
-static enum channel_types inline
-extract_channel_media(char *name)
-{
- enum channel_types ret = channel_type_unknown;
-
- if (name != NULL) {
- if (strncmp(name, "ctc", 3) == 0)
- ret = channel_type_parallel;
- if (strncmp(name, "escon", 5) == 0)
- ret = channel_type_escon;
- }
- return ret;
-}
-
-static long
-__ctc_check_irb_error(struct ccw_device *cdev, struct irb *irb)
-{
- if (!IS_ERR(irb))
- return 0;
-
- switch (PTR_ERR(irb)) {
- case -EIO:
- ctc_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT_(trace, 2, " rc%d", -EIO);
- break;
- case -ETIMEDOUT:
- ctc_pr_warn("timeout on device %s\n", cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT);
- break;
- default:
- ctc_pr_warn("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
-// CTC_DBF_TEXT(trace, 2, "ckirberr");
-// CTC_DBF_TEXT(trace, 2, " rc???");
- }
- return PTR_ERR(irb);
-}
-
-/**
- * Main IRQ handler.
- *
- * @param cdev The ccw_device the interrupt is for.
- * @param intparm interruption parameter.
- * @param irb interruption response block.
- */
-static void
-ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
-{
- struct channel *ch;
- struct net_device *dev;
- struct ctc_priv *priv;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- if (__ctc_check_irb_error(cdev, irb))
- return;
-
- /* Check for unsolicited interrupts. */
- if (!cdev->dev.driver_data) {
- ctc_pr_warn("ctc: Got unsolicited irq: %s c-%02x d-%02x\n",
- cdev->dev.bus_id, irb->scsw.cstat,
- irb->scsw.dstat);
- return;
- }
-
- priv = ((struct ccwgroup_device *)cdev->dev.driver_data)
- ->dev.driver_data;
-
- /* Try to extract channel from driver data. */
- if (priv->channel[READ]->cdev == cdev)
- ch = priv->channel[READ];
- else if (priv->channel[WRITE]->cdev == cdev)
- ch = priv->channel[WRITE];
- else {
- ctc_pr_err("ctc: Can't determine channel for interrupt, "
- "device %s\n", cdev->dev.bus_id);
- return;
- }
-
- dev = (struct net_device *) (ch->netdev);
- if (dev == NULL) {
- ctc_pr_crit("ctc: ctc_irq_handler dev=NULL bus_id=%s, ch=0x%p\n",
- cdev->dev.bus_id, ch);
- return;
- }
-
-#ifdef DEBUG
- ctc_pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n",
- dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat);
-#endif
-
- /* Copy interruption response block. */
- memcpy(ch->irb, irb, sizeof(struct irb));
-
- /* Check for good subchannel return code, otherwise error message */
- if (ch->irb->scsw.cstat) {
- fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch);
- ctc_pr_warn("%s: subchannel check for device: %s - %02x %02x\n",
- dev->name, ch->id, ch->irb->scsw.cstat,
- ch->irb->scsw.dstat);
- return;
- }
-
- /* Check the reason-code of a unit check */
- if (ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
- ccw_unit_check(ch, ch->irb->ecw[0]);
- return;
- }
- if (ch->irb->scsw.dstat & DEV_STAT_BUSY) {
- if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION)
- fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_BUSY, ch);
- return;
- }
- if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) {
- fsm_event(ch->fsm, CH_EVENT_ATTN, ch);
- return;
- }
- if ((ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
- fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
-
-}
-
-/**
- * Actions for interface - statemachine.
- *****************************************************************************/
-
-/**
- * Startup channels by sending CH_EVENT_START to each channel.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_start(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
- int direction;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- fsm_deltimer(&privptr->restart_timer);
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- struct channel *ch = privptr->channel[direction];
- fsm_event(ch->fsm, CH_EVENT_START, ch);
- }
-}
-
-/**
- * Shutdown channels by sending CH_EVENT_STOP to each channel.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_stop(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
- struct ctc_priv *privptr = dev->priv;
- int direction;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- struct channel *ch = privptr->channel[direction];
- fsm_event(ch->fsm, CH_EVENT_STOP, ch);
- }
-}
-static void
-dev_action_restart(fsm_instance *fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *)arg;
- struct ctc_priv *privptr = dev->priv;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- ctc_pr_debug("%s: Restarting\n", dev->name);
- dev_action_stop(fi, event, arg);
- fsm_event(privptr->fsm, DEV_EVENT_STOP, dev);
- fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC,
- DEV_EVENT_START, dev);
-}
-
-/**
- * Called from channel statemachine
- * when a channel is up and running.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_chup(fsm_instance * fi, int event, void *arg)
-{
- struct net_device *dev = (struct net_device *) arg;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- switch (fsm_getstate(fi)) {
- case DEV_STATE_STARTWAIT_RXTX:
- if (event == DEV_EVENT_RXUP)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
- else
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
- break;
- case DEV_STATE_STARTWAIT_RX:
- if (event == DEV_EVENT_RXUP) {
- fsm_newstate(fi, DEV_STATE_RUNNING);
- ctc_pr_info("%s: connected with remote side\n",
- dev->name);
- ctc_clear_busy(dev);
- }
- break;
- case DEV_STATE_STARTWAIT_TX:
- if (event == DEV_EVENT_TXUP) {
- fsm_newstate(fi, DEV_STATE_RUNNING);
- ctc_pr_info("%s: connected with remote side\n",
- dev->name);
- ctc_clear_busy(dev);
- }
- break;
- case DEV_STATE_STOPWAIT_TX:
- if (event == DEV_EVENT_RXUP)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- break;
- case DEV_STATE_STOPWAIT_RX:
- if (event == DEV_EVENT_TXUP)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- break;
- }
-}
-
-/**
- * Called from channel statemachine
- * when a channel has been shutdown.
- *
- * @param fi An instance of an interface statemachine.
- * @param event The event, just happened.
- * @param arg Generic pointer, casted from struct net_device * upon call.
- */
-static void
-dev_action_chdown(fsm_instance * fi, int event, void *arg)
-{
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- switch (fsm_getstate(fi)) {
- case DEV_STATE_RUNNING:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
- else
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
- break;
- case DEV_STATE_STARTWAIT_RX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- break;
- case DEV_STATE_STARTWAIT_TX:
- if (event == DEV_EVENT_RXDOWN)
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- break;
- case DEV_STATE_STOPWAIT_RXTX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
- else
- fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
- break;
- case DEV_STATE_STOPWAIT_RX:
- if (event == DEV_EVENT_RXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPPED);
- break;
- case DEV_STATE_STOPWAIT_TX:
- if (event == DEV_EVENT_TXDOWN)
- fsm_newstate(fi, DEV_STATE_STOPPED);
- break;
- }
-}
-
-static const fsm_node dev_fsm[] = {
- {DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start},
-
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart },
-
- {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop },
- {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown },
- {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown },
- {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop },
- {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop },
- {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart },
-};
-
-static const int DEV_FSM_LEN = sizeof (dev_fsm) / sizeof (fsm_node);
-
-/**
- * Transmit a packet.
- * This is a helper function for ctc_tx().
- *
- * @param ch Channel to be used for sending.
- * @param skb Pointer to struct sk_buff of packet to send.
- * The linklevel header has already been set up
- * by ctc_tx().
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-transmit_skb(struct channel *ch, struct sk_buff *skb)
-{
- unsigned long saveflags;
- struct ll_header header;
- int rc = 0;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- /* we need to acquire the lock for testing the state
- * otherwise we can have an IRQ changing the state to
- * TXIDLE after the test but before acquiring the lock.
- */
- spin_lock_irqsave(&ch->collect_lock, saveflags);
- if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
- int l = skb->len + LL_HEADER_LENGTH;
-
- if (ch->collect_len + l > ch->max_bufsize - 2) {
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- return -EBUSY;
- } else {
- atomic_inc(&skb->users);
- header.length = l;
- header.type = skb->protocol;
- header.unused = 0;
- memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
- LL_HEADER_LENGTH);
- skb_queue_tail(&ch->collect_queue, skb);
- ch->collect_len += l;
- }
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- } else {
- __u16 block_len;
- int ccw_idx;
- struct sk_buff *nskb;
- unsigned long hi;
- spin_unlock_irqrestore(&ch->collect_lock, saveflags);
- /**
- * Protect skb against beeing free'd by upper
- * layers.
- */
- atomic_inc(&skb->users);
- ch->prof.txlen += skb->len;
- header.length = skb->len + LL_HEADER_LENGTH;
- header.type = skb->protocol;
- header.unused = 0;
- memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
- LL_HEADER_LENGTH);
- block_len = skb->len + 2;
- *((__u16 *) skb_push(skb, 2)) = block_len;
-
- /**
- * IDAL support in CTC is broken, so we have to
- * care about skb's above 2G ourselves.
- */
- hi = ((unsigned long)skb_tail_pointer(skb) +
- LL_HEADER_LENGTH) >> 31;
- if (hi) {
- nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
- if (!nskb) {
- atomic_dec(&skb->users);
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- ctc_clear_busy(ch->netdev);
- return -ENOMEM;
- } else {
- memcpy(skb_put(nskb, skb->len),
- skb->data, skb->len);
- atomic_inc(&nskb->users);
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- skb = nskb;
- }
- }
-
- ch->ccw[4].count = block_len;
- if (set_normalized_cda(&ch->ccw[4], skb->data)) {
- /**
- * idal allocation failed, try via copying to
- * trans_skb. trans_skb usually has a pre-allocated
- * idal.
- */
- if (ctc_checkalloc_buffer(ch, 1)) {
- /**
- * Remove our header. It gets added
- * again on retransmit.
- */
- atomic_dec(&skb->users);
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- ctc_clear_busy(ch->netdev);
- return -EBUSY;
- }
-
- skb_reset_tail_pointer(ch->trans_skb);
- ch->trans_skb->len = 0;
- ch->ccw[1].count = skb->len;
- skb_copy_from_linear_data(skb, skb_put(ch->trans_skb,
- skb->len),
- skb->len);
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- ccw_idx = 0;
- } else {
- skb_queue_tail(&ch->io_queue, skb);
- ccw_idx = 3;
- }
- ch->retry = 0;
- fsm_newstate(ch->fsm, CH_STATE_TX);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
- ch->prof.send_stamp = current_kernel_time();
- rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx],
- (unsigned long) ch, 0xff, 0);
- spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
- if (ccw_idx == 3)
- ch->prof.doios_single++;
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc, "single skb TX");
- if (ccw_idx == 3)
- skb_dequeue_tail(&ch->io_queue);
- /**
- * Remove our header. It gets added
- * again on retransmit.
- */
- skb_pull(skb, LL_HEADER_LENGTH + 2);
- } else {
- if (ccw_idx == 0) {
- struct net_device *dev = ch->netdev;
- struct ctc_priv *privptr = dev->priv;
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes +=
- skb->len - LL_HEADER_LENGTH;
- }
- }
- }
-
- ctc_clear_busy(ch->netdev);
- return rc;
-}
-
-/**
- * Interface API for upper network layers
- *****************************************************************************/
-
-/**
- * Open an interface.
- * Called from generic network layer when ifconfig up is run.
- *
- * @param dev Pointer to interface struct.
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-ctc_open(struct net_device * dev)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_START, dev);
- return 0;
-}
-
-/**
- * Close an interface.
- * Called from generic network layer when ifconfig down is run.
- *
- * @param dev Pointer to interface struct.
- *
- * @return 0 on success, -ERRNO on failure. (Never fails.)
- */
-static int
-ctc_close(struct net_device * dev)
-{
- DBF_TEXT(trace, 5, __FUNCTION__);
- fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_STOP, dev);
- return 0;
-}
-
-/**
- * Start transmission of a packet.
- * Called from generic network device layer.
- *
- * @param skb Pointer to buffer containing the packet.
- * @param dev Pointer to interface struct.
- *
- * @return 0 if packet consumed, !0 if packet rejected.
- * Note: If we return !0, then the packet is free'd by
- * the generic network layer.
- */
-static int
-ctc_tx(struct sk_buff *skb, struct net_device * dev)
-{
- int rc = 0;
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- /**
- * Some sanity checks ...
- */
- if (skb == NULL) {
- ctc_pr_warn("%s: NULL sk_buff passed\n", dev->name);
- privptr->stats.tx_dropped++;
- return 0;
- }
- if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
- ctc_pr_warn("%s: Got sk_buff with head room < %ld bytes\n",
- dev->name, LL_HEADER_LENGTH + 2);
- dev_kfree_skb(skb);
- privptr->stats.tx_dropped++;
- return 0;
- }
-
- /**
- * If channels are not running, try to restart them
- * and throw away packet.
- */
- if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- fsm_event(privptr->fsm, DEV_EVENT_START, dev);
- dev_kfree_skb(skb);
- privptr->stats.tx_dropped++;
- privptr->stats.tx_errors++;
- privptr->stats.tx_carrier_errors++;
- return 0;
- }
-
- if (ctc_test_and_set_busy(dev))
- return -EBUSY;
-
- dev->trans_start = jiffies;
- if (transmit_skb(privptr->channel[WRITE], skb) != 0)
- rc = 1;
- return rc;
-}
-
-/**
- * Sets MTU of an interface.
- *
- * @param dev Pointer to interface struct.
- * @param new_mtu The new MTU to use for this interface.
- *
- * @return 0 on success, -EINVAL if MTU is out of valid range.
- * (valid range is 576 .. 65527). If VM is on the
- * remote side, maximum MTU is 32760, however this is
- * <em>not</em> checked here.
- */
-static int
-ctc_change_mtu(struct net_device * dev, int new_mtu)
-{
- struct ctc_priv *privptr = (struct ctc_priv *) dev->priv;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- if ((new_mtu < 576) || (new_mtu > 65527) ||
- (new_mtu > (privptr->channel[READ]->max_bufsize -
- LL_HEADER_LENGTH - 2)))
- return -EINVAL;
- dev->mtu = new_mtu;
- dev->hard_header_len = LL_HEADER_LENGTH + 2;
- return 0;
-}
-
-/**
- * Returns interface statistics of a device.
- *
- * @param dev Pointer to interface struct.
- *
- * @return Pointer to stats struct of this interface.
- */
-static struct net_device_stats *
-ctc_stats(struct net_device * dev)
-{
- return &((struct ctc_priv *) dev->priv)->stats;
-}
-
-/*
- * sysfs attributes
- */
-
-static ssize_t
-buffer_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv;
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- return sprintf(buf, "%d\n",
- priv->buffer_size);
-}
-
-static ssize_t
-buffer_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv;
- struct net_device *ndev;
- int bs1;
- char buffer[16];
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- DBF_TEXT(trace, 3, buf);
- priv = dev->driver_data;
- if (!priv) {
- DBF_TEXT(trace, 3, "bfnopriv");
- return -ENODEV;
- }
-
- sscanf(buf, "%u", &bs1);
- if (bs1 > CTC_BUFSIZE_LIMIT)
- goto einval;
- if (bs1 < (576 + LL_HEADER_LENGTH + 2))
- goto einval;
- priv->buffer_size = bs1; // just to overwrite the default
-
- ndev = priv->channel[READ]->netdev;
- if (!ndev) {
- DBF_TEXT(trace, 3, "bfnondev");
- return -ENODEV;
- }
-
- if ((ndev->flags & IFF_RUNNING) &&
- (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
- goto einval;
-
- priv->channel[READ]->max_bufsize = bs1;
- priv->channel[WRITE]->max_bufsize = bs1;
- if (!(ndev->flags & IFF_RUNNING))
- ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
- priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
- priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
-
- sprintf(buffer, "%d",priv->buffer_size);
- DBF_TEXT(trace, 3, buffer);
- return count;
-
-einval:
- DBF_TEXT(trace, 3, "buff_err");
- return -EINVAL;
-}
-
-static ssize_t
-loglevel_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%d\n", loglevel);
-}
-
-static ssize_t
-loglevel_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- int ll1;
-
- DBF_TEXT(trace, 5, __FUNCTION__);
- sscanf(buf, "%i", &ll1);
-
- if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0))
- return -EINVAL;
- loglevel = ll1;
- return count;
-}
-
-static void
-ctc_print_statistics(struct ctc_priv *priv)
-{
- char *sbuf;
- char *p;
-
- DBF_TEXT(trace, 4, __FUNCTION__);
- if (!priv)
- return;
- sbuf = kmalloc(2048, GFP_KERNEL);
- if (sbuf == NULL)
- return;
- p = sbuf;
-
- p += sprintf(p, " Device FSM state: %s\n",
- fsm_getstate_str(priv->fsm));
- p += sprintf(p, " RX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[READ]->fsm));
- p += sprintf(p, " TX channel FSM state: %s\n",
- fsm_getstate_str(priv->channel[WRITE]->fsm));
- p += sprintf(p, " Max. TX buffer used: %ld\n",
- priv->channel[WRITE]->prof.maxmulti);
- p += sprintf(p, " Max. chained SKBs: %ld\n",
- priv->channel[WRITE]->prof.maxcqueue);
- p += sprintf(p, " TX single write ops: %ld\n",
- priv->channel[WRITE]->prof.doios_single);
- p += sprintf(p, " TX multi write ops: %ld\n",
- priv->channel[WRITE]->prof.doios_multi);
- p += sprintf(p, " Netto bytes written: %ld\n",
- priv->channel[WRITE]->prof.txlen);
- p += sprintf(p, " Max. TX IO-time: %ld\n",
- priv->channel[WRITE]->prof.tx_time);
-
- ctc_pr_debug("Statistics for %s:\n%s",
- priv->channel[WRITE]->netdev->name, sbuf);
- kfree(sbuf);
- return;
-}
-
-static ssize_t
-stats_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- ctc_print_statistics(priv);
- return sprintf(buf, "0\n");
-}
-
-static ssize_t
-stats_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- /* Reset statistics */
- memset(&priv->channel[WRITE]->prof, 0,
- sizeof(priv->channel[WRITE]->prof));
- return count;
-}
-
-static void
-ctc_netdev_unregister(struct net_device * dev)
-{
- struct ctc_priv *privptr;
-
- if (!dev)
- return;
- privptr = (struct ctc_priv *) dev->priv;
- unregister_netdev(dev);
-}
-
-static int
-ctc_netdev_register(struct net_device * dev)
-{
- return register_netdev(dev);
-}
-
-static void
-ctc_free_netdevice(struct net_device * dev, int free_dev)
-{
- struct ctc_priv *privptr;
- if (!dev)
- return;
- privptr = dev->priv;
- if (privptr) {
- if (privptr->fsm)
- kfree_fsm(privptr->fsm);
- kfree(privptr);
- }
-#ifdef MODULE
- if (free_dev)
- free_netdev(dev);
-#endif
-}
-
-static ssize_t
-ctc_proto_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ctc_priv *priv;
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
-
- return sprintf(buf, "%d\n", priv->protocol);
-}
-
-static ssize_t
-ctc_proto_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct ctc_priv *priv;
- int value;
-
- DBF_TEXT(trace, 3, __FUNCTION__);
- pr_debug("%s() called\n", __FUNCTION__);
-
- priv = dev->driver_data;
- if (!priv)
- return -ENODEV;
- sscanf(buf, "%u", &value);
- if (!((value == CTC_PROTO_S390) ||
- (value == CTC_PROTO_LINUX) ||
- (value == CTC_PROTO_OS390)))
- return -EINVAL;
- priv->protocol = value;
-
- return count;
-}
-
-static ssize_t
-ctc_type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct ccwgroup_device *cgdev;
-
- cgdev = to_ccwgroupdev(dev);
- if (!cgdev)
- return -ENODEV;
-
- return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]);
-}
-
-static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
-static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store);
-static DEVICE_ATTR(type, 0444, ctc_type_show, NULL);
-
-static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write);
-static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
-
-static struct attribute *ctc_attr[] = {
- &dev_attr_protocol.attr,
- &dev_attr_type.attr,
- &dev_attr_buffer.attr,
- NULL,
-};
-
-static struct attribute_group ctc_attr_group = {
- .attrs = ctc_attr,
-};
-
-static int
-ctc_add_attributes(struct device *dev)
-{
- int rc;
-
- rc = device_create_file(dev, &dev_attr_loglevel);
- if (rc)
- goto out;
- rc = device_create_file(dev, &dev_attr_stats);
- if (!rc)
- goto out;
- device_remove_file(dev, &dev_attr_loglevel);
-out:
- return rc;
-}
-
-static void
-ctc_remove_attributes(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_stats);
- device_remove_file(dev, &dev_attr_loglevel);
-}
-
-static int
-ctc_add_files(struct device *dev)
-{
- pr_debug("%s() called\n", __FUNCTION__);
-
- return sysfs_create_group(&dev->kobj, &ctc_attr_group);
-}
-
-static void
-ctc_remove_files(struct device *dev)
-{
- pr_debug("%s() called\n", __FUNCTION__);
-
- sysfs_remove_group(&dev->kobj, &ctc_attr_group);
-}
-
-/**
- * Add ctc specific attributes.
- * Add ctc private data.
- *
- * @param cgdev pointer to ccwgroup_device just added
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_probe_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
- int rc;
- char buffer[16];
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- if (!get_device(&cgdev->dev))
- return -ENODEV;
-
- priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL);
- if (!priv) {
- ctc_pr_err("%s: Out of memory\n", __func__);
- put_device(&cgdev->dev);
- return -ENOMEM;
- }
-
- rc = ctc_add_files(&cgdev->dev);
- if (rc) {
- kfree(priv);
- put_device(&cgdev->dev);
- return rc;
- }
- priv->buffer_size = CTC_BUFSIZE_DEFAULT;
- cgdev->cdev[0]->handler = ctc_irq_handler;
- cgdev->cdev[1]->handler = ctc_irq_handler;
- cgdev->dev.driver_data = priv;
-
- sprintf(buffer, "%p", priv);
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%u", (unsigned int)sizeof(struct ctc_priv));
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%p", &channels);
- DBF_TEXT(data, 3, buffer);
-
- sprintf(buffer, "%u", (unsigned int)sizeof(struct channel));
- DBF_TEXT(data, 3, buffer);
-
- return 0;
-}
-
-/**
- * Device setup function called by alloc_netdev().
- *
- * @param dev Device to be setup.
- */
-void ctc_init_netdevice(struct net_device * dev)
-{
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- if (dev->mtu == 0)
- dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
- dev->hard_start_xmit = ctc_tx;
- dev->open = ctc_open;
- dev->stop = ctc_close;
- dev->get_stats = ctc_stats;
- dev->change_mtu = ctc_change_mtu;
- dev->hard_header_len = LL_HEADER_LENGTH + 2;
- dev->addr_len = 0;
- dev->type = ARPHRD_SLIP;
- dev->tx_queue_len = 100;
- dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-}
-
-
-/**
- *
- * Setup an interface.
- *
- * @param cgdev Device to be setup.
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_new_device(struct ccwgroup_device *cgdev)
-{
- char read_id[CTC_ID_SIZE];
- char write_id[CTC_ID_SIZE];
- int direction;
- enum channel_types type;
- struct ctc_priv *privptr;
- struct net_device *dev;
- int ret;
- char buffer[16];
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- privptr = cgdev->dev.driver_data;
- if (!privptr)
- return -ENODEV;
-
- sprintf(buffer, "%d", privptr->buffer_size);
- DBF_TEXT(setup, 3, buffer);
-
- type = get_channel_type(&cgdev->cdev[0]->id);
-
- snprintf(read_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
- snprintf(write_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id);
-
- if (add_channel(cgdev->cdev[0], type))
- return -ENOMEM;
- if (add_channel(cgdev->cdev[1], type))
- return -ENOMEM;
-
- ret = ccw_device_set_online(cgdev->cdev[0]);
- if (ret != 0) {
- printk(KERN_WARNING
- "ccw_device_set_online (cdev[0]) failed with ret = %d\n", ret);
- }
-
- ret = ccw_device_set_online(cgdev->cdev[1]);
- if (ret != 0) {
- printk(KERN_WARNING
- "ccw_device_set_online (cdev[1]) failed with ret = %d\n", ret);
- }
-
- dev = alloc_netdev(0, "ctc%d", ctc_init_netdevice);
- if (!dev) {
- ctc_pr_warn("ctc_init_netdevice failed\n");
- goto out;
- }
- dev->priv = privptr;
-
- privptr->fsm = init_fsm("ctcdev", dev_state_names,
- dev_event_names, CTC_NR_DEV_STATES, CTC_NR_DEV_EVENTS,
- dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
- if (privptr->fsm == NULL) {
- free_netdev(dev);
- goto out;
- }
- fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
- fsm_settimer(privptr->fsm, &privptr->restart_timer);
-
- for (direction = READ; direction <= WRITE; direction++) {
- privptr->channel[direction] =
- channel_get(type, direction == READ ? read_id : write_id,
- direction);
- if (privptr->channel[direction] == NULL) {
- if (direction == WRITE)
- channel_free(privptr->channel[READ]);
-
- ctc_free_netdevice(dev, 1);
- goto out;
- }
- privptr->channel[direction]->netdev = dev;
- privptr->channel[direction]->protocol = privptr->protocol;
- privptr->channel[direction]->max_bufsize = privptr->buffer_size;
- }
- /* sysfs magic */
- SET_NETDEV_DEV(dev, &cgdev->dev);
-
- if (ctc_netdev_register(dev) != 0) {
- ctc_free_netdevice(dev, 1);
- goto out;
- }
-
- if (ctc_add_attributes(&cgdev->dev)) {
- ctc_netdev_unregister(dev);
- dev->priv = NULL;
- ctc_free_netdevice(dev, 1);
- goto out;
- }
-
- strlcpy(privptr->fsm->name, dev->name, sizeof (privptr->fsm->name));
-
- print_banner();
-
- ctc_pr_info("%s: read: %s, write: %s, proto: %d\n",
- dev->name, privptr->channel[READ]->id,
- privptr->channel[WRITE]->id, privptr->protocol);
-
- return 0;
-out:
- ccw_device_set_offline(cgdev->cdev[1]);
- ccw_device_set_offline(cgdev->cdev[0]);
-
- return -ENODEV;
-}
-
-/**
- * Shutdown an interface.
- *
- * @param cgdev Device to be shut down.
- *
- * @returns 0 on success, !0 on failure.
- */
-static int
-ctc_shutdown_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
- struct net_device *ndev;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
- pr_debug("%s() called\n", __FUNCTION__);
-
-
- priv = cgdev->dev.driver_data;
- ndev = NULL;
- if (!priv)
- return -ENODEV;
-
- if (priv->channel[READ]) {
- ndev = priv->channel[READ]->netdev;
-
- /* Close the device */
- ctc_close(ndev);
- ndev->flags &=~IFF_RUNNING;
-
- ctc_remove_attributes(&cgdev->dev);
-
- channel_free(priv->channel[READ]);
- }
- if (priv->channel[WRITE])
- channel_free(priv->channel[WRITE]);
-
- if (ndev) {
- ctc_netdev_unregister(ndev);
- ndev->priv = NULL;
- ctc_free_netdevice(ndev, 1);
- }
-
- if (priv->fsm)
- kfree_fsm(priv->fsm);
-
- ccw_device_set_offline(cgdev->cdev[1]);
- ccw_device_set_offline(cgdev->cdev[0]);
-
- if (priv->channel[READ])
- channel_remove(priv->channel[READ]);
- if (priv->channel[WRITE])
- channel_remove(priv->channel[WRITE]);
- priv->channel[READ] = priv->channel[WRITE] = NULL;
-
- return 0;
-
-}
-
-static void
-ctc_remove_device(struct ccwgroup_device *cgdev)
-{
- struct ctc_priv *priv;
-
- pr_debug("%s() called\n", __FUNCTION__);
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- priv = cgdev->dev.driver_data;
- if (!priv)
- return;
- if (cgdev->state == CCWGROUP_ONLINE)
- ctc_shutdown_device(cgdev);
- ctc_remove_files(&cgdev->dev);
- cgdev->dev.driver_data = NULL;
- kfree(priv);
- put_device(&cgdev->dev);
-}
-
-static struct ccwgroup_driver ctc_group_driver = {
- .owner = THIS_MODULE,
- .name = "ctc",
- .max_slaves = 2,
- .driver_id = 0xC3E3C3,
- .probe = ctc_probe_device,
- .remove = ctc_remove_device,
- .set_online = ctc_new_device,
- .set_offline = ctc_shutdown_device,
-};
-
-/**
- * Module related routines
- *****************************************************************************/
-
-/**
- * Prepare to be unloaded. Free IRQ's and release all resources.
- * This is called just before this module is unloaded. It is
- * <em>not</em> called, if the usage count is !0, so we don't need to check
- * for that.
- */
-static void __exit
-ctc_exit(void)
-{
- DBF_TEXT(setup, 3, __FUNCTION__);
- unregister_cu3088_discipline(&ctc_group_driver);
- ctc_unregister_dbf_views();
- ctc_pr_info("CTC driver unloaded\n");
-}
-
-/**
- * Initialize module.
- * This is called just after the module is loaded.
- *
- * @return 0 on success, !0 on error.
- */
-static int __init
-ctc_init(void)
-{
- int ret = 0;
-
- loglevel = CTC_LOGLEVEL_DEFAULT;
-
- DBF_TEXT(setup, 3, __FUNCTION__);
-
- print_banner();
-
- ret = ctc_register_dbf_views();
- if (ret){
- ctc_pr_crit("ctc_init failed with ctc_register_dbf_views rc = %d\n", ret);
- return ret;
- }
- ret = register_cu3088_discipline(&ctc_group_driver);
- if (ret) {
- ctc_unregister_dbf_views();
- }
- return ret;
-}
-
-module_init(ctc_init);
-module_exit(ctc_exit);
-
-/* --- This is the END my friend --- */
Index: linux-2.6-uschi/drivers/s390/net/ctcmain.h
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/ctcmain.h
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * CTC / ESCON network driver
- *
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
- Peter Tiedemann (ptiedem@de.ibm.com)
- *
- *
- * Documentation used:
- * - Principles of Operation (IBM doc#: SA22-7201-06)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
- * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
- * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
- * - ESCON I/O Interface (IBM doc#: SA22-7202-029
- *
- * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _CTCMAIN_H_
-#define _CTCMAIN_H_
-
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-
-#include "fsm.h"
-#include "cu3088.h"
-
-
-/**
- * CCW commands, used in this driver.
- */
-#define CCW_CMD_WRITE 0x01
-#define CCW_CMD_READ 0x02
-#define CCW_CMD_SET_EXTENDED 0xc3
-#define CCW_CMD_PREPARE 0xe3
-
-#define CTC_PROTO_S390 0
-#define CTC_PROTO_LINUX 1
-#define CTC_PROTO_OS390 3
-
-#define CTC_BUFSIZE_LIMIT 65535
-#define CTC_BUFSIZE_DEFAULT 32768
-
-#define CTC_TIMEOUT_5SEC 5000
-
-#define CTC_INITIAL_BLOCKLEN 2
-
-#define READ 0
-#define WRITE 1
-
-#define CTC_ID_SIZE BUS_ID_SIZE+3
-
-
-struct ctc_profile {
- unsigned long maxmulti;
- unsigned long maxcqueue;
- unsigned long doios_single;
- unsigned long doios_multi;
- unsigned long txlen;
- unsigned long tx_time;
- struct timespec send_stamp;
-};
-
-/**
- * Definition of one channel
- */
-struct channel {
-
- /**
- * Pointer to next channel in list.
- */
- struct channel *next;
- char id[CTC_ID_SIZE];
- struct ccw_device *cdev;
-
- /**
- * Type of this channel.
- * CTC/A or Escon for valid channels.
- */
- enum channel_types type;
-
- /**
- * Misc. flags. See CHANNEL_FLAGS_... below
- */
- __u32 flags;
-
- /**
- * The protocol of this channel
- */
- __u16 protocol;
-
- /**
- * I/O and irq related stuff
- */
- struct ccw1 *ccw;
- struct irb *irb;
-
- /**
- * RX/TX buffer size
- */
- int max_bufsize;
-
- /**
- * Transmit/Receive buffer.
- */
- struct sk_buff *trans_skb;
-
- /**
- * Universal I/O queue.
- */
- struct sk_buff_head io_queue;
-
- /**
- * TX queue for collecting skb's during busy.
- */
- struct sk_buff_head collect_queue;
-
- /**
- * Amount of data in collect_queue.
- */
- int collect_len;
-
- /**
- * spinlock for collect_queue and collect_len
- */
- spinlock_t collect_lock;
-
- /**
- * Timer for detecting unresposive
- * I/O operations.
- */
- fsm_timer timer;
-
- /**
- * Retry counter for misc. operations.
- */
- int retry;
-
- /**
- * The finite state machine of this channel
- */
- fsm_instance *fsm;
-
- /**
- * The corresponding net_device this channel
- * belongs to.
- */
- struct net_device *netdev;
-
- struct ctc_profile prof;
-
- unsigned char *trans_skb_data;
-
- __u16 logflags;
-};
-
-#define CHANNEL_FLAGS_READ 0
-#define CHANNEL_FLAGS_WRITE 1
-#define CHANNEL_FLAGS_INUSE 2
-#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
-#define CHANNEL_FLAGS_FAILED 8
-#define CHANNEL_FLAGS_WAITIRQ 16
-#define CHANNEL_FLAGS_RWMASK 1
-#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
-
-#define LOG_FLAG_ILLEGALPKT 1
-#define LOG_FLAG_ILLEGALSIZE 2
-#define LOG_FLAG_OVERRUN 4
-#define LOG_FLAG_NOMEM 8
-
-#define CTC_LOGLEVEL_INFO 1
-#define CTC_LOGLEVEL_NOTICE 2
-#define CTC_LOGLEVEL_WARN 4
-#define CTC_LOGLEVEL_EMERG 8
-#define CTC_LOGLEVEL_ERR 16
-#define CTC_LOGLEVEL_DEBUG 32
-#define CTC_LOGLEVEL_CRIT 64
-
-#define CTC_LOGLEVEL_DEFAULT \
-(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT)
-
-#define CTC_LOGLEVEL_MAX ((CTC_LOGLEVEL_CRIT<<1)-1)
-
-#define ctc_pr_debug(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0)
-
-#define ctc_pr_info(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0)
-
-#define ctc_pr_notice(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0)
-
-#define ctc_pr_warn(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0)
-
-#define ctc_pr_emerg(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0)
-
-#define ctc_pr_err(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0)
-
-#define ctc_pr_crit(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0)
-
-struct ctc_priv {
- struct net_device_stats stats;
- unsigned long tbusy;
- /**
- * The finite state machine of this interface.
- */
- fsm_instance *fsm;
- /**
- * The protocol of this device
- */
- __u16 protocol;
- /**
- * Timer for restarting after I/O Errors
- */
- fsm_timer restart_timer;
-
- int buffer_size;
-
- struct channel *channel[2];
-};
-
-/**
- * Definition of our link level header.
- */
-struct ll_header {
- __u16 length;
- __u16 type;
- __u16 unused;
-};
-#define LL_HEADER_LENGTH (sizeof(struct ll_header))
-
-/**
- * Compatibility macros for busy handling
- * of network devices.
- */
-static __inline__ void
-ctc_clear_busy(struct net_device * dev)
-{
- clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
- netif_wake_queue(dev);
-}
-
-static __inline__ int
-ctc_test_and_set_busy(struct net_device * dev)
-{
- netif_stop_queue(dev);
- return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
-}
-
-#endif
--
^ permalink raw reply
* [patch 1/3] drivers/s390/net: Kconfig brush up
From: Ursula Braun @ 2008-02-07 23:03 UTC (permalink / raw)
To: jgarzik, netdev, linux-s390; +Cc: Peter Tiedemann
In-Reply-To: <20080207230347.075759000@linux.vnet.ibm.com>
[-- Attachment #1: 701-kconfig.diff --]
[-- Type: text/plain, Size: 4356 bytes --]
From: Peter Tiedemann <ptiedem@de.ibm.com>
From: Ursula Braun <braunu@de.ibm.com>
adapt drivers/s390/net/Kconfig to current IBM wording
and further cosmetics
Signed-off-by: Peter Tiedemann <ptiedem@de.ibm.com>
Signed-off-by: Ursula Braun <braunu@de.ibm.com>
---
drivers/s390/net/Kconfig | 43 ++++++++++++++++++++++---------------------
1 file changed, 22 insertions(+), 21 deletions(-)
Index: linux-2.6-uschi/drivers/s390/net/Kconfig
===================================================================
--- linux-2.6-uschi.orig/drivers/s390/net/Kconfig
+++ linux-2.6-uschi/drivers/s390/net/Kconfig
@@ -5,22 +5,23 @@ config LCS
tristate "Lan Channel Station Interface"
depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI)
help
- Select this option if you want to use LCS networking on IBM S/390
- or zSeries. This device driver supports Token Ring (IEEE 802.5),
- FDDI (IEEE 802.7) and Ethernet.
- This option is also available as a module which will be
- called lcs.ko. If you do not know what it is, it's safe to say "Y".
+ Select this option if you want to use LCS networking on IBM System z.
+ This device driver supports Token Ring (IEEE 802.5),
+ FDDI (IEEE 802.7) and Ethernet.
+ To compile as a module, choose M. The module name is lcs.ko.
+ If you do not know what it is, it's safe to choose Y.
config CTC
tristate "CTC device support"
depends on CCW && NETDEVICES
help
- Select this option if you want to use channel-to-channel networking
- on IBM S/390 or zSeries. This device driver supports real CTC
- coupling using ESCON. It also supports virtual CTCs when running
- under VM. It will use the channel device configuration if this is
- available. This option is also available as a module which will be
- called ctc.ko. If you do not know what it is, it's safe to say "Y".
+ Select this option if you want to use channel-to-channel
+ point-to-point networking on IBM System z.
+ This device driver supports real CTC coupling using ESCON.
+ It also supports virtual CTCs when running under VM.
+ To compile as a module, choose M. The module name is ctc.ko.
+ To compile into the kernel, choose Y.
+ If you do not need any channel-to-channel connection, choose N.
config NETIUCV
tristate "IUCV network device support (VM only)"
@@ -29,9 +30,9 @@ config NETIUCV
Select this option if you want to use inter-user communication
vehicle networking under VM or VIF. It enables a fast communication
link between VM guests. Using ifconfig a point-to-point connection
- can be established to the Linux for zSeries and S7390 system
- running on the other VM guest. This option is also available
- as a module which will be called netiucv.ko. If unsure, say "Y".
+ can be established to the Linux on IBM System z
+ running on the other VM guest. To compile as a module, choose M.
+ The module name is netiucv.ko. If unsure, choose Y.
config SMSGIUCV
tristate "IUCV special message support (VM only)"
@@ -47,22 +48,22 @@ config CLAW
This driver supports channel attached CLAW devices.
CLAW is Common Link Access for Workstation. Common devices
that use CLAW are RS/6000s, Cisco Routers (CIP) and 3172 devices.
- To compile as a module choose M here: The module will be called
- claw.ko to compile into the kernel choose Y
+ To compile as a module, choose M. The module name is claw.ko.
+ To compile into the kernel, choose Y.
config QETH
tristate "Gigabit Ethernet device support"
depends on CCW && NETDEVICES && IP_MULTICAST && QDIO
help
- This driver supports the IBM S/390 and zSeries OSA Express adapters
+ This driver supports the IBM System z OSA Express adapters
in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN
interfaces in QDIO and HIPER mode.
- For details please refer to the documentation provided by IBM at
- <http://www10.software.ibm.com/developerworks/opensource/linux390>
+ For details please refer to the documentation provided by IBM at
+ <http://www.ibm.com/developerworks/linux/linux390>
- To compile this driver as a module, choose M here: the
- module will be called qeth.ko.
+ To compile this driver as a module, choose M.
+ The module name is qeth.ko.
comment "Gigabit Ethernet default settings"
--
^ permalink raw reply
* [patch 0/3] s390: ctc patches for 2.6.25 (4th try)
From: Ursula Braun @ 2008-02-07 23:03 UTC (permalink / raw)
To: jgarzik, netdev, linux-s390
--
Jeff,
this resend includes improvements proposed by Christoph Hellwig:
netiucv changes are postponed to a future patch
drivers/s390/net/fsm.h is not changed
drivers/s390/net/Makefile is adapted
changed indentation of cpp directives
removed forward declaration of chx_rxidle
removed kerneldoc-like keywords
removed unnecessary type casts
kept local variable of type struct ctcm_priv
We kept the filenames in the top of file comments, since this is common
to drivers/s390-parts.
And we kept the ccw_device_halt call due to different usages
in different drivers.
The following patches are intended for 2.6.25. Besides clean-ups
they replace the old ctc driver by a reworked ctcm driver.
This ctcm driver supports the channel-to-channel connections of the
old ctc driver plus an additional MPC protocol to provide SNA
connectivity.
Patch 1/3: clean-ups in Kconfig
Patch 2/3: reworked ctc driver
Patch 3/3: removal of old ctc driver
Regards, Ursula Braun
^ permalink raw reply
* Re: Bug? Kernels 2.6.2x drops TCP packets over wireless (independentof card used)
From: Eric Dumazet @ 2008-02-07 19:24 UTC (permalink / raw)
To: Marcin Koziej; +Cc: linux-kernel, netdev
In-Reply-To: <5d83a64e.4ddf1deb.47ab4ef3.49eb3@o2.pl>
Marcin Koziej a écrit :
>>> I have problem with wireless network connectivity;
>>> TCP connections hang and timeout before all data is read.
>>>
>>> Problem description:
>>> I am connected to the network via hotspot wifi router:
>>> ath0 IEEE 802.11g ESSID:"hotspot" Nickname:""
>>> Mode:Managed Frequency:2.462 GHz Access Point: 00:60:B3:6C:A1:2E
>>> Bit Rate:11 Mb/s Tx-Power:17 dBm Sensitivity=1/1
>>> Retry:off RTS thr:off Fragment thr:off
>>> Power Management:off
>>> Link Quality=19/70 Signal level=-77 dBm Noise level=-96 dBm
>>> Rx invalid nwid:43882 Rx invalid crypt:0 Rx invalid frag:0
>>> Tx excessive retries:0 Invalid misc:0 Missed beacon:0
>>>
>>> ICMP and UDP protocols seem to work (provided by 0% packet loss
>>> ping to router (and internet servers), and a working DNS resolver)
>>> However, when I try to connect to a TCP service, for example FTP or HTTP, the
>>> connection is made, request
>>> sent but truncated answer (or no answer) ever read back.
>>>
>>> For example:
>>> # ftp ftp.icm.edu.pl
>>> Connected to ftp.icm.edu.pl (193.219.28.140).
>>> 220-
>>> (hangs)
>>>
>>> The packet dump is:
>>> 23:56:08.807674 IP 192.168.1.2.51909 > sunsite2.icm.edu.pl.ftp: S
>>> 980196671:980196671(0) win 5840 <mss 1460,sackOK,timestamp 1971553
>>> 0,nop,wscale 5>
see here wscale = 5, so win = 186880
>>> 0x0000: 4500 003c 885e 4000 4006 124c c0a8 0102 E..<.^@.@..L....
>>> 0x0010: c1db 1c8c cac5 0015 3a6c 9d3f 0000 0000 ........:l.?....
>>> 0x0020: a002 16d0 d91a 0000 0204 05b4 0402 080a ................
>>> 0x0030: 001e 1561 0000 0000 0103 0305 ...a........
>>> 23:56:08.821639 IP sunsite2.icm.edu.pl.ftp > 192.168.1.2.51909: S
>>> 2723526584:2723526584(0) ack 980196672 win 5792 <mss 1460,sackOK,timestamp
>>> 983901832 1971553,nop,wscale 7>
>>> 0x0000: 4500 003c 0000 4000 3c06 9eaa c1db 1c8c E..<..@.<.......
>>> 0x0010: c0a8 0102 0015 cac5 a255 b7b8 3a6c 9d40 .........U..:l.@
>>> 0x0020: a012 16a0 1dfc 0000 0204 05b4 0402 080a ................
>>> 0x0030: 3aa5 2688 001e 1561 0103 0307 :.&....a....
>>> 23:56:08.821685 IP 192.168.1.2.51909 > sunsite2.icm.edu.pl.ftp: . ack 1 win 183
>>> <nop,nop,timestamp 1971567 983901832>
>>> 0x0000: 4500 0034 885f 4000 4006 1253 c0a8 0102 E..4._@.@..S....
>>> 0x0010: c1db 1c8c cac5 0015 3a6c 9d40 a255 b7b9 ........:l.@.U..
>>> 0x0020: 8010 00b7 62a3 0000 0101 080a 001e 156f ....b..........o
>>> 0x0030: 3aa5 2688 :.&.
>>> 23:56:08.842801 IP sunsite2.icm.edu.pl.ftp > 192.168.1.2.51909: P 1:7(6) ack 1
>>> win 46 <nop,nop,timestamp 983901837 1971567>
>>> 0x0000: 4510 003a 9135 4000 3c06 0d67 c1db 1c8c E..:.5@.<..g....
>>> 0x0010: c0a8 0102 0015 cac5 a255 b7b9 3a6c 9d40 .........U..:l.@
>>> 0x0020: 8018 002e f3af 0000 0101 080a 3aa5 268d ............:.&.
>>> 0x0030: 001e 156f 3232 302d 0d0a ...o220-..
>>> 23:56:08.843069 IP 192.168.1.2.51909 > sunsite2.icm.edu.pl.ftp: . ack 7 win 183
>>> <nop,nop,timestamp 1971588 983901837>
here, win = 183 is probably not correctly understood by remote peer (as 5856)
>>> 0x0000: 4510 0034 8860 4000 4006 1242 c0a8 0102 E..4.`@.@..B....
>>> 0x0010: c1db 1c8c cac5 0015 3a6c 9d40 a255 b7bf ........:l.@.U..
>>> 0x0020: 8010 00b7 6283 0000 0101 080a 001e 1584 ....b...........
>>> 0x0030: 3aa5 268d :.&.
>>> 23:56:31.920327 IP 192.168.1.2.51909 > sunsite2.icm.edu.pl.ftp: F 1:1(0) ack 7
>>> win 183 <nop,nop,timestamp 1994676 983901837>
>>> 0x0000: 4510 0034 8861 4000 4006 1241 c0a8 0102 E..4.a@.@..A....
>>> 0x0010: c1db 1c8c cac5 0015 3a6c 9d40 a255 b7bf ........:l.@.U..
>>> 0x0020: 8011 00b7 0852 0000 0101 080a 001e 6fb4 .....R........o.
>>> 0x0030: 3aa5 268d :.&.
>>> 23:56:31.935145 IP sunsite2.icm.edu.pl.ftp > 192.168.1.2.51909: . ack 2 win 46
>>> <nop,nop,timestamp 983907613 1994676>
>>> 0x0000: 4510 0034 913d 4000 3c06 0d65 c1db 1c8c E..4.=@.<..e....
>>> 0x0010: c0a8 0102 0015 cac5 a255 b8e5 3a6c 9d41 .........U..:l.A
>>> 0x0020: 8010 002e f124 0000 0101 080a 3aa5 3d1d .....$......:.=.
>>> 0x0030: 001e 6fb4 ..o.
>>>
>>> Other machines work with the router fine.
>>>
>>> How can I solve this problem?
>>> I tried to look for some relevand info with athdebug, but i'm not sure what to
>>> look for.
>>> Can You help me?
>>>
>>> System:
>>> I am using Atheros Communications, Inc. AR5212 802.11abg NIC PCMCIA card.
>>> Computer is Acer Aspire 1520.
>>>
>>> I use vanilla kernel:
>>> Linux 2.6.23 #1 Sat Jan 12 12:07:39 CET 2008 i686 AMD Athlon(tm) 64 Processor
>>> 3700+ AuthenticAMD GNU/Linux
>>> and madwifi driver ath_pci 0.9.4.5 (0.9.3.3)
>>> kernel options: irqpoll (without irqpoll system doesn't detect pcmcia cards).
>>>
>>> Device detection:
>>> ath_hal: module license 'Proprietary' taints kernel.
>>> ath_hal: 0.9.18.0 (AR5210, AR5211, AR5212, RF5111, RF5112, RF2413, RF5413)
>>> ath_pci: 0.9.4.5 (0.9.3.3)
>>> ath_rate_sample: 1.2 (0.9.3.3)
>>> wifi0: 11b rates: 1Mbps 2Mbps 5.5Mbps 11Mbps
>>> wifi0: 11g rates: 1Mbps 2Mbps 5.5Mbps 11Mbps 6Mbps 9Mbps 12Mbps 18Mbps 24Mbps
>>> 36Mbps 48Mbps 54Mbps
>>> wifi0: turboG rates: 6Mbps 12Mbps 18Mbps 24Mbps 36Mbps 48Mbps 54Mbps
>>> wifi0: H/W encryption support: WEP AES AES_CCM TKIP
>>> wifi0: mac 5.9 phy 4.3 radio 4.6
>>> wifi0: Use hw queue 1 for WME_AC_BE traffic
>>> wifi0: Use hw queue 0 for WME_AC_BK traffic
>>> wifi0: Use hw queue 2 for WME_AC_VI traffic
>>> wifi0: Use hw queue 3 for WME_AC_VO traffic
>>> wifi0: Use hw queue 8 for CAB traffic
>>> wifi0: Use hw queue 9 for beacons
>>> wifi0: Atheros 5212: mem=0x34000000, irq=11
>>>
>>> On the same irq are:
>>> ehci_hcd:usb1
>>> eth0
>>> uhci_hcd:usb4
>>> wifi%d
>>> yenta
>>>
>
> Eric Dumazet <dada1@cosmosbay.com> -----------------------------------------
>> Very strange, as the tcpdump you gave shows that the remote peer only
>> sent "220-\r\n"
>>
>> This was ACKed, and then nothing but timeout. We can conclude remote
>> peer is *very* slow or a firewall is blocking trafic after 6 bytes sent :)
>>
>> Could you give a tcpdump for the same destination, on 2.6.19 this time ?
>
> Here goes, this happens after running:
> ftp sunsite.icm.edu.pl
> The ftp session is successful
>
>
> ----------------------------------------------------
> tcpdump -i ath0 tcp and port 21 -X -s 300 -vv
> tcpdump: listening on ath0, link-type EN10MB (Ethernet), capture size 300 bytes
> 18:09:48.435612 IP (tos 0x0, ttl 64, id 6423, offset 0, flags [DF], proto: TCP (6), length: 60) sabayonx86.local.41649 > sunsite2.icm.edu.pl.ftp: S, cksum 0xe759 (correct),
1749015234:1749015234(0) win 5840 <mss 1460,sackOK,timestamp 1885141
0,nop,wscale ²2>
wscale 2 here (instead of 5 on your non working case)
So win = 23360
> 0x0000: 4500 003c 1917 4000 4006 8184 c0a8 0111 E..<..@.@.......
> 0x0010: c1db 1c8c a2b1 0015 683f dac2 0000 0000 ........h?......
> 0x0020: a002 16d0 e759 0000 0204 05b4 0402 080a .....Y..........
> 0x0030: 001c c3d5 0000 0000 0103 0302 ............
> 18:09:48.449755 IP (tos 0x0, ttl 60, id 0, offset 0, flags [DF], proto: TCP (6), length: 60) sunsite2.icm.edu.pl.ftp > sabayonx86.local.41649: S, cksum 0xc97a (correct), 1528170639:1528170639(0) ack 1749015235 win 5792 <mss 1460,sackOK,timestamp 536320604 1885141,nop,wscale 7>
> 0x0000: 4500 003c 0000 4000 3c06 9e9b c1db 1c8c E..<..@.<.......
> 0x0010: c0a8 0111 0015 a2b1 5b16 088f 683f dac3 ........[...h?..
> 0x0020: a012 16a0 c97a 0000 0204 05b4 0402 080a .....z..........
> 0x0030: 1ff7 9a5c 001c c3d5 0103 0307 ...\........
> 18:09:48.449827 IP (tos 0x0, ttl 64, id 6424, offset 0, flags [DF], proto: TCP (6), length: 52) sabayonx86.local.41649 > sunsite2.icm.edu.pl.ftp: ., cksum 0x0925 (correct), 1:1(0) ack 1 win 1460 <nop,nop,timestamp 1885155 536320604>
> 0x0000: 4500 0034 1918 4000 4006 818b c0a8 0111 E..4..@.@.......
> 0x0010: c1db 1c8c a2b1 0015 683f dac3 5b16 0890 ........h?..[...
> 0x0020: 8010 05b4 0925 0000 0101 080a 001c c3e3 .....%..........
> 0x0030: 1ff7 9a5c ...\
> 18:09:48.485234 IP (tos 0x10, ttl 60, id 37583, offset 0, flags [DF], proto: TCP (6), length: 58) sunsite2.icm.edu.pl.ftp > sabayonx86.local.41649: P, cksum 0x9f2a (correct), 1:7(6) ack 1 win 46 <nop,nop,timestamp 536320613 1885155>
> 0x0000: 4510 003a 92cf 4000 3c06 0bbe c1db 1c8c E..:..@.<.......
> 0x0010: c0a8 0111 0015 a2b1 5b16 0890 683f dac3 ........[...h?..
> 0x0020: 8018 002e 9f2a 0000 0101 080a 1ff7 9a65 .....*.........e
> 0x0030: 001c c3e3 3232 302d 0d0a ....220-..
> 18:09:48.485337 IP (tos 0x10, ttl 64, id 6425, offset 0, flags [DF], proto: TCP (6), length: 52) sabayonx86.local.41649 > sunsite2.icm.edu.pl.ftp: ., cksum 0x08f2 (correct),
1:1(0) ack 7 win 1460 <nop,nop,timestamp 1885191 536320613>
this win=1460 is correctly taken by remote peer as 5840 (wscale=2)
> 0x0000: 4510 0034 1919 4000 4006 817a c0a8 0111 E..4..@.@..z....
> 0x0010: c1db 1c8c a2b1 0015 683f dac3 5b16 0896 ........h?..[...
> 0x0020: 8010 05b4 08f2 0000 0101 080a 001c c407 ................
> 0x0030: 1ff7 9a65 ...e
Typical window scaling problem here... (well, for previous traces, with
wscaling of 5, since with wscale 2 it seems to work), you probably have a
buggy router or something...
http://lwn.net/Articles/92727/
Try :
# echo 0 >/proc/sys/net/ipv4/tcp_window_scaling
And retry to connect to this ftp server
You could alternativly play with /proc/sys/net/ipv4/tcp_rmem
# echo "4096 8192 50000" >/proc/sys/net/ipv4/tcp_rmem
> 18:09:48.500421 IP (tos 0x10, ttl 60, id 37584, offset 0, flags [DF], proto: TCP (6), length: 191) sunsite2.icm.edu.pl.ftp > sabayonx86.local.41649: P, cksum 0xc74e (correct), 7:146(139) ack 1 win 46 <nop,nop,timestamp 536320616 1885191>
> 0x0000: 4510 00bf 92d0 4000 3c06 0b38 c1db 1c8c E.....@.<..8....
> 0x0010: c0a8 0111 0015 a2b1 5b16 0896 683f dac3 ........[...h?..
> 0x0020: 8018 002e c74e 0000 0101 080a 1ff7 9a68 .....N.........h
> 0x0030: 001c c407 3232 302d 2020 2020 2020 2020 ....220-........
> 0x0040: 2020 2020 4654 5020 6e61 2053 756e 5349 ....FTP.na.SunSI
> 0x0050: 5445 2028 3620 5442 206f 7072 6f67 7261 TE.(6.TB.oprogra
> 0x0060: 6d6f 7761 6e69 6129 2e0d 0a32 3230 2d20 mowania)...220-.
> 0x0070: 2020 2020 5577 6167 693a 2073 756e 7369 ....Uwagi:.sunsi
> 0x0080: 7465 4069 636d 2e65 6475 2e70 6c20 286f te@icm.edu.pl.(o
> 0x0090: 6470 6f77 6961 6461 6d79 206e 6120 7773 dpowiadamy.na.ws
> 0x00a0: 7a79 7374 6b69 6520 646f 742e 2053 756e zystkie.dot..Sun
> 0x00b0: 5349 5445 2761 290d 0a32 3230 2d0d 0a SITE'a)..220-..
^ permalink raw reply
* [patch 23/45] forcedeth: mac address mcp77/79
From: Greg KH @ 2008-02-07 20:47 UTC (permalink / raw)
To: linux-kernel, stable, Jeff Garzik, Andrew Morton, nedev
Cc: Justin Forbes, Zwane Mwaikambo, Theodore Ts'o, Randy Dunlap,
Dave Jones, Chuck Wolber, Chris Wedgwood, Michael Krufky,
Chuck Ebbert, Domenico Andreoli, torvalds, akpm, alan,
Ayaz Abdulla, Jeff Garzik, David S. Miller
In-Reply-To: <20080207204549.GA16389@suse.de>
[-- Attachment #1: forcedeth-mac-address-mcp77-79.patch --]
[-- Type: text/plain, Size: 5172 bytes --]
2.6.24-stable review patch. If anyone has any objections, please let us know.
------------------
From: Ayaz Abdulla <aabdulla@nvidia.com>
patch 2b91213064bd882c3adf35f028c6d12fab3269ec in mainline.
This patch is a critical fix for MCP77 and MCP79 devices. The feature
flags were missing the define for correct mac address
(DEV_HAS_CORRECT_MACADDR).
Signed-off-by: Ayaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/net/forcedeth.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -5593,35 +5593,35 @@ static struct pci_device_id pci_tbl[] =
},
{ /* MCP77 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_32),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP77 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_33),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP77 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_34),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP77 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_35),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_38),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_39),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
},
{0,},
};
--
^ permalink raw reply
* Re: [BUG][AX25] Fwd: SMP with AX.25
From: Jarek Poplawski @ 2008-02-07 20:34 UTC (permalink / raw)
To: netdev; +Cc: Ralf Baechle, Jann Traschewski
In-Reply-To: <20080207193511.GB8149@ami.dom.local>
On Thu, Feb 07, 2008 at 08:35:11PM +0100, Jarek Poplawski wrote:
> Here I resend another OOPS I got from Jann:
>
> On Thu, Feb 07, 2008 at 05:42:42PM +0100, Jann Traschewski wrote:
> ...
> > BUG: unable to handle kernel NULL pointer dereference at virtual address
> > 00000065
> > printing eip: c02266c7 *pde = 00000000
> > Oops: 0000 [#1] SMP
> > Modules linked in: netconsole ppp_deflate zlib_deflate zlib_inflate bsd_comp
> > ppp_async ppp_generic slhc tun bitrev crc32 mkiss ax25 crc16 iptable_nat
> > nf_nat nf_conntrack_ipv4 xt_state nf_conntrack ipt_REJECT iptable_filter
> > iptable_mangle xt_MARK ipv6 ipip tunnel4 ide_cd cdrom aic7xxx
> > scsi_transport_spi parport_serial parport_pc parport i2c_piix4 genrtc
> >
> > Pid: 3035, comm: linuxnet Not tainted (2.6.24-dg8ngn #1)
> > EIP: 0060:[<c02266c7>] EFLAGS: 00010202 CPU: 0
> > EIP is at skb_clone+0x3/0x4d
> > EAX: 00000000 EBX: 00000000 ECX: 00000001 EDX: 00000020
> > ESI: 00000008 EDI: 00000000 EBP: f700a4ac ESP: f720b9ac
> > DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
> > Process linuxnet (pid: 3035, ti=f720a000 task=f7c63570 task.ti=f720a000)
> > Stack: f700a400 f8a0a8f9 c01254db 00000000 00000000 f700a400 f700a4cc
> > f700a400 __wake_up_common+0x32/0x5c
> > __wake_up+0x32/0x42
Jann, this report is a bit damaged here, so I could be wrong, but it
looks similar to your first reports, and I guess this skb_clone is
called from ax25_kick too. Then my today testing patch #2 should help
for this, I hope.
Jarek P.
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: Paul Moore @ 2008-02-07 20:14 UTC (permalink / raw)
To: Andrew Morton; +Cc: casey, davem, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <20080207120459.d4994f44.akpm@linux-foundation.org>
On Thursday 07 February 2008 3:04:59 pm Andrew Morton wrote:
> On Thu, 7 Feb 2008 14:50:41 -0500
>
> Paul Moore <paul.moore@hp.com> wrote:
> > On Thursday 07 February 2008 2:02:06 pm akpm@linux-foundation.org wrote:
> > > The patch titled
> > > Smack: unlabeled outgoing ambient packets
> > > has been added to the -mm tree. Its filename is
> > > smack-unlabeled-outgoing-ambient-packets.patch
> > >
> > > Before you just go and hit "reply", please:
> > > a) Consider who else should be cc'ed
> > > b) Prefer to cc a suitable mailing list as well
> > > c) Ideally: find the original patch on the mailing list and do a
> > > reply-to-all to that, adding suitable additional cc's
> >
> > I didn't see this patch hit any of the relevant mailing lists (am I
> > missing one somewhere?) so I'm just CC'ing everyone on the To/CC line,
> > minus mm-commits.
>
> It was on linux-kernel and netdev. I've restored those cc's.
My apologies, those mailing list postings there haven't hit my inbox yet.
--
paul moore
linux security @ hp
^ permalink raw reply
* Re: + smack-unlabeled-outgoing-ambient-packets.patch added to -mm tree
From: Andrew Morton @ 2008-02-07 20:04 UTC (permalink / raw)
To: Paul Moore; +Cc: casey, davem, jmorris, mingo, sds, linux-kernel, netdev
In-Reply-To: <200802071450.41529.paul.moore@hp.com>
On Thu, 7 Feb 2008 14:50:41 -0500
Paul Moore <paul.moore@hp.com> wrote:
> On Thursday 07 February 2008 2:02:06 pm akpm@linux-foundation.org wrote:
> > The patch titled
> > Smack: unlabeled outgoing ambient packets
> > has been added to the -mm tree. Its filename is
> > smack-unlabeled-outgoing-ambient-packets.patch
> >
> > Before you just go and hit "reply", please:
> > a) Consider who else should be cc'ed
> > b) Prefer to cc a suitable mailing list as well
> > c) Ideally: find the original patch on the mailing list and do a
> > reply-to-all to that, adding suitable additional cc's
>
> I didn't see this patch hit any of the relevant mailing lists (am I missing
> one somewhere?) so I'm just CC'ing everyone on the To/CC line, minus
> mm-commits.
It was on linux-kernel and netdev. I've restored those cc's.
> > ------------------------------------------------------
> > Subject: Smack: unlabeled outgoing ambient packets
> > From: Casey Schaufler <casey@schaufler-ca.com>
> >
> > Smack uses CIPSO labeling, but allows for unlabeled packets by specifying
> > an "ambient" label that is applied to incoming unlabeled packets. Because
> > the other end of the connection may dislike IP options, and ssh is one know
> > application that behaves thus, it is prudent to respond in kind. This
> > patch changes the network labeling behavior such that an outgoing packet
> > that would be given a CIPSO label that matches the ambient label is left
> > unlabeled.
>
> I suppose you are entitled to use NetLabel however you want, so long as it
> works and doesn't cause problems for other users, but I think you are
> starting down a rather ugly road with this patch. In my mind a cleaner
> solution would be to make of use of the built-in NetLabel/LSM domain mapping
> functionality to accomplish the same thing. In other words, there is already
> a mechanism to do what you want, it's probably a good idea to make use of it
> instead of recreating it.
>
> I would suggest that when you set the NetLabel security attributes for a
> socket you set the domain field to the smack label (see the SELinux code for
> an example, if you are unsure see selinux_netlbl_sock_setsid() and
> security_netlbl_sid_to_secattr()). Once you do that you should continue to
> set the default NetLabel domain mapping to send CIPSO tagged packets but also
> create a new NetLabel domain mapping so that the ambient smack label causes
> packets to be sent "unlabeled". The only other change you would have to make
> is to ensure that the NetLabel domain mappings are kept in sync with any
> ambient label changes (should be easy enough and a rather infrequent
> operation in practice).
>
> This also should have the advantage of making your life easier if/when more
> advanced labeled network controls are added to Smack (see the SELinux changes
> made in 2.6.25 and our previous discussions).
>
^ permalink raw reply
* Re: [PATCH] Add IPv6 support to TCP SYN cookies
From: Ross Vandegrift @ 2008-02-07 19:44 UTC (permalink / raw)
To: Andi Kleen; +Cc: Glenn Griffin, netdev, linux-kernel
In-Reply-To: <20080206085357.GA1402@one.firstfloor.org>
On Wed, Feb 06, 2008 at 09:53:57AM +0100, Andi Kleen wrote:
> That would be useful yes -- for different bandwidths.
My initial test is end-to-end 1000Mbps, but I've got a few different
packet rates.
> If the young/old heuristics do not work well enough anymore most
> likely we should try readding RED to the syn queue again. That used
> to be pretty effective in the early days. I don't quite remember why
> Linux didn't end up using it in fact.
I'm running juno-z with 2, 4, & 8 threads of syn flood to port 80.
wireshark measures 2 threads at 350pps, 4 threads at 750pps, and 8
threads at 1200pps. Under no SYN flood, the server handles 750 HTTP
requests per second, measured via httping in flood mode.
With a default tcp_max_syn_backlog of 1024, I can trivially prevent
any inbound client connections with 2 threads of syn flood.
Enabling tcp_syncookies brings the connection handling back up to 725
fetches per second.
If I raise the backlog to 16384, 4 threads gives me about 650 legit
requests per sec. Going to 8 threads makes connections very unreliable - a
handful will get through every 15 to 20 seconds. Again,
tcp_syncookies returns performance almost totally back to normal.
Cranking juno-z to the max generates me about 16kpps. Any syn backlog
is easily overwhelmed and nothing gets through. tcp_syncookies gets
me back to 650 requests per second.
At these levels the CPU impact of tcp_syncookies is nothing. I can't
measure a difference. In the real world, a 16kpps syn flood is small.
People with a distributed botnet can easily get to the hundreds of
thousands, and I have seen over million packets per second of SYN flood.
BTW, I can trigger a soft lockup BUG when I restart apache to change the
backlog during the 16kpps test-case:
BUG: soft lockup detected on CPU#1!
[<c044d1ec>] softlockup_tick+0x96/0xa4
[<c042ddb0>] update_process_times+0x39/0x5c
[<c04196f7>] smp_apic_timer_interrupt+0x5b/0x6c
[<c04059bf>] apic_timer_interrupt+0x1f/0x24
[<c045007b>] taskstats_exit_send+0x152/0x371
[<c05c007b>] netlink_kernel_create+0x5/0x11c
[<c05a7415>] reqsk_queue_alloc+0x32/0x81
[<c05a5aca>] lock_sock+0x8e/0x96
[<c05ce8c4>] inet_csk_listen_start+0x17/0x106
[<c05e720f>] inet_listen+0x3c/0x5f
[<c05a3e55>] sys_listen+0x4a/0x66
[<c05a4f4d>] sys_socketcall+0x98/0x19e
[<c0407ef7>] do_syscall_trace+0xab/0xb1
[<c0404eff>] syscall_call+0x7/0xb
=======================
BUG: soft lockup detected on CPU#3!
[<c044d1ec>] softlockup_tick+0x96/0xa4
[<c042ddb0>] update_process_times+0x39/0x5c
[<c04196f7>] smp_apic_timer_interrupt+0x5b/0x6c
[<c04059bf>] apic_timer_interrupt+0x1f/0x24
[<c045007b>] taskstats_exit_send+0x152/0x371
[<c05c007b>] netlink_kernel_create+0x5/0x11c
[<c05a7415>] reqsk_queue_alloc+0x32/0x81
[<c05a5aca>] lock_sock+0x8e/0x96
[<c05ce8c4>] inet_csk_listen_start+0x17/0x106
[<c05e720f>] inet_listen+0x3c/0x5f
[<c05a3e55>] sys_listen+0x4a/0x66
[<c05a4f4d>] sys_socketcall+0x98/0x19e
[<c0407ef7>] do_syscall_trace+0xab/0xb1
[<c0404eff>] syscall_call+0x7/0xb
=======================
--
Ross Vandegrift
ross@kallisti.us
"The good Christian should beware of mathematicians, and all those who
make empty prophecies. The danger already exists that the mathematicians
have made a covenant with the devil to darken the spirit and to confine
man in the bonds of Hell."
--St. Augustine, De Genesi ad Litteram, Book II, xviii, 37
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox