OpenCloudOS-Kernel/drivers/s390/net/ctcm_fsms.c

2292 lines
73 KiB
C
Raw Permalink Normal View History

License cleanup: add SPDX GPL-2.0 license identifier to files with no license Many source files in the tree are missing licensing information, which makes it harder for compliance tools to determine the correct license. By default all files without license information are under the default license of the kernel, which is GPL version 2. Update the files which contain no license information with the 'GPL-2.0' SPDX license identifier. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This patch is based on work done by Thomas Gleixner and Kate Stewart and Philippe Ombredanne. How this work was done: Patches were generated and checked against linux-4.14-rc6 for a subset of the use cases: - file had no licensing information it it. - file was a */uapi/* one with no licensing information in it, - file was a */uapi/* one with existing licensing information, Further patches will be generated in subsequent months to fix up cases where non-standard license headers were used, and references to license had to be inferred by heuristics based on keywords. The analysis to determine which SPDX License Identifier to be applied to a file was done in a spreadsheet of side by side results from of the output of two independent scanners (ScanCode & Windriver) producing SPDX tag:value files created by Philippe Ombredanne. Philippe prepared the base worksheet, and did an initial spot review of a few 1000 files. The 4.13 kernel was the starting point of the analysis with 60,537 files assessed. Kate Stewart did a file by file comparison of the scanner results in the spreadsheet to determine which SPDX license identifier(s) to be applied to the file. She confirmed any determination that was not immediately clear with lawyers working with the Linux Foundation. Criteria used to select files for SPDX license identifier tagging was: - Files considered eligible had to be source code files. - Make and config files were included as candidates if they contained >5 lines of source - File already had some variant of a license header in it (even if <5 lines). All documentation files were explicitly excluded. The following heuristics were used to determine which SPDX license identifiers to apply. - when both scanners couldn't find any license traces, file was considered to have no license information in it, and the top level COPYING file license applied. For non */uapi/* files that summary was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 11139 and resulted in the first patch in this series. If that file was a */uapi/* path one, it was "GPL-2.0 WITH Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 WITH Linux-syscall-note 930 and resulted in the second patch in this series. - if a file had some form of licensing information in it, and was one of the */uapi/* ones, it was denoted with the Linux-syscall-note if any GPL family license was found in the file or had no licensing in it (per prior point). Results summary: SPDX license identifier # files ---------------------------------------------------|------ GPL-2.0 WITH Linux-syscall-note 270 GPL-2.0+ WITH Linux-syscall-note 169 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17 LGPL-2.1+ WITH Linux-syscall-note 15 GPL-1.0+ WITH Linux-syscall-note 14 ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5 LGPL-2.0+ WITH Linux-syscall-note 4 LGPL-2.1 WITH Linux-syscall-note 3 ((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3 ((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1 and that resulted in the third patch in this series. - when the two scanners agreed on the detected license(s), that became the concluded license(s). - when there was disagreement between the two scanners (one detected a license but the other didn't, or they both detected different licenses) a manual inspection of the file occurred. - In most cases a manual inspection of the information in the file resulted in a clear resolution of the license that should apply (and which scanner probably needed to revisit its heuristics). - When it was not immediately clear, the license identifier was confirmed with lawyers working with the Linux Foundation. - If there was any question as to the appropriate license identifier, the file was flagged for further research and to be revisited later in time. In total, over 70 hours of logged manual review was done on the spreadsheet to determine the SPDX license identifiers to apply to the source files by Kate, Philippe, Thomas and, in some cases, confirmation by lawyers working with the Linux Foundation. Kate also obtained a third independent scan of the 4.13 code base from FOSSology, and compared selected files where the other two scanners disagreed against that SPDX file, to see if there was new insights. The Windriver scanner is based on an older version of FOSSology in part, so they are related. Thomas did random spot checks in about 500 files from the spreadsheets for the uapi headers and agreed with SPDX license identifier in the files he inspected. For the non-uapi files Thomas did random spot checks in about 15000 files. In initial set of patches against 4.14-rc6, 3 files were found to have copy/paste license identifier errors, and have been fixed to reflect the correct identifier. Additionally Philippe spent 10 hours this week doing a detailed manual inspection and review of the 12,461 patched files from the initial patch version early this week with: - a full scancode scan run, collecting the matched texts, detected license ids and scores - reviewing anything where there was a license detected (about 500+ files) to ensure that the applied SPDX license was correct - reviewing anything where there was no detection but the patch license was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied SPDX license was correct This produced a worksheet with 20 files needing minor correction. This worksheet was then exported into 3 different .csv files for the different types of files to be modified. These .csv files were then reviewed by Greg. Thomas wrote a script to parse the csv files and add the proper SPDX tag to the file, in the format that the file expected. This script was further refined by Greg based on the output to detect more types of files automatically and to distinguish between header and source .c files (which need different comment types.) Finally Greg ran the script using the .csv files to generate the patches. Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 22:07:57 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* 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
#define KMSG_COMPONENT "ctcm"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#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 "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 preceding 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,
"%s(%s): %s: %04x\n",
CTCM_FUNTAIL, ch->id, msg, rc);
switch (rc) {
case -EBUSY:
pr_info("%s: The communication peer is busy\n",
ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
break;
case -ENODEV:
pr_err("%s: The specified target device is not valid\n",
ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
break;
default:
pr_err("An I/O operation resulted in error %04x\n",
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, CTC_DBF_DEBUG, __func__);
while ((skb = skb_dequeue(q))) {
refcount_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->ml_priv;
struct sk_buff *skb;
int first = 1;
int i;
unsigned long duration;
unsigned long done_stamp = jiffies;
CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
duration = done_stamp - ch->prof.send_stamp;
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
if (ch->irb->scsw.cmd.count != 0)
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
"%s(%s): TX not complete, remaining %d bytes",
CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.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;
}
refcount_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;
refcount_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 = jiffies;
rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 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->ml_priv;
CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
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->ml_priv;
int len = ch->max_bufsize - ch->irb->scsw.cmd.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_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s): got packet with length %d < 8\n",
CTCM_FUNTAIL, dev->name, len);
priv->stats.rx_dropped++;
priv->stats.rx_length_errors++;
goto again;
}
if (len > ch->max_bufsize) {
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s): got packet with length %d > %d\n",
CTCM_FUNTAIL, 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_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s): got block length %d != rx length %d\n",
CTCM_FUNTAIL, 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;
}
if (block_len > 2) {
*((__u16 *)skb->data) = block_len - 2;
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], 0, 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)
{
int rc;
struct channel *ch = arg;
int fsmstate = fsm_getstate(fi);
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s) : %02x",
CTCM_FUNTAIL, ch->id, fsmstate);
ch->sense_rc = 0; /* reset unit check report control */
if (fsmstate == CTC_STATE_TXIDLE)
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
"%s(%s): remote side issued READ?, init.\n",
CTCM_FUNTAIL, ch->id);
fsm_deltimer(&ch->timer);
if (ctcm_checkalloc_buffer(ch))
return;
if ((fsmstate == CTC_STATE_SETUPWAIT) &&
(ch->protocol == CTCM_PROTO_OS390)) {
/* OS/390 resp. z/OS */
if (CHANNEL_DIRECTION(ch->flags) == CTCM_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->ml_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) == CTCM_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) == CTCM_READ)
? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 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) == CTCM_READ) &&
(ch->protocol == CTCM_PROTO_S390)) {
struct net_device *dev = ch->netdev;
struct ctcm_priv *priv = dev->ml_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->ml_priv;
__u16 buflen;
int rc;
fsm_deltimer(&ch->timer);
buflen = *((__u16 *)ch->trans_skb->data);
CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n",
__func__, 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], 0, 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 {
CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n",
__func__, 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;
CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ch, ch->id);
}
fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
fsm_newstate(fi, CTC_STATE_SETUPWAIT);
CTCM_CCW_DUMP((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], 0, 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;
unsigned long saveflags;
int rc;
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == CTCM_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) == CTCM_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_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
"%s(%s): %s trans_skb alloc delayed "
"until first transfer",
CTCM_FUNTAIL, ch->id,
(CHANNEL_DIRECTION(ch->flags) == CTCM_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, 0);
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;
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, 0);
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 *)__func__);
}
}
}
/**
* 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->ml_priv;
CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
"%s(%s): %s[%d]\n",
CTCM_FUNTAIL, dev->name, ch->id, state);
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) == CTCM_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_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_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->ml_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) == CTCM_READ)) {
int rc = ccw_device_halt(ch->cdev, 0);
if (rc != 0)
ctcm_ccw_check_rc(ch, rc,
"HaltIO in chx_setuperr");
}
return;
}
CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
"%s(%s) : %s error during %s channel setup state=%s\n",
CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX",
fsm_getstate_str(fi));
if (CHANNEL_DIRECTION(ch->flags) == CTCM_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,
"%s: %s[%d] of %s\n",
CTCM_FUNTAIL, ch->id, event, dev->name);
fsm_deltimer(&ch->timer);
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, 0);
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->ml_priv;
if (event == CTC_EVENT_TIMER) {
if (!IS_MPCDEV(dev))
/* TODO : check if MPC deletes timer somewhere */
fsm_deltimer(&ch->timer);
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_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
ctc_ch_event_names[event], fsm_getstate_str(fi));
dev_warn(&dev->dev,
"Initialization failed with RX/TX init handshake "
"error %s\n", ctc_ch_event_names[event]);
}
}
/**
* 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->ml_priv;
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s(%s): RX %s busy, init. fail",
CTCM_FUNTAIL, dev->name, ch->id);
fsm_newstate(fi, CTC_STATE_RXERR);
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->ml_priv;
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s: %s: remote disconnect - re-init ...",
CTCM_FUNTAIL, dev->name);
fsm_deltimer(&ch->timer);
/*
* 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[CTCM_WRITE];
fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
ccw_device_halt(ch->cdev, 0);
ccw_device_halt(ch2->cdev, 0);
}
/**
* 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->ml_priv;
if (event == CTC_EVENT_TIMER) {
fsm_deltimer(&ch->timer);
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): %s in %s", CTCM_FUNTAIL, ch->id,
ctc_ch_event_names[event], fsm_getstate_str(fi));
dev_warn(&dev->dev,
"Initialization failed with RX/TX init handshake "
"error %s\n", ctc_ch_event_names[event]);
}
}
/**
* 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->ml_priv;
struct sk_buff *skb;
CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ch, ch->id);
fsm_deltimer(&ch->timer);
if (ch->retry++ > 3) {
struct mpc_group *gptr = priv->mpcg;
CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
"%s: %s: retries exceeded",
CTCM_FUNTAIL, ch->id);
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_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
"%s : %s: retry %d",
CTCM_FUNTAIL, ch->id, 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_DBF_TEXT_(TRACE, CTC_DBF_INFO,
"%s: %s: IDAL alloc failed",
CTCM_FUNTAIL, ch->id);
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], 0, 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->ml_priv;
int rd = CHANNEL_DIRECTION(ch->flags);
fsm_deltimer(&ch->timer);
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
"%s: %s: %s unrecoverable channel error",
CTCM_FUNTAIL, ch->id, rd == CTCM_READ ? "RX" : "TX");
if (IS_MPC(ch)) {
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
}
if (rd == CTCM_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);
}
}
/*
* 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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct sk_buff *skb;
int first = 1;
int i;
__u32 data_space;
unsigned long duration;
struct sk_buff *peekskb;
int rc;
struct th_header *header;
struct pdu *p_header;
unsigned long done_stamp = jiffies;
CTCM_PR_DEBUG("Enter %s: %s cp:%i\n",
__func__, dev->name, smp_processor_id());
duration = done_stamp - ch->prof.send_stamp;
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
if (ch->irb->scsw.cmd.count != 0)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
"%s(%s): TX not complete, remaining %d bytes",
CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.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;
}
refcount_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;
p_header = NULL;
data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
CTCM_PR_DBGDATA("%s: building trans_skb from collect_q"
" data_space:%04x\n",
__func__, data_space);
while ((skb = skb_dequeue(&ch->collect_queue))) {
skb_put_data(ch->trans_skb, skb->data, skb->len);
p_header = (struct pdu *)
(skb_tail_pointer(ch->trans_skb) - skb->len);
p_header->pdu_flag = 0x00;
if (be16_to_cpu(skb->protocol) == ETH_P_SNAP)
p_header->pdu_flag |= 0x60;
else
p_header->pdu_flag |= 0x20;
CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
__func__, ch->trans_skb->len);
CTCM_PR_DBGDATA("%s: pdu header and data for up"
" to 32 bytes sent to vtam\n", __func__);
CTCM_D3_DUMP((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;
refcount_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) {
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;
CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" ,
__func__, 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);
CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
__func__, ch->trans_skb->len);
CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb "
"data to vtam from collect_q\n", __func__);
CTCM_D3_DUMP((char *)ch->trans_skb->data,
min_t(int, ch->trans_skb->len, 50));
spin_unlock(&ch->collect_lock);
clear_normalized_cda(&ch->ccw[1]);
CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
(void *)(unsigned long)ch->ccw[1].cda,
ch->trans_skb->data);
ch->ccw[1].count = ch->max_bufsize;
if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
dev_kfree_skb_any(ch->trans_skb);
ch->trans_skb = NULL;
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
"%s: %s: IDAL alloc failed",
CTCM_FUNTAIL, ch->id);
fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
return;
}
CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
(void *)(unsigned long)ch->ccw[1].cda,
ch->trans_skb->data);
ch->ccw[1].count = ch->trans_skb->len;
fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
ch->prof.send_stamp = jiffies;
if (do_debug_ccw)
ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 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);
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->ml_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.cmd.count;
CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n",
CTCM_FUNTAIL, dev->name, smp_processor_id(),
ch->id, ch->max_bufsize, len);
fsm_deltimer(&ch->timer);
if (skb == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): TRANS_SKB = NULL",
CTCM_FUNTAIL, dev->name);
goto again;
}
if (len < TH_HEADER_LENGTH) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): packet length %d to short",
CTCM_FUNTAIL, 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) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%d): skb allocation failed",
CTCM_FUNTAIL, dev->name);
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:
skb_put_data(new_skb, skb->data, block_len);
skb_queue_tail(&ch->io_queue, new_skb);
tasklet_schedule(&ch->ch_tasklet);
break;
default:
skb_put_data(new_skb, 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], 0, 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;
}
CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n",
__func__, dev->name, 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->ml_priv;
struct mpc_group *gptr = priv->mpcg;
CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n",
__func__, ch->id, ch);
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
"%s: %s: chstate:%i, grpstate:%i, prot:%i\n",
CTCM_FUNTAIL, 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) == CTCM_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) == CTCM_READ)
? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
done:
CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n",
__func__, ch->id, ch);
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
int rc;
unsigned long saveflags = 0; /* avoids compiler warning */
fsm_deltimer(&ch->timer);
CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n",
__func__, ch->id, dev->name, smp_processor_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;
CTCM_CCW_DUMP((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], 0, 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:
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n",
__func__, dev->name, ch->id, ch, smp_processor_id(),
fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->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;
}
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("%s(%s): %s\n ChState:%s GrpState:%s\n",
__func__, dev->name, ch->id,
fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->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;
}
s390/net: Mark expected switch fall-throughs Mark switch cases where we are expecting to fall through. This patch fixes the following warnings (Building: s390): drivers/s390/net/ctcm_fsms.c: In function ‘ctcmpc_chx_attnbusy’: drivers/s390/net/ctcm_fsms.c:1703:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (grp->changed_side == 1) { ^ drivers/s390/net/ctcm_fsms.c:1707:2: note: here case MPCG_STATE_XID0IOWAIX: ^~~~ drivers/s390/net/ctcm_mpc.c: In function ‘ctc_mpc_alloc_channel’: drivers/s390/net/ctcm_mpc.c:358:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (callback) ^ drivers/s390/net/ctcm_mpc.c:360:2: note: here case MPCG_STATE_XID0IOWAIT: ^~~~ drivers/s390/net/ctcm_mpc.c: In function ‘mpc_action_timeout’: drivers/s390/net/ctcm_mpc.c:1469:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if ((fsm_getstate(rch->fsm) == CH_XID0_PENDING) && ^ drivers/s390/net/ctcm_mpc.c:1472:2: note: here default: ^~~~~~~ drivers/s390/net/ctcm_mpc.c: In function ‘mpc_send_qllc_discontact’: drivers/s390/net/ctcm_mpc.c:2087:6: warning: this statement may fall through [-Wimplicit-fallthrough=] if (grp->estconnfunc) { ^ drivers/s390/net/ctcm_mpc.c:2092:2: note: here case MPCG_STATE_FLOWC: ^~~~ drivers/s390/net/qeth_l2_main.c: In function ‘qeth_l2_process_inbound_buffer’: drivers/s390/net/qeth_l2_main.c:328:7: warning: this statement may fall through [-Wimplicit-fallthrough=] if (IS_OSN(card)) { ^ drivers/s390/net/qeth_l2_main.c:337:3: note: here default: ^~~~~~~ Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
2019-07-30 08:13:54 +08:00
/* Else, fall through */
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
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): channel %s not added to group",
CTCM_FUNTAIL, dev->name, ch->id);
done:
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *wch = priv->channel[CTCM_WRITE];
struct channel *rch = priv->channel[CTCM_READ];
struct sk_buff *skb;
struct th_sweep *header;
int rc = 0;
unsigned long saveflags = 0;
CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ach, ach->id);
if (grp->in_sweep == 0)
goto done;
CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" ,
__func__, wch->th_seq_num);
CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" ,
__func__, 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 {
refcount_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;
CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
CTCM_PR_DBGDATA("%s: sweep packet\n", __func__);
CTCM_D3_DUMP((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 = jiffies;
rc = ccw_device_start(wch->cdev, &wch->ccw[3], 0, 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);
}
CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" ,
__func__, wch->th_seq_num, rch->th_seq_num);
if (rc != 0)
ctcm_ccw_check_rc(wch, rc, "send sweep");
done:
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->ml_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 = CTCM_READ; direction <= CTCM_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->ml_priv;
CTCMY_DBF_DEV_NAME(SETUP, dev, "");
fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
struct channel *ch = priv->channel[direction];
fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
ch->th_seq_num = 0x00;
CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n",
__func__, 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->ml_priv;
CTCMY_DBF_DEV_NAME(TRACE, dev, "");
if (IS_MPC(priv)) {
restart_timer = CTCM_TIME_1_SEC;
} else {
restart_timer = CTCM_TIME_5_SEC;
}
dev_info(&dev->dev, "Restarting device\n");
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->ml_priv;
int dev_stat = fsm_getstate(fi);
CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
"%s(%s): priv = %p [%d,%d]\n ", CTCM_FUNTAIL,
dev->name, dev->ml_priv, dev_stat, event);
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);
dev_info(&dev->dev,
"Connected with remote side\n");
ctcm_clear_busy(dev);
}
break;
case DEV_STATE_STARTWAIT_TX:
if (event == DEV_EVENT_TXUP) {
fsm_newstate(fi, DEV_STATE_RUNNING);
dev_info(&dev->dev,
"Connected with remote side\n");
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[CTCM_READ],
CTCM_READ, MPC_CHANNEL_ADD);
else
mpc_channel_action(priv->channel[CTCM_WRITE],
CTCM_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->ml_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[CTCM_READ],
CTCM_READ, MPC_CHANNEL_REMOVE);
else
mpc_channel_action(priv->channel[CTCM_WRITE],
CTCM_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 --- */