Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu
[linux-2.6.git] / drivers / s390 / net / netiucv.c
index aec9e5d..98c04ca 100644 (file)
@@ -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:
@@ -109,11 +113,9 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
 #define IUCV_DBF_TEXT_(name, level, text...) \
        do { \
                if (iucv_dbf_passes(iucv_dbf_##name, level)) { \
-                       char* iucv_dbf_txt_buf = \
-                                       get_cpu_var(iucv_dbf_txt_buf); \
-                       sprintf(iucv_dbf_txt_buf, text); \
-                       debug_text_event(iucv_dbf_##name, level, \
-                                               iucv_dbf_txt_buf); \
+                       char* __buf = get_cpu_var(iucv_dbf_txt_buf); \
+                       sprintf(__buf, text); \
+                       debug_text_event(iucv_dbf_##name, level, __buf); \
                        put_cpu_var(iucv_dbf_txt_buf); \
                } \
        } while (0)
@@ -149,10 +151,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 +252,7 @@ struct netiucv_priv {
        fsm_instance            *fsm;
         struct iucv_connection  *conn;
        struct device           *dev;
+       int                      pm_state;
 };
 
 /**
@@ -654,7 +674,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn,
                 * we must use netif_rx_ni() instead of netif_rx()
                 */
                netif_rx_ni(skb);
-               dev->last_rx = jiffies;
                skb_pull(pskb, header->next);
                skb_put(pskb, NETIUCV_HDRLEN);
        }
@@ -720,13 +739,13 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
        if (single_flag) {
                if ((skb = skb_dequeue(&conn->commit_queue))) {
                        atomic_dec(&skb->users);
-                       dev_kfree_skb_any(skb);
                        if (privptr) {
                                privptr->stats.tx_packets++;
                                privptr->stats.tx_bytes +=
                                        (skb->len - NETIUCV_HDRLEN
-                                                 - NETIUCV_HDRLEN);
+                                                 - NETIUCV_HDRLEN);
                        }
+                       dev_kfree_skb_any(skb);
                }
        }
        conn->tx_buff->data = conn->tx_buff->head;
@@ -1265,6 +1284,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_get_drvdata(dev);
+       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_get_drvdata(dev);
+       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.
@@ -1288,14 +1373,14 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
        if (skb == NULL) {
                IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
                privptr->stats.tx_dropped++;
-               return 0;
+               return NETDEV_TX_OK;
        }
        if (skb_headroom(skb) < NETIUCV_HDRLEN) {
                IUCV_DBF_TEXT(data, 2,
                        "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
                dev_kfree_skb(skb);
                privptr->stats.tx_dropped++;
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        /**
@@ -1307,7 +1392,7 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev)
                privptr->stats.tx_dropped++;
                privptr->stats.tx_errors++;
                privptr->stats.tx_carrier_errors++;
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        if (netiucv_test_and_set_busy(dev)) {
@@ -1364,7 +1449,7 @@ static int netiucv_change_mtu(struct net_device * dev, int new_mtu)
 static ssize_t user_show(struct device *dev, struct device_attribute *attr,
                         char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
@@ -1373,7 +1458,7 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr,
 static ssize_t user_write(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
        struct net_device *ndev = priv->conn->netdev;
        char    *p;
        char    *tmp;
@@ -1430,7 +1515,8 @@ static DEVICE_ATTR(user, 0644, user_show, user_write);
 
 static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
                            char *buf)
-{      struct netiucv_priv *priv = dev->driver_data;
+{
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%d\n", priv->conn->max_buffsize);
@@ -1439,7 +1525,7 @@ static ssize_t buffer_show (struct device *dev, struct device_attribute *attr,
 static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
        struct net_device *ndev = priv->conn->netdev;
        char         *e;
        int          bs1;
@@ -1487,7 +1573,7 @@ static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
 static ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm));
@@ -1498,7 +1584,7 @@ static DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL);
 static ssize_t conn_fsm_show (struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm));
@@ -1509,7 +1595,7 @@ static DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL);
 static ssize_t maxmulti_show (struct device *dev,
                              struct device_attribute *attr, char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti);
@@ -1519,7 +1605,7 @@ static ssize_t maxmulti_write (struct device *dev,
                               struct device_attribute *attr,
                               const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.maxmulti = 0;
@@ -1531,7 +1617,7 @@ static DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write);
 static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue);
@@ -1540,7 +1626,7 @@ static ssize_t maxcq_show (struct device *dev, struct device_attribute *attr,
 static ssize_t maxcq_write (struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.maxcqueue = 0;
@@ -1552,7 +1638,7 @@ static DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write);
 static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.doios_single);
@@ -1561,7 +1647,7 @@ static ssize_t sdoio_show (struct device *dev, struct device_attribute *attr,
 static ssize_t sdoio_write (struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.doios_single = 0;
@@ -1573,7 +1659,7 @@ static DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write);
 static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi);
@@ -1582,7 +1668,7 @@ static ssize_t mdoio_show (struct device *dev, struct device_attribute *attr,
 static ssize_t mdoio_write (struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        priv->conn->prof.doios_multi = 0;
@@ -1594,7 +1680,7 @@ static DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write);
 static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.txlen);
@@ -1603,7 +1689,7 @@ static ssize_t txlen_show (struct device *dev, struct device_attribute *attr,
 static ssize_t txlen_write (struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.txlen = 0;
@@ -1615,7 +1701,7 @@ static DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write);
 static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);
@@ -1624,7 +1710,7 @@ static ssize_t txtime_show (struct device *dev, struct device_attribute *attr,
 static ssize_t txtime_write (struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.tx_time = 0;
@@ -1636,7 +1722,7 @@ static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);
 static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);
@@ -1645,7 +1731,7 @@ static ssize_t txpend_show (struct device *dev, struct device_attribute *attr,
 static ssize_t txpend_write (struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.tx_pending = 0;
@@ -1657,7 +1743,7 @@ static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);
 static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
                            char *buf)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 5, __func__);
        return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);
@@ -1666,7 +1752,7 @@ static ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr,
 static ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       struct netiucv_priv *priv = dev->driver_data;
+       struct netiucv_priv *priv = dev_get_drvdata(dev);
 
        IUCV_DBF_TEXT(trace, 4, __func__);
        priv->conn->prof.tx_max_pending = 0;
@@ -1731,7 +1817,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) {
@@ -1751,14 +1836,15 @@ static int netiucv_register_device(struct net_device *ndev)
                return -ENOMEM;
 
        ret = device_register(dev);
-
-       if (ret)
+       if (ret) {
+               put_device(dev);
                return ret;
+       }
        ret = netiucv_add_files(dev);
        if (ret)
                goto out_unreg;
        priv->dev = dev;
-       dev->driver_data = priv;
+       dev_set_drvdata(dev, priv);
        return 0;
 
 out_unreg:
@@ -2025,7 +2111,7 @@ static ssize_t remove_write (struct device_driver *drv,
        IUCV_DBF_TEXT(trace, 3, __func__);
 
         if (count >= IFNAMSIZ)
-                count = IFNAMSIZ - 1;;
+                count = IFNAMSIZ - 1;
 
        for (i = 0, p = buf; i < count && *p; i++, p++) {
                if (*p == '\n' || *p == ' ')
@@ -2071,7 +2157,7 @@ static struct attribute_group netiucv_drv_attr_group = {
        .attrs = netiucv_drv_attrs,
 };
 
-static struct attribute_group *netiucv_drv_attr_groups[] = {
+static const struct attribute_group *netiucv_drv_attr_groups[] = {
        &netiucv_drv_attr_group,
        NULL,
 };
@@ -2100,6 +2186,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 +2212,27 @@ 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) {
+               put_device(netiucv_dev);
+               goto out_driver;
+       }
        netiucv_banner();
        return rc;
 
+out_driver:
+       driver_unregister(&netiucv_driver);
 out_iucv:
        iucv_unregister(&netiucv_handler, 1);
 out_dbf: