diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/Kconfig | 10 | ||||
-rw-r--r-- | drivers/s390/net/Makefile | 3 | ||||
-rw-r--r-- | drivers/s390/net/ism.h | 221 | ||||
-rw-r--r-- | drivers/s390/net/ism_drv.c | 623 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 33 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 414 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 3 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_sys.c | 18 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2.h | 5 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 196 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 396 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_sys.c | 6 |
12 files changed, 1387 insertions, 541 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index c7e484f70654..7c5a25ddf832 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -95,4 +95,14 @@ config CCWGROUP tristate default (LCS || CTCM || QETH) +config ISM + tristate "Support for ISM vPCI Adapter" + depends on PCI && SMC + default n + help + Select this option if you want to use the Internal Shared Memory + vPCI Adapter. + + To compile as a module choose M. The module name is ism. + If unsure, choose N. endmenu diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 513b7ae64980..f2d6bbe57a6f 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -15,3 +15,6 @@ qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o obj-$(CONFIG_QETH_L2) += qeth_l2.o qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o obj-$(CONFIG_QETH_L3) += qeth_l3.o + +ism-y := ism_drv.o +obj-$(CONFIG_ISM) += ism.o diff --git a/drivers/s390/net/ism.h b/drivers/s390/net/ism.h new file mode 100644 index 000000000000..0aab90817326 --- /dev/null +++ b/drivers/s390/net/ism.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef S390_ISM_H +#define S390_ISM_H + +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <net/smc.h> + +#define UTIL_STR_LEN 16 + +/* + * Do not use the first word of the DMB bits to ensure 8 byte aligned access. + */ +#define ISM_DMB_WORD_OFFSET 1 +#define ISM_DMB_BIT_OFFSET (ISM_DMB_WORD_OFFSET * 32) +#define ISM_NR_DMBS 1920 + +#define ISM_REG_SBA 0x1 +#define ISM_REG_IEQ 0x2 +#define ISM_READ_GID 0x3 +#define ISM_ADD_VLAN_ID 0x4 +#define ISM_DEL_VLAN_ID 0x5 +#define ISM_SET_VLAN 0x6 +#define ISM_RESET_VLAN 0x7 +#define ISM_QUERY_INFO 0x8 +#define ISM_QUERY_RGID 0x9 +#define ISM_REG_DMB 0xA +#define ISM_UNREG_DMB 0xB +#define ISM_SIGNAL_IEQ 0xE +#define ISM_UNREG_SBA 0x11 +#define ISM_UNREG_IEQ 0x12 + +#define ISM_ERROR 0xFFFF + +struct ism_req_hdr { + u32 cmd; + u16 : 16; + u16 len; +}; + +struct ism_resp_hdr { + u32 cmd; + u16 ret; + u16 len; +}; + +union ism_reg_sba { + struct { + struct ism_req_hdr hdr; + u64 sba; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(16); + +union ism_reg_ieq { + struct { + struct ism_req_hdr hdr; + u64 ieq; + u64 len; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(16); + +union ism_read_gid { + struct { + struct ism_req_hdr hdr; + } request; + struct { + struct ism_resp_hdr hdr; + u64 gid; + } response; +} __aligned(16); + +union ism_qi { + struct { + struct ism_req_hdr hdr; + } request; + struct { + struct ism_resp_hdr hdr; + u32 version; + u32 max_len; + u64 ism_state; + u64 my_gid; + u64 sba; + u64 ieq; + u32 ieq_len; + u32 : 32; + u32 dmbs_owned; + u32 dmbs_used; + u32 vlan_required; + u32 vlan_nr_ids; + u16 vlan_id[64]; + } response; +} __aligned(64); + +union ism_query_rgid { + struct { + struct ism_req_hdr hdr; + u64 rgid; + u32 vlan_valid; + u32 vlan_id; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(16); + +union ism_reg_dmb { + struct { + struct ism_req_hdr hdr; + u64 dmb; + u32 dmb_len; + u32 sba_idx; + u32 vlan_valid; + u32 vlan_id; + u64 rgid; + } request; + struct { + struct ism_resp_hdr hdr; + u64 dmb_tok; + } response; +} __aligned(32); + +union ism_sig_ieq { + struct { + struct ism_req_hdr hdr; + u64 rgid; + u32 trigger_irq; + u32 event_code; + u64 info; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(32); + +union ism_unreg_dmb { + struct { + struct ism_req_hdr hdr; + u64 dmb_tok; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(16); + +union ism_cmd_simple { + struct { + struct ism_req_hdr hdr; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(8); + +union ism_set_vlan_id { + struct { + struct ism_req_hdr hdr; + u64 vlan_id; + } request; + struct { + struct ism_resp_hdr hdr; + } response; +} __aligned(16); + +struct ism_eq_header { + u64 idx; + u64 ieq_len; + u64 entry_len; + u64 : 64; +}; + +struct ism_eq { + struct ism_eq_header header; + struct smcd_event entry[15]; +}; + +struct ism_sba { + u32 s : 1; /* summary bit */ + u32 e : 1; /* event bit */ + u32 : 30; + u32 dmb_bits[ISM_NR_DMBS / 32]; + u32 reserved[3]; + u16 dmbe_mask[ISM_NR_DMBS]; +}; + +struct ism_dev { + spinlock_t lock; + struct pci_dev *pdev; + struct smcd_dev *smcd; + + void __iomem *ctl; + + struct ism_sba *sba; + dma_addr_t sba_dma_addr; + DECLARE_BITMAP(sba_bitmap, ISM_NR_DMBS); + + struct ism_eq *ieq; + dma_addr_t ieq_dma_addr; + + int ieq_idx; +}; + +#define ISM_CREATE_REQ(dmb, idx, sf, offset) \ + ((dmb) | (idx) << 24 | (sf) << 23 | (offset)) + +static inline int __ism_move(struct ism_dev *ism, u64 dmb_req, void *data, + unsigned int size) +{ + struct zpci_dev *zdev = to_zpci(ism->pdev); + u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, size); + + return zpci_write_block(req, data, dmb_req); +} + +#endif /* S390_ISM_H */ diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c new file mode 100644 index 000000000000..c0631895154e --- /dev/null +++ b/drivers/s390/net/ism_drv.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ISM driver for s390. + * + * Copyright IBM Corp. 2018 + */ +#define KMSG_COMPONENT "ism" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/err.h> +#include <net/smc.h> + +#include <asm/debug.h> + +#include "ism.h" + +MODULE_DESCRIPTION("ISM driver for s390"); +MODULE_LICENSE("GPL"); + +#define PCI_DEVICE_ID_IBM_ISM 0x04ED +#define DRV_NAME "ism" + +static const struct pci_device_id ism_device_table[] = { + { PCI_VDEVICE(IBM, PCI_DEVICE_ID_IBM_ISM), 0 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ism_device_table); + +static debug_info_t *ism_debug_info; + +static int ism_cmd(struct ism_dev *ism, void *cmd) +{ + struct ism_req_hdr *req = cmd; + struct ism_resp_hdr *resp = cmd; + + memcpy_toio(ism->ctl + sizeof(*req), req + 1, req->len - sizeof(*req)); + memcpy_toio(ism->ctl, req, sizeof(*req)); + + WRITE_ONCE(resp->ret, ISM_ERROR); + + memcpy_fromio(resp, ism->ctl, sizeof(*resp)); + if (resp->ret) { + debug_text_event(ism_debug_info, 0, "cmd failure"); + debug_event(ism_debug_info, 0, resp, sizeof(*resp)); + goto out; + } + memcpy_fromio(resp + 1, ism->ctl + sizeof(*resp), + resp->len - sizeof(*resp)); +out: + return resp->ret; +} + +static int ism_cmd_simple(struct ism_dev *ism, u32 cmd_code) +{ + union ism_cmd_simple cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = cmd_code; + cmd.request.hdr.len = sizeof(cmd.request); + + return ism_cmd(ism, &cmd); +} + +static int query_info(struct ism_dev *ism) +{ + union ism_qi cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_QUERY_INFO; + cmd.request.hdr.len = sizeof(cmd.request); + + if (ism_cmd(ism, &cmd)) + goto out; + + debug_text_event(ism_debug_info, 3, "query info"); + debug_event(ism_debug_info, 3, &cmd.response, sizeof(cmd.response)); +out: + return 0; +} + +static int register_sba(struct ism_dev *ism) +{ + union ism_reg_sba cmd; + dma_addr_t dma_handle; + struct ism_sba *sba; + + sba = dma_zalloc_coherent(&ism->pdev->dev, PAGE_SIZE, + &dma_handle, GFP_KERNEL); + if (!sba) + return -ENOMEM; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_REG_SBA; + cmd.request.hdr.len = sizeof(cmd.request); + cmd.request.sba = dma_handle; + + if (ism_cmd(ism, &cmd)) { + dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, sba, dma_handle); + return -EIO; + } + + ism->sba = sba; + ism->sba_dma_addr = dma_handle; + + return 0; +} + +static int register_ieq(struct ism_dev *ism) +{ + union ism_reg_ieq cmd; + dma_addr_t dma_handle; + struct ism_eq *ieq; + + ieq = dma_zalloc_coherent(&ism->pdev->dev, PAGE_SIZE, + &dma_handle, GFP_KERNEL); + if (!ieq) + return -ENOMEM; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_REG_IEQ; + cmd.request.hdr.len = sizeof(cmd.request); + cmd.request.ieq = dma_handle; + cmd.request.len = sizeof(*ieq); + + if (ism_cmd(ism, &cmd)) { + dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ieq, dma_handle); + return -EIO; + } + + ism->ieq = ieq; + ism->ieq_idx = -1; + ism->ieq_dma_addr = dma_handle; + + return 0; +} + +static int unregister_sba(struct ism_dev *ism) +{ + if (!ism->sba) + return 0; + + if (ism_cmd_simple(ism, ISM_UNREG_SBA)) + return -EIO; + + dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, + ism->sba, ism->sba_dma_addr); + + ism->sba = NULL; + ism->sba_dma_addr = 0; + + return 0; +} + +static int unregister_ieq(struct ism_dev *ism) +{ + if (!ism->ieq) + return 0; + + if (ism_cmd_simple(ism, ISM_UNREG_IEQ)) + return -EIO; + + dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, + ism->ieq, ism->ieq_dma_addr); + + ism->ieq = NULL; + ism->ieq_dma_addr = 0; + + return 0; +} + +static int ism_read_local_gid(struct ism_dev *ism) +{ + union ism_read_gid cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_READ_GID; + cmd.request.hdr.len = sizeof(cmd.request); + + ret = ism_cmd(ism, &cmd); + if (ret) + goto out; + + ism->smcd->local_gid = cmd.response.gid; +out: + return ret; +} + +static int ism_query_rgid(struct smcd_dev *smcd, u64 rgid, u32 vid_valid, + u32 vid) +{ + struct ism_dev *ism = smcd->priv; + union ism_query_rgid cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_QUERY_RGID; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.rgid = rgid; + cmd.request.vlan_valid = vid_valid; + cmd.request.vlan_id = vid; + + return ism_cmd(ism, &cmd); +} + +static void ism_free_dmb(struct ism_dev *ism, struct smcd_dmb *dmb) +{ + clear_bit(dmb->sba_idx, ism->sba_bitmap); + dma_free_coherent(&ism->pdev->dev, dmb->dmb_len, + dmb->cpu_addr, dmb->dma_addr); +} + +static int ism_alloc_dmb(struct ism_dev *ism, struct smcd_dmb *dmb) +{ + unsigned long bit; + + if (PAGE_ALIGN(dmb->dmb_len) > dma_get_max_seg_size(&ism->pdev->dev)) + return -EINVAL; + + if (!dmb->sba_idx) { + bit = find_next_zero_bit(ism->sba_bitmap, ISM_NR_DMBS, + ISM_DMB_BIT_OFFSET); + if (bit == ISM_NR_DMBS) + return -ENOMEM; + + dmb->sba_idx = bit; + } + if (dmb->sba_idx < ISM_DMB_BIT_OFFSET || + test_and_set_bit(dmb->sba_idx, ism->sba_bitmap)) + return -EINVAL; + + dmb->cpu_addr = dma_zalloc_coherent(&ism->pdev->dev, dmb->dmb_len, + &dmb->dma_addr, GFP_KERNEL | + __GFP_NOWARN | __GFP_NOMEMALLOC | + __GFP_COMP | __GFP_NORETRY); + if (!dmb->cpu_addr) + clear_bit(dmb->sba_idx, ism->sba_bitmap); + + return dmb->cpu_addr ? 0 : -ENOMEM; +} + +static int ism_register_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb) +{ + struct ism_dev *ism = smcd->priv; + union ism_reg_dmb cmd; + int ret; + + ret = ism_alloc_dmb(ism, dmb); + if (ret) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_REG_DMB; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.dmb = dmb->dma_addr; + cmd.request.dmb_len = dmb->dmb_len; + cmd.request.sba_idx = dmb->sba_idx; + cmd.request.vlan_valid = dmb->vlan_valid; + cmd.request.vlan_id = dmb->vlan_id; + cmd.request.rgid = dmb->rgid; + + ret = ism_cmd(ism, &cmd); + if (ret) { + ism_free_dmb(ism, dmb); + goto out; + } + dmb->dmb_tok = cmd.response.dmb_tok; +out: + return ret; +} + +static int ism_unregister_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb) +{ + struct ism_dev *ism = smcd->priv; + union ism_unreg_dmb cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_UNREG_DMB; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.dmb_tok = dmb->dmb_tok; + + ret = ism_cmd(ism, &cmd); + if (ret) + goto out; + + ism_free_dmb(ism, dmb); +out: + return ret; +} + +static int ism_add_vlan_id(struct smcd_dev *smcd, u64 vlan_id) +{ + struct ism_dev *ism = smcd->priv; + union ism_set_vlan_id cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_ADD_VLAN_ID; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.vlan_id = vlan_id; + + return ism_cmd(ism, &cmd); +} + +static int ism_del_vlan_id(struct smcd_dev *smcd, u64 vlan_id) +{ + struct ism_dev *ism = smcd->priv; + union ism_set_vlan_id cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_DEL_VLAN_ID; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.vlan_id = vlan_id; + + return ism_cmd(ism, &cmd); +} + +static int ism_set_vlan_required(struct smcd_dev *smcd) +{ + return ism_cmd_simple(smcd->priv, ISM_SET_VLAN); +} + +static int ism_reset_vlan_required(struct smcd_dev *smcd) +{ + return ism_cmd_simple(smcd->priv, ISM_RESET_VLAN); +} + +static int ism_signal_ieq(struct smcd_dev *smcd, u64 rgid, u32 trigger_irq, + u32 event_code, u64 info) +{ + struct ism_dev *ism = smcd->priv; + union ism_sig_ieq cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_SIGNAL_IEQ; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.rgid = rgid; + cmd.request.trigger_irq = trigger_irq; + cmd.request.event_code = event_code; + cmd.request.info = info; + + return ism_cmd(ism, &cmd); +} + +static unsigned int max_bytes(unsigned int start, unsigned int len, + unsigned int boundary) +{ + return min(boundary - (start & (boundary - 1)), len); +} + +static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx, + bool sf, unsigned int offset, void *data, unsigned int size) +{ + struct ism_dev *ism = smcd->priv; + unsigned int bytes; + u64 dmb_req; + int ret; + + while (size) { + bytes = max_bytes(offset, size, PAGE_SIZE); + dmb_req = ISM_CREATE_REQ(dmb_tok, idx, size == bytes ? sf : 0, + offset); + + ret = __ism_move(ism, dmb_req, data, bytes); + if (ret) + return ret; + + size -= bytes; + data += bytes; + offset += bytes; + } + + return 0; +} + +static void ism_handle_event(struct ism_dev *ism) +{ + struct smcd_event *entry; + + while ((ism->ieq_idx + 1) != READ_ONCE(ism->ieq->header.idx)) { + if (++(ism->ieq_idx) == ARRAY_SIZE(ism->ieq->entry)) + ism->ieq_idx = 0; + + entry = &ism->ieq->entry[ism->ieq_idx]; + debug_event(ism_debug_info, 2, entry, sizeof(*entry)); + smcd_handle_event(ism->smcd, entry); + } +} + +static irqreturn_t ism_handle_irq(int irq, void *data) +{ + struct ism_dev *ism = data; + unsigned long bit, end; + unsigned long *bv; + + bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET]; + end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET; + + spin_lock(&ism->lock); + ism->sba->s = 0; + barrier(); + for (bit = 0;;) { + bit = find_next_bit_inv(bv, end, bit); + if (bit >= end) + break; + + clear_bit_inv(bit, bv); + barrier(); + smcd_handle_irq(ism->smcd, bit + ISM_DMB_BIT_OFFSET); + ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0; + } + + if (ism->sba->e) { + ism->sba->e = 0; + barrier(); + ism_handle_event(ism); + } + spin_unlock(&ism->lock); + return IRQ_HANDLED; +} + +static const struct smcd_ops ism_ops = { + .query_remote_gid = ism_query_rgid, + .register_dmb = ism_register_dmb, + .unregister_dmb = ism_unregister_dmb, + .add_vlan_id = ism_add_vlan_id, + .del_vlan_id = ism_del_vlan_id, + .set_vlan_required = ism_set_vlan_required, + .reset_vlan_required = ism_reset_vlan_required, + .signal_event = ism_signal_ieq, + .move_data = ism_move, +}; + +static int ism_dev_init(struct ism_dev *ism) +{ + struct pci_dev *pdev = ism->pdev; + int ret; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (ret <= 0) + goto out; + + ret = request_irq(pci_irq_vector(pdev, 0), ism_handle_irq, 0, + pci_name(pdev), ism); + if (ret) + goto free_vectors; + + ret = register_sba(ism); + if (ret) + goto free_irq; + + ret = register_ieq(ism); + if (ret) + goto unreg_sba; + + ret = ism_read_local_gid(ism); + if (ret) + goto unreg_ieq; + + ret = smcd_register_dev(ism->smcd); + if (ret) + goto unreg_ieq; + + query_info(ism); + return 0; + +unreg_ieq: + unregister_ieq(ism); +unreg_sba: + unregister_sba(ism); +free_irq: + free_irq(pci_irq_vector(pdev, 0), ism); +free_vectors: + pci_free_irq_vectors(pdev); +out: + return ret; +} + +static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct ism_dev *ism; + int ret; + + ism = kzalloc(sizeof(*ism), GFP_KERNEL); + if (!ism) + return -ENOMEM; + + spin_lock_init(&ism->lock); + dev_set_drvdata(&pdev->dev, ism); + ism->pdev = pdev; + + ret = pci_enable_device_mem(pdev); + if (ret) + goto err; + + ret = pci_request_mem_regions(pdev, DRV_NAME); + if (ret) + goto err_disable; + + ism->ctl = pci_iomap(pdev, 2, 0); + if (!ism->ctl) + goto err_resource; + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (ret) + goto err_unmap; + + pci_set_dma_seg_boundary(pdev, SZ_1M - 1); + pci_set_dma_max_seg_size(pdev, SZ_1M); + pci_set_master(pdev); + + ism->smcd = smcd_alloc_dev(&pdev->dev, dev_name(&pdev->dev), &ism_ops, + ISM_NR_DMBS); + if (!ism->smcd) + goto err_unmap; + + ism->smcd->priv = ism; + ret = ism_dev_init(ism); + if (ret) + goto err_free; + + return 0; + +err_free: + smcd_free_dev(ism->smcd); +err_unmap: + pci_iounmap(pdev, ism->ctl); +err_resource: + pci_release_mem_regions(pdev); +err_disable: + pci_disable_device(pdev); +err: + kfree(ism); + dev_set_drvdata(&pdev->dev, NULL); + return ret; +} + +static void ism_dev_exit(struct ism_dev *ism) +{ + struct pci_dev *pdev = ism->pdev; + + smcd_unregister_dev(ism->smcd); + unregister_ieq(ism); + unregister_sba(ism); + free_irq(pci_irq_vector(pdev, 0), ism); + pci_free_irq_vectors(pdev); +} + +static void ism_remove(struct pci_dev *pdev) +{ + struct ism_dev *ism = dev_get_drvdata(&pdev->dev); + + ism_dev_exit(ism); + + smcd_free_dev(ism->smcd); + pci_iounmap(pdev, ism->ctl); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); + dev_set_drvdata(&pdev->dev, NULL); + kfree(ism); +} + +static int ism_suspend(struct device *dev) +{ + struct ism_dev *ism = dev_get_drvdata(dev); + + ism_dev_exit(ism); + return 0; +} + +static int ism_resume(struct device *dev) +{ + struct ism_dev *ism = dev_get_drvdata(dev); + + return ism_dev_init(ism); +} + +static SIMPLE_DEV_PM_OPS(ism_pm_ops, ism_suspend, ism_resume); + +static struct pci_driver ism_driver = { + .name = DRV_NAME, + .id_table = ism_device_table, + .probe = ism_probe, + .remove = ism_remove, + .driver = { + .pm = &ism_pm_ops, + }, +}; + +static int __init ism_init(void) +{ + int ret; + + ism_debug_info = debug_register("ism", 2, 1, 16); + if (!ism_debug_info) + return -ENODEV; + + debug_register_view(ism_debug_info, &debug_hex_ascii_view); + ret = pci_register_driver(&ism_driver); + if (ret) + debug_unregister(ism_debug_info); + + return ret; +} + +static void __exit ism_exit(void) +{ + pci_unregister_driver(&ism_driver); + debug_unregister(ism_debug_info); +} + +module_init(ism_init); +module_exit(ism_exit); diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index a246a618f9a4..605ec4706773 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -104,6 +104,7 @@ struct qeth_dbf_info { struct qeth_perf_stats { unsigned int bufs_rec; unsigned int bufs_sent; + unsigned int buf_elements_sent; unsigned int skbs_sent_pack; unsigned int bufs_sent_pack; @@ -137,7 +138,6 @@ struct qeth_perf_stats { unsigned int large_send_bytes; unsigned int large_send_cnt; unsigned int sg_skbs_sent; - unsigned int sg_frags_sent; /* initial values when measuring starts */ unsigned long initial_rx_packets; unsigned long initial_tx_packets; @@ -465,7 +465,6 @@ struct qeth_qdio_out_buffer { struct sk_buff_head skb_list; int is_header[QDIO_MAX_ELEMENTS_PER_BUFFER]; - struct qaob *aob; struct qeth_qdio_out_q *q; struct qeth_qdio_out_buffer *next_pending; }; @@ -659,12 +658,8 @@ struct qeth_card_info { char mcl_level[QETH_MCL_LENGTH + 1]; int guestlan; int mac_bits; - int portno; enum qeth_card_types type; enum qeth_link_types link_type; - int is_multicast_different; - int initial_mtu; - int max_mtu; int broadcast_capable; int unique_id; bool layer_enforced; @@ -935,6 +930,19 @@ static inline int qeth_send_simple_setassparms_v6(struct qeth_card *card, data, QETH_PROT_IPV6); } +int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, + int ipv); +static inline struct qeth_qdio_out_q *qeth_get_tx_queue(struct qeth_card *card, + struct sk_buff *skb, + int ipv, int cast_type) +{ + if (IS_IQD(card) && cast_type != RTN_UNICAST) + return card->qdio.out_qs[card->qdio.no_out_queues - 1]; + if (!card->qdio.do_prio_queueing) + return card->qdio.out_qs[card->qdio.default_out_queue]; + return card->qdio.out_qs[qeth_get_priority_queue(card, skb, ipv)]; +} + extern struct qeth_discipline qeth_l2_discipline; extern struct qeth_discipline qeth_l3_discipline; extern const struct attribute_group *qeth_generic_attr_groups[]; @@ -955,6 +963,7 @@ extern struct qeth_card_list_struct qeth_core_card_list; extern struct kmem_cache *qeth_core_header_cache; extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS]; +struct net_device *qeth_clone_netdev(struct net_device *orig); void qeth_set_recovery_task(struct qeth_card *); void qeth_clear_recovery_task(struct qeth_card *); void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int); @@ -972,7 +981,6 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, void *); struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds, enum qeth_prot_versions); -int qeth_query_setadapterparms(struct qeth_card *); struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *, struct qeth_hdr **); @@ -985,7 +993,6 @@ void qeth_clear_cmd_buffers(struct qeth_channel *); void qeth_clear_qdio_buffers(struct qeth_card *); void qeth_setadp_promisc_mode(struct qeth_card *); struct net_device_stats *qeth_get_stats(struct net_device *); -int qeth_change_mtu(struct net_device *, int); int qeth_setadpparms_change_macaddr(struct qeth_card *); void qeth_tx_timeout(struct net_device *); void qeth_prepare_control_data(struct qeth_card *, int, @@ -998,11 +1005,6 @@ int qeth_query_switch_attributes(struct qeth_card *card, int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), void *reply_param); -int qeth_bridgeport_query_ports(struct qeth_card *card, - enum qeth_sbp_roles *role, enum qeth_sbp_states *state); -int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); -int qeth_bridgeport_an_set(struct qeth_card *card, int enable); -int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb, int extra_elems, int data_offset); int qeth_get_elements_for_frags(struct sk_buff *); @@ -1026,7 +1028,6 @@ int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback); int qeth_hdr_chk_and_bounce(struct sk_buff *, struct qeth_hdr **, int); int qeth_configure_cq(struct qeth_card *, enum qeth_cq); int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action); -int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot); void qeth_trace_features(struct qeth_card *); void qeth_close_dev(struct qeth_card *); int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16, @@ -1046,7 +1047,9 @@ netdev_features_t qeth_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features); int qeth_vm_request_mac(struct qeth_card *card); -int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len); +int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr **hdr, unsigned int hdr_len, + unsigned int proto_len, unsigned int *elements); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d01ac29fd986..d09a7110b381 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -473,7 +473,6 @@ static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx, if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == QETH_QDIO_BUF_HANDLED_DELAYED)) { /* for recovery situations */ - q->bufs[bidx]->aob = q->bufstates[bidx].aob; qeth_init_qdio_out_buf(q, bidx); QETH_CARD_TEXT(q->card, 2, "clprecov"); } @@ -510,7 +509,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card, } qeth_notify_skbs(buffer->q, buffer, notification); - buffer->aob = NULL; /* Free dangling allocations. The attached skbs are handled by * qeth_cleanup_handled_pending(). */ @@ -655,8 +653,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, cmd->hdr.return_code, card); } card->lan_online = 0; - if (card->dev && netif_carrier_ok(card->dev)) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); return NULL; case IPA_CMD_STARTLAN: dev_info(&card->gdev->dev, @@ -1267,8 +1264,7 @@ static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) } static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, - struct qeth_qdio_out_buffer *buf, - enum qeth_qdio_buffer_states newbufstate) + struct qeth_qdio_out_buffer *buf) { int i; @@ -1276,23 +1272,19 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, if (buf->buffer->element[0].sflags & SBAL_SFLAGS0_PCI_REQ) atomic_dec(&queue->set_pci_flags_count); - if (newbufstate == QETH_QDIO_BUF_EMPTY) { - qeth_release_skbs(buf); - } + qeth_release_skbs(buf); + for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { if (buf->buffer->element[i].addr && buf->is_header[i]) kmem_cache_free(qeth_core_header_cache, buf->buffer->element[i].addr); buf->is_header[i] = 0; - buf->buffer->element[i].length = 0; - buf->buffer->element[i].addr = NULL; - buf->buffer->element[i].eflags = 0; - buf->buffer->element[i].sflags = 0; } - buf->buffer->element[15].eflags = 0; - buf->buffer->element[15].sflags = 0; + + qeth_scrub_qdio_buffer(buf->buffer, + QETH_MAX_BUFFER_ELEMENTS(queue->card)); buf->next_element_to_fill = 0; - atomic_set(&buf->state, newbufstate); + atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY); } static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) @@ -1303,7 +1295,7 @@ static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) if (!q->bufs[j]) continue; qeth_cleanup_handled_pending(q, j, 1); - qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY); + qeth_clear_output_buffer(q, q->bufs[j]); if (free) { kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); q->bufs[j] = NULL; @@ -1544,8 +1536,6 @@ static void qeth_determine_card_type(struct qeth_card *card) card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; card->info.type = CARD_RDEV(card)->id.driver_info; card->qdio.no_out_queues = QETH_MAX_QUEUES; - if (card->info.type == QETH_CARD_TYPE_IQD) - card->info.is_multicast_different = 0x0103; qeth_update_from_chp_desc(card); } @@ -1930,7 +1920,7 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel, memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); } - tmp = ((__u8)card->info.portno) | 0x80; + tmp = ((u8)card->dev->dev_port) | 0x80; memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1); memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); @@ -2288,19 +2278,42 @@ static int qeth_cm_setup(struct qeth_card *card) } -static int qeth_get_initial_mtu_for_card(struct qeth_card *card) +static int qeth_update_max_mtu(struct qeth_card *card, unsigned int max_mtu) { - switch (card->info.type) { - case QETH_CARD_TYPE_IQD: - return card->info.max_mtu; - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSX: - if (!card->options.layer2) - return ETH_DATA_LEN - 8; /* L3: allow for LLC + SNAP */ - /* fall through */ - default: - return ETH_DATA_LEN; + struct net_device *dev = card->dev; + unsigned int new_mtu; + + if (!max_mtu) { + /* IQD needs accurate max MTU to set up its RX buffers: */ + if (IS_IQD(card)) + return -EINVAL; + /* tolerate quirky HW: */ + max_mtu = ETH_MAX_MTU; + } + + rtnl_lock(); + if (IS_IQD(card)) { + /* move any device with default MTU to new max MTU: */ + new_mtu = (dev->mtu == dev->max_mtu) ? max_mtu : dev->mtu; + + /* adjust RX buffer size to new max MTU: */ + card->qdio.in_buf_size = max_mtu + 2 * PAGE_SIZE; + if (dev->max_mtu && dev->max_mtu != max_mtu) + qeth_free_qdio_buffers(card); + } else { + if (dev->mtu) + new_mtu = dev->mtu; + /* default MTUs for first setup: */ + else if (card->options.layer2) + new_mtu = ETH_DATA_LEN; + else + new_mtu = ETH_DATA_LEN - 8; /* allow for LLC + SNAP */ } + + dev->max_mtu = max_mtu; + dev->mtu = min(new_mtu, max_mtu); + rtnl_unlock(); + return 0; } static int qeth_get_mtu_outof_framesize(int framesize) @@ -2319,21 +2332,6 @@ static int qeth_get_mtu_outof_framesize(int framesize) } } -static int qeth_mtu_is_valid(struct qeth_card *card, int mtu) -{ - switch (card->info.type) { - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSM: - case QETH_CARD_TYPE_OSX: - case QETH_CARD_TYPE_IQD: - return ((mtu >= 576) && - (mtu <= card->info.max_mtu)); - case QETH_CARD_TYPE_OSN: - default: - return 1; - } -} - static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { @@ -2352,29 +2350,10 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, if (card->info.type == QETH_CARD_TYPE_IQD) { memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); mtu = qeth_get_mtu_outof_framesize(framesize); - if (!mtu) { - iob->rc = -EINVAL; - QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); - return 0; - } - if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { - /* frame size has changed */ - if (card->dev && - ((card->dev->mtu == card->info.initial_mtu) || - (card->dev->mtu > mtu))) - card->dev->mtu = mtu; - qeth_free_qdio_buffers(card); - } - card->info.initial_mtu = mtu; - card->info.max_mtu = mtu; - card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; } else { - card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( - iob->data); - card->info.initial_mtu = min(card->info.max_mtu, - qeth_get_initial_mtu_for_card(card)); - card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; + mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data); } + *(u16 *)reply->param = mtu; memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { @@ -2393,6 +2372,7 @@ static int qeth_ulp_enable(struct qeth_card *card) int rc; char prot_type; struct qeth_cmd_buffer *iob; + u16 max_mtu; /*FIXME: trace view callbacks*/ QETH_DBF_TEXT(SETUP, 2, "ulpenabl"); @@ -2400,8 +2380,7 @@ static int qeth_ulp_enable(struct qeth_card *card) iob = qeth_wait_for_buffer(&card->write); memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); - *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = - (__u8) card->info.portno; + *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = (u8) card->dev->dev_port; if (card->options.layer2) if (card->info.type == QETH_CARD_TYPE_OSN) prot_type = QETH_PROT_OSN2; @@ -2416,9 +2395,10 @@ static int qeth_ulp_enable(struct qeth_card *card) memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, - qeth_ulp_enable_cb, NULL); - return rc; - + qeth_ulp_enable_cb, &max_mtu); + if (rc) + return rc; + return qeth_update_max_mtu(card, max_mtu); } static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, @@ -2473,32 +2453,20 @@ static int qeth_ulp_setup(struct qeth_card *card) static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx) { - int rc; struct qeth_qdio_out_buffer *newbuf; - rc = 0; newbuf = kmem_cache_zalloc(qeth_qdio_outbuf_cache, GFP_ATOMIC); - if (!newbuf) { - rc = -ENOMEM; - goto out; - } + if (!newbuf) + return -ENOMEM; + newbuf->buffer = q->qdio_bufs[bidx]; skb_queue_head_init(&newbuf->skb_list); lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key); newbuf->q = q; - newbuf->aob = NULL; newbuf->next_pending = q->bufs[bidx]; atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY); q->bufs[bidx] = newbuf; - if (q->bufstates) { - q->bufstates[bidx].user = newbuf; - QETH_CARD_TEXT_(q->card, 2, "nbs%d", bidx); - QETH_CARD_TEXT_(q->card, 2, "%lx", (long) newbuf); - QETH_CARD_TEXT_(q->card, 2, "%lx", - (long) newbuf->next_pending); - } -out: - return rc; + return 0; } static void qeth_free_qdio_out_buf(struct qeth_qdio_out_q *q) @@ -2908,8 +2876,7 @@ int qeth_init_qdio_queues(struct qeth_card *card) QDIO_MAX_BUFFERS_PER_Q); for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { qeth_clear_output_buffer(card->qdio.out_qs[i], - card->qdio.out_qs[i]->bufs[j], - QETH_QDIO_BUF_EMPTY); + card->qdio.out_qs[i]->bufs[j]); } card->qdio.out_qs[i]->card = card; card->qdio.out_qs[i]->next_buf_to_fill = 0; @@ -2942,7 +2909,7 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card, cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; /* cmd->hdr.seqno is set by qeth_send_control_data() */ cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); - cmd->hdr.rel_adapter_no = (__u8) card->info.portno; + cmd->hdr.rel_adapter_no = (u8) card->dev->dev_port; if (card->options.layer2) cmd->hdr.prim_version_no = 2; else @@ -3076,7 +3043,7 @@ static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, return iob; } -int qeth_query_setadapterparms(struct qeth_card *card) +static int qeth_query_setadapterparms(struct qeth_card *card) { int rc; struct qeth_cmd_buffer *iob; @@ -3089,7 +3056,6 @@ int qeth_query_setadapterparms(struct qeth_card *card) rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); return rc; } -EXPORT_SYMBOL_GPL(qeth_query_setadapterparms); static int qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -3129,7 +3095,8 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, return 0; } -int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) +static int qeth_query_ipassists(struct qeth_card *card, + enum qeth_prot_versions prot) { int rc; struct qeth_cmd_buffer *iob; @@ -3141,7 +3108,6 @@ int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); return rc; } -EXPORT_SYMBOL_GPL(qeth_query_ipassists); static int qeth_query_switch_attributes_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -3180,7 +3146,6 @@ int qeth_query_switch_attributes(struct qeth_card *card, return qeth_send_ipa_cmd(card, iob, qeth_query_switch_attributes_cb, sw_info); } -EXPORT_SYMBOL_GPL(qeth_query_switch_attributes); static int qeth_query_setdiagass_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -3530,13 +3495,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, qdio_flags = QDIO_FLAG_SYNC_OUTPUT; if (atomic_read(&queue->set_pci_flags_count)) qdio_flags |= QDIO_FLAG_PCI_OUT; + atomic_add(count, &queue->used_buffers); + rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, queue->queue_no, index, count); if (queue->card->options.performance_stats) queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() - queue->card->perf_stats.outbound_do_qdio_start_time; - atomic_add(count, &queue->used_buffers); if (rc) { queue->card->stats.tx_errors += count; /* ignore temporary SIGA errors without busy condition */ @@ -3601,7 +3567,7 @@ static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, { struct qeth_card *card = (struct qeth_card *)card_ptr; - if (card->dev && (card->dev->flags & IFF_UP)) + if (card->dev->flags & IFF_UP) napi_schedule(&card->napi); } @@ -3634,10 +3600,10 @@ out: } EXPORT_SYMBOL_GPL(qeth_configure_cq); - -static void qeth_qdio_cq_handler(struct qeth_card *card, - unsigned int qdio_err, - unsigned int queue, int first_element, int count) { +static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, + unsigned int queue, int first_element, + int count) +{ struct qeth_qdio_q *cq = card->qdio.c_q; int i; int rc; @@ -3663,25 +3629,17 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, for (i = first_element; i < first_element + count; ++i) { int bidx = i % QDIO_MAX_BUFFERS_PER_Q; struct qdio_buffer *buffer = cq->qdio_bufs[bidx]; - int e; + int e = 0; - e = 0; while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && buffer->element[e].addr) { unsigned long phys_aob_addr; phys_aob_addr = (unsigned long) buffer->element[e].addr; qeth_qdio_handle_aob(card, phys_aob_addr); - buffer->element[e].addr = NULL; - buffer->element[e].eflags = 0; - buffer->element[e].sflags = 0; - buffer->element[e].length = 0; - ++e; } - - buffer->element[15].eflags = 0; - buffer->element[15].sflags = 0; + qeth_scrub_qdio_buffer(buffer, QDIO_MAX_ELEMENTS_PER_BUFFER); } rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, queue, card->qdio.c_q->next_buf_to_init, @@ -3760,11 +3718,7 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, qeth_notify_skbs(queue, buffer, TX_NOTIFY_PENDING); } - buffer->aob = queue->bufstates[bidx].aob; QETH_CARD_TEXT_(queue->card, 5, "pel%d", bidx); - QETH_CARD_TEXT(queue->card, 5, "aob"); - QETH_CARD_TEXT_(queue->card, 5, "%lx", - virt_to_phys(buffer->aob)); /* prepare the queue slot for re-use: */ qeth_scrub_qdio_buffer(buffer->buffer, @@ -3782,8 +3736,7 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, qeth_notify_skbs(queue, buffer, n); } - qeth_clear_output_buffer(queue, buffer, - QETH_QDIO_BUF_EMPTY); + qeth_clear_output_buffer(queue, buffer); } qeth_cleanup_handled_pending(queue, bidx, 0); } @@ -3810,15 +3763,11 @@ static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num) * Note: Function assumes that we have 4 outbound queues. */ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, - int ipv, int cast_type) + int ipv) { __be16 *tci; u8 tos; - if (cast_type && card->info.is_multicast_different) - return card->info.is_multicast_different & - (card->qdio.no_out_queues - 1); - switch (card->qdio.do_prio_queueing) { case QETH_PRIO_Q_ING_TOS: case QETH_PRIO_Q_ING_PREC: @@ -3882,6 +3831,17 @@ int qeth_get_elements_for_frags(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); +static unsigned int qeth_count_elements(struct sk_buff *skb, int data_offset) +{ + unsigned int elements = qeth_get_elements_for_frags(skb); + addr_t end = (addr_t)skb->data + skb_headlen(skb); + addr_t start = (addr_t)skb->data + data_offset; + + if (start != end) + elements += qeth_get_elements_for_range(start, end); + return elements; +} + /** * qeth_get_elements_no() - find number of SBALEs for skb data, inc. frags. * @card: qeth card structure, to check max. elems. @@ -3897,12 +3857,7 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb, int extra_elems, int data_offset) { - addr_t end = (addr_t)skb->data + skb_headlen(skb); - int elements = qeth_get_elements_for_frags(skb); - addr_t start = (addr_t)skb->data + data_offset; - - if (start != end) - elements += qeth_get_elements_for_range(start, end); + int elements = qeth_count_elements(skb, data_offset); if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { QETH_DBF_MESSAGE(2, "Invalid size of IP packet " @@ -3936,32 +3891,87 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); /** - * qeth_push_hdr() - push a qeth_hdr onto an skb. - * @skb: skb that the qeth_hdr should be pushed onto. + * qeth_add_hw_header() - add a HW header to an skb. + * @skb: skb that the HW header should be added to. * @hdr: double pointer to a qeth_hdr. When returning with >= 0, * it contains a valid pointer to a qeth_hdr. - * @len: length of the hdr that needs to be pushed on. + * @hdr_len: length of the HW header. + * @proto_len: length of protocol headers that need to be in same page as the + * HW header. * * Returns the pushed length. If the header can't be pushed on * (eg. because it would cross a page boundary), it is allocated from * the cache instead and 0 is returned. + * The number of needed buffer elements is returned in @elements. * Error to create the hdr is indicated by returning with < 0. */ -int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len) -{ - if (skb_headroom(skb) >= len && - qeth_get_elements_for_range((addr_t)skb->data - len, - (addr_t)skb->data) == 1) { - *hdr = skb_push(skb, len); - return len; +int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr **hdr, unsigned int hdr_len, + unsigned int proto_len, unsigned int *elements) +{ + const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card); + const unsigned int contiguous = proto_len ? proto_len : 1; + unsigned int __elements; + addr_t start, end; + bool push_ok; + int rc; + +check_layout: + start = (addr_t)skb->data - hdr_len; + end = (addr_t)skb->data; + + if (qeth_get_elements_for_range(start, end + contiguous) == 1) { + /* Push HW header into same page as first protocol header. */ + push_ok = true; + __elements = qeth_count_elements(skb, 0); + } else if (!proto_len && qeth_get_elements_for_range(start, end) == 1) { + /* Push HW header into a new page. */ + push_ok = true; + __elements = 1 + qeth_count_elements(skb, 0); + } else { + /* Use header cache, copy protocol headers up. */ + push_ok = false; + __elements = 1 + qeth_count_elements(skb, proto_len); + } + + /* Compress skb to fit into one IO buffer: */ + if (__elements > max_elements) { + if (!skb_is_nonlinear(skb)) { + /* Drop it, no easy way of shrinking it further. */ + QETH_DBF_MESSAGE(2, "Dropped an oversized skb (Max Elements=%u / Actual=%u / Length=%u).\n", + max_elements, __elements, skb->len); + return -E2BIG; + } + + rc = skb_linearize(skb); + if (card->options.performance_stats) { + if (rc) + card->perf_stats.tx_linfail++; + else + card->perf_stats.tx_lin++; + } + if (rc) + return rc; + + /* Linearization changed the layout, re-evaluate: */ + goto check_layout; + } + + *elements = __elements; + /* Add the header: */ + if (push_ok) { + *hdr = skb_push(skb, hdr_len); + return hdr_len; } /* fall back */ *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); if (!*hdr) return -ENOMEM; + /* Copy protocol headers behind HW header: */ + skb_copy_from_linear_data(skb, ((char *)*hdr) + hdr_len, proto_len); return 0; } -EXPORT_SYMBOL_GPL(qeth_push_hdr); +EXPORT_SYMBOL_GPL(qeth_add_hw_header); static void __qeth_fill_buffer(struct sk_buff *skb, struct qeth_qdio_out_buffer *buf, @@ -4241,24 +4251,6 @@ void qeth_setadp_promisc_mode(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode); -int qeth_change_mtu(struct net_device *dev, int new_mtu) -{ - struct qeth_card *card; - char dbf_text[15]; - - card = dev->ml_priv; - - QETH_CARD_TEXT(card, 4, "chgmtu"); - sprintf(dbf_text, "%8x", new_mtu); - QETH_CARD_TEXT(card, 4, dbf_text); - - if (!qeth_mtu_is_valid(card, new_mtu)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL_GPL(qeth_change_mtu); - struct net_device_stats *qeth_get_stats(struct net_device *dev) { struct qeth_card *card; @@ -4834,9 +4826,6 @@ int qeth_vm_request_mac(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "vmreqmac"); - if (!card->dev) - return -ENODEV; - request = kzalloc(sizeof(*request), GFP_KERNEL | GFP_DMA); response = kzalloc(sizeof(*response), GFP_KERNEL | GFP_DMA); if (!request || !response) { @@ -5716,6 +5705,53 @@ static void qeth_clear_dbf_list(void) mutex_unlock(&qeth_dbf_list_mutex); } +static struct net_device *qeth_alloc_netdev(struct qeth_card *card) +{ + struct net_device *dev; + + switch (card->info.type) { + case QETH_CARD_TYPE_IQD: + dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, ether_setup); + break; + case QETH_CARD_TYPE_OSN: + dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup); + break; + default: + dev = alloc_etherdev(0); + } + + if (!dev) + return NULL; + + dev->ml_priv = card; + dev->watchdog_timeo = QETH_TX_TIMEOUT; + dev->min_mtu = IS_OSN(card) ? 64 : 576; + /* initialized when device first goes online: */ + dev->max_mtu = 0; + dev->mtu = 0; + SET_NETDEV_DEV(dev, &card->gdev->dev); + netif_carrier_off(dev); + + if (!IS_OSN(card)) { + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->hw_features |= NETIF_F_SG; + dev->vlan_features |= NETIF_F_SG; + } + + return dev; +} + +struct net_device *qeth_clone_netdev(struct net_device *orig) +{ + struct net_device *clone = qeth_alloc_netdev(orig->ml_priv); + + if (!clone) + return NULL; + + clone->dev_port = orig->dev_port; + return clone; +} + static int qeth_core_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card; @@ -5765,6 +5801,10 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) goto err_card; } + card->dev = qeth_alloc_netdev(card); + if (!card->dev) + goto err_card; + qeth_determine_capabilities(card); enforced_disc = qeth_enforce_discipline(card); switch (enforced_disc) { @@ -5775,7 +5815,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) card->info.layer_enforced = true; rc = qeth_core_load_discipline(card, enforced_disc); if (rc) - goto err_card; + goto err_load; gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) ? card->discipline->devtype @@ -5793,6 +5833,8 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) err_disc: qeth_core_free_discipline(card); +err_load: + free_netdev(card->dev); err_card: qeth_core_free_card(card); err_dev: @@ -5815,10 +5857,10 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev) write_lock_irqsave(&qeth_core_card_list.rwlock, flags); list_del(&card->list); write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); + free_netdev(card->dev); qeth_core_free_card(card); dev_set_drvdata(&gdev->dev, NULL); put_device(&gdev->dev); - return; } static int qeth_core_set_online(struct ccwgroup_device *gdev) @@ -5887,31 +5929,13 @@ static int qeth_core_restore(struct ccwgroup_device *gdev) return 0; } -static struct ccwgroup_driver qeth_core_ccwgroup_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "qeth", - }, - .ccw_driver = &qeth_ccw_driver, - .setup = qeth_core_probe_device, - .remove = qeth_core_remove_device, - .set_online = qeth_core_set_online, - .set_offline = qeth_core_set_offline, - .shutdown = qeth_core_shutdown, - .prepare = NULL, - .complete = NULL, - .freeze = qeth_core_freeze, - .thaw = qeth_core_thaw, - .restore = qeth_core_restore, -}; - static ssize_t group_store(struct device_driver *ddrv, const char *buf, size_t count) { int err; - err = ccwgroup_create_dev(qeth_core_root_dev, - &qeth_core_ccwgroup_driver, 3, buf); + err = ccwgroup_create_dev(qeth_core_root_dev, to_ccwgroupdrv(ddrv), 3, + buf); return err ? err : count; } @@ -5929,6 +5953,25 @@ static const struct attribute_group *qeth_drv_attr_groups[] = { NULL, }; +static struct ccwgroup_driver qeth_core_ccwgroup_driver = { + .driver = { + .groups = qeth_drv_attr_groups, + .owner = THIS_MODULE, + .name = "qeth", + }, + .ccw_driver = &qeth_ccw_driver, + .setup = qeth_core_probe_device, + .remove = qeth_core_remove_device, + .set_online = qeth_core_set_online, + .set_offline = qeth_core_set_offline, + .shutdown = qeth_core_shutdown, + .prepare = NULL, + .complete = NULL, + .freeze = qeth_core_freeze, + .thaw = qeth_core_thaw, + .restore = qeth_core_restore, +}; + int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct qeth_card *card = dev->ml_priv; @@ -5995,7 +6038,7 @@ static struct { {"tx skbs packing"}, {"tx buffers packing"}, {"tx sg skbs"}, - {"tx sg frags"}, + {"tx buffer elements"}, /* 10 */{"rx sg skbs"}, {"rx sg frags"}, {"rx sg page allocs"}, @@ -6054,7 +6097,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[6] = card->perf_stats.skbs_sent_pack; data[7] = card->perf_stats.bufs_sent_pack; data[8] = card->perf_stats.sg_skbs_sent; - data[9] = card->perf_stats.sg_frags_sent; + data[9] = card->perf_stats.buf_elements_sent; data[10] = card->perf_stats.sg_skbs_rx; data[11] = card->perf_stats.sg_frags_rx; data[12] = card->perf_stats.sg_alloc_page_rx; @@ -6620,7 +6663,6 @@ static int __init qeth_core_init(void) rc = ccw_driver_register(&qeth_ccw_driver); if (rc) goto ccw_err; - qeth_core_ccwgroup_driver.driver.groups = qeth_drv_attr_groups; rc = ccwgroup_driver_register(&qeth_core_ccwgroup_driver); if (rc) goto ccwgroup_err; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 878e62f35169..cf5ad94e960a 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -64,6 +64,9 @@ enum qeth_card_types { QETH_CARD_TYPE_OSX = 2, }; +#define IS_IQD(card) ((card)->info.type == QETH_CARD_TYPE_IQD) +#define IS_OSN(card) ((card)->info.type == QETH_CARD_TYPE_OSN) + #define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18 /* only the first two bytes are looked at in qeth_get_cardname_short */ enum qeth_link_types { diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index c3f18afb368b..25d0be25bcb3 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -112,7 +112,7 @@ static ssize_t qeth_dev_portno_show(struct device *dev, if (!card) return -EINVAL; - return sprintf(buf, "%i\n", card->info.portno); + return sprintf(buf, "%i\n", card->dev->dev_port); } static ssize_t qeth_dev_portno_store(struct device *dev, @@ -143,9 +143,7 @@ static ssize_t qeth_dev_portno_store(struct device *dev, rc = -EINVAL; goto out; } - card->info.portno = portno; - if (card->dev) - card->dev->dev_port = portno; + card->dev->dev_port = portno; out: mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -388,6 +386,7 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); + struct net_device *ndev; char *tmp; int i, rc = 0; enum qeth_discipline_id newdis; @@ -424,8 +423,19 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, card->info.mac_bits = 0; if (card->discipline) { + /* start with a new, pristine netdevice: */ + ndev = qeth_clone_netdev(card->dev); + if (!ndev) { + rc = -ENOMEM; + goto out; + } + card->discipline->remove(card->gdev); qeth_core_free_discipline(card); + card->options.layer2 = -1; + + free_netdev(card->dev); + card->dev = ndev; } rc = qeth_core_load_discipline(card, newdis); diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h index f2130051ca11..ddc615b431a8 100644 --- a/drivers/s390/net/qeth_l2.h +++ b/drivers/s390/net/qeth_l2.h @@ -14,6 +14,11 @@ extern const struct attribute_group *qeth_l2_attr_groups[]; int qeth_l2_create_device_attributes(struct device *); void qeth_l2_remove_device_attributes(struct device *); void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); +int qeth_bridgeport_query_ports(struct qeth_card *card, + enum qeth_sbp_roles *role, + enum qeth_sbp_states *state); +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); +int qeth_bridgeport_an_set(struct qeth_card *card, int enable); int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state); int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 2487f0aeb165..c1829a4b955d 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -26,7 +26,6 @@ static int qeth_l2_set_offline(struct ccwgroup_device *); static int qeth_l2_stop(struct net_device *); -static void qeth_l2_set_rx_mode(struct net_device *); static void qeth_bridgeport_query_support(struct qeth_card *card); static void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd); @@ -186,12 +185,12 @@ static void qeth_l2_del_all_macs(struct qeth_card *card) static int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb) { if (card->info.type == QETH_CARD_TYPE_OSN) - return RTN_UNSPEC; + return RTN_UNICAST; if (is_broadcast_ether_addr(skb->data)) return RTN_BROADCAST; if (is_multicast_ether_addr(skb->data)) return RTN_MULTICAST; - return RTN_UNSPEC; + return RTN_UNICAST; } static void qeth_l2_fill_header(struct qeth_hdr *hdr, struct sk_buff *skb, @@ -344,7 +343,6 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); kfree(tmpid); } - qeth_l2_set_rx_mode(card->dev); return rc; } @@ -643,97 +641,58 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) qeth_promisc_to_bridge(card); } -static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb, - struct qeth_qdio_out_q *queue, int cast_type) +static int qeth_l2_xmit(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int cast_type, int ipv) { - unsigned int data_offset = ETH_HLEN; - struct qeth_hdr *hdr; - int rc; - - hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); - if (!hdr) - return -ENOMEM; - qeth_l2_fill_header(hdr, skb, cast_type, skb->len); - skb_copy_from_linear_data(skb, ((char *)hdr) + sizeof(*hdr), - data_offset); - - if (!qeth_get_elements_no(card, skb, 1, data_offset)) { - rc = -E2BIG; - goto out; - } - rc = qeth_do_send_packet_fast(queue, skb, hdr, data_offset, - sizeof(*hdr) + data_offset); -out: - if (rc) - kmem_cache_free(qeth_core_header_cache, hdr); - return rc; -} - -static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, - struct qeth_qdio_out_q *queue, int cast_type, - int ipv) -{ - int push_len = sizeof(struct qeth_hdr); - unsigned int elements, nr_frags; - unsigned int hdr_elements = 0; + const unsigned int proto_len = IS_IQD(card) ? ETH_HLEN : 0; + const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); + unsigned int frame_len = skb->len; + unsigned int data_offset = 0; struct qeth_hdr *hdr = NULL; unsigned int hd_len = 0; - int rc; - - /* fix hardware limitation: as long as we do not have sbal - * chaining we can not send long frag lists - */ - if (!qeth_get_elements_no(card, skb, 0, 0)) { - rc = skb_linearize(skb); - - if (card->options.performance_stats) { - if (rc) - card->perf_stats.tx_linfail++; - else - card->perf_stats.tx_lin++; - } - if (rc) - return rc; - } - nr_frags = skb_shinfo(skb)->nr_frags; + unsigned int elements; + int push_len, rc; + bool is_sg; - rc = skb_cow_head(skb, push_len); + rc = skb_cow_head(skb, hw_hdr_len); if (rc) return rc; - push_len = qeth_push_hdr(skb, &hdr, push_len); + + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, proto_len, + &elements); if (push_len < 0) return push_len; if (!push_len) { - /* hdr was allocated from cache */ - hd_len = sizeof(*hdr); - hdr_elements = 1; + /* HW header needs its own buffer element. */ + hd_len = hw_hdr_len + proto_len; + data_offset = proto_len; } - qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len); + qeth_l2_fill_header(hdr, skb, cast_type, frame_len); if (skb->ip_summed == CHECKSUM_PARTIAL) { qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv); if (card->options.performance_stats) card->perf_stats.tx_csum++; } - elements = qeth_get_elements_no(card, skb, hdr_elements, 0); - if (!elements) { - rc = -E2BIG; - goto out; + is_sg = skb_is_nonlinear(skb); + if (IS_IQD(card)) { + rc = qeth_do_send_packet_fast(queue, skb, hdr, data_offset, + hd_len); + } else { + /* TODO: drop skb_orphan() once TX completion is fast enough */ + skb_orphan(skb); + rc = qeth_do_send_packet(card, queue, skb, hdr, data_offset, + hd_len, elements); } - elements += hdr_elements; - /* TODO: remove the skb_orphan() once TX completion is fast enough */ - skb_orphan(skb); - rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements); -out: if (!rc) { - if (card->options.performance_stats && nr_frags) { - card->perf_stats.sg_skbs_sent++; - /* nr_frags + skb->data */ - card->perf_stats.sg_frags_sent += nr_frags + 1; + if (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; } } else { - if (hd_len) + if (!push_len) kmem_cache_free(qeth_core_header_cache, hdr); if (rc == -EBUSY) /* roll back to ETH header */ @@ -770,34 +729,23 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, int tx_bytes = skb->len; int rc; - if (card->qdio.do_prio_queueing || (cast_type && - card->info.is_multicast_different)) - queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb, - ipv, cast_type)]; - else - queue = card->qdio.out_qs[card->qdio.default_out_queue]; - if ((card->state != CARD_STATE_UP) || !card->lan_online) { card->stats.tx_carrier_errors++; goto tx_drop; } + queue = qeth_get_tx_queue(card, skb, ipv, cast_type); + if (card->options.performance_stats) { card->perf_stats.outbound_cnt++; card->perf_stats.outbound_start_time = qeth_get_micros(); } netif_stop_queue(dev); - switch (card->info.type) { - case QETH_CARD_TYPE_OSN: + if (IS_OSN(card)) rc = qeth_l2_xmit_osn(card, skb, queue); - break; - case QETH_CARD_TYPE_IQD: - rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type); - break; - default: - rc = qeth_l2_xmit_osa(card, skb, queue, cast_type, ipv); - } + else + rc = qeth_l2_xmit(card, skb, queue, cast_type, ipv); if (!rc) { card->stats.tx_packets++; @@ -906,13 +854,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) if (cgdev->state == CCWGROUP_ONLINE) qeth_l2_set_offline(cgdev); - - if (card->dev) { - unregister_netdev(card->dev); - free_netdev(card->dev); - card->dev = NULL; - } - return; + unregister_netdev(card->dev); } static const struct ethtool_ops qeth_l2_ethtool_ops = { @@ -941,7 +883,6 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_set_rx_mode = qeth_l2_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, .ndo_set_mac_address = qeth_l2_set_mac_address, - .ndo_change_mtu = qeth_change_mtu, .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid, .ndo_tx_timeout = qeth_tx_timeout, @@ -951,35 +892,19 @@ static const struct net_device_ops qeth_l2_netdev_ops = { static int qeth_l2_setup_netdev(struct qeth_card *card) { - switch (card->info.type) { - case QETH_CARD_TYPE_IQD: - card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, - ether_setup); - break; - case QETH_CARD_TYPE_OSN: - card->dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, - ether_setup); - break; - default: - card->dev = alloc_etherdev(0); - } + int rc; - if (!card->dev) - return -ENODEV; + if (card->dev->netdev_ops) + return 0; - card->dev->ml_priv = card; card->dev->priv_flags |= IFF_UNICAST_FLT; - card->dev->watchdog_timeo = QETH_TX_TIMEOUT; - card->dev->mtu = card->info.initial_mtu; - card->dev->min_mtu = 64; - card->dev->max_mtu = ETH_MAX_MTU; - card->dev->dev_port = card->info.portno; card->dev->netdev_ops = &qeth_l2_netdev_ops; if (card->info.type == QETH_CARD_TYPE_OSN) { card->dev->ethtool_ops = &qeth_l2_osn_ops; card->dev->flags |= IFF_NOARP; } else { card->dev->ethtool_ops = &qeth_l2_ethtool_ops; + card->dev->needed_headroom = sizeof(struct qeth_hdr); } if (card->info.type == QETH_CARD_TYPE_OSM) @@ -987,14 +912,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) else card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; - if (card->info.type != QETH_CARD_TYPE_OSN && - card->info.type != QETH_CARD_TYPE_IQD) { - card->dev->priv_flags &= ~IFF_TX_SKB_SHARING; - card->dev->needed_headroom = sizeof(struct qeth_hdr); - card->dev->hw_features |= NETIF_F_SG; - card->dev->vlan_features |= NETIF_F_SG; - } - if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) { card->dev->features |= NETIF_F_SG; /* OSA 3S and earlier has no RX/TX support */ @@ -1013,12 +930,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->vlan_features |= NETIF_F_RXCSUM; } - card->info.broadcast_capable = 1; qeth_l2_request_initial_mac(card); - SET_NETDEV_DEV(card->dev, &card->gdev->dev); netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); - netif_carrier_off(card->dev); - return register_netdev(card->dev); + rc = register_netdev(card->dev); + if (rc) + card->dev->netdev_ops = NULL; + return rc; } static int qeth_l2_start_ipassists(struct qeth_card *card) @@ -1064,10 +981,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) dev_info(&card->gdev->dev, "The device represents a Bridge Capable Port\n"); - if (!card->dev && qeth_l2_setup_netdev(card)) { - rc = -ENODEV; + rc = qeth_l2_setup_netdev(card); + if (rc) goto out_remove; - } if (card->info.type != QETH_CARD_TYPE_OSN && !qeth_l2_send_setmac(card, card->dev->dev_addr)) @@ -1125,13 +1041,12 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) if (recovery_mode && card->info.type != QETH_CARD_TYPE_OSN) { __qeth_l2_open(card->dev); + qeth_l2_set_rx_mode(card->dev); } else { rtnl_lock(); dev_open(card->dev); rtnl_unlock(); } - /* this also sets saved unicast addresses */ - qeth_l2_set_rx_mode(card->dev); } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); @@ -1171,8 +1086,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev && netif_carrier_ok(card->dev)) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); @@ -1245,8 +1159,7 @@ 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); + 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) @@ -1279,8 +1192,7 @@ static int qeth_l2_pm_resume(struct ccwgroup_device *gdev) rc = __qeth_l2_set_online(card->gdev, 0); out: qeth_set_allowed_threads(card, 0xffffffff, 0); - if (card->dev) - netif_device_attach(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"); @@ -1877,7 +1789,6 @@ int qeth_bridgeport_query_ports(struct qeth_card *card, return rc; return qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); } -EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); static int qeth_bridgeport_set_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -2025,7 +1936,6 @@ int qeth_bridgeport_an_set(struct qeth_card *card, int enable) rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); return qeth_anset_makerc(card, rc, response); } -EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); static bool qeth_bridgeport_is_in_use(struct qeth_card *card) { diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 5905dc63e256..1833e7505aca 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1978,17 +1978,17 @@ static int qeth_l3_get_cast_type(struct sk_buff *skb) (cast_type == RTN_MULTICAST) || (cast_type == RTN_ANYCAST)) return cast_type; - return RTN_UNSPEC; + return RTN_UNICAST; } rcu_read_unlock(); /* no neighbour (eg AF_PACKET), fall back to target's IP address ... */ if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ? - RTN_MULTICAST : RTN_UNSPEC; + RTN_MULTICAST : RTN_UNICAST; else if (be16_to_cpu(skb->protocol) == ETH_P_IP) return ipv4_is_multicast(ip_hdr(skb)->daddr) ? - RTN_MULTICAST : RTN_UNSPEC; + RTN_MULTICAST : RTN_UNICAST; /* ... and MAC address */ if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, skb->dev->broadcast)) @@ -1997,22 +1997,21 @@ static int qeth_l3_get_cast_type(struct sk_buff *skb) return RTN_MULTICAST; /* default to unicast */ - return RTN_UNSPEC; + return RTN_UNICAST; } -static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card, - struct qeth_hdr *hdr, struct sk_buff *skb) +static void qeth_l3_fill_af_iucv_hdr(struct qeth_hdr *hdr, struct sk_buff *skb, + unsigned int data_len) { char daddr[16]; struct af_iucv_trans_hdr *iucv_hdr; memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; - hdr->hdr.l3.ext_flags = 0; - hdr->hdr.l3.length = skb->len - ETH_HLEN; + hdr->hdr.l3.length = data_len; hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST; - iucv_hdr = (struct af_iucv_trans_hdr *) (skb->data + ETH_HLEN); + iucv_hdr = (struct af_iucv_trans_hdr *)(skb_mac_header(skb) + ETH_HLEN); memset(daddr, 0, sizeof(daddr)); daddr[0] = 0xfe; daddr[1] = 0x80; @@ -2051,6 +2050,12 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb); } + if (!skb_is_gso(skb) && skb->ip_summed == CHECKSUM_PARTIAL) { + qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, ipv); + if (card->options.performance_stats) + card->perf_stats.tx_csum++; + } + /* OSA only: */ if (!ipv) { hdr->hdr.l3.flags = QETH_HDR_PASSTHRU; @@ -2156,106 +2161,121 @@ static int qeth_l3_get_elements_no_tso(struct qeth_card *card, return elements; } -static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) +static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int ipv, + int cast_type) { - int rc; - __be16 *tag; + const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); + unsigned int frame_len, elements; + unsigned char eth_hdr[ETH_HLEN]; struct qeth_hdr *hdr = NULL; - int hdr_elements = 0; - int elements; - struct qeth_card *card = dev->ml_priv; - struct sk_buff *new_skb = NULL; - int ipv = qeth_get_ip_version(skb); - int cast_type = qeth_l3_get_cast_type(skb); - struct qeth_qdio_out_q *queue = - card->qdio.out_qs[card->qdio.do_prio_queueing - || (cast_type && card->info.is_multicast_different) ? - qeth_get_priority_queue(card, skb, ipv, cast_type) : - card->qdio.default_out_queue]; - int tx_bytes = skb->len; unsigned int hd_len = 0; - bool use_tso; - int data_offset = -1; - unsigned int nr_frags; - - if (((card->info.type == QETH_CARD_TYPE_IQD) && - (((card->options.cq != QETH_CQ_ENABLED) && !ipv) || - ((card->options.cq == QETH_CQ_ENABLED) && - (be16_to_cpu(skb->protocol) != ETH_P_AF_IUCV)))) || - card->options.sniffer) - goto tx_drop; + int push_len, rc; + bool is_sg; - if ((card->state != CARD_STATE_UP) || !card->lan_online) { - card->stats.tx_carrier_errors++; - goto tx_drop; + /* re-use the L2 header area for the HW header: */ + rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN); + if (rc) + return rc; + skb_copy_from_linear_data(skb, eth_hdr, ETH_HLEN); + skb_pull(skb, ETH_HLEN); + frame_len = skb->len; + + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, 0, + &elements); + if (push_len < 0) + return push_len; + if (!push_len) { + /* hdr was added discontiguous from skb->data */ + hd_len = hw_hdr_len; } - if ((cast_type == RTN_BROADCAST) && - (card->info.broadcast_capable == 0)) - goto tx_drop; + if (skb->protocol == htons(ETH_P_AF_IUCV)) + qeth_l3_fill_af_iucv_hdr(hdr, skb, frame_len); + else + qeth_l3_fill_header(card, hdr, skb, ipv, cast_type, frame_len); - if (card->options.performance_stats) { - card->perf_stats.outbound_cnt++; - card->perf_stats.outbound_start_time = qeth_get_micros(); + is_sg = skb_is_nonlinear(skb); + if (IS_IQD(card)) { + rc = qeth_do_send_packet_fast(queue, skb, hdr, 0, hd_len); + } else { + /* TODO: drop skb_orphan() once TX completion is fast enough */ + skb_orphan(skb); + rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, + elements); + } + + if (!rc) { + if (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; + } + } else { + if (!push_len) + kmem_cache_free(qeth_core_header_cache, hdr); + if (rc == -EBUSY) { + /* roll back to ETH header */ + skb_pull(skb, push_len); + skb_push(skb, ETH_HLEN); + skb_copy_to_linear_data(skb, eth_hdr, ETH_HLEN); + } } + return rc; +} + +static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int ipv, int cast_type) +{ + int elements, len, rc; + __be16 *tag; + struct qeth_hdr *hdr = NULL; + int hdr_elements = 0; + struct sk_buff *new_skb = NULL; + int tx_bytes = skb->len; + unsigned int hd_len; + bool use_tso, is_sg; /* Ignore segment size from skb_is_gso(), 1 page is always used. */ use_tso = skb_is_gso(skb) && (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4); - if (card->info.type == QETH_CARD_TYPE_IQD) { - new_skb = skb; - data_offset = ETH_HLEN; - hd_len = sizeof(*hdr); - hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); - if (!hdr) - goto tx_drop; - hdr_elements++; - } else { - /* create a clone with writeable headroom */ - new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) - + VLAN_HLEN); - if (!new_skb) - goto tx_drop; - - if (ipv == 4) { - skb_pull(new_skb, ETH_HLEN); - } + /* create a clone with writeable headroom */ + new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) + + VLAN_HLEN); + if (!new_skb) + return -ENOMEM; - if (ipv != 4 && skb_vlan_tag_present(new_skb)) { - skb_push(new_skb, VLAN_HLEN); - skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); - skb_copy_to_linear_data_offset(new_skb, 4, - new_skb->data + 8, 4); - skb_copy_to_linear_data_offset(new_skb, 8, - new_skb->data + 12, 4); - tag = (__be16 *)(new_skb->data + 12); - *tag = cpu_to_be16(ETH_P_8021Q); - *(tag + 1) = cpu_to_be16(skb_vlan_tag_get(new_skb)); - } + if (ipv == 4) { + skb_pull(new_skb, ETH_HLEN); + } else if (skb_vlan_tag_present(new_skb)) { + skb_push(new_skb, VLAN_HLEN); + skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); + skb_copy_to_linear_data_offset(new_skb, 4, + new_skb->data + 8, 4); + skb_copy_to_linear_data_offset(new_skb, 8, + new_skb->data + 12, 4); + tag = (__be16 *)(new_skb->data + 12); + *tag = cpu_to_be16(ETH_P_8021Q); + *(tag + 1) = cpu_to_be16(skb_vlan_tag_get(new_skb)); } - netif_stop_queue(dev); - /* fix hardware limitation: as long as we do not have sbal * chaining we can not send long frag lists */ - if ((card->info.type != QETH_CARD_TYPE_IQD) && - ((use_tso && !qeth_l3_get_elements_no_tso(card, new_skb, 1)) || - (!use_tso && !qeth_get_elements_no(card, new_skb, 0, 0)))) { - int lin_rc = skb_linearize(new_skb); + if ((use_tso && !qeth_l3_get_elements_no_tso(card, new_skb, 1)) || + (!use_tso && !qeth_get_elements_no(card, new_skb, 0, 0))) { + rc = skb_linearize(new_skb); if (card->options.performance_stats) { - if (lin_rc) + if (rc) card->perf_stats.tx_linfail++; else card->perf_stats.tx_lin++; } - if (lin_rc) - goto tx_drop; + if (rc) + goto out; } - nr_frags = skb_shinfo(new_skb)->nr_frags; if (use_tso) { hdr = skb_push(new_skb, sizeof(struct qeth_hdr_tso)); @@ -2265,97 +2285,112 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, qeth_tso_fill_header(card, hdr, new_skb); hdr_elements++; } else { - if (data_offset < 0) { - hdr = skb_push(new_skb, sizeof(struct qeth_hdr)); - qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type, - new_skb->len - - sizeof(struct qeth_hdr)); - } else { - if (be16_to_cpu(new_skb->protocol) == ETH_P_AF_IUCV) - qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb); - else { - qeth_l3_fill_header(card, hdr, new_skb, ipv, - cast_type, - new_skb->len - data_offset); - } - } - - if (new_skb->ip_summed == CHECKSUM_PARTIAL) { - qeth_tx_csum(new_skb, &hdr->hdr.l3.ext_flags, ipv); - if (card->options.performance_stats) - card->perf_stats.tx_csum++; - } + hdr = skb_push(new_skb, sizeof(struct qeth_hdr)); + qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type, + new_skb->len - sizeof(struct qeth_hdr)); } elements = use_tso ? qeth_l3_get_elements_no_tso(card, new_skb, hdr_elements) : - qeth_get_elements_no(card, new_skb, hdr_elements, - (data_offset > 0) ? data_offset : 0); + qeth_get_elements_no(card, new_skb, hdr_elements, 0); if (!elements) { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); - goto tx_drop; + rc = -E2BIG; + goto out; } elements += hdr_elements; - if (card->info.type != QETH_CARD_TYPE_IQD) { - int len; - if (use_tso) { - hd_len = sizeof(struct qeth_hdr_tso) + - ip_hdrlen(new_skb) + tcp_hdrlen(new_skb); - len = hd_len; - } else { - len = sizeof(struct qeth_hdr_layer3); - } + if (use_tso) { + hd_len = sizeof(struct qeth_hdr_tso) + + ip_hdrlen(new_skb) + tcp_hdrlen(new_skb); + len = hd_len; + } else { + hd_len = 0; + len = sizeof(struct qeth_hdr_layer3); + } - if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len)) - goto tx_drop; - rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len, - hd_len, elements); - } else - rc = qeth_do_send_packet_fast(queue, new_skb, hdr, data_offset, - hd_len); + if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len)) { + rc = -EINVAL; + goto out; + } + is_sg = skb_is_nonlinear(new_skb); + rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len, hd_len, + elements); +out: if (!rc) { - card->stats.tx_packets++; - card->stats.tx_bytes += tx_bytes; if (new_skb != skb) dev_kfree_skb_any(skb); if (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; if (use_tso) { card->perf_stats.large_send_bytes += tx_bytes; card->perf_stats.large_send_cnt++; } - if (nr_frags) { - card->perf_stats.sg_skbs_sent++; - /* nr_frags + skb->data */ - card->perf_stats.sg_frags_sent += nr_frags + 1; - } } - rc = NETDEV_TX_OK; } else { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); + if (new_skb != skb) + dev_kfree_skb_any(new_skb); + } + return rc; +} - if (rc == -EBUSY) { - if (new_skb != skb) - dev_kfree_skb_any(new_skb); - return NETDEV_TX_BUSY; - } else +static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + int cast_type = qeth_l3_get_cast_type(skb); + struct qeth_card *card = dev->ml_priv; + int ipv = qeth_get_ip_version(skb); + struct qeth_qdio_out_q *queue; + int tx_bytes = skb->len; + int rc; + + if (IS_IQD(card)) { + if (card->options.sniffer) + goto tx_drop; + if ((card->options.cq != QETH_CQ_ENABLED && !ipv) || + (card->options.cq == QETH_CQ_ENABLED && + skb->protocol != htons(ETH_P_AF_IUCV))) goto tx_drop; } - netif_wake_queue(dev); - if (card->options.performance_stats) - card->perf_stats.outbound_time += qeth_get_micros() - - card->perf_stats.outbound_start_time; - return rc; + if (card->state != CARD_STATE_UP || !card->lan_online) { + card->stats.tx_carrier_errors++; + goto tx_drop; + } + + if (cast_type == RTN_BROADCAST && !card->info.broadcast_capable) + goto tx_drop; + + queue = qeth_get_tx_queue(card, skb, ipv, cast_type); + + if (card->options.performance_stats) { + card->perf_stats.outbound_cnt++; + card->perf_stats.outbound_start_time = qeth_get_micros(); + } + netif_stop_queue(dev); + + if (IS_IQD(card) || (!skb_is_gso(skb) && ipv == 4)) + rc = qeth_l3_xmit_offload(card, skb, queue, ipv, cast_type); + else + rc = qeth_l3_xmit(card, skb, queue, ipv, cast_type); + + if (!rc) { + card->stats.tx_packets++; + card->stats.tx_bytes += tx_bytes; + if (card->options.performance_stats) + card->perf_stats.outbound_time += qeth_get_micros() - + card->perf_stats.outbound_start_time; + netif_wake_queue(dev); + return NETDEV_TX_OK; + } else if (rc == -EBUSY) { + return NETDEV_TX_BUSY; + } /* else fall through */ tx_drop: card->stats.tx_dropped++; card->stats.tx_errors++; - if ((new_skb != skb) && new_skb) - dev_kfree_skb_any(new_skb); dev_kfree_skb_any(skb); netif_wake_queue(dev); return NETDEV_TX_OK; @@ -2449,7 +2484,6 @@ static const struct net_device_ops qeth_l3_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, @@ -2466,7 +2500,6 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, @@ -2479,6 +2512,9 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) { int rc; + if (card->dev->netdev_ops) + return 0; + if (card->info.type == QETH_CARD_TYPE_OSD || card->info.type == QETH_CARD_TYPE_OSX) { if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || @@ -2487,9 +2523,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) return -ENODEV; } - card->dev = alloc_etherdev(0); - if (!card->dev) - return -ENODEV; card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; /*IPv6 address autoconfiguration stuff*/ @@ -2497,9 +2530,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD)) card->dev->dev_id = card->info.unique_id & 0xffff; - card->dev->hw_features |= NETIF_F_SG; - card->dev->vlan_features |= NETIF_F_SG; - if (!card->info.guestlan) { card->dev->features |= NETIF_F_SG; card->dev->hw_features |= NETIF_F_TSO | @@ -2513,38 +2543,35 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev->vlan_features |= NETIF_F_IPV6_CSUM; } } else if (card->info.type == QETH_CARD_TYPE_IQD) { - card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, - ether_setup); - if (!card->dev) - return -ENODEV; card->dev->flags |= IFF_NOARP; card->dev->netdev_ops = &qeth_l3_netdev_ops; + rc = qeth_l3_iqd_read_initial_mac(card); if (rc) - return rc; + goto out; + if (card->options.hsuid[0]) memcpy(card->dev->perm_addr, card->options.hsuid, 9); } else return -ENODEV; - card->dev->ml_priv = card; - card->dev->watchdog_timeo = QETH_TX_TIMEOUT; - card->dev->mtu = card->info.initial_mtu; - card->dev->min_mtu = 64; - card->dev->max_mtu = ETH_MAX_MTU; - card->dev->dev_port = card->info.portno; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; + card->dev->needed_headroom = sizeof(struct qeth_hdr) - ETH_HLEN; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; + netif_keep_dst(card->dev); - netif_set_gso_max_size(card->dev, (QETH_MAX_BUFFER_ELEMENTS(card) - 1) * - PAGE_SIZE); + if (card->dev->hw_features & NETIF_F_TSO) + netif_set_gso_max_size(card->dev, + PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1)); - SET_NETDEV_DEV(card->dev, &card->gdev->dev); netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); - netif_carrier_off(card->dev); - return register_netdev(card->dev); + rc = register_netdev(card->dev); +out: + if (rc) + card->dev->netdev_ops = NULL; + return rc; } static const struct device_type qeth_l3_devtype = { @@ -2582,15 +2609,9 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) if (cgdev->state == CCWGROUP_ONLINE) qeth_l3_set_offline(cgdev); - if (card->dev) { - unregister_netdev(card->dev); - free_netdev(card->dev); - card->dev = NULL; - } - + unregister_netdev(card->dev); qeth_l3_clear_ip_htable(card, 0); qeth_l3_clear_ipato_list(card); - return; } static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) @@ -2612,10 +2633,9 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) goto out_remove; } - if (!card->dev && qeth_l3_setup_netdev(card)) { - rc = -ENODEV; + rc = qeth_l3_setup_netdev(card); + if (rc) goto out_remove; - } if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { if (card->info.hwtrap && @@ -2666,11 +2686,12 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) qeth_enable_hw_features(card->dev); if (recover_flag == CARD_STATE_RECOVER) { rtnl_lock(); - if (recovery_mode) + if (recovery_mode) { __qeth_l3_open(card->dev); - else + qeth_l3_set_rx_mode(card->dev); + } else { dev_open(card->dev); - qeth_l3_set_rx_mode(card->dev); + } rtnl_unlock(); } qeth_trace_features(card); @@ -2711,8 +2732,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev && netif_carrier_ok(card->dev)) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); @@ -2780,8 +2800,7 @@ 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); + 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) @@ -2814,8 +2833,7 @@ static int qeth_l3_pm_resume(struct ccwgroup_device *gdev) rc = __qeth_l3_set_online(card->gdev, 0); out: qeth_set_allowed_threads(card, 0xffffffff, 0); - if (card->dev) - netif_device_attach(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"); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index f61192a048f4..45ac6d8705c6 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -299,8 +299,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, if (strlen(tmp) == 0) { /* delete ip address only */ card->options.hsuid[0] = '\0'; - if (card->dev) - memcpy(card->dev->perm_addr, card->options.hsuid, 9); + memcpy(card->dev->perm_addr, card->options.hsuid, 9); qeth_configure_cq(card, QETH_CQ_DISABLED); return count; } @@ -311,8 +310,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, snprintf(card->options.hsuid, sizeof(card->options.hsuid), "%-8s", tmp); ASCEBC(card->options.hsuid, 8); - if (card->dev) - memcpy(card->dev->perm_addr, card->options.hsuid, 9); + memcpy(card->dev->perm_addr, card->options.hsuid, 9); rc = qeth_l3_modify_hsuid(card, true); |