linux-stable/drivers/s390/net/ctcm_mpc.c

2129 lines
57 KiB
C
Raw 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 14:07:57 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* 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
#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/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>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
#include <asm/idals.h>
#include "ctcm_main.h"
#include "ctcm_mpc.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];
char addr[22];
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) {
scnprintf(addr, sizeof(addr), "%16.16llx", (__u64)rptr);
scnprintf(boff, sizeof(boff), "%4.4X", (__u32)ct);
bhex[0] = '\0';
basc[0] = '\0';
}
if ((sw == 4) || (sw == 12))
strcat(bhex, " ");
if (sw == 8)
strcat(bhex, " ");
scnprintf(tbuf, sizeof(tbuf), "%2.2llX", (__u64)*ptr);
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)
continue;
if ((strcmp(duphex, bhex)) != 0) {
if (dup != 0) {
scnprintf(tdup, sizeof(tdup),
"Duplicate as above to %s", addr);
ctcm_pr_debug(" --- %s ---\n",
tdup);
}
ctcm_pr_debug(" %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) {
scnprintf(tdup, sizeof(tdup),
"Duplicate as above to %s", addr);
ctcm_pr_debug(" --- %s ---\n", tdup);
}
ctcm_pr_debug(" %s (+%s) : %s [%s]\n",
addr, boff, bhex, basc);
} else {
if (dup >= 1) {
scnprintf(tdup, sizeof(tdup),
"Duplicate as above to %s", addr);
ctcm_pr_debug(" --- %s ---\n", tdup);
}
if (dup != 0) {
ctcm_pr_debug(" %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)
{
__u8 *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;
ctcm_pr_debug("dump:\n");
ctcm_pr_debug("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;
ctcm_pr_debug("pdu->offset: %d hex: %04x\n",
pheader->pdu_offset, pheader->pdu_offset);
ctcm_pr_debug("pdu->flag : %02x\n", pheader->pdu_flag);
ctcm_pr_debug("pdu->proto : %02x\n", pheader->pdu_proto);
ctcm_pr_debug("pdu->seq : %02x\n", pheader->pdu_seq);
goto dumpdata;
dumpth:
ctcm_pr_debug("th->seg : %02x\n", header->th_seg);
ctcm_pr_debug("th->ch : %02x\n", header->th_ch_flag);
ctcm_pr_debug("th->blk_flag: %02x\n", header->th_blk_flag);
ctcm_pr_debug("th->type : %s\n",
(header->th_is_xid) ? "DATA" : "XID");
ctcm_pr_debug("th->seqnum : %04x\n", header->th_seq_num);
}
dumpdata:
if (bl > 32)
bl = 32;
ctcm_pr_debug("data: ");
for (i = 0; i < bl; i++)
ctcm_pr_debug("%02x%s", *p++, (i % 16) ? " " : "\n");
ctcm_pr_debug("\n");
}
#endif
static struct net_device *ctcmpc_get_dev(int port_num)
{
char device[20];
struct net_device *dev;
struct ctcm_priv *priv;
scnprintf(device, sizeof(device), "%s%i", MPC_DEVICE_NAME, port_num);
dev = __dev_get_by_name(&init_net, device);
if (dev == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s: Device not found by name: %s",
CTCM_FUNTAIL, device);
return NULL;
}
priv = dev->ml_priv;
if (priv == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): dev->ml_priv is NULL",
CTCM_FUNTAIL, device);
return NULL;
}
if (priv->mpcg == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): priv->mpcg is NULL",
CTCM_FUNTAIL, device);
return NULL;
}
return dev;
}
/*
* ctc_mpc_alloc_channel
* (exported interface)
*
* Device Initialization :
* ACTPATH driven IO operations
*/
int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
{
struct net_device *dev;
struct mpc_group *grp;
struct ctcm_priv *priv;
dev = ctcmpc_get_dev(port_num);
if (dev == NULL)
return 1;
priv = dev->ml_priv;
grp = priv->mpcg;
grp->allochanfunc = callback;
grp->port_num = port_num;
grp->port_persist = 1;
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
"%s(%s): state=%s",
CTCM_FUNTAIL, 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;
fallthrough;
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 */
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
grp->allocchan_callback_retries = 0;
}
break;
}
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))
{
struct net_device *dev;
struct mpc_group *grp;
struct ctcm_priv *priv;
struct channel *rch, *wch;
dev = ctcmpc_get_dev(port_num);
if (dev == NULL)
return;
priv = dev->ml_priv;
grp = priv->mpcg;
rch = priv->channel[CTCM_READ];
wch = priv->channel[CTCM_WRITE];
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
"%s(%s): state=%s",
CTCM_FUNTAIL, 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*/
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): REJECTED - inactive channels",
CTCM_FUNTAIL, dev->name);
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)) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): ABORT - PASSIVE XID",
CTCM_FUNTAIL, dev->name);
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 {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): RX-%s not ready for ACTIVE XID0",
CTCM_FUNTAIL, dev->name, rch->id);
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 {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): WX-%s not ready for ACTIVE XID0",
CTCM_FUNTAIL, dev->name, wch->id);
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("Exit %s()\n", __func__);
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;
struct ctcm_priv *priv;
struct mpc_group *grp;
dev = ctcmpc_get_dev(port_num);
if (dev == NULL)
return;
priv = dev->ml_priv;
grp = priv->mpcg;
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_DEBUG,
"%s: %s: refcount = %d\n",
CTCM_FUNTAIL, dev->name, netdev_refcnt_read(dev));
fsm_deltimer(&priv->restart_timer);
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);
return;
}
EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
/*
* ctc_mpc_flow_control
* (exported interface)
*/
void ctc_mpc_flow_control(int port_num, int flowc)
{
struct ctcm_priv *priv;
struct mpc_group *grp;
struct net_device *dev;
struct channel *rch;
int mpcg_state;
dev = ctcmpc_get_dev(port_num);
if (dev == NULL)
return;
priv = dev->ml_priv;
grp = priv->mpcg;
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
"%s: %s: flowc = %d",
CTCM_FUNTAIL, dev->name, flowc);
rch = priv->channel[CTCM_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;
}
}
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *ch = priv->channel[CTCM_WRITE];
CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id);
CTCM_D3_DUMP((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);
}
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct th_sweep *header;
struct sk_buff *sweep_skb;
struct channel *ch = priv->channel[CTCM_WRITE];
CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id);
sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
if (sweep_skb == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): sweep_skb allocation ERROR\n",
CTCM_FUNTAIL, rch->id);
goto done;
}
header = skb_put_zero(sweep_skb, TH_SWEEP_LENGTH);
header->th.th_ch_flag = TH_SWEEP_RESP;
header->sw.th_last_seq = ch->th_seq_num;
netif_trans_update(dev);
skb_queue_tail(&ch->sweep_queue, sweep_skb);
fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
return;
done:
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *ch = priv->channel[CTCM_WRITE];
if (do_debug)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
" %s(): ch=0x%p id=%s\n", __func__, 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[CTCM_READ];
grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
}
CTCM_D3_DUMP((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 = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
if (grp == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): No MPC group",
CTCM_FUNTAIL, dev->name);
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;
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): fails",
CTCM_FUNTAIL, dev->name);
return;
}
grp->port_persist = 1;
grp->out_of_sequence = 0;
grp->estconn_called = 0;
tasklet_hi_schedule(&grp->mpc_tasklet2);
return;
}
/*
* 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 = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct channel *ch = NULL;
if (grp == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): No MPC group",
CTCM_FUNTAIL, dev->name);
return;
}
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
"%s: %s: GROUP TRANSITIONED TO READY, maxbuf = %d\n",
CTCM_FUNTAIL, dev->name, grp->group_max_buflen);
fsm_newstate(grp->fsm, MPCG_STATE_READY);
/* Put up a read on the channel */
ch = priv->channel[CTCM_READ];
ch->pdu_seq = 0;
CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
__func__, ch->pdu_seq);
ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
/* Put the write channel in idle state */
ch = priv->channel[CTCM_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;
return;
}
/*
* Increment the MPC Group Active Channel Counts
* helper of dev_action (called from channel fsm)
*/
void mpc_channel_action(struct channel *ch, int direction, int action)
{
struct net_device *dev = ch->netdev;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
if (grp == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): No MPC group",
CTCM_FUNTAIL, dev->name);
return;
}
CTCM_PR_DEBUG("enter %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
"%s: %i / Grp:%s total_channels=%i, active_channels: "
"read=%i, write=%i\n", __func__, action,
fsm_getstate_str(grp->fsm), grp->num_channel_paths,
grp->active_channels[CTCM_READ],
grp->active_channels[CTCM_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) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): Couldn't alloc ch xid_skb\n",
CTCM_FUNTAIL, dev->name);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
return;
}
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;
skb_put_data(ch->xid_skb, grp->xid_skb->data,
grp->xid_skb->len);
ch->xid->xid2_dlc_type =
((CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
? XID2_READ_SIDE : XID2_WRITE_SIDE);
if (CHANNEL_DIRECTION(ch->flags) == CTCM_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[CTCM_READ] > 0) &&
(grp->active_channels[CTCM_WRITE] > 0) &&
(fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
"%s: %s: MPC GROUP CHANNELS ACTIVE\n",
__func__, 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[CTCM_READ] == 0) &&
(grp->active_channels[CTCM_WRITE] > 0))
|| ((grp->active_channels[CTCM_WRITE] == 0) &&
(grp->active_channels[CTCM_READ] > 0)))
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
done:
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
"exit %s: %i / Grp:%s total_channels=%i, active_channels: "
"read=%i, write=%i\n", __func__, action,
fsm_getstate_str(grp->fsm), grp->num_channel_paths,
grp->active_channels[CTCM_READ],
grp->active_channels[CTCM_WRITE]);
CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
}
/*
* 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->ml_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;
CTCM_PR_DEBUG("ctcmpc enter: %s() %s cp:%i ch:%s\n",
__func__, 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;
CTCM_PR_DBGDATA("%s: th_header\n", __func__);
CTCM_D3_DUMP((char *)header, TH_HEADER_LENGTH);
CTCM_PR_DBGDATA("%s: pskb len: %04x \n", __func__, 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)) {
CTCM_PR_DBGDATA("%s: came into th_has_pdu\n", __func__);
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);
CTCM_PR_DBGDATA("%s: th_seq_num expect:%08x "
"got:%08x\n", __func__,
ch->th_seq_num + 1, header->th_seq_num);
return;
}
grp->out_of_sequence = 0;
ch->th_seq_num = header->th_seq_num;
CTCM_PR_DBGDATA("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
__func__, ch->th_seq_num);
if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY))
goto done;
while ((pskb->len > 0) && !pdu_last_seen) {
curr_pdu = (struct pdu *)pskb->data;
CTCM_PR_DBGDATA("%s: pdu_header\n", __func__);
CTCM_D3_DUMP((char *)pskb->data, PDU_HEADER_LENGTH);
CTCM_PR_DBGDATA("%s: pskb len: %04x \n",
__func__, 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)) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): Dropping packet with "
"illegal siize %d",
CTCM_FUNTAIL, 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;
CTCM_PR_DBGDATA("%s: new_len: %04x \n",
__func__, new_len);
if ((new_len == 0) || (new_len > pskb->len)) {
/* should never happen */
/* pskb len must be hosed...bail out */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): non valid pdu_offset: %04x",
/* "data may be lost", */
CTCM_FUNTAIL, dev->name, new_len);
goto done;
}
skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC);
if (!skb) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): MEMORY allocation error",
CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
goto done;
}
skb_put_data(skb, 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",
__func__, ch->pdu_seq);
ctcm_pr_debug("%s: skb:%0lx "
"skb len: %d \n", __func__,
(unsigned long)skb, skb->len);
ctcm_pr_debug("%s: up to 32 bytes "
"of pdu_data sent\n", __func__);
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 = kmalloc(sizeof(struct mpcg_info), GFP_ATOMIC);
if (mpcginfo == NULL)
goto done;
mpcginfo->ch = ch;
mpcginfo->th = header;
mpcginfo->skb = pskb;
CTCM_PR_DEBUG("%s: Not PDU - may be control pkt\n",
__func__);
/* 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) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): control pkt expected\n",
CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
/* mpcginfo only used for non-data transfers */
if (do_debug_data)
ctcmpc_dump_skb(pskb, -8);
}
kfree(mpcginfo);
}
done:
dev_kfree_skb_any(pskb);
if (sendrc == NET_RX_DROP) {
dev_warn(&dev->dev,
"The network backlog for %s is exceeded, "
"package dropped\n", __func__);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
__func__, dev->name, 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->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("%s cp:%i enter: %s() %s\n",
dev->name, smp_processor_id(), __func__, 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;
}
CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
__func__, dev->name, 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, CTC_DBF_INFO,
"Enter %s(%p)", CTCM_FUNTAIL, priv);
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) {
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;
skb_put_data(grp->xid_skb, &thnorm, TH_HEADER_LENGTH);
grp->xid = (struct xid2 *)skb_tail_pointer(grp->xid_skb);
skb_put_data(grp->xid_skb, &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);
skb_put_data(grp->xid_skb, "VTAM", 4);
grp->rcvd_xid_skb =
__dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
if (grp->rcvd_xid_skb == NULL) {
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;
skb_put_data(grp->rcvd_xid_skb, &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;
struct channel *wch;
CTCM_PR_DEBUG("Enter %s: %s\n", __func__, dev->name);
priv = dev->ml_priv;
grp = priv->mpcg;
grp->flow_off_called = 0;
fsm_deltimer(&grp->timer);
if (grp->channels_terminating)
return;
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)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
"%s(%s): MPC GROUP INOPERATIVE",
CTCM_FUNTAIL, dev->name);
if ((grp->saved_state != MPCG_STATE_RESET) ||
/* dealloc_channel has been called */
(grp->port_persist == 0))
fsm_deltimer(&priv->restart_timer);
wch = priv->channel[CTCM_WRITE];
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;
skb_put_data(grp->rcvd_xid_skb, &thnorm, TH_HEADER_LENGTH);
if (grp->send_qllc_disc == 1) {
grp->send_qllc_disc = 0;
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*/
/* become 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)
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
"%s(%s): MPC GROUP RECOVERY SCHEDULED",
CTCM_FUNTAIL, 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);
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
"%s(%s): NO MPC GROUP RECOVERY ATTEMPTED",
CTCM_FUNTAIL, dev->name);
}
}
/*
* 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;
priv = dev->ml_priv;
grp = priv->mpcg;
wch = priv->channel[CTCM_WRITE];
rch = priv->channel[CTCM_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;
fallthrough;
default:
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
"%s: dev=%s exit",
CTCM_FUNTAIL, 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;
struct ctcm_priv *priv;
struct mpc_group *grp;
if (ch) {
dev = ch->netdev;
if (dev) {
priv = dev->ml_priv;
if (priv) {
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
"%s: %s: %s\n",
CTCM_FUNTAIL, dev->name, ch->id);
grp = priv->mpcg;
grp->send_qllc_disc = 1;
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
}
}
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)
{
int rc;
struct channel *ch = (struct channel *)thischan;
unsigned long saveflags = 0;
spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
rc = ccw_device_start(ch->cdev, &ch->ccw[15], 0, 0xff, 0);
spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
if (rc != 0) {
ctcm_ccw_check_rc(ch, rc, (char *)__func__);
}
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->ml_priv;
struct mpc_group *grp = priv->mpcg;
struct xid2 *xid = mpcginfo->xid;
int rc = 0;
__u64 our_id = 0;
__u64 their_id = 0;
int len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
CTCM_PR_DEBUG("Enter %s: xid=%p\n", __func__, xid);
if (xid == NULL) {
rc = 1;
/* XID REJECTED: xid == NULL */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): xid = NULL",
CTCM_FUNTAIL, ch->id);
goto done;
}
CTCM_D3_DUMP((char *)xid, XID2_LENGTH);
/*the received direction should be the opposite of ours */
if (((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? XID2_WRITE_SIDE :
XID2_READ_SIDE) != xid->xid2_dlc_type) {
rc = 2;
/* XID REJECTED: r/w channel pairing mismatch */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): r/w channel pairing mismatch",
CTCM_FUNTAIL, ch->id);
goto done;
}
if (xid->xid2_dlc_type == XID2_READ_SIDE) {
CTCM_PR_DEBUG("%s: grpmaxbuf:%d xid2buflen:%d\n", __func__,
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);
skb_put_data(grp->rcvd_xid_skb, 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_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
"%s(%s): WE HAVE LOW ID - TAKE XSIDE",
CTCM_FUNTAIL, ch->id);
} else {
grp->roll = YSIDE;
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
"%s(%s): WE HAVE HIGH ID - TAKE YSIDE",
CTCM_FUNTAIL, ch->id);
}
} else {
if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) {
rc = 3;
/* XID REJECTED: xid flag byte4 mismatch */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): xid flag byte4 mismatch",
CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_flag2 == 0x40) {
rc = 4;
/* XID REJECTED - xid NOGOOD */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): xid NOGOOD",
CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) {
rc = 5;
/* XID REJECTED - Adjacent Station ID Mismatch */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): Adjacent Station ID Mismatch",
CTCM_FUNTAIL, ch->id);
}
if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) {
rc = 6;
/* XID REJECTED - Sender Address Mismatch */
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): Sender Address Mismatch",
CTCM_FUNTAIL, ch->id);
}
}
done:
if (rc) {
dev_warn(&dev->dev,
"The XID used in the MPC protocol is not valid, "
"rc = %d\n", rc);
priv->xid->xid2_flag2 = 0x40;
grp->saved_xid2->xid2_flag2 = 0x40;
}
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;
int rc = 0;
int gotlock = 0;
unsigned long saveflags = 0; /* avoids compiler warning with
spin_unlock_irqrestore */
CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ch, ch->id);
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 (!(ch->xid_th && ch->xid && ch->xid_id))
CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
"%s(%s): xid_th=%p, xid=%p, xid_id=%p",
CTCM_FUNTAIL, ch->id, ch->xid_th, ch->xid, ch->xid_id);
if (side == XSIDE) {
/* mpc_action_xside_xid */
if (ch->xid_th == NULL)
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_dma32(ch->xid_th);
if (ch->xid == NULL)
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_dma32(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_dma32(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_dma32(ch->rcvd_xid);
ch->ccw[13].cmd_code = CCW_CMD_READ;
ch->ccw[13].cda = virt_to_dma32(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_dma32(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_dma32(ch->rcvd_xid);
if (ch->xid_th == NULL)
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_dma32(ch->xid_th);
if (ch->xid == NULL)
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_dma32(ch->xid);
if (ch->xid_id == NULL)
goto done;
ch->ccw[13].cmd_code = CCW_CMD_WRITE;
ch->ccw[13].cda = virt_to_dma32(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;
CTCM_CCW_DUMP((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
CTCM_D3_DUMP((char *)ch->xid_th, TH_HEADER_LENGTH);
CTCM_D3_DUMP((char *)ch->xid, XID2_LENGTH);
CTCM_D3_DUMP((char *)ch->xid_id, 4);
if (!in_hardirq()) {
/* 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], 0, 0xff, 0);
if (gotlock) /* see remark above about conditional locking */
spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
if (rc != 0) {
ctcm_ccw_check_rc(ch, rc,
(side == XSIDE) ? "x-side XID" : "y-side XID");
}
done:
CTCM_PR_DEBUG("Exit %s: ch=0x%p id=%s\n",
__func__, 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 net_device *dev = ch->netdev;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ch, ch->id);
if (ch->xid == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): ch->xid == NULL",
CTCM_FUNTAIL, dev->name);
return;
}
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);
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 = dev->ml_priv;
struct mpc_group *grp = NULL;
int direction;
int send = 0;
if (priv)
grp = priv->mpcg;
if (grp == NULL)
return;
for (direction = CTCM_READ; direction <= CTCM_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;
skb_put_data(ch->xid_skb, &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;
skb_put_data(ch->xid_skb, &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);
skb_put_data(ch->xid_skb, &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;
skb_put_data(ch->xid_skb, &thdummy,
TH_HEADER_LENGTH);
send = 1;
}
}
if (send)
fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
}
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 = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("%s: ch-id:%s xid2:%i xid7:%i xidt_p2:%i \n",
__func__, 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;
}
CTCM_PR_DEBUG("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
__func__, ch->id, grp->outstanding_xid2,
grp->outstanding_xid7, grp->outstanding_xid7_p2);
CTCM_PR_DEBUG("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
__func__, 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->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
__func__, smp_processor_id(), ch, ch->id);
CTCM_PR_DEBUG("%s: outstanding_xid7: %i, outstanding_xid7_p2: %i\n",
__func__, 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;
}
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)
{
struct sk_buff *skb;
struct qllc *qllcptr;
struct ctcm_priv *priv = dev->ml_priv;
struct mpc_group *grp = priv->mpcg;
CTCM_PR_DEBUG("%s: GROUP STATE: %s\n",
__func__, 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;
}
fallthrough;
case MPCG_STATE_FLOWC:
case MPCG_STATE_READY:
grp->send_qllc_disc = 2;
skb = __dev_alloc_skb(sizeof(struct qllc), GFP_ATOMIC);
if (skb == NULL) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): skb allocation error",
CTCM_FUNTAIL, dev->name);
priv->stats.rx_dropped++;
return -ENOMEM;
}
qllcptr = skb_put(skb, sizeof(struct qllc));
qllcptr->qllc_address = 0xcc;
qllcptr->qllc_commands = 0x03;
if (skb_headroom(skb) < 4) {
CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
"%s(%s): skb_headroom error",
CTCM_FUNTAIL, dev->name);
dev_kfree_skb_any(skb);
return -ENOMEM;
}
*((__u32 *)skb_push(skb, 4)) =
priv->channel[CTCM_READ]->pdu_seq;
priv->channel[CTCM_READ]->pdu_seq++;
CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
__func__, priv->channel[CTCM_READ]->pdu_seq);
/* receipt of CC03 resets anticipated sequence number on
receiving side */
priv->channel[CTCM_READ]->pdu_seq = 0x00;
skb_reset_mac_header(skb);
skb->dev = dev;
skb->protocol = htons(ETH_P_SNAP);
skb->ip_summed = CHECKSUM_UNNECESSARY;
CTCM_D3_DUMP(skb->data, (sizeof(struct qllc) + 4));
netif_rx(skb);
break;
default:
break;
}
return 0;
}
/* --- This is the END my friend --- */