qeth: l2 write unicast list to hardware
[safe/jmp/linux-2.6] / drivers / s390 / net / qeth_l2_main.c
index 0263d94..3ac3cc1 100644 (file)
 #include "qeth_core.h"
 #include "qeth_core_offl.h"
 
-#define QETH_DBF_TEXT_(name, level, text...) \
-       do { \
-               if (qeth_dbf_passes(qeth_dbf_##name, level)) { \
-                       char *dbf_txt_buf = get_cpu_var(qeth_l2_dbf_txt_buf); \
-                       sprintf(dbf_txt_buf, text); \
-                       debug_text_event(qeth_dbf_##name, level, dbf_txt_buf); \
-                       put_cpu_var(qeth_l2_dbf_txt_buf); \
-               } \
-       } while (0)
-
-static DEFINE_PER_CPU(char[256], qeth_l2_dbf_txt_buf);
-
 static int qeth_l2_set_offline(struct ccwgroup_device *);
 static int qeth_l2_stop(struct net_device *);
 static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
@@ -47,7 +35,7 @@ static int qeth_l2_recover(void *);
 
 static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct mii_ioctl_data *mii_data;
        int rc = 0;
 
@@ -87,7 +75,7 @@ static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                rc = -EOPNOTSUPP;
        }
        if (rc)
-               QETH_DBF_TEXT_(trace, 2, "ioce%d", rc);
+               QETH_DBF_TEXT_(TRACE, 2, "ioce%d", rc);
        return rc;
 }
 
@@ -113,19 +101,16 @@ static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no)
 {
        struct qeth_card *card;
        struct net_device *ndev;
-       unsigned char *readno;
-       __u16 temp_dev_no, card_dev_no;
-       char *endp;
+       __u16 temp_dev_no;
        unsigned long flags;
+       struct ccw_dev_id read_devid;
 
        ndev = NULL;
        memcpy(&temp_dev_no, read_dev_no, 2);
        read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
        list_for_each_entry(card, &qeth_core_card_list.list, list) {
-               readno = CARD_RDEV_ID(card);
-               readno += (strlen(readno) - 4);
-               card_dev_no = simple_strtoul(readno, &endp, 16);
-               if (card_dev_no == temp_dev_no) {
+               ccw_device_get_id(CARD_RDEV(card), &read_devid);
+               if (read_devid.devno == temp_dev_no) {
                        ndev = card->dev;
                        break;
                }
@@ -141,19 +126,19 @@ static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
        struct qeth_ipa_cmd *cmd;
        __u8 *mac;
 
-       QETH_DBF_TEXT(trace, 2, "L2Sgmacb");
+       QETH_DBF_TEXT(TRACE, 2, "L2Sgmacb");
        cmd = (struct qeth_ipa_cmd *) data;
        mac = &cmd->data.setdelmac.mac[0];
        /* MAC already registered, needed in couple/uncouple case */
        if (cmd->hdr.return_code == 0x2005) {
-               PRINT_WARN("Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
+               QETH_DBF_MESSAGE(2, "Group MAC %02x:%02x:%02x:%02x:%02x:%02x "
                          "already existing on %s \n",
                          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
                          QETH_CARD_IFNAME(card));
                cmd->hdr.return_code = 0;
        }
        if (cmd->hdr.return_code)
-               PRINT_ERR("Could not set group MAC " \
+               QETH_DBF_MESSAGE(2, "Could not set group MAC "
                          "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
                          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
                          QETH_CARD_IFNAME(card), cmd->hdr.return_code);
@@ -162,7 +147,7 @@ static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
 
 static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
 {
-       QETH_DBF_TEXT(trace, 2, "L2Sgmac");
+       QETH_DBF_TEXT(TRACE, 2, "L2Sgmac");
        return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
                                          qeth_l2_send_setgroupmac_cb);
 }
@@ -174,11 +159,11 @@ static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
        struct qeth_ipa_cmd *cmd;
        __u8 *mac;
 
-       QETH_DBF_TEXT(trace, 2, "L2Dgmacb");
+       QETH_DBF_TEXT(TRACE, 2, "L2Dgmacb");
        cmd = (struct qeth_ipa_cmd *) data;
        mac = &cmd->data.setdelmac.mac[0];
        if (cmd->hdr.return_code)
-               PRINT_ERR("Could not delete group MAC " \
+               QETH_DBF_MESSAGE(2, "Could not delete group MAC "
                          "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
                          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
                          QETH_CARD_IFNAME(card), cmd->hdr.return_code);
@@ -187,26 +172,33 @@ static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
 
 static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
 {
-       QETH_DBF_TEXT(trace, 2, "L2Dgmac");
+       QETH_DBF_TEXT(TRACE, 2, "L2Dgmac");
        return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
                                          qeth_l2_send_delgroupmac_cb);
 }
 
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac)
+static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
 {
        struct qeth_mc_mac *mc;
+       int rc;
 
        mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
 
-       if (!mc) {
-               PRINT_ERR("no mem vor mc mac address\n");
+       if (!mc)
                return;
-       }
 
        memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
        mc->mc_addrlen = OSA_ADDR_LEN;
+       mc->is_vmac = vmac;
+
+       if (vmac) {
+               rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+                                       NULL);
+       } else {
+               rc = qeth_l2_send_setgroupmac(card, mac);
+       }
 
-       if (!qeth_l2_send_setgroupmac(card, mac))
+       if (!rc)
                list_add_tail(&mc->list, &card->mc_list);
        else
                kfree(mc);
@@ -218,7 +210,11 @@ static void qeth_l2_del_all_mc(struct qeth_card *card)
 
        spin_lock_bh(&card->mclock);
        list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
-               qeth_l2_send_delgroupmac(card, mc->mc_addr);
+               if (mc->is_vmac)
+                       qeth_l2_send_setdelmac(card, mc->mc_addr,
+                                       IPA_CMD_DELVMAC, NULL);
+               else
+                       qeth_l2_send_delgroupmac(card, mc->mc_addr);
                list_del(&mc->list);
                kfree(mc);
        }
@@ -260,8 +256,7 @@ static void qeth_l2_get_packet_type(struct qeth_card *card,
 static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        struct sk_buff *skb, int ipv, int cast_type)
 {
-       struct vlan_ethhdr *veth = (struct vlan_ethhdr *)((skb->data) +
-                                       QETH_HEADER_SIZE);
+       struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb);
 
        memset(hdr, 0, sizeof(struct qeth_hdr));
        hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
@@ -289,15 +284,15 @@ static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
 {
        struct qeth_ipa_cmd *cmd;
 
-       QETH_DBF_TEXT(trace, 2, "L2sdvcb");
+       QETH_DBF_TEXT(TRACE, 2, "L2sdvcb");
        cmd = (struct qeth_ipa_cmd *) data;
        if (cmd->hdr.return_code) {
-               PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
+               QETH_DBF_MESSAGE(2, "Error in processing VLAN %i on %s: 0x%x. "
                          "Continuing\n", cmd->data.setdelvlan.vlan_id,
                          QETH_CARD_IFNAME(card), cmd->hdr.return_code);
-               QETH_DBF_TEXT_(trace, 2, "L2VL%4x", cmd->hdr.command);
-               QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
-               QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+               QETH_DBF_TEXT_(TRACE, 2, "L2VL%4x", cmd->hdr.command);
+               QETH_DBF_TEXT_(TRACE, 2, "L2%s", CARD_BUS_ID(card));
+               QETH_DBF_TEXT_(TRACE, 2, "err%d", cmd->hdr.return_code);
        }
        return 0;
 }
@@ -308,7 +303,7 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
        struct qeth_ipa_cmd *cmd;
        struct qeth_cmd_buffer *iob;
 
-       QETH_DBF_TEXT_(trace, 4, "L2sdv%x", ipacmd);
+       QETH_DBF_TEXT_(TRACE, 4, "L2sdv%x", ipacmd);
        iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        cmd->data.setdelvlan.vlan_id = i;
@@ -319,7 +314,7 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
 static void qeth_l2_process_vlans(struct qeth_card *card, int clear)
 {
        struct qeth_vlan_vid *id;
-       QETH_DBF_TEXT(trace, 3, "L2prcvln");
+       QETH_DBF_TEXT(TRACE, 3, "L2prcvln");
        spin_lock_bh(&card->vlanlock);
        list_for_each_entry(id, &card->vid_list, list) {
                if (clear)
@@ -334,10 +329,10 @@ static void qeth_l2_process_vlans(struct qeth_card *card, int clear)
 
 static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct qeth_vlan_vid *id;
 
-       QETH_DBF_TEXT_(trace, 4, "aid:%d", vid);
+       QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid);
        id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC);
        if (id) {
                id->vid = vid;
@@ -345,17 +340,15 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
                spin_lock_bh(&card->vlanlock);
                list_add_tail(&id->list, &card->vid_list);
                spin_unlock_bh(&card->vlanlock);
-       } else {
-               PRINT_ERR("no memory for vid\n");
        }
 }
 
 static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
        struct qeth_vlan_vid *id, *tmpid = NULL;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
-       QETH_DBF_TEXT_(trace, 4, "kid:%d", vid);
+       QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
        spin_lock_bh(&card->vlanlock);
        list_for_each_entry(id, &card->vid_list, list) {
                if (id->vid == vid) {
@@ -376,8 +369,8 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
 {
        int rc = 0;
 
-       QETH_DBF_TEXT(setup , 2, "stopcard");
-       QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+       QETH_DBF_TEXT(SETUP , 2, "stopcard");
+       QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
 
        qeth_set_allowed_threads(card, 0, 1);
        if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD))
@@ -396,7 +389,7 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
                if (!card->use_hard_stop) {
                        __u8 *mac = &card->dev->dev_addr[0];
                        rc = qeth_l2_send_delmac(card, mac);
-                       QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
+                       QETH_DBF_TEXT_(SETUP, 2, "Lerr%d", rc);
                }
                card->state = CARD_STATE_SOFTSETUP;
        }
@@ -465,8 +458,8 @@ static void qeth_l2_process_inbound_buffer(struct qeth_card *card,
                        break;
                default:
                        dev_kfree_skb_any(skb);
-                       QETH_DBF_TEXT(trace, 3, "inbunkno");
-                       QETH_DBF_HEX(control, 3, hdr, QETH_DBF_CONTROL_LEN);
+                       QETH_DBF_TEXT(TRACE, 3, "inbunkno");
+                       QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
                        continue;
                }
                card->dev->last_rx = jiffies;
@@ -484,7 +477,7 @@ static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
        struct qeth_ipa_cmd *cmd;
        struct qeth_cmd_buffer *iob;
 
-       QETH_DBF_TEXT(trace, 2, "L2sdmac");
+       QETH_DBF_TEXT(TRACE, 2, "L2sdmac");
        iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
@@ -498,10 +491,10 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,
 {
        struct qeth_ipa_cmd *cmd;
 
-       QETH_DBF_TEXT(trace, 2, "L2Smaccb");
+       QETH_DBF_TEXT(TRACE, 2, "L2Smaccb");
        cmd = (struct qeth_ipa_cmd *) data;
        if (cmd->hdr.return_code) {
-               QETH_DBF_TEXT_(trace, 2, "L2er%x", cmd->hdr.return_code);
+               QETH_DBF_TEXT_(TRACE, 2, "L2er%x", cmd->hdr.return_code);
                card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
                cmd->hdr.return_code = -EIO;
        } else {
@@ -520,7 +513,7 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,
 
 static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
 {
-       QETH_DBF_TEXT(trace, 2, "L2Setmac");
+       QETH_DBF_TEXT(TRACE, 2, "L2Setmac");
        return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
                                          qeth_l2_send_setmac_cb);
 }
@@ -531,10 +524,10 @@ static int qeth_l2_send_delmac_cb(struct qeth_card *card,
 {
        struct qeth_ipa_cmd *cmd;
 
-       QETH_DBF_TEXT(trace, 2, "L2Dmaccb");
+       QETH_DBF_TEXT(TRACE, 2, "L2Dmaccb");
        cmd = (struct qeth_ipa_cmd *) data;
        if (cmd->hdr.return_code) {
-               QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+               QETH_DBF_TEXT_(TRACE, 2, "err%d", cmd->hdr.return_code);
                cmd->hdr.return_code = -EIO;
                return 0;
        }
@@ -545,7 +538,7 @@ static int qeth_l2_send_delmac_cb(struct qeth_card *card,
 
 static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac)
 {
-       QETH_DBF_TEXT(trace, 2, "L2Delmac");
+       QETH_DBF_TEXT(TRACE, 2, "L2Delmac");
        if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
                return 0;
        return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
@@ -557,25 +550,24 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
        int rc = 0;
        char vendor_pre[] = {0x02, 0x00, 0x00};
 
-       QETH_DBF_TEXT(setup, 2, "doL2init");
-       QETH_DBF_TEXT_(setup, 2, "doL2%s", CARD_BUS_ID(card));
+       QETH_DBF_TEXT(SETUP, 2, "doL2init");
+       QETH_DBF_TEXT_(SETUP, 2, "doL2%s", CARD_BUS_ID(card));
 
        rc = qeth_query_setadapterparms(card);
        if (rc) {
-               PRINT_WARN("could not query adapter parameters on device %s: "
-                          "x%x\n", CARD_BUS_ID(card), rc);
+               QETH_DBF_MESSAGE(2, "could not query adapter parameters on "
+                       "device %s: x%x\n", CARD_BUS_ID(card), rc);
        }
 
        if (card->info.guestlan) {
                rc = qeth_setadpparms_change_macaddr(card);
                if (rc) {
-                       PRINT_WARN("couldn't get MAC address on "
-                          "device %s: x%x\n",
-                          CARD_BUS_ID(card), rc);
-                       QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+                       QETH_DBF_MESSAGE(2, "couldn't get MAC address on "
+                               "device %s: x%x\n", CARD_BUS_ID(card), rc);
+                       QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                        return rc;
                }
-               QETH_DBF_HEX(setup, 2, card->dev->dev_addr, OSA_ADDR_LEN);
+               QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, OSA_ADDR_LEN);
        } else {
                random_ether_addr(card->dev->dev_addr);
                memcpy(card->dev->dev_addr, vendor_pre, 3);
@@ -586,24 +578,22 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
 static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 {
        struct sockaddr *addr = p;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
-       QETH_DBF_TEXT(trace, 3, "setmac");
+       QETH_DBF_TEXT(TRACE, 3, "setmac");
 
        if (qeth_l2_verify_dev(dev) != QETH_REAL_CARD) {
-               QETH_DBF_TEXT(trace, 3, "setmcINV");
+               QETH_DBF_TEXT(TRACE, 3, "setmcINV");
                return -EOPNOTSUPP;
        }
 
        if (card->info.type == QETH_CARD_TYPE_OSN) {
-               PRINT_WARN("Setting MAC address on %s is not supported.\n",
-                          dev->name);
-               QETH_DBF_TEXT(trace, 3, "setmcOSN");
+               QETH_DBF_TEXT(TRACE, 3, "setmcOSN");
                return -EOPNOTSUPP;
        }
-       QETH_DBF_TEXT_(trace, 3, "%s", CARD_BUS_ID(card));
-       QETH_DBF_HEX(trace, 3, addr->sa_data, OSA_ADDR_LEN);
+       QETH_DBF_TEXT_(TRACE, 3, "%s", CARD_BUS_ID(card));
+       QETH_DBF_HEX(TRACE, 3, addr->sa_data, OSA_ADDR_LEN);
        rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]);
        if (!rc)
                rc = qeth_l2_send_setmac(card, addr->sa_data);
@@ -612,17 +602,21 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 
 static void qeth_l2_set_multicast_list(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
-       struct dev_mc_list *dm;
+       struct qeth_card *card = dev->ml_priv;
+       struct dev_addr_list *dm;
 
        if (card->info.type == QETH_CARD_TYPE_OSN)
                return ;
 
-       QETH_DBF_TEXT(trace, 3, "setmulti");
+       QETH_DBF_TEXT(TRACE, 3, "setmulti");
        qeth_l2_del_all_mc(card);
        spin_lock_bh(&card->mclock);
        for (dm = dev->mc_list; dm; dm = dm->next)
-               qeth_l2_add_mc(card, dm->dmi_addr);
+               qeth_l2_add_mc(card, dm->da_addr, 0);
+
+       for (dm = dev->uc_list; dm; dm = dm->next)
+               qeth_l2_add_mc(card, dm->da_addr, 1);
+
        spin_unlock_bh(&card->mclock);
        if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
                return;
@@ -634,7 +628,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int rc;
        struct qeth_hdr *hdr = NULL;
        int elements = 0;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = skb;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_get_cast_type(card, skb);
@@ -643,8 +637,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int tx_bytes = skb->len;
        enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
        struct qeth_eddp_context *ctx = NULL;
-
-       QETH_DBF_TEXT(trace, 6, "l2xmit");
+       int data_offset = -1;
+       int elements_needed = 0;
+       int hd_len = 0;
 
        if ((card->state != CARD_STATE_UP) || !card->lan_online) {
                card->stats.tx_carrier_errors++;
@@ -667,23 +662,49 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (card->info.type == QETH_CARD_TYPE_OSN)
                hdr = (struct qeth_hdr *)skb->data;
        else {
-               new_skb = qeth_prepare_skb(card, skb, &hdr);
-               if (!new_skb)
-                       goto tx_drop;
-               qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+               if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) &&
+                   (skb_shinfo(skb)->nr_frags == 0)) {
+                       new_skb = skb;
+                       data_offset = ETH_HLEN;
+                       hd_len = ETH_HLEN;
+                       hdr = kmem_cache_alloc(qeth_core_header_cache,
+                                               GFP_ATOMIC);
+                       if (!hdr)
+                               goto tx_drop;
+                       elements_needed++;
+                       skb_reset_mac_header(new_skb);
+                       qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+                       hdr->hdr.l2.pkt_length = new_skb->len;
+                       memcpy(((char *)hdr) + sizeof(struct qeth_hdr),
+                               skb_mac_header(new_skb), ETH_HLEN);
+               } else {
+                       /* create a clone with writeable headroom */
+                       new_skb = skb_realloc_headroom(skb,
+                                               sizeof(struct qeth_hdr));
+                       if (!new_skb)
+                               goto tx_drop;
+                       hdr = (struct qeth_hdr *)skb_push(new_skb,
+                                               sizeof(struct qeth_hdr));
+                       skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
+                       qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type);
+               }
        }
 
        if (large_send == QETH_LARGE_SEND_EDDP) {
                ctx = qeth_eddp_create_context(card, new_skb, hdr,
                                                skb->sk->sk_protocol);
                if (ctx == NULL) {
-                       PRINT_WARN("could not create eddp context\n");
+                       QETH_DBF_MESSAGE(2, "could not create eddp context\n");
                        goto tx_drop;
                }
        } else {
-               elements = qeth_get_elements_no(card, (void *)hdr, new_skb, 0);
-               if (!elements)
+               elements = qeth_get_elements_no(card, (void *)hdr, new_skb,
+                                               elements_needed);
+               if (!elements) {
+                       if (data_offset >= 0)
+                               kmem_cache_free(qeth_core_header_cache, hdr);
                        goto tx_drop;
+               }
        }
 
        if ((large_send == QETH_LARGE_SEND_NO) &&
@@ -695,7 +716,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                         elements, ctx);
        else
                rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
-                                             elements, ctx);
+                                       elements, ctx, data_offset, hd_len);
        if (!rc) {
                card->stats.tx_packets++;
                card->stats.tx_bytes += tx_bytes;
@@ -722,6 +743,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                if (ctx != NULL)
                        qeth_eddp_put_context(ctx);
 
+               if (data_offset >= 0)
+                       kmem_cache_free(qeth_core_header_cache, hdr);
+
                if (rc == -EBUSY) {
                        if (new_skb != skb)
                                dev_kfree_skb_any(new_skb);
@@ -742,12 +766,12 @@ tx_drop:
        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;
 }
 
 static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
-                       unsigned int status, unsigned int qdio_err,
-                       unsigned int siga_err, unsigned int queue,
+                       unsigned int qdio_err, unsigned int queue,
                        int first_element, int count, unsigned long card_ptr)
 {
        struct net_device *net_dev;
@@ -756,30 +780,26 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
        int index;
        int i;
 
-       QETH_DBF_TEXT(trace, 6, "qdinput");
        card = (struct qeth_card *) card_ptr;
        net_dev = card->dev;
        if (card->options.performance_stats) {
                card->perf_stats.inbound_cnt++;
                card->perf_stats.inbound_start_time = qeth_get_micros();
        }
-       if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
-               if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
-                       QETH_DBF_TEXT(trace, 1, "qdinchk");
-                       QETH_DBF_TEXT_(trace, 1, "%s", CARD_BUS_ID(card));
-                       QETH_DBF_TEXT_(trace, 1, "%04X%04X", first_element,
-                                       count);
-                       QETH_DBF_TEXT_(trace, 1, "%04X%04X", queue, status);
-                       qeth_schedule_recovery(card);
-                       return;
-               }
+       if (qdio_err & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
+               QETH_DBF_TEXT(TRACE, 1, "qdinchk");
+               QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
+               QETH_DBF_TEXT_(TRACE, 1, "%04X%04X", first_element,
+                               count);
+               QETH_DBF_TEXT_(TRACE, 1, "%04X", queue);
+               qeth_schedule_recovery(card);
+               return;
        }
        for (i = first_element; i < (first_element + count); ++i) {
                index = i % QDIO_MAX_BUFFERS_PER_Q;
                buffer = &card->qdio.in_q->bufs[index];
-               if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
-                     qeth_check_qdio_errors(buffer->buffer,
-                                            qdio_err, siga_err, "qinerr")))
+               if (!(qdio_err &&
+                     qeth_check_qdio_errors(buffer->buffer, qdio_err, "qinerr")))
                        qeth_l2_process_inbound_buffer(card, buffer, index);
                /* clear buffer and give back to hardware */
                qeth_put_buffer_pool_entry(card, buffer->pool_entry);
@@ -792,15 +812,15 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
 
 static int qeth_l2_open(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
-       QETH_DBF_TEXT(trace, 4, "qethopen");
+       QETH_DBF_TEXT(TRACE, 4, "qethopen");
        if (card->state != CARD_STATE_SOFTSETUP)
                return -ENODEV;
 
        if ((card->info.type != QETH_CARD_TYPE_OSN) &&
             (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
-               QETH_DBF_TEXT(trace, 4, "nomacadr");
+               QETH_DBF_TEXT(TRACE, 4, "nomacadr");
                return -EPERM;
        }
        card->data.state = CH_STATE_UP;
@@ -816,9 +836,9 @@ static int qeth_l2_open(struct net_device *dev)
 
 static int qeth_l2_stop(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
-       QETH_DBF_TEXT(trace, 4, "qethstop");
+       QETH_DBF_TEXT(TRACE, 4, "qethstop");
        netif_tx_disable(dev);
        card->dev->flags &= ~IFF_UP;
        if (card->state == CARD_STATE_UP)
@@ -861,6 +881,22 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
        return;
 }
 
+static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       if (data) {
+               if (card->options.large_send == QETH_LARGE_SEND_NO) {
+                       card->options.large_send = QETH_LARGE_SEND_EDDP;
+                       dev->features |= NETIF_F_TSO;
+               }
+       } else {
+               dev->features &= ~NETIF_F_TSO;
+               card->options.large_send = QETH_LARGE_SEND_NO;
+       }
+       return 0;
+}
+
 static struct ethtool_ops qeth_l2_ethtool_ops = {
        .get_link = ethtool_op_get_link,
        .get_tx_csum = ethtool_op_get_tx_csum,
@@ -868,11 +904,12 @@ static struct ethtool_ops qeth_l2_ethtool_ops = {
        .get_sg = ethtool_op_get_sg,
        .set_sg = ethtool_op_set_sg,
        .get_tso = ethtool_op_get_tso,
-       .set_tso = ethtool_op_set_tso,
+       .set_tso = qeth_l2_ethtool_set_tso,
        .get_strings = qeth_core_get_strings,
        .get_ethtool_stats = qeth_core_get_ethtool_stats,
        .get_stats_count = qeth_core_get_stats_count,
        .get_drvinfo = qeth_core_get_drvinfo,
+       .get_settings = qeth_core_ethtool_get_settings,
 };
 
 static struct ethtool_ops qeth_l2_osn_ops = {
@@ -902,7 +939,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        if (!card->dev)
                return -ENODEV;
 
-       card->dev->priv = card;
+       card->dev->ml_priv = card;
        card->dev->tx_timeout = &qeth_tx_timeout;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->open = qeth_l2_open;
@@ -934,8 +971,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
        enum qeth_card_states recover_flag;
 
        BUG_ON(!card);
-       QETH_DBF_TEXT(setup, 2, "setonlin");
-       QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
+       QETH_DBF_TEXT(SETUP, 2, "setonlin");
+       QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
 
        qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1);
        if (qeth_wait_for_threads(card, ~QETH_RECOVER_THREAD)) {
@@ -947,23 +984,23 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
        recover_flag = card->state;
        rc = ccw_device_set_online(CARD_RDEV(card));
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                return -EIO;
        }
        rc = ccw_device_set_online(CARD_WDEV(card));
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                return -EIO;
        }
        rc = ccw_device_set_online(CARD_DDEV(card));
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                return -EIO;
        }
 
        rc = qeth_core_hardsetup_card(card);
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
                goto out_remove;
        }
 
@@ -977,11 +1014,11 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
        qeth_print_status_message(card);
 
        /* softsetup */
-       QETH_DBF_TEXT(setup, 2, "softsetp");
+       QETH_DBF_TEXT(SETUP, 2, "softsetp");
 
        rc = qeth_send_startlan(card);
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                if (rc == 0xe080) {
                        PRINT_WARN("LAN on card %s if offline! "
                                   "Waiting for STARTLAN from card.\n",
@@ -1001,7 +1038,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 
        rc = qeth_init_qdio_queues(card);
        if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
                goto out_remove;
        }
        card->state = CARD_STATE_SOFTSETUP;
@@ -1048,8 +1085,8 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
        int rc = 0, rc2 = 0, rc3 = 0;
        enum qeth_card_states recover_flag;
 
-       QETH_DBF_TEXT(setup, 3, "setoffl");
-       QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
+       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);
@@ -1065,7 +1102,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
        if (!rc)
                rc = (rc2) ? rc2 : rc3;
        if (rc)
-               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
        if (recover_flag == CARD_STATE_UP)
                card->state = CARD_STATE_RECOVER;
        /* let user_space know that device is offline */
@@ -1084,11 +1121,11 @@ static int qeth_l2_recover(void *ptr)
        int rc = 0;
 
        card = (struct qeth_card *) ptr;
-       QETH_DBF_TEXT(trace, 2, "recover1");
-       QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
+       QETH_DBF_TEXT(TRACE, 2, "recover1");
+       QETH_DBF_HEX(TRACE, 2, &card, sizeof(void *));
        if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
                return 0;
-       QETH_DBF_TEXT(trace, 2, "recover2");
+       QETH_DBF_TEXT(TRACE, 2, "recover2");
        PRINT_WARN("Recovery of device %s started ...\n",
                   CARD_BUS_ID(card));
        card->use_hard_stop = 1;
@@ -1139,20 +1176,20 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
        unsigned long flags;
        int rc = 0;
 
-       QETH_DBF_TEXT(trace, 5, "osndctrd");
+       QETH_DBF_TEXT(TRACE, 5, "osndctrd");
 
        wait_event(card->wait_q,
                   atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
        qeth_prepare_control_data(card, len, iob);
-       QETH_DBF_TEXT(trace, 6, "osnoirqp");
+       QETH_DBF_TEXT(TRACE, 6, "osnoirqp");
        spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
        rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
                              (addr_t) iob, 0, 0);
        spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
        if (rc) {
-               PRINT_WARN("qeth_osn_send_control_data: "
+               QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
                           "ccw_device_start rc = %i\n", rc);
-               QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+               QETH_DBF_TEXT_(TRACE, 2, " err%d", rc);
                qeth_release_buffer(iob->channel, iob);
                atomic_set(&card->write.irq_pending, 0);
                wake_up(&card->wait_q);
@@ -1165,7 +1202,7 @@ static int qeth_osn_send_ipa_cmd(struct qeth_card *card,
 {
        u16 s1, s2;
 
-       QETH_DBF_TEXT(trace, 4, "osndipa");
+       QETH_DBF_TEXT(TRACE, 4, "osndipa");
 
        qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
        s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
@@ -1183,10 +1220,10 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
        struct qeth_card *card;
        int rc;
 
-       QETH_DBF_TEXT(trace, 2, "osnsdmc");
+       QETH_DBF_TEXT(TRACE, 2, "osnsdmc");
        if (!dev)
                return -ENODEV;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return -ENODEV;
        if ((card->state != CARD_STATE_UP) &&
@@ -1205,11 +1242,11 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
 {
        struct qeth_card *card;
 
-       QETH_DBF_TEXT(trace, 2, "osnreg");
+       QETH_DBF_TEXT(TRACE, 2, "osnreg");
        *dev = qeth_l2_netdev_by_devno(read_dev_no);
        if (*dev == NULL)
                return -ENODEV;
-       card = netdev_priv(*dev);
+       card = (*dev)->ml_priv;
        if (!card)
                return -ENODEV;
        if ((assist_cb == NULL) || (data_cb == NULL))
@@ -1224,10 +1261,10 @@ void qeth_osn_deregister(struct net_device *dev)
 {
        struct qeth_card *card;
 
-       QETH_DBF_TEXT(trace, 2, "osndereg");
+       QETH_DBF_TEXT(TRACE, 2, "osndereg");
        if (!dev)
                return;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return;
        card->osn_info.assist_cb = NULL;