summaryrefslogtreecommitdiffstats
path: root/drivers/s390/net/qeth_l3_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net/qeth_l3_main.c')
-rw-r--r--drivers/s390/net/qeth_l3_main.c267
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;