diff options
Diffstat (limited to 'drivers/s390/net/qeth_l3_main.c')
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 267 |
1 files changed, 180 insertions, 87 deletions
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 74d1401a5d5..d09b0c44fc3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -30,6 +30,7 @@ #include "qeth_l3.h" + static int qeth_l3_set_offline(struct ccwgroup_device *); static int qeth_l3_recover(void *); static int qeth_l3_stop(struct net_device *); @@ -455,8 +456,11 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "sdiplist"); QETH_CARD_HEX(card, 2, &card, sizeof(void *)); - if (card->options.sniffer) + if ((card->state != CARD_STATE_UP && + card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) { return; + } + spin_lock_irqsave(&card->ip_lock, flags); tbd_list = card->ip_tbd_list; card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC); @@ -1796,7 +1800,8 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) char buf[MAX_ADDR_LEN]; QETH_CARD_TEXT(card, 4, "addmc"); - for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rcu_dereference(im4->next_rcu)) { qeth_l3_get_mac_for_ipm(im4->multiaddr, buf, in4_dev->dev); ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); if (!ipm) @@ -1828,9 +1833,9 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) in_dev = in_dev_get(netdev); if (!in_dev) continue; - read_lock(&in_dev->mc_list_lock); + rcu_read_lock(); qeth_l3_add_mc(card, in_dev); - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in_dev); } } @@ -1843,10 +1848,10 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) in4_dev = in_dev_get(card->dev); if (in4_dev == NULL) return; - read_lock(&in4_dev->mc_list_lock); + rcu_read_lock(); qeth_l3_add_mc(card, in4_dev); qeth_l3_add_vlan_mc(card); - read_unlock(&in4_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in4_dev); } @@ -2454,22 +2459,46 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) return rc; } -static void qeth_l3_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo, - struct qeth_arp_query_data *qdata, int entry_size, - int uentry_size) +static __u32 get_arp_entry_size(struct qeth_card *card, + struct qeth_arp_query_data *qdata, + struct qeth_arp_entrytype *type, __u8 strip_entries) { - char *entry_ptr; - char *uentry_ptr; - int i; + __u32 rc; + __u8 is_hsi; - entry_ptr = (char *)&qdata->data; - uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset); - for (i = 0; i < qdata->no_entries; ++i) { - /* strip off 32 bytes "media specific information" */ - memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32); - entry_ptr += entry_size; - uentry_ptr += uentry_size; + is_hsi = qdata->reply_bits == 5; + if (type->ip == QETHARP_IP_ADDR_V4) { + QETH_CARD_TEXT(card, 4, "arpev4"); + if (strip_entries) { + rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) : + sizeof(struct qeth_arp_qi_entry7_short); + } else { + rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) : + sizeof(struct qeth_arp_qi_entry7); + } + } else if (type->ip == QETHARP_IP_ADDR_V6) { + QETH_CARD_TEXT(card, 4, "arpev6"); + if (strip_entries) { + rc = is_hsi ? + sizeof(struct qeth_arp_qi_entry5_short_ipv6) : + sizeof(struct qeth_arp_qi_entry7_short_ipv6); + } else { + rc = is_hsi ? + sizeof(struct qeth_arp_qi_entry5_ipv6) : + sizeof(struct qeth_arp_qi_entry7_ipv6); + } + } else { + QETH_CARD_TEXT(card, 4, "arpinv"); + rc = 0; } + + return rc; +} + +static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot) +{ + return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) || + (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6); } static int qeth_l3_arp_query_cb(struct qeth_card *card, @@ -2478,72 +2507,77 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, struct qeth_ipa_cmd *cmd; struct qeth_arp_query_data *qdata; struct qeth_arp_query_info *qinfo; - int entry_size; - int uentry_size; int i; + int e; + int entrybytes_done; + int stripped_bytes; + __u8 do_strip_entries; - QETH_CARD_TEXT(card, 4, "arpquecb"); + QETH_CARD_TEXT(card, 3, "arpquecb"); qinfo = (struct qeth_arp_query_info *) reply->param; cmd = (struct qeth_ipa_cmd *) data; + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version); if (cmd->hdr.return_code) { - QETH_CARD_TEXT_(card, 4, "qaer1%i", cmd->hdr.return_code); + QETH_CARD_TEXT(card, 4, "arpcberr"); + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); return 0; } if (cmd->data.setassparms.hdr.return_code) { cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; - QETH_CARD_TEXT_(card, 4, "qaer2%i", cmd->hdr.return_code); + QETH_CARD_TEXT(card, 4, "setaperr"); + QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); return 0; } qdata = &cmd->data.setassparms.data.query_arp; - switch (qdata->reply_bits) { - case 5: - uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) - uentry_size = sizeof(struct qeth_arp_qi_entry5_short); - break; - case 7: - /* fall through to default */ - default: - /* tr is the same as eth -> entry7 */ - uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) - uentry_size = sizeof(struct qeth_arp_qi_entry7_short); - break; - } - /* check if there is enough room in userspace */ - if ((qinfo->udata_len - qinfo->udata_offset) < - qdata->no_entries * uentry_size){ - QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); - cmd->hdr.return_code = -ENOMEM; - goto out_error; - } - QETH_CARD_TEXT_(card, 4, "anore%i", - cmd->data.setassparms.hdr.number_of_replies); - QETH_CARD_TEXT_(card, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no); QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); - if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) { - /* strip off "media specific information" */ - qeth_l3_copy_arp_entries_stripped(qinfo, qdata, entry_size, - uentry_size); - } else - /*copy entries to user buffer*/ - memcpy(qinfo->udata + qinfo->udata_offset, - (char *)&qdata->data, qdata->no_entries*uentry_size); + do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0; + stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0; + entrybytes_done = 0; + for (e = 0; e < qdata->no_entries; ++e) { + char *cur_entry; + __u32 esize; + struct qeth_arp_entrytype *etype; + + cur_entry = &qdata->data + entrybytes_done; + etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type; + if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) { + QETH_CARD_TEXT(card, 4, "pmis"); + QETH_CARD_TEXT_(card, 4, "%i", etype->ip); + break; + } + esize = get_arp_entry_size(card, qdata, etype, + do_strip_entries); + QETH_CARD_TEXT_(card, 5, "esz%i", esize); + if (!esize) + break; + + if ((qinfo->udata_len - qinfo->udata_offset) < esize) { + QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); + cmd->hdr.return_code = -ENOMEM; + goto out_error; + } - qinfo->no_entries += qdata->no_entries; - qinfo->udata_offset += (qdata->no_entries*uentry_size); + memcpy(qinfo->udata + qinfo->udata_offset, + &qdata->data + entrybytes_done + stripped_bytes, + esize); + entrybytes_done += esize + stripped_bytes; + qinfo->udata_offset += esize; + ++qinfo->no_entries; + } /* check if all replies received ... */ if (cmd->data.setassparms.hdr.seq_no < cmd->data.setassparms.hdr.number_of_replies) return 1; + QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries); memcpy(qinfo->udata, &qinfo->no_entries, 4); /* keep STRIP_ENTRIES flag so the user program can distinguish * stripped entries from normal ones */ if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); + QETH_CARD_TEXT_(card, 4, "rc%i", 0); return 0; out_error: i = 0; @@ -2566,45 +2600,86 @@ static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card, reply_cb, reply_param); } -static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +static int qeth_l3_query_arp_cache_info(struct qeth_card *card, + enum qeth_prot_versions prot, + struct qeth_arp_query_info *qinfo) { struct qeth_cmd_buffer *iob; - struct qeth_arp_query_info qinfo = {0, }; + struct qeth_ipa_cmd *cmd; int tmp; int rc; + QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); + + iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_QUERY_INFO, + sizeof(struct qeth_arp_query_data) - sizeof(char), + prot); + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setassparms.data.query_arp.request_bits = 0x000F; + cmd->data.setassparms.data.query_arp.reply_bits = 0; + cmd->data.setassparms.data.query_arp.no_entries = 0; + rc = qeth_l3_send_ipa_arp_cmd(card, iob, + QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, + qeth_l3_arp_query_cb, (void *)qinfo); + if (rc) { + tmp = rc; + QETH_DBF_MESSAGE(2, + "Error while querying ARP cache on %s: %s " + "(0x%x/%d)\n", QETH_CARD_IFNAME(card), + qeth_l3_arp_get_error_cause(&rc), tmp, tmp); + } + + return rc; +} + +static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) +{ + struct qeth_arp_query_info qinfo = {0, }; + int rc; + QETH_CARD_TEXT(card, 3, "arpquery"); if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ IPA_ARP_PROCESSING)) { - return -EOPNOTSUPP; + QETH_CARD_TEXT(card, 3, "arpqnsup"); + rc = -EOPNOTSUPP; + goto out; } /* get size of userspace buffer and mask_bits -> 6 bytes */ - if (copy_from_user(&qinfo, udata, 6)) - return -EFAULT; + if (copy_from_user(&qinfo, udata, 6)) { + rc = -EFAULT; + goto out; + } qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); - if (!qinfo.udata) - return -ENOMEM; + if (!qinfo.udata) { + rc = -ENOMEM; + goto out; + } qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_QUERY_INFO, - sizeof(int), QETH_PROT_IPV4); - - rc = qeth_l3_send_ipa_arp_cmd(card, iob, - QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, - qeth_l3_arp_query_cb, (void *)&qinfo); + rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo); if (rc) { - tmp = rc; - QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s " - "(0x%x/%d)\n", QETH_CARD_IFNAME(card), - qeth_l3_arp_get_error_cause(&rc), tmp, tmp); if (copy_to_user(udata, qinfo.udata, 4)) rc = -EFAULT; + goto free_and_out; } else { - if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) +#ifdef CONFIG_QETH_IPV6 + if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { + /* fails in case of GuestLAN QDIO mode */ + qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, + &qinfo); + } +#endif + if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { + QETH_CARD_TEXT(card, 4, "qactf"); rc = -EFAULT; + goto free_and_out; + } + QETH_CARD_TEXT_(card, 4, "qacts"); } +free_and_out: kfree(qinfo.udata); +out: return rc; } @@ -2923,7 +2998,9 @@ static inline void qeth_l3_hdr_csum(struct qeth_card *card, */ if (iph->protocol == IPPROTO_UDP) hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP; - hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; + hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ | + QETH_HDR_EXT_CSUM_HDR_REQ; + iph->check = 0; if (card->options.performance_stats) card->perf_stats.tx_csum++; } @@ -2938,6 +3015,7 @@ static void qeth_tso_fill_header(struct qeth_card *card, /*fix header to TSO values ...*/ hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; + hdr->hdr.hdr.l3.length = skb->len - sizeof(struct qeth_hdr_tso); /*set values which are fix for the first approach ...*/ hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); hdr->ext.imb_hdr_no = 1; @@ -3039,7 +3117,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_pull(new_skb, ETH_HLEN); } - if (ipv == 6 && card->vlangrp && + if (ipv != 4 && card->vlangrp && vlan_tx_tag_present(new_skb)) { skb_push(new_skb, VLAN_HLEN); skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); @@ -3164,20 +3242,20 @@ tx_drop: return NETDEV_TX_OK; } -static int qeth_l3_open(struct net_device *dev) +static int __qeth_l3_open(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; int rc = 0; QETH_CARD_TEXT(card, 4, "qethopen"); + if (card->state == CARD_STATE_UP) + return rc; if (card->state != CARD_STATE_SOFTSETUP) return -ENODEV; card->data.state = CH_STATE_UP; card->state = CARD_STATE_UP; netif_start_queue(dev); - if (!card->lan_online && netif_carrier_ok(dev)) - netif_carrier_off(dev); if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) { napi_enable(&card->napi); napi_schedule(&card->napi); @@ -3186,6 +3264,18 @@ static int qeth_l3_open(struct net_device *dev) return rc; } +static int qeth_l3_open(struct net_device *dev) +{ + struct qeth_card *card = dev->ml_priv; + + QETH_CARD_TEXT(card, 5, "qethope_"); + if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { + QETH_CARD_TEXT(card, 3, "openREC"); + return -ERESTARTSYS; + } + return __qeth_l3_open(dev); +} + static int qeth_l3_stop(struct net_device *dev) { struct qeth_card *card = dev->ml_priv; @@ -3449,13 +3539,14 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) dev_warn(&card->gdev->dev, "The LAN is offline\n"); card->lan_online = 0; - goto out; + goto contin; } rc = -ENODEV; goto out_remove; } else card->lan_online = 1; +contin: rc = qeth_l3_setadapter_parms(card); if (rc) QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); @@ -3480,13 +3571,16 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) goto out_remove; } card->state = CARD_STATE_SOFTSETUP; - netif_carrier_on(card->dev); qeth_set_allowed_threads(card, 0xffffffff, 0); qeth_l3_set_ip_addr_list(card); + if (card->lan_online) + netif_carrier_on(card->dev); + else + netif_carrier_off(card->dev); if (recover_flag == CARD_STATE_RECOVER) { if (recovery_mode) - qeth_l3_open(card->dev); + __qeth_l3_open(card->dev); else { rtnl_lock(); dev_open(card->dev); @@ -3496,7 +3590,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); -out: mutex_unlock(&card->conf_mutex); mutex_unlock(&card->discipline_mutex); return 0; |