summaryrefslogtreecommitdiffstats
path: root/drivers/s390/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net')
-rw-r--r--drivers/s390/net/Kconfig14
-rw-r--r--drivers/s390/net/claw.c20
-rw-r--r--drivers/s390/net/ctcm_main.c43
-rw-r--r--drivers/s390/net/lcs.c74
-rw-r--r--drivers/s390/net/netiucv.c119
-rw-r--r--drivers/s390/net/qeth_core_main.c58
-rw-r--r--drivers/s390/net/qeth_core_mpc.c2
-rw-r--r--drivers/s390/net/qeth_core_mpc.h2
-rw-r--r--drivers/s390/net/qeth_l2_main.c88
-rw-r--r--drivers/s390/net/qeth_l3_main.c85
-rw-r--r--drivers/s390/net/smsgiucv.c63
11 files changed, 505 insertions, 63 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig
index a7745c82b4a..cb909a5b504 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -8,7 +8,7 @@ config LCS
Select this option if you want to use LCS networking on IBM System z.
This device driver supports Token Ring (IEEE 802.5),
FDDI (IEEE 802.7) and Ethernet.
- To compile as a module, choose M. The module name is lcs.ko.
+ To compile as a module, choose M. The module name is lcs.
If you do not know what it is, it's safe to choose Y.
config CTCM
@@ -21,7 +21,7 @@ config CTCM
It also supports virtual CTCs when running under VM.
This driver also supports channel-to-channel MPC SNA devices.
MPC is an SNA protocol device used by Communication Server for Linux.
- To compile as a module, choose M. The module name is ctcm.ko.
+ To compile as a module, choose M. The module name is ctcm.
To compile into the kernel, choose Y.
If you do not need any channel-to-channel connection, choose N.
@@ -34,7 +34,7 @@ config NETIUCV
link between VM guests. Using ifconfig a point-to-point connection
can be established to the Linux on IBM System z
running on the other VM guest. To compile as a module, choose M.
- The module name is netiucv.ko. If unsure, choose Y.
+ The module name is netiucv. If unsure, choose Y.
config SMSGIUCV
tristate "IUCV special message support (VM only)"
@@ -50,7 +50,7 @@ config CLAW
This driver supports channel attached CLAW devices.
CLAW is Common Link Access for Workstation. Common devices
that use CLAW are RS/6000s, Cisco Routers (CIP) and 3172 devices.
- To compile as a module, choose M. The module name is claw.ko.
+ To compile as a module, choose M. The module name is claw.
To compile into the kernel, choose Y.
config QETH
@@ -65,14 +65,14 @@ config QETH
<http://www.ibm.com/developerworks/linux/linux390>
To compile this driver as a module, choose M.
- The module name is qeth.ko.
+ The module name is qeth.
config QETH_L2
tristate "qeth layer 2 device support"
depends on QETH
help
Select this option to be able to run qeth devices in layer 2 mode.
- To compile as a module, choose M. The module name is qeth_l2.ko.
+ To compile as a module, choose M. The module name is qeth_l2.
If unsure, choose y.
config QETH_L3
@@ -80,7 +80,7 @@ config QETH_L3
depends on QETH
help
Select this option to be able to run qeth devices in layer 3 mode.
- To compile as a module choose M. The module name is qeth_l3.ko.
+ To compile as a module choose M. The module name is qeth_l3.
If unsure, choose Y.
config QETH_IPV6
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 30a43cc79e7..d40f7a934f9 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -3,12 +3,12 @@
* ESCON CLAW network driver
*
* Linux for zSeries version
- * Copyright (C) 2002,2005 IBM Corporation
+ * Copyright IBM Corp. 2002, 2009
* Author(s) Original code written by:
- * Kazuo Iimura (iimura@jp.ibm.com)
+ * Kazuo Iimura <iimura@jp.ibm.com>
* Rewritten by
- * Andy Richter (richtera@us.ibm.com)
- * Marc Price (mwprice@us.ibm.com)
+ * Andy Richter <richtera@us.ibm.com>
+ * Marc Price <mwprice@us.ibm.com>
*
* sysfs parms:
* group x.x.rrrr,x.x.wwww
@@ -253,6 +253,11 @@ static void claw_free_wrt_buf(struct net_device *dev);
/* Functions for unpack reads */
static void unpack_read(struct net_device *dev);
+static int claw_pm_prepare(struct ccwgroup_device *gdev)
+{
+ return -EPERM;
+}
+
/* ccwgroup table */
static struct ccwgroup_driver claw_group_driver = {
@@ -264,6 +269,7 @@ static struct ccwgroup_driver claw_group_driver = {
.remove = claw_remove_device,
.set_online = claw_new_device,
.set_offline = claw_shutdown_device,
+ .prepare = claw_pm_prepare,
};
/*
@@ -338,12 +344,6 @@ claw_tx(struct sk_buff *skb, struct net_device *dev)
CLAW_DBF_TEXT(4, trace, "claw_tx");
p_ch=&privptr->channel[WRITE];
- if (skb == NULL) {
- privptr->stats.tx_dropped++;
- privptr->stats.tx_errors++;
- CLAW_DBF_TEXT_(2, trace, "clawtx%d", -EIO);
- return -EIO;
- }
spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
rc=claw_hw_tx( skb, dev, 1 );
spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 77f4033a0f4..222e4739443 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/ctcm_main.c
*
- * Copyright IBM Corp. 2001, 2007
+ * Copyright IBM Corp. 2001, 2009
* Author(s):
* Original CTC driver(s):
* Fritz Elfert (felfert@millenux.com)
@@ -1677,10 +1677,8 @@ static void ctcm_remove_device(struct ccwgroup_device *cgdev)
BUG_ON(priv == NULL);
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
- "removing device %s, r/w = %s/%s, proto : %d",
- priv->channel[READ]->netdev->name,
- priv->channel[READ]->id, priv->channel[WRITE]->id,
- priv->protocol);
+ "removing device %p, proto : %d",
+ cgdev, priv->protocol);
if (cgdev->state == CCWGROUP_ONLINE)
ctcm_shutdown_device(cgdev);
@@ -1690,6 +1688,38 @@ static void ctcm_remove_device(struct ccwgroup_device *cgdev)
put_device(&cgdev->dev);
}
+static int ctcm_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ netif_device_detach(priv->channel[READ]->netdev);
+ ctcm_close(priv->channel[READ]->netdev);
+ ccw_device_set_offline(gdev->cdev[1]);
+ ccw_device_set_offline(gdev->cdev[0]);
+ return 0;
+}
+
+static int ctcm_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct ctcm_priv *priv = dev_get_drvdata(&gdev->dev);
+ int rc;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ rc = ccw_device_set_online(gdev->cdev[1]);
+ if (rc)
+ goto err_out;
+ rc = ccw_device_set_online(gdev->cdev[0]);
+ if (rc)
+ goto err_out;
+ ctcm_open(priv->channel[READ]->netdev);
+err_out:
+ netif_device_attach(priv->channel[READ]->netdev);
+ return rc;
+}
+
static struct ccwgroup_driver ctcm_group_driver = {
.owner = THIS_MODULE,
.name = CTC_DRIVER_NAME,
@@ -1699,6 +1729,9 @@ static struct ccwgroup_driver ctcm_group_driver = {
.remove = ctcm_remove_device,
.set_online = ctcm_new_device,
.set_offline = ctcm_shutdown_device,
+ .freeze = ctcm_pm_suspend,
+ .thaw = ctcm_pm_resume,
+ .restore = ctcm_pm_resume,
};
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index a45bc24eb5f..07a25c3f94b 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1,15 +1,12 @@
/*
- * linux/drivers/s390/net/lcs.c
- *
* Linux for S/390 Lan Channel Station Network Driver
*
- * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Original Code written by
- * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Rewritten by
- * Frank Pavlic (fpavlic@de.ibm.com) and
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Copyright IBM Corp. 1999, 2009
+ * Author(s): Original Code written by
+ * DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com>
+ * Rewritten by
+ * Frank Pavlic <fpavlic@de.ibm.com> and
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -2313,6 +2310,60 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev)
put_device(&ccwgdev->dev);
}
+static int lcs_pm_suspend(struct lcs_card *card)
+{
+ if (card->dev)
+ netif_device_detach(card->dev);
+ lcs_set_allowed_threads(card, 0);
+ lcs_wait_for_threads(card, 0xffffffff);
+ if (card->state != DEV_STATE_DOWN)
+ __lcs_shutdown_device(card->gdev, 1);
+ return 0;
+}
+
+static int lcs_pm_resume(struct lcs_card *card)
+{
+ int rc = 0;
+
+ if (card->state == DEV_STATE_RECOVER)
+ rc = lcs_new_device(card->gdev);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "The lcs device driver "
+ "failed to recover the device\n");
+ }
+ return rc;
+}
+
+static int lcs_prepare(struct ccwgroup_device *gdev)
+{
+ return 0;
+}
+
+static void lcs_complete(struct ccwgroup_device *gdev)
+{
+ return;
+}
+
+static int lcs_freeze(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_suspend(card);
+}
+
+static int lcs_thaw(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
+static int lcs_restore(struct ccwgroup_device *gdev)
+{
+ struct lcs_card *card = dev_get_drvdata(&gdev->dev);
+ return lcs_pm_resume(card);
+}
+
/**
* LCS ccwgroup driver registration
*/
@@ -2325,6 +2376,11 @@ static struct ccwgroup_driver lcs_group_driver = {
.remove = lcs_remove_device,
.set_online = lcs_new_device,
.set_offline = lcs_shutdown_device,
+ .prepare = lcs_prepare,
+ .complete = lcs_complete,
+ .freeze = lcs_freeze,
+ .thaw = lcs_thaw,
+ .restore = lcs_restore,
};
/**
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index be716e45f7a..fdb02d043d3 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -1,11 +1,15 @@
/*
* IUCV network driver
*
- * Copyright 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Copyright IBM Corp. 2001, 2009
*
- * Sysfs integration and all bugs therein by Cornelia Huck
- * (cornelia.huck@de.ibm.com)
+ * Author(s):
+ * Original netiucv driver:
+ * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+ * Sysfs integration and all bugs therein:
+ * Cornelia Huck (cornelia.huck@de.ibm.com)
+ * PM functions:
+ * Ursula Braun (ursula.braun@de.ibm.com)
*
* Documentation used:
* the source of the original IUCV driver by:
@@ -149,10 +153,27 @@ PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
#define PRINTK_HEADER " iucv: " /* for debugging */
+/* dummy device to make sure netiucv_pm functions are called */
+static struct device *netiucv_dev;
+
+static int netiucv_pm_prepare(struct device *);
+static void netiucv_pm_complete(struct device *);
+static int netiucv_pm_freeze(struct device *);
+static int netiucv_pm_restore_thaw(struct device *);
+
+static struct dev_pm_ops netiucv_pm_ops = {
+ .prepare = netiucv_pm_prepare,
+ .complete = netiucv_pm_complete,
+ .freeze = netiucv_pm_freeze,
+ .thaw = netiucv_pm_restore_thaw,
+ .restore = netiucv_pm_restore_thaw,
+};
+
static struct device_driver netiucv_driver = {
.owner = THIS_MODULE,
.name = "netiucv",
.bus = &iucv_bus,
+ .pm = &netiucv_pm_ops,
};
static int netiucv_callback_connreq(struct iucv_path *,
@@ -233,6 +254,7 @@ struct netiucv_priv {
fsm_instance *fsm;
struct iucv_connection *conn;
struct device *dev;
+ int pm_state;
};
/**
@@ -1265,6 +1287,72 @@ static int netiucv_close(struct net_device *dev)
return 0;
}
+static int netiucv_pm_prepare(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return 0;
+}
+
+static void netiucv_pm_complete(struct device *dev)
+{
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ return;
+}
+
+/**
+ * netiucv_pm_freeze() - Freeze PM callback
+ * @dev: netiucv device
+ *
+ * close open netiucv interfaces
+ */
+static int netiucv_pm_freeze(struct device *dev)
+{
+ struct netiucv_priv *priv = dev->driver_data;
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ netif_device_detach(ndev);
+ priv->pm_state = fsm_getstate(priv->fsm);
+ rc = netiucv_close(ndev);
+out:
+ return rc;
+}
+
+/**
+ * netiucv_pm_restore_thaw() - Thaw and restore PM callback
+ * @dev: netiucv device
+ *
+ * re-open netiucv interfaces closed during freeze
+ */
+static int netiucv_pm_restore_thaw(struct device *dev)
+{
+ struct netiucv_priv *priv = dev->driver_data;
+ struct net_device *ndev = NULL;
+ int rc = 0;
+
+ IUCV_DBF_TEXT(trace, 3, __func__);
+ if (priv && priv->conn)
+ ndev = priv->conn->netdev;
+ if (!ndev)
+ goto out;
+ switch (priv->pm_state) {
+ case DEV_STATE_RUNNING:
+ case DEV_STATE_STARTWAIT:
+ rc = netiucv_open(ndev);
+ break;
+ default:
+ break;
+ }
+ netif_device_attach(ndev);
+out:
+ return rc;
+}
+
/**
* Start transmission of a packet.
* Called from generic network device layer.
@@ -1315,9 +1403,9 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
dev->trans_start = jiffies;
- rc = netiucv_transmit_skb(privptr->conn, skb) != 0;
+ rc = netiucv_transmit_skb(privptr->conn, skb);
netiucv_clear_busy(dev);
- return rc;
+ return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK;
}
/**
@@ -1731,7 +1819,6 @@ static int netiucv_register_device(struct net_device *ndev)
struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
int ret;
-
IUCV_DBF_TEXT(trace, 3, __func__);
if (dev) {
@@ -2100,6 +2187,7 @@ static void __exit netiucv_exit(void)
netiucv_unregister_device(dev);
}
+ device_unregister(netiucv_dev);
driver_unregister(&netiucv_driver);
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
@@ -2125,10 +2213,25 @@ static int __init netiucv_init(void)
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
goto out_iucv;
}
-
+ /* establish dummy device */
+ netiucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!netiucv_dev) {
+ rc = -ENOMEM;
+ goto out_driver;
+ }
+ dev_set_name(netiucv_dev, "netiucv");
+ netiucv_dev->bus = &iucv_bus;
+ netiucv_dev->parent = iucv_root;
+ netiucv_dev->release = (void (*)(struct device *))kfree;
+ netiucv_dev->driver = &netiucv_driver;
+ rc = device_register(netiucv_dev);
+ if (rc)
+ goto out_driver;
netiucv_banner();
return rc;
+out_driver:
+ driver_unregister(&netiucv_driver);
out_iucv:
iucv_unregister(&netiucv_handler, 1);
out_dbf:
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index c827d69b5a9..d53621c4acb 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_core_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -952,6 +952,7 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
buf->buffer->element[i].addr = NULL;
buf->buffer->element[i].flags = 0;
}
+ buf->buffer->element[15].flags = 0;
buf->next_element_to_fill = 0;
atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
}
@@ -1140,6 +1141,8 @@ static int qeth_setup_card(struct qeth_card *card)
card->ipato.enabled = 0;
card->ipato.invert4 = 0;
card->ipato.invert6 = 0;
+ if (card->info.type == QETH_CARD_TYPE_IQD)
+ card->options.checksum_type = NO_CHECKSUMMING;
/* init QDIO stuff */
qeth_init_qdio_info(card);
return 0;
@@ -2934,8 +2937,8 @@ int qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
if (card->info.type == QETH_CARD_TYPE_OSN)
return cast_type;
- if (skb->dst && skb->dst->neighbour) {
- cast_type = skb->dst->neighbour->type;
+ if (skb_dst(skb) && skb_dst(skb)->neighbour) {
+ cast_type = skb_dst(skb)->neighbour->type;
if ((cast_type == RTN_BROADCAST) ||
(cast_type == RTN_MULTICAST) ||
(cast_type == RTN_ANYCAST))
@@ -4192,6 +4195,50 @@ static void qeth_core_shutdown(struct ccwgroup_device *gdev)
card->discipline.ccwgdriver->shutdown(gdev);
}
+static int qeth_core_prepare(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->prepare)
+ return card->discipline.ccwgdriver->prepare(gdev);
+ return 0;
+}
+
+static void qeth_core_complete(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->complete)
+ card->discipline.ccwgdriver->complete(gdev);
+}
+
+static int qeth_core_freeze(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->freeze)
+ return card->discipline.ccwgdriver->freeze(gdev);
+ return 0;
+}
+
+static int qeth_core_thaw(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->thaw)
+ return card->discipline.ccwgdriver->thaw(gdev);
+ return 0;
+}
+
+static int qeth_core_restore(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ if (card->discipline.ccwgdriver &&
+ card->discipline.ccwgdriver->restore)
+ return card->discipline.ccwgdriver->restore(gdev);
+ return 0;
+}
+
static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
.owner = THIS_MODULE,
.name = "qeth",
@@ -4201,6 +4248,11 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
.set_online = qeth_core_set_online,
.set_offline = qeth_core_set_offline,
.shutdown = qeth_core_shutdown,
+ .prepare = qeth_core_prepare,
+ .complete = qeth_core_complete,
+ .freeze = qeth_core_freeze,
+ .thaw = qeth_core_thaw,
+ .restore = qeth_core_restore,
};
static ssize_t
diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c
index 06f4de1f050..ec24901c802 100644
--- a/drivers/s390/net/qeth_core_mpc.c
+++ b/drivers/s390/net/qeth_core_mpc.c
@@ -181,6 +181,8 @@ static struct ipa_rc_msg qeth_ipa_rc_msg[] = {
{IPA_RC_L2_ADDR_TABLE_FULL, "Layer2 address table full"},
{IPA_RC_L2_DUP_LAYER3_MAC, "Duplicate with layer 3 MAC"},
{IPA_RC_L2_GMAC_NOT_FOUND, "GMAC not found"},
+ {IPA_RC_L2_MAC_NOT_AUTH_BY_HYP, "L2 mac not authorized by hypervisor"},
+ {IPA_RC_L2_MAC_NOT_AUTH_BY_ADP, "L2 mac not authorized by adapter"},
{IPA_RC_L2_MAC_NOT_FOUND, "L2 mac address not found"},
{IPA_RC_L2_INVALID_VLAN_ID, "L2 invalid vlan id"},
{IPA_RC_L2_DUP_VLAN_ID, "L2 duplicate vlan id"},
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index 18548822e37..eecb2ee62e8 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -168,6 +168,8 @@ enum qeth_ipa_return_codes {
IPA_RC_L2_ADDR_TABLE_FULL = 0x2006,
IPA_RC_L2_DUP_LAYER3_MAC = 0x200a,
IPA_RC_L2_GMAC_NOT_FOUND = 0x200b,
+ IPA_RC_L2_MAC_NOT_AUTH_BY_HYP = 0x200c,
+ IPA_RC_L2_MAC_NOT_AUTH_BY_ADP = 0x200d,
IPA_RC_L2_MAC_NOT_FOUND = 0x2010,
IPA_RC_L2_INVALID_VLAN_ID = 0x2015,
IPA_RC_L2_DUP_VLAN_ID = 0x2016,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 172031baedc..81d7f268418 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_l2_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -19,6 +19,7 @@
#include <linux/etherdevice.h>
#include <linux/mii.h>
#include <linux/ip.h>
+#include <linux/list.h>
#include "qeth_core.h"
@@ -130,7 +131,7 @@ static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
cmd = (struct qeth_ipa_cmd *) data;
mac = &cmd->data.setdelmac.mac[0];
/* MAC already registered, needed in couple/uncouple case */
- if (cmd->hdr.return_code == 0x2005) {
+ if (cmd->hdr.return_code == IPA_RC_L2_DUP_MAC) {
QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s \n",
mac, QETH_CARD_IFNAME(card));
cmd->hdr.return_code = 0;
@@ -502,6 +503,30 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,
if (cmd->hdr.return_code) {
QETH_DBF_TEXT_(TRACE, 2, "L2er%x", cmd->hdr.return_code);
card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+ switch (cmd->hdr.return_code) {
+ case IPA_RC_L2_DUP_MAC:
+ case IPA_RC_L2_DUP_LAYER3_MAC:
+ dev_warn(&card->gdev->dev,
+ "MAC address "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ "already exists\n",
+ card->dev->dev_addr[0], card->dev->dev_addr[1],
+ card->dev->dev_addr[2], card->dev->dev_addr[3],
+ card->dev->dev_addr[4], card->dev->dev_addr[5]);
+ break;
+ case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP:
+ case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP:
+ dev_warn(&card->gdev->dev,
+ "MAC address "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ "is not authorized\n",
+ card->dev->dev_addr[0], card->dev->dev_addr[1],
+ card->dev->dev_addr[2], card->dev->dev_addr[3],
+ card->dev->dev_addr[4], card->dev->dev_addr[5]);
+ break;
+ default:
+ break;
+ }
cmd->hdr.return_code = -EIO;
} else {
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
@@ -616,6 +641,7 @@ static void qeth_l2_set_multicast_list(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
struct dev_addr_list *dm;
+ struct netdev_hw_addr *ha;
if (card->info.type == QETH_CARD_TYPE_OSN)
return ;
@@ -629,8 +655,8 @@ static void qeth_l2_set_multicast_list(struct net_device *dev)
for (dm = dev->mc_list; dm; dm = dm->next)
qeth_l2_add_mc(card, dm->da_addr, 0);
- for (dm = dev->uc_list; dm; dm = dm->next)
- qeth_l2_add_mc(card, dm->da_addr, 1);
+ list_for_each_entry(ha, &dev->uc_list, list)
+ qeth_l2_add_mc(card, ha->addr, 1);
spin_unlock_bh(&card->mclock);
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
@@ -839,6 +865,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
{
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+ qeth_set_allowed_threads(card, 0, 1);
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
if (cgdev->state == CCWGROUP_ONLINE) {
@@ -974,8 +1001,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
dev_warn(&card->gdev->dev,
"The LAN is offline\n");
card->lan_online = 0;
+ return 0;
}
- return rc;
+ goto out_remove;
} else
card->lan_online = 1;
@@ -1113,12 +1141,62 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
qeth_clear_qdio_buffers(card);
}
+static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ card->use_hard_stop = 1;
+ __qeth_l2_set_offline(card->gdev, 1);
+ } else
+ __qeth_l2_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l2_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l2_set_online(card->gdev, 1);
+ if (rc) {
+ if (card->dev) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ }
+ } else
+ rc = __qeth_l2_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
struct ccwgroup_driver qeth_l2_ccwgroup_driver = {
.probe = qeth_l2_probe_device,
.remove = qeth_l2_remove_device,
.set_online = qeth_l2_set_online,
.set_offline = qeth_l2_set_offline,
.shutdown = qeth_l2_shutdown,
+ .freeze = qeth_l2_pm_suspend,
+ .thaw = qeth_l2_pm_resume,
+ .restore = qeth_l2_pm_resume,
};
EXPORT_SYMBOL_GPL(qeth_l2_ccwgroup_driver);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 0ba3817cb6a..54872406864 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1,7 +1,7 @@
/*
* drivers/s390/net/qeth_l3_main.c
*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2009
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
@@ -1920,16 +1920,22 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card,
hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
}
- skb->ip_summed = card->options.checksum_type;
- if (card->options.checksum_type == HW_CHECKSUMMING) {
+ switch (card->options.checksum_type) {
+ case SW_CHECKSUMMING:
+ skb->ip_summed = CHECKSUM_NONE;
+ break;
+ case NO_CHECKSUMMING:
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ case HW_CHECKSUMMING:
if ((hdr->hdr.l3.ext_flags &
- (QETH_HDR_EXT_CSUM_HDR_REQ |
- QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
- (QETH_HDR_EXT_CSUM_HDR_REQ |
- QETH_HDR_EXT_CSUM_TRANSP_REQ))
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
- skb->ip_summed = SW_CHECKSUMMING;
+ skb->ip_summed = CHECKSUM_NONE;
}
return vlan_id;
@@ -2543,9 +2549,9 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
/* IPv4 */
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);
memset(hdr->hdr.l3.dest_addr, 0, 12);
- if ((skb->dst) && (skb->dst->neighbour)) {
+ if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) {
*((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
- *((u32 *) skb->dst->neighbour->primary_key);
+ *((u32 *) skb_dst(skb)->neighbour->primary_key);
} else {
/* fill in destination address used in ip header */
*((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
@@ -2556,9 +2562,9 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);
if (card->info.type == QETH_CARD_TYPE_IQD)
hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU;
- if ((skb->dst) && (skb->dst->neighbour)) {
+ if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) {
memcpy(hdr->hdr.l3.dest_addr,
- skb->dst->neighbour->primary_key, 16);
+ skb_dst(skb)->neighbour->primary_key, 16);
} else {
/* fill in destination address used in ip header */
memcpy(hdr->hdr.l3.dest_addr,
@@ -3006,6 +3012,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
card->dev->features |= NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
+ card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
return register_netdev(card->dev);
@@ -3070,6 +3077,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
{
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
+ qeth_set_allowed_threads(card, 0, 1);
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
if (cgdev->state == CCWGROUP_ONLINE) {
@@ -3141,8 +3149,9 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
dev_warn(&card->gdev->dev,
"The LAN is offline\n");
card->lan_online = 0;
+ return 0;
}
- return rc;
+ goto out_remove;
} else
card->lan_online = 1;
qeth_set_large_send(card, card->options.large_send);
@@ -3274,12 +3283,62 @@ static void qeth_l3_shutdown(struct ccwgroup_device *gdev)
qeth_clear_qdio_buffers(card);
}
+static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+
+ if (card->dev)
+ netif_device_detach(card->dev);
+ qeth_set_allowed_threads(card, 0, 1);
+ wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
+ if (gdev->state == CCWGROUP_OFFLINE)
+ return 0;
+ if (card->state == CARD_STATE_UP) {
+ card->use_hard_stop = 1;
+ __qeth_l3_set_offline(card->gdev, 1);
+ } else
+ __qeth_l3_set_offline(card->gdev, 0);
+ return 0;
+}
+
+static int qeth_l3_pm_resume(struct ccwgroup_device *gdev)
+{
+ struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+ int rc = 0;
+
+ if (gdev->state == CCWGROUP_OFFLINE)
+ goto out;
+
+ if (card->state == CARD_STATE_RECOVER) {
+ rc = __qeth_l3_set_online(card->gdev, 1);
+ if (rc) {
+ if (card->dev) {
+ rtnl_lock();
+ dev_close(card->dev);
+ rtnl_unlock();
+ }
+ }
+ } else
+ rc = __qeth_l3_set_online(card->gdev, 0);
+out:
+ qeth_set_allowed_threads(card, 0xffffffff, 0);
+ if (card->dev)
+ netif_device_attach(card->dev);
+ if (rc)
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ return rc;
+}
+
struct ccwgroup_driver qeth_l3_ccwgroup_driver = {
.probe = qeth_l3_probe_device,
.remove = qeth_l3_remove_device,
.set_online = qeth_l3_set_online,
.set_offline = qeth_l3_set_offline,
.shutdown = qeth_l3_shutdown,
+ .freeze = qeth_l3_pm_suspend,
+ .thaw = qeth_l3_pm_resume,
+ .restore = qeth_l3_pm_resume,
};
EXPORT_SYMBOL_GPL(qeth_l3_ccwgroup_driver);
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 164e090c262..e76a320d373 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -1,7 +1,8 @@
/*
* IUCV special message driver
*
- * Copyright 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2003, 2009
+ *
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* This program is free software; you can redistribute it and/or modify
@@ -40,6 +41,8 @@ MODULE_AUTHOR
MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
static struct iucv_path *smsg_path;
+/* dummy device used as trigger for PM functions */
+static struct device *smsg_dev;
static DEFINE_SPINLOCK(smsg_list_lock);
static LIST_HEAD(smsg_list);
@@ -132,14 +135,51 @@ void smsg_unregister_callback(char *prefix,
kfree(cb);
}
+static int smsg_pm_freeze(struct device *dev)
+{
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_freeze\n");
+#endif
+ if (smsg_path)
+ iucv_path_sever(smsg_path, NULL);
+ return 0;
+}
+
+static int smsg_pm_restore_thaw(struct device *dev)
+{
+ int rc;
+
+#ifdef CONFIG_PM_DEBUG
+ printk(KERN_WARNING "smsg_pm_restore_thaw\n");
+#endif
+ if (smsg_path) {
+ memset(smsg_path, 0, sizeof(*smsg_path));
+ smsg_path->msglim = 255;
+ smsg_path->flags = 0;
+ rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
+ NULL, NULL, NULL);
+ printk(KERN_ERR "iucv_path_connect returned with rc %i\n", rc);
+ }
+ return 0;
+}
+
+static struct dev_pm_ops smsg_pm_ops = {
+ .freeze = smsg_pm_freeze,
+ .thaw = smsg_pm_restore_thaw,
+ .restore = smsg_pm_restore_thaw,
+};
+
static struct device_driver smsg_driver = {
+ .owner = THIS_MODULE,
.name = "SMSGIUCV",
.bus = &iucv_bus,
+ .pm = &smsg_pm_ops,
};
static void __exit smsg_exit(void)
{
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
+ device_unregister(smsg_dev);
iucv_unregister(&smsg_handler, 1);
driver_unregister(&smsg_driver);
}
@@ -166,12 +206,29 @@ static int __init smsg_init(void)
rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ",
NULL, NULL, NULL);
if (rc)
- goto out_free;
+ goto out_free_path;
+ smsg_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!smsg_dev) {
+ rc = -ENOMEM;
+ goto out_free_path;
+ }
+ dev_set_name(smsg_dev, "smsg_iucv");
+ smsg_dev->bus = &iucv_bus;
+ smsg_dev->parent = iucv_root;
+ smsg_dev->release = (void (*)(struct device *))kfree;
+ smsg_dev->driver = &smsg_driver;
+ rc = device_register(smsg_dev);
+ if (rc)
+ goto out_free_dev;
+
cpcmd("SET SMSG IUCV", NULL, 0, NULL);
return 0;
-out_free:
+out_free_dev:
+ kfree(smsg_dev);
+out_free_path:
iucv_path_free(smsg_path);
+ smsg_path = NULL;
out_register:
iucv_unregister(&smsg_handler, 1);
out_driver: