diff options
Diffstat (limited to 'drivers/s390/net')
-rw-r--r-- | drivers/s390/net/Kconfig | 9 | ||||
-rw-r--r-- | drivers/s390/net/ctcm_main.c | 40 | ||||
-rw-r--r-- | drivers/s390/net/ism_drv.c | 4 | ||||
-rw-r--r-- | drivers/s390/net/lcs.c | 59 | ||||
-rw-r--r-- | drivers/s390/net/netiucv.c | 104 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 49 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 506 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 25 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_sys.c | 15 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 19 | ||||
-rw-r--r-- | drivers/s390/net/smsgiucv.c | 65 |
12 files changed, 495 insertions, 402 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 3850a0f5f0bc..53120e68796e 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -63,12 +63,9 @@ config QETH prompt "Gigabit Ethernet device support" depends on CCW && NETDEVICES && IP_MULTICAST && QDIO && ETHERNET help - This driver supports the IBM System z OSA Express adapters - in QDIO mode (all media types), HiperSockets interfaces and z/VM - virtual NICs for Guest LAN and VSWITCH. - - For details please refer to the documentation provided by IBM at - <http://www.ibm.com/developerworks/linux/linux390> + This driver supports IBM's OSA Express network adapters in QDIO mode, + HiperSockets interfaces and z/VM virtual NICs for Guest LAN and + VSWITCH. To compile this driver as a module, choose M. The module name is qeth. diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 437a6d822105..d06809eac16d 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1698,43 +1698,6 @@ 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[CTCM_READ]->netdev); - ctcm_close(priv->channel[CTCM_READ]->netdev); - if (!wait_event_timeout(priv->fsm->wait_q, - fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) { - netif_device_attach(priv->channel[CTCM_READ]->netdev); - return -EBUSY; - } - 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[CTCM_READ]->netdev); -err_out: - netif_device_attach(priv->channel[CTCM_READ]->netdev); - return rc; -} - static struct ccw_device_id ctcm_ids[] = { {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel}, {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon}, @@ -1764,9 +1727,6 @@ 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, }; static ssize_t group_store(struct device_driver *ddrv, const char *buf, diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index c75112ee7b97..c7fade836d83 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -521,8 +521,10 @@ static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id) ism->smcd = smcd_alloc_dev(&pdev->dev, dev_name(&pdev->dev), &ism_ops, ISM_NR_DMBS); - if (!ism->smcd) + if (!ism->smcd) { + ret = -ENOMEM; goto err_resource; + } ism->smcd->priv = ism; ret = ism_dev_init(ism); diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 8f08b0a2917c..440219bcaa2b 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -2296,60 +2296,6 @@ 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); -} - static struct ccw_device_id lcs_ids[] = { {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel}, {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216}, @@ -2382,11 +2328,6 @@ 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, }; static ssize_t group_store(struct device_driver *ddrv, const char *buf, diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 5ce2424ca729..260860cf3aa1 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -112,27 +112,10 @@ DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); */ #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 const 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 *, u8 *, u8 *); @@ -213,7 +196,6 @@ struct netiucv_priv { fsm_instance *fsm; struct iucv_connection *conn; struct device *dev; - int pm_state; }; /** @@ -1275,72 +1257,6 @@ 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. @@ -2156,7 +2072,6 @@ 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(); @@ -2182,27 +2097,10 @@ 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: diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index e0b26310ecab..51ea56b73a97 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -11,6 +11,7 @@ #define __QETH_CORE_H__ #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/if.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> @@ -21,8 +22,10 @@ #include <linux/seq_file.h> #include <linux/hashtable.h> #include <linux/ip.h> +#include <linux/rcupdate.h> #include <linux/refcount.h> #include <linux/timer.h> +#include <linux/types.h> #include <linux/wait.h> #include <linux/workqueue.h> @@ -31,6 +34,7 @@ #include <net/ipv6.h> #include <net/if_inet6.h> #include <net/addrconf.h> +#include <net/route.h> #include <net/sch_generic.h> #include <net/tcp.h> @@ -231,11 +235,7 @@ struct qeth_hdr_layer3 { __u16 frame_offset; union { /* TX: */ - struct in6_addr ipv6_addr; - struct ipv4 { - u8 res[12]; - u32 addr; - } ipv4; + struct in6_addr addr; /* RX: */ struct rx { u8 res1[2]; @@ -352,10 +352,15 @@ static inline bool qeth_l3_same_next_hop(struct qeth_hdr_layer3 *h1, struct qeth_hdr_layer3 *h2) { return !((h1->flags ^ h2->flags) & QETH_HDR_IPV6) && - ipv6_addr_equal(&h1->next_hop.ipv6_addr, - &h2->next_hop.ipv6_addr); + ipv6_addr_equal(&h1->next_hop.addr, &h2->next_hop.addr); } +struct qeth_local_addr { + struct hlist_node hnode; + struct rcu_head rcu; + struct in6_addr addr; +}; + enum qeth_qdio_info_states { QETH_QDIO_UNINITIALIZED, QETH_QDIO_ALLOCATED, @@ -688,6 +693,9 @@ struct qeth_card_info { u8 promisc_mode:1; u8 use_v1_blkt:1; u8 is_vm_nic:1; + /* no bitfield, we take a pointer on these two: */ + u8 has_lp2lp_cso_v6; + u8 has_lp2lp_cso_v4; enum qeth_card_types type; enum qeth_link_types link_type; int broadcast_capable; @@ -786,6 +794,7 @@ struct qeth_card { struct qeth_channel data; struct net_device *dev; + struct dentry *debugfs; struct qeth_card_stats stats; struct qeth_card_info info; struct qeth_token token; @@ -797,6 +806,10 @@ struct qeth_card { wait_queue_head_t wait_q; DECLARE_HASHTABLE(mac_htable, 4); DECLARE_HASHTABLE(ip_htable, 4); + DECLARE_HASHTABLE(local_addrs4, 4); + DECLARE_HASHTABLE(local_addrs6, 4); + spinlock_t local_addrs4_lock; + spinlock_t local_addrs6_lock; struct mutex ip_lock; DECLARE_HASHTABLE(ip_mc_htable, 4); struct work_struct rx_mode_work; @@ -928,6 +941,25 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb, int ipv) return dst; } +static inline __be32 qeth_next_hop_v4_rcu(struct sk_buff *skb, + struct dst_entry *dst) +{ + struct rtable *rt = (struct rtable *) dst; + + return (rt) ? rt_nexthop(rt, ip_hdr(skb)->daddr) : ip_hdr(skb)->daddr; +} + +static inline struct in6_addr *qeth_next_hop_v6_rcu(struct sk_buff *skb, + struct dst_entry *dst) +{ + struct rt6_info *rt = (struct rt6_info *) dst; + + if (rt && !ipv6_addr_any(&rt->rt6i_gateway)) + return &rt->rt6i_gateway; + else + return &ipv6_hdr(skb)->daddr; +} + static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv) { *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; @@ -1021,7 +1053,8 @@ struct qeth_cmd_buffer *qeth_get_diag_cmd(struct qeth_card *card, void qeth_notify_cmd(struct qeth_cmd_buffer *iob, int reason); void qeth_put_cmd(struct qeth_cmd_buffer *iob); -void qeth_schedule_recovery(struct qeth_card *); +int qeth_schedule_recovery(struct qeth_card *card); +void qeth_flush_local_addrs(struct qeth_card *card); int qeth_poll(struct napi_struct *napi, int budget); void qeth_clear_ipacmd_list(struct qeth_card *); int qeth_qdio_clear_card(struct qeth_card *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f7689461c242..18a0fb75a710 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -26,6 +26,7 @@ #include <linux/if_vlan.h> #include <linux/netdevice.h> #include <linux/netdev_features.h> +#include <linux/rcutree.h> #include <linux/skbuff.h> #include <linux/vmalloc.h> @@ -60,6 +61,7 @@ EXPORT_SYMBOL_GPL(qeth_core_header_cache); static struct kmem_cache *qeth_qdio_outbuf_cache; static struct device *qeth_core_root_dev; +static struct dentry *qeth_debugfs_root; static struct lock_class_key qdio_out_skb_queue_key; static void qeth_issue_next_read_cb(struct qeth_card *card, @@ -623,6 +625,257 @@ void qeth_notify_cmd(struct qeth_cmd_buffer *iob, int reason) } EXPORT_SYMBOL_GPL(qeth_notify_cmd); +static void qeth_flush_local_addrs4(struct qeth_card *card) +{ + struct qeth_local_addr *addr; + struct hlist_node *tmp; + unsigned int i; + + spin_lock_irq(&card->local_addrs4_lock); + hash_for_each_safe(card->local_addrs4, i, tmp, addr, hnode) { + hash_del_rcu(&addr->hnode); + kfree_rcu(addr, rcu); + } + spin_unlock_irq(&card->local_addrs4_lock); +} + +static void qeth_flush_local_addrs6(struct qeth_card *card) +{ + struct qeth_local_addr *addr; + struct hlist_node *tmp; + unsigned int i; + + spin_lock_irq(&card->local_addrs6_lock); + hash_for_each_safe(card->local_addrs6, i, tmp, addr, hnode) { + hash_del_rcu(&addr->hnode); + kfree_rcu(addr, rcu); + } + spin_unlock_irq(&card->local_addrs6_lock); +} + +void qeth_flush_local_addrs(struct qeth_card *card) +{ + qeth_flush_local_addrs4(card); + qeth_flush_local_addrs6(card); +} +EXPORT_SYMBOL_GPL(qeth_flush_local_addrs); + +static void qeth_add_local_addrs4(struct qeth_card *card, + struct qeth_ipacmd_local_addrs4 *cmd) +{ + unsigned int i; + + if (cmd->addr_length != + sizeof_field(struct qeth_ipacmd_local_addr4, addr)) { + dev_err_ratelimited(&card->gdev->dev, + "Dropped IPv4 ADD LOCAL ADDR event with bad length %u\n", + cmd->addr_length); + return; + } + + spin_lock(&card->local_addrs4_lock); + for (i = 0; i < cmd->count; i++) { + unsigned int key = ipv4_addr_hash(cmd->addrs[i].addr); + struct qeth_local_addr *addr; + bool duplicate = false; + + hash_for_each_possible(card->local_addrs4, addr, hnode, key) { + if (addr->addr.s6_addr32[3] == cmd->addrs[i].addr) { + duplicate = true; + break; + } + } + + if (duplicate) + continue; + + addr = kmalloc(sizeof(*addr), GFP_ATOMIC); + if (!addr) { + dev_err(&card->gdev->dev, + "Failed to allocate local addr object. Traffic to %pI4 might suffer.\n", + &cmd->addrs[i].addr); + continue; + } + + ipv6_addr_set(&addr->addr, 0, 0, 0, cmd->addrs[i].addr); + hash_add_rcu(card->local_addrs4, &addr->hnode, key); + } + spin_unlock(&card->local_addrs4_lock); +} + +static void qeth_add_local_addrs6(struct qeth_card *card, + struct qeth_ipacmd_local_addrs6 *cmd) +{ + unsigned int i; + + if (cmd->addr_length != + sizeof_field(struct qeth_ipacmd_local_addr6, addr)) { + dev_err_ratelimited(&card->gdev->dev, + "Dropped IPv6 ADD LOCAL ADDR event with bad length %u\n", + cmd->addr_length); + return; + } + + spin_lock(&card->local_addrs6_lock); + for (i = 0; i < cmd->count; i++) { + u32 key = ipv6_addr_hash(&cmd->addrs[i].addr); + struct qeth_local_addr *addr; + bool duplicate = false; + + hash_for_each_possible(card->local_addrs6, addr, hnode, key) { + if (ipv6_addr_equal(&addr->addr, &cmd->addrs[i].addr)) { + duplicate = true; + break; + } + } + + if (duplicate) + continue; + + addr = kmalloc(sizeof(*addr), GFP_ATOMIC); + if (!addr) { + dev_err(&card->gdev->dev, + "Failed to allocate local addr object. Traffic to %pI6c might suffer.\n", + &cmd->addrs[i].addr); + continue; + } + + addr->addr = cmd->addrs[i].addr; + hash_add_rcu(card->local_addrs6, &addr->hnode, key); + } + spin_unlock(&card->local_addrs6_lock); +} + +static void qeth_del_local_addrs4(struct qeth_card *card, + struct qeth_ipacmd_local_addrs4 *cmd) +{ + unsigned int i; + + if (cmd->addr_length != + sizeof_field(struct qeth_ipacmd_local_addr4, addr)) { + dev_err_ratelimited(&card->gdev->dev, + "Dropped IPv4 DEL LOCAL ADDR event with bad length %u\n", + cmd->addr_length); + return; + } + + spin_lock(&card->local_addrs4_lock); + for (i = 0; i < cmd->count; i++) { + struct qeth_ipacmd_local_addr4 *addr = &cmd->addrs[i]; + unsigned int key = ipv4_addr_hash(addr->addr); + struct qeth_local_addr *tmp; + + hash_for_each_possible(card->local_addrs4, tmp, hnode, key) { + if (tmp->addr.s6_addr32[3] == addr->addr) { + hash_del_rcu(&tmp->hnode); + kfree_rcu(tmp, rcu); + break; + } + } + } + spin_unlock(&card->local_addrs4_lock); +} + +static void qeth_del_local_addrs6(struct qeth_card *card, + struct qeth_ipacmd_local_addrs6 *cmd) +{ + unsigned int i; + + if (cmd->addr_length != + sizeof_field(struct qeth_ipacmd_local_addr6, addr)) { + dev_err_ratelimited(&card->gdev->dev, + "Dropped IPv6 DEL LOCAL ADDR event with bad length %u\n", + cmd->addr_length); + return; + } + + spin_lock(&card->local_addrs6_lock); + for (i = 0; i < cmd->count; i++) { + struct qeth_ipacmd_local_addr6 *addr = &cmd->addrs[i]; + u32 key = ipv6_addr_hash(&addr->addr); + struct qeth_local_addr *tmp; + + hash_for_each_possible(card->local_addrs6, tmp, hnode, key) { + if (ipv6_addr_equal(&tmp->addr, &addr->addr)) { + hash_del_rcu(&tmp->hnode); + kfree_rcu(tmp, rcu); + break; + } + } + } + spin_unlock(&card->local_addrs6_lock); +} + +static bool qeth_next_hop_is_local_v4(struct qeth_card *card, + struct sk_buff *skb) +{ + struct qeth_local_addr *tmp; + bool is_local = false; + unsigned int key; + __be32 next_hop; + + if (hash_empty(card->local_addrs4)) + return false; + + rcu_read_lock(); + next_hop = qeth_next_hop_v4_rcu(skb, qeth_dst_check_rcu(skb, 4)); + key = ipv4_addr_hash(next_hop); + + hash_for_each_possible_rcu(card->local_addrs4, tmp, hnode, key) { + if (tmp->addr.s6_addr32[3] == next_hop) { + is_local = true; + break; + } + } + rcu_read_unlock(); + + return is_local; +} + +static bool qeth_next_hop_is_local_v6(struct qeth_card *card, + struct sk_buff *skb) +{ + struct qeth_local_addr *tmp; + struct in6_addr *next_hop; + bool is_local = false; + u32 key; + + if (hash_empty(card->local_addrs6)) + return false; + + rcu_read_lock(); + next_hop = qeth_next_hop_v6_rcu(skb, qeth_dst_check_rcu(skb, 6)); + key = ipv6_addr_hash(next_hop); + + hash_for_each_possible_rcu(card->local_addrs6, tmp, hnode, key) { + if (ipv6_addr_equal(&tmp->addr, next_hop)) { + is_local = true; + break; + } + } + rcu_read_unlock(); + + return is_local; +} + +static int qeth_debugfs_local_addr_show(struct seq_file *m, void *v) +{ + struct qeth_card *card = m->private; + struct qeth_local_addr *tmp; + unsigned int i; + + rcu_read_lock(); + hash_for_each_rcu(card->local_addrs4, i, tmp, hnode) + seq_printf(m, "%pI4\n", &tmp->addr.s6_addr32[3]); + hash_for_each_rcu(card->local_addrs6, i, tmp, hnode) + seq_printf(m, "%pI6c\n", &tmp->addr); + rcu_read_unlock(); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(qeth_debugfs_local_addr); + static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, struct qeth_card *card) { @@ -686,9 +939,19 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, case IPA_CMD_MODCCID: return cmd; case IPA_CMD_REGISTER_LOCAL_ADDR: + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + qeth_add_local_addrs4(card, &cmd->data.local_addrs4); + else if (cmd->hdr.prot_version == QETH_PROT_IPV6) + qeth_add_local_addrs6(card, &cmd->data.local_addrs6); + QETH_CARD_TEXT(card, 3, "irla"); return NULL; case IPA_CMD_UNREGISTER_LOCAL_ADDR: + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + qeth_del_local_addrs4(card, &cmd->data.local_addrs4); + else if (cmd->hdr.prot_version == QETH_PROT_IPV6) + qeth_del_local_addrs6(card, &cmd->data.local_addrs6); + QETH_CARD_TEXT(card, 3, "urla"); return NULL; default: @@ -868,16 +1131,18 @@ static int qeth_set_thread_start_bit(struct qeth_card *card, unsigned long thread) { unsigned long flags; + int rc = 0; spin_lock_irqsave(&card->thread_mask_lock, flags); - if (!(card->thread_allowed_mask & thread) || - (card->thread_start_mask & thread)) { - spin_unlock_irqrestore(&card->thread_mask_lock, flags); - return -EPERM; - } - card->thread_start_mask |= thread; + if (!(card->thread_allowed_mask & thread)) + rc = -EPERM; + else if (card->thread_start_mask & thread) + rc = -EBUSY; + else + card->thread_start_mask |= thread; spin_unlock_irqrestore(&card->thread_mask_lock, flags); - return 0; + + return rc; } static void qeth_clear_thread_start_bit(struct qeth_card *card, @@ -930,11 +1195,17 @@ static int qeth_do_run_thread(struct qeth_card *card, unsigned long thread) return rc; } -void qeth_schedule_recovery(struct qeth_card *card) +int qeth_schedule_recovery(struct qeth_card *card) { + int rc; + QETH_CARD_TEXT(card, 2, "startrec"); - if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0) + + rc = qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD); + if (!rc) schedule_work(&card->kernel_thread_starter); + + return rc; } static int qeth_get_problem(struct qeth_card *card, struct ccw_device *cdev, @@ -1376,6 +1647,10 @@ static void qeth_setup_card(struct qeth_card *card) qeth_init_qdio_info(card); INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work); INIT_WORK(&card->close_dev_work, qeth_close_dev_handler); + hash_init(card->local_addrs4); + hash_init(card->local_addrs6); + spin_lock_init(&card->local_addrs4_lock); + spin_lock_init(&card->local_addrs6_lock); } static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr) @@ -1412,6 +1687,11 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev) if (!card->read_cmd) goto out_read_cmd; + card->debugfs = debugfs_create_dir(dev_name(&gdev->dev), + qeth_debugfs_root); + debugfs_create_file("local_addrs", 0400, card->debugfs, card, + &qeth_debugfs_local_addr_fops); + card->qeth_service_level.seq_print = qeth_core_sl_print; register_service_level(&card->qeth_service_level); return card; @@ -3345,11 +3625,11 @@ static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue) static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, int count) { + struct qeth_qdio_out_buffer *buf = queue->bufs[index]; + unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT; struct qeth_card *card = queue->card; - struct qeth_qdio_out_buffer *buf; int rc; int i; - unsigned int qdio_flags; for (i = index; i < index + count; ++i) { unsigned int bidx = QDIO_BUFNR(i); @@ -3366,9 +3646,10 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, if (IS_IQD(card)) { skb_queue_walk(&buf->skb_list, skb) skb_tx_timestamp(skb); - continue; } + } + if (!IS_IQD(card)) { if (!queue->do_pack) { if ((atomic_read(&queue->used_buffers) >= (QETH_HIGH_WATERMARK_PACK - @@ -3393,12 +3674,12 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ; } } + + if (atomic_read(&queue->set_pci_flags_count)) + qdio_flags |= QDIO_FLAG_PCI_OUT; } QETH_TXQ_STAT_INC(queue, doorbell); - qdio_flags = QDIO_FLAG_SYNC_OUTPUT; - if (atomic_read(&queue->set_pci_flags_count)) - qdio_flags |= QDIO_FLAG_PCI_OUT; rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, queue->queue_no, index, count); @@ -3809,15 +4090,47 @@ static bool qeth_iqd_may_bulk(struct qeth_qdio_out_q *queue, qeth_l3_iqd_same_vlan(&prev_hdr->hdr.l3, &curr_hdr->hdr.l3); } -static unsigned int __qeth_fill_buffer(struct sk_buff *skb, - struct qeth_qdio_out_buffer *buf, - bool is_first_elem, unsigned int offset) +/** + * qeth_fill_buffer() - map skb into an output buffer + * @buf: buffer to transport the skb + * @skb: skb to map into the buffer + * @hdr: qeth_hdr for this skb. Either at skb->data, or allocated + * from qeth_core_header_cache. + * @offset: when mapping the skb, start at skb->data + offset + * @hd_len: if > 0, build a dedicated header element of this size + */ +static unsigned int qeth_fill_buffer(struct qeth_qdio_out_buffer *buf, + struct sk_buff *skb, struct qeth_hdr *hdr, + unsigned int offset, unsigned int hd_len) { struct qdio_buffer *buffer = buf->buffer; int element = buf->next_element_to_fill; int length = skb_headlen(skb) - offset; char *data = skb->data + offset; unsigned int elem_length, cnt; + bool is_first_elem = true; + + __skb_queue_tail(&buf->skb_list, skb); + + /* build dedicated element for HW Header */ + if (hd_len) { + is_first_elem = false; + + buffer->element[element].addr = virt_to_phys(hdr); + buffer->element[element].length = hd_len; + buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; + + /* HW header is allocated from cache: */ + if ((void *)hdr != skb->data) + buf->is_header[element] = 1; + /* HW header was pushed and is contiguous with linear part: */ + else if (length > 0 && !PAGE_ALIGNED(data) && + (data == (char *)hdr + hd_len)) + buffer->element[element].eflags |= + SBAL_EFLAGS_CONTIGUOUS; + + element++; + } /* map linear part into buffer element(s) */ while (length > 0) { @@ -3871,40 +4184,6 @@ static unsigned int __qeth_fill_buffer(struct sk_buff *skb, return element; } -/** - * qeth_fill_buffer() - map skb into an output buffer - * @buf: buffer to transport the skb - * @skb: skb to map into the buffer - * @hdr: qeth_hdr for this skb. Either at skb->data, or allocated - * from qeth_core_header_cache. - * @offset: when mapping the skb, start at skb->data + offset - * @hd_len: if > 0, build a dedicated header element of this size - */ -static unsigned int qeth_fill_buffer(struct qeth_qdio_out_buffer *buf, - struct sk_buff *skb, struct qeth_hdr *hdr, - unsigned int offset, unsigned int hd_len) -{ - struct qdio_buffer *buffer = buf->buffer; - bool is_first_elem = true; - - __skb_queue_tail(&buf->skb_list, skb); - - /* build dedicated header element */ - if (hd_len) { - int element = buf->next_element_to_fill; - is_first_elem = false; - - buffer->element[element].addr = virt_to_phys(hdr); - buffer->element[element].length = hd_len; - buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG; - /* remember to free cache-allocated qeth_hdr: */ - buf->is_header[element] = ((void *)hdr != skb->data); - buf->next_element_to_fill++; - } - - return __qeth_fill_buffer(skb, buf, is_first_elem, offset); -} - static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue, struct sk_buff *skb, unsigned int elements, struct qeth_hdr *hdr, unsigned int offset, @@ -4889,9 +5168,11 @@ out_free_nothing: static void qeth_core_free_card(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "freecrd"); + + unregister_service_level(&card->qeth_service_level); + debugfs_remove_recursive(card->debugfs); qeth_put_cmd(card->read_cmd); destroy_workqueue(card->event_wq); - unregister_service_level(&card->qeth_service_level); dev_set_drvdata(&card->gdev->dev, NULL); kfree(card); } @@ -6153,32 +6434,6 @@ static void qeth_core_shutdown(struct ccwgroup_device *gdev) qdio_free(CARD_DDEV(card)); } -static int qeth_suspend(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->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; - - qeth_set_offline(card, false); - return 0; -} - -static int qeth_resume(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - int rc; - - rc = qeth_set_online(card); - - qeth_set_allowed_threads(card, 0xffffffff, 0); - if (rc) - dev_warn(&card->gdev->dev, "The qeth device driver failed to recover an error on the device\n"); - return rc; -} - static ssize_t group_store(struct device_driver *ddrv, const char *buf, size_t count) { @@ -6215,11 +6470,6 @@ 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 = NULL, - .complete = NULL, - .freeze = qeth_suspend, - .thaw = qeth_resume, - .restore = qeth_resume, }; struct qeth_card *qeth_get_card_by_busid(char *bus_id) @@ -6300,7 +6550,7 @@ static int qeth_set_csum_off(struct qeth_card *card, enum qeth_ipa_funcs cstype, } static int qeth_set_csum_on(struct qeth_card *card, enum qeth_ipa_funcs cstype, - enum qeth_prot_versions prot) + enum qeth_prot_versions prot, u8 *lp2lp) { u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP; struct qeth_cmd_buffer *iob; @@ -6352,18 +6602,17 @@ static int qeth_set_csum_on(struct qeth_card *card, enum qeth_ipa_funcs cstype, dev_info(&card->gdev->dev, "HW Checksumming (%sbound IPv%d) enabled\n", cstype == IPA_INBOUND_CHECKSUM ? "in" : "out", prot); - if (!qeth_ipa_caps_enabled(&caps, QETH_IPA_CHECKSUM_LP2LP) && - cstype == IPA_OUTBOUND_CHECKSUM) - dev_warn(&card->gdev->dev, - "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n", - QETH_CARD_IFNAME(card)); + + if (lp2lp) + *lp2lp = qeth_ipa_caps_enabled(&caps, QETH_IPA_CHECKSUM_LP2LP); + return 0; } static int qeth_set_ipa_csum(struct qeth_card *card, bool on, int cstype, - enum qeth_prot_versions prot) + enum qeth_prot_versions prot, u8 *lp2lp) { - return on ? qeth_set_csum_on(card, cstype, prot) : + return on ? qeth_set_csum_on(card, cstype, prot, lp2lp) : qeth_set_csum_off(card, cstype, prot); } @@ -6451,13 +6700,13 @@ static int qeth_set_ipa_rx_csum(struct qeth_card *card, bool on) if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) rc_ipv4 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, - QETH_PROT_IPV4); + QETH_PROT_IPV4, NULL); if (!qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6)) /* no/one Offload Assist available, so the rc is trivial */ return rc_ipv4; rc_ipv6 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM, - QETH_PROT_IPV6); + QETH_PROT_IPV6, NULL); if (on) /* enable: success if any Assist is active */ @@ -6493,6 +6742,24 @@ void qeth_enable_hw_features(struct net_device *dev) } EXPORT_SYMBOL_GPL(qeth_enable_hw_features); +static void qeth_check_restricted_features(struct qeth_card *card, + netdev_features_t changed, + netdev_features_t actual) +{ + netdev_features_t ipv6_features = NETIF_F_TSO6; + netdev_features_t ipv4_features = NETIF_F_TSO; + + if (!card->info.has_lp2lp_cso_v6) + ipv6_features |= NETIF_F_IPV6_CSUM; + if (!card->info.has_lp2lp_cso_v4) + ipv4_features |= NETIF_F_IP_CSUM; + + if ((changed & ipv6_features) && !(actual & ipv6_features)) + qeth_flush_local_addrs6(card); + if ((changed & ipv4_features) && !(actual & ipv4_features)) + qeth_flush_local_addrs4(card); +} + int qeth_set_features(struct net_device *dev, netdev_features_t features) { struct qeth_card *card = dev->ml_priv; @@ -6504,13 +6771,15 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features) if ((changed & NETIF_F_IP_CSUM)) { rc = qeth_set_ipa_csum(card, features & NETIF_F_IP_CSUM, - IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4); + IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4, + &card->info.has_lp2lp_cso_v4); if (rc) changed ^= NETIF_F_IP_CSUM; } if (changed & NETIF_F_IPV6_CSUM) { rc = qeth_set_ipa_csum(card, features & NETIF_F_IPV6_CSUM, - IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6); + IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6, + &card->info.has_lp2lp_cso_v6); if (rc) changed ^= NETIF_F_IPV6_CSUM; } @@ -6532,6 +6801,9 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features) changed ^= NETIF_F_TSO6; } + qeth_check_restricted_features(card, dev->features ^ features, + dev->features ^ changed); + /* everything changed successfully? */ if ((dev->features ^ features) == changed) return 0; @@ -6568,6 +6840,34 @@ netdev_features_t qeth_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { + /* Traffic with local next-hop is not eligible for some offloads: */ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + struct qeth_card *card = dev->ml_priv; + netdev_features_t restricted = 0; + + if (skb_is_gso(skb) && !netif_needs_gso(skb, features)) + restricted |= NETIF_F_ALL_TSO; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + if (!card->info.has_lp2lp_cso_v4) + restricted |= NETIF_F_IP_CSUM; + + if (restricted && qeth_next_hop_is_local_v4(card, skb)) + features &= ~restricted; + break; + case htons(ETH_P_IPV6): + if (!card->info.has_lp2lp_cso_v6) + restricted |= NETIF_F_IPV6_CSUM; + + if (restricted && qeth_next_hop_is_local_v6(card, skb)) + features &= ~restricted; + break; + default: + break; + } + } + /* GSO segmentation builds skbs with * a (small) linear part for the headers, and * page frags for the data. @@ -6717,17 +7017,17 @@ int qeth_stop(struct net_device *dev) unsigned int i; /* Quiesce the NAPI instances: */ - qeth_for_each_output_queue(card, queue, i) { + qeth_for_each_output_queue(card, queue, i) napi_disable(&queue->napi); - del_timer_sync(&queue->timer); - } /* Stop .ndo_start_xmit, might still access queue->napi. */ netif_tx_disable(dev); - /* Queues may get re-allocated, so remove the NAPIs here. */ - qeth_for_each_output_queue(card, queue, i) + qeth_for_each_output_queue(card, queue, i) { + del_timer_sync(&queue->timer); + /* Queues may get re-allocated, so remove the NAPIs. */ netif_napi_del(&queue->napi); + } } else { netif_tx_disable(dev); } @@ -6745,6 +7045,8 @@ static int __init qeth_core_init(void) pr_info("loading core functions\n"); + qeth_debugfs_root = debugfs_create_dir("qeth", NULL); + rc = qeth_register_dbf_views(); if (rc) goto dbf_err; @@ -6786,6 +7088,7 @@ slab_err: register_err: qeth_unregister_dbf_views(); dbf_err: + debugfs_remove_recursive(qeth_debugfs_root); pr_err("Initializing the qeth device driver failed\n"); return rc; } @@ -6799,6 +7102,7 @@ static void __exit qeth_core_exit(void) kmem_cache_destroy(qeth_core_header_cache); root_device_unregister(qeth_core_root_dev); qeth_unregister_dbf_views(); + debugfs_remove_recursive(qeth_debugfs_root); pr_info("core functions removed\n"); } diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index d89a04bfd8b0..9d6f39d8f9ab 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -772,6 +772,29 @@ struct qeth_ipacmd_addr_change { struct qeth_ipacmd_addr_change_entry entry[]; } __packed; +/* [UN]REGISTER_LOCAL_ADDRESS notifications */ +struct qeth_ipacmd_local_addr4 { + __be32 addr; + u32 flags; +}; + +struct qeth_ipacmd_local_addrs4 { + u32 count; + u32 addr_length; + struct qeth_ipacmd_local_addr4 addrs[]; +}; + +struct qeth_ipacmd_local_addr6 { + struct in6_addr addr; + u32 flags; +}; + +struct qeth_ipacmd_local_addrs6 { + u32 count; + u32 addr_length; + struct qeth_ipacmd_local_addr6 addrs[]; +}; + /* Header for each IPA command */ struct qeth_ipacmd_hdr { __u8 command; @@ -803,6 +826,8 @@ struct qeth_ipa_cmd { struct qeth_ipacmd_setbridgeport sbp; struct qeth_ipacmd_addr_change addrchange; struct qeth_ipacmd_vnicc vnicc; + struct qeth_ipacmd_local_addrs4 local_addrs4; + struct qeth_ipacmd_local_addrs6 local_addrs6; } data; } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index d7e429f6631e..c901c942fed7 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -275,17 +275,20 @@ static ssize_t qeth_dev_recover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - char *tmp; - int i; + bool reset; + int rc; + + rc = kstrtobool(buf, &reset); + if (rc) + return rc; if (!qeth_card_hw_is_reachable(card)) return -EPERM; - i = simple_strtoul(buf, &tmp, 16); - if (i == 1) - qeth_schedule_recovery(card); + if (reset) + rc = qeth_schedule_recovery(card); - return count; + return rc ? rc : count; } static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index f60b865c73ad..2d3bca3c0141 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -292,6 +292,7 @@ static void qeth_l2_stop_card(struct qeth_card *card) qeth_qdio_clear_card(card, 0); qeth_clear_working_pool_list(card); flush_workqueue(card->event_wq); + qeth_flush_local_addrs(card); card->info.promisc_mode = 0; } @@ -776,6 +777,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) { card->dev->needed_headroom = sizeof(struct qeth_hdr_tso); + netif_keep_dst(card->dev); netif_set_gso_max_size(card->dev, PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)); } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 0742a749d26e..1e50aa0297a3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1176,6 +1176,7 @@ static void qeth_l3_stop_card(struct qeth_card *card) qeth_qdio_clear_card(card, 0); qeth_clear_working_pool_list(card); flush_workqueue(card->event_wq); + qeth_flush_local_addrs(card); card->info.promisc_mode = 0; } @@ -1693,8 +1694,8 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue, if (skb->protocol == htons(ETH_P_AF_IUCV)) { l3_hdr->flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; - l3_hdr->next_hop.ipv6_addr.s6_addr16[0] = htons(0xfe80); - memcpy(&l3_hdr->next_hop.ipv6_addr.s6_addr32[2], + l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80); + memcpy(&l3_hdr->next_hop.addr.s6_addr32[2], iucv_trans_hdr(skb)->destUserID, 8); return; } @@ -1728,18 +1729,10 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue, l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type); if (ipv == 4) { - struct rtable *rt = (struct rtable *) dst; - - *((__be32 *) &hdr->hdr.l3.next_hop.ipv4.addr) = (rt) ? - rt_nexthop(rt, ip_hdr(skb)->daddr) : - ip_hdr(skb)->daddr; + l3_hdr->next_hop.addr.s6_addr32[3] = + qeth_next_hop_v4_rcu(skb, dst); } else if (ipv == 6) { - struct rt6_info *rt = (struct rt6_info *) dst; - - if (rt && !ipv6_addr_any(&rt->rt6i_gateway)) - l3_hdr->next_hop.ipv6_addr = rt->rt6i_gateway; - else - l3_hdr->next_hop.ipv6_addr = ipv6_hdr(skb)->daddr; + l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst); hdr->hdr.l3.flags |= QETH_HDR_IPV6; if (!IS_IQD(card)) diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 066b5c3aaae6..c84ec2fbf99b 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -29,12 +29,9 @@ 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); -static int iucv_path_connected; static int smsg_path_pending(struct iucv_path *, u8 *, u8 *); static void smsg_message_pending(struct iucv_path *, struct iucv_message *); @@ -124,60 +121,15 @@ void smsg_unregister_callback(const 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_connected) { - iucv_path_sever(smsg_path, NULL); - iucv_path_connected = 0; - } - 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 && !iucv_path_connected) { - 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); -#ifdef CONFIG_PM_DEBUG - if (rc) - printk(KERN_ERR - "iucv_path_connect returned with rc %i\n", rc); -#endif - if (!rc) - iucv_path_connected = 1; - cpcmd("SET SMSG IUCV", NULL, 0, NULL); - } - return 0; -} - -static const 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_DRV_NAME, .bus = &iucv_bus, - .pm = &smsg_pm_ops, }; static void __exit smsg_exit(void) { cpcmd("SET SMSG OFF", NULL, 0, NULL); - device_unregister(smsg_dev); iucv_unregister(&smsg_handler, 1); driver_unregister(&smsg_driver); } @@ -205,27 +157,10 @@ static int __init smsg_init(void) NULL, NULL, NULL); if (rc) goto out_free_path; - else - iucv_path_connected = 1; - 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_put; cpcmd("SET SMSG IUCV", NULL, 0, NULL); return 0; -out_put: - put_device(smsg_dev); out_free_path: iucv_path_free(smsg_path); smsg_path = NULL; |