summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/net/qeth_core.h1
-rw-r--r--drivers/s390/net/qeth_core_main.c100
-rw-r--r--drivers/s390/net/qeth_core_mpc.h13
-rw-r--r--drivers/s390/net/qeth_l2_main.c3
-rw-r--r--drivers/s390/net/qeth_l3_main.c38
5 files changed, 136 insertions, 19 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 4abc79d3963f..ec7921b5138e 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -906,6 +906,7 @@ void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char);
struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);
int qeth_mdio_read(struct net_device *, int, int);
int qeth_snmp_command(struct qeth_card *, char __user *);
+int qeth_query_oat_command(struct qeth_card *, char __user *);
struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *, __u32, __u32);
int qeth_default_setadapterparms_cb(struct qeth_card *, struct qeth_reply *,
unsigned long);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 9c3f38da4c01..cbb101828dc3 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -26,6 +26,7 @@
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/sysinfo.h>
+#include <asm/compat.h>
#include "qeth_core.h"
@@ -677,6 +678,7 @@ void qeth_release_buffer(struct qeth_channel *channel,
iob->callback = qeth_send_control_data_cb;
iob->rc = 0;
spin_unlock_irqrestore(&channel->iob_lock, flags);
+ wake_up(&channel->wait_q);
}
EXPORT_SYMBOL_GPL(qeth_release_buffer);
@@ -4402,6 +4404,104 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)
}
EXPORT_SYMBOL_GPL(qeth_snmp_command);
+static int qeth_setadpparms_query_oat_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_qoat_priv *priv;
+ char *resdata;
+ int resdatalen;
+
+ QETH_CARD_TEXT(card, 3, "qoatcb");
+
+ cmd = (struct qeth_ipa_cmd *)data;
+ priv = (struct qeth_qoat_priv *)reply->param;
+ resdatalen = cmd->data.setadapterparms.hdr.cmdlength;
+ resdata = (char *)data + 28;
+
+ if (resdatalen > (priv->buffer_len - priv->response_len)) {
+ cmd->hdr.return_code = IPA_RC_FFFF;
+ return 0;
+ }
+
+ memcpy((priv->buffer + priv->response_len), resdata,
+ resdatalen);
+ priv->response_len += resdatalen;
+
+ if (cmd->data.setadapterparms.hdr.seq_no <
+ cmd->data.setadapterparms.hdr.used_total)
+ return 1;
+ return 0;
+}
+
+int qeth_query_oat_command(struct qeth_card *card, char __user *udata)
+{
+ int rc = 0;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_query_oat *oat_req;
+ struct qeth_query_oat_data oat_data;
+ struct qeth_qoat_priv priv;
+ void __user *tmp;
+
+ QETH_CARD_TEXT(card, 3, "qoatcmd");
+
+ if (!qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (copy_from_user(&oat_data, udata,
+ sizeof(struct qeth_query_oat_data))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ priv.buffer_len = oat_data.buffer_len;
+ priv.response_len = 0;
+ priv.buffer = kzalloc(oat_data.buffer_len, GFP_KERNEL);
+ if (!priv.buffer) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT,
+ sizeof(struct qeth_ipacmd_setadpparms_hdr) +
+ sizeof(struct qeth_query_oat));
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ oat_req = &cmd->data.setadapterparms.data.query_oat;
+ oat_req->subcmd_code = oat_data.command;
+
+ rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_query_oat_cb,
+ &priv);
+ if (!rc) {
+ if (is_compat_task())
+ tmp = compat_ptr(oat_data.ptr);
+ else
+ tmp = (void __user *)(unsigned long)oat_data.ptr;
+
+ if (copy_to_user(tmp, priv.buffer,
+ priv.response_len)) {
+ rc = -EFAULT;
+ goto out_free;
+ }
+
+ oat_data.response_len = priv.response_len;
+
+ if (copy_to_user(udata, &oat_data,
+ sizeof(struct qeth_query_oat_data)))
+ rc = -EFAULT;
+ } else
+ if (rc == IPA_RC_FFFF)
+ rc = -EFAULT;
+
+out_free:
+ kfree(priv.buffer);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_query_oat_command);
+
static inline int qeth_get_qdio_q_format(struct qeth_card *card)
{
switch (card->info.type) {
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index e5a9d1c03839..578e19a2de6b 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -249,6 +249,7 @@ enum qeth_ipa_setadp_cmd {
IPA_SETADP_SET_PROMISC_MODE = 0x00000800L,
IPA_SETADP_SET_DIAG_ASSIST = 0x00002000L,
IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L,
+ IPA_SETADP_QUERY_OAT = 0x00080000L,
};
enum qeth_ipa_mac_ops {
CHANGE_ADDR_READ_MAC = 0,
@@ -398,6 +399,17 @@ struct qeth_set_access_ctrl {
__u32 subcmd_code;
} __attribute__((packed));
+struct qeth_query_oat {
+ __u32 subcmd_code;
+ __u8 reserved[12];
+} __packed;
+
+struct qeth_qoat_priv {
+ __u32 buffer_len;
+ __u32 response_len;
+ char *buffer;
+};
+
struct qeth_ipacmd_setadpparms_hdr {
__u32 supp_hw_cmds;
__u32 reserved1;
@@ -417,6 +429,7 @@ struct qeth_ipacmd_setadpparms {
struct qeth_change_addr change_addr;
struct qeth_snmp_cmd snmp;
struct qeth_set_access_ctrl set_access_ctrl;
+ struct qeth_query_oat query_oat;
__u32 mode;
} data;
} __attribute__ ((packed));
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index c12967133114..e5c9cf15e5c6 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -75,6 +75,9 @@ static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
mii_data->val_out = qeth_mdio_read(dev,
mii_data->phy_id, mii_data->reg_num);
break;
+ case SIOC_QETH_QUERY_OAT:
+ rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data);
+ break;
default:
rc = -EOPNOTSUPP;
}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 9648e4e68337..73bf8889984b 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -28,6 +28,8 @@
#include <net/ip.h>
#include <net/arp.h>
+#include <net/route.h>
+#include <net/ip6_fib.h>
#include <net/ip6_checksum.h>
#include <net/iucv/af_iucv.h>
@@ -2743,6 +2745,9 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
mii_data->phy_id,
mii_data->reg_num);
break;
+ case SIOC_QETH_QUERY_OAT:
+ rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data);
+ break;
default:
rc = -EOPNOTSUPP;
}
@@ -2832,7 +2837,6 @@ static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int ipv, int cast_type)
{
- struct neighbour *n = NULL;
struct dst_entry *dst;
memset(hdr, 0, sizeof(struct qeth_hdr));
@@ -2855,33 +2859,29 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
rcu_read_lock();
dst = skb_dst(skb);
- if (dst)
- n = dst_get_neighbour_noref(dst);
if (ipv == 4) {
+ struct rtable *rt = (struct rtable *) dst;
+ __be32 *pkey = &ip_hdr(skb)->daddr;
+
+ if (rt->rt_gateway)
+ pkey = &rt->rt_gateway;
+
/* IPv4 */
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);
memset(hdr->hdr.l3.dest_addr, 0, 12);
- if (n) {
- *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
- *((u32 *) n->primary_key);
- } else {
- /* fill in destination address used in ip header */
- *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
- ip_hdr(skb)->daddr;
- }
+ *((__be32 *) (&hdr->hdr.l3.dest_addr[12])) = *pkey;
} else if (ipv == 6) {
+ struct rt6_info *rt = (struct rt6_info *) dst;
+ struct in6_addr *pkey = &ipv6_hdr(skb)->daddr;
+
+ if (!ipv6_addr_any(&rt->rt6i_gateway))
+ pkey = &rt->rt6i_gateway;
+
/* IPv6 */
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);
if (card->info.type == QETH_CARD_TYPE_IQD)
hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU;
- if (n) {
- memcpy(hdr->hdr.l3.dest_addr,
- n->primary_key, 16);
- } else {
- /* fill in destination address used in ip header */
- memcpy(hdr->hdr.l3.dest_addr,
- &ipv6_hdr(skb)->daddr, 16);
- }
+ memcpy(hdr->hdr.l3.dest_addr, pkey, 16);
} else {
/* passthrough */
if ((skb->dev->type == ARPHRD_IEEE802_TR) &&