summaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-12-08 13:28:11 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2019-12-08 13:28:11 -0800
commit95e6ba5133163f8241c9ea2439369cec0452fec6 (patch)
tree3b84a8150537c03573ea8bfbd123e9cd52e19f3d /drivers/s390
parent138f371ddf4ff50207dbe33ebfc237e756cd6222 (diff)
parent0fc75219fe9a3c90631453e9870e4f6d956f0ebc (diff)
downloadlinux-95e6ba5133163f8241c9ea2439369cec0452fec6.tar.bz2
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from David Miller: 1) More jumbo frame fixes in r8169, from Heiner Kallweit. 2) Fix bpf build in minimal configuration, from Alexei Starovoitov. 3) Use after free in slcan driver, from Jouni Hogander. 4) Flower classifier port ranges don't work properly in the HW offload case, from Yoshiki Komachi. 5) Use after free in hns3_nic_maybe_stop_tx(), from Yunsheng Lin. 6) Out of bounds access in mqprio_dump(), from Vladyslav Tarasiuk. 7) Fix flow dissection in dsa TX path, from Alexander Lobakin. 8) Stale syncookie timestampe fixes from Guillaume Nault. [ Did an evil merge to silence a warning introduced by this pull - Linus ] * git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (84 commits) r8169: fix rtl_hw_jumbo_disable for RTL8168evl net_sched: validate TCA_KIND attribute in tc_chain_tmplt_add() r8169: add missing RX enabling for WoL on RTL8125 vhost/vsock: accept only packets with the right dst_cid net: phy: dp83867: fix hfs boot in rgmii mode net: ethernet: ti: cpsw: fix extra rx interrupt inet: protect against too small mtu values. gre: refetch erspan header from skb->data after pskb_may_pull() pppoe: remove redundant BUG_ON() check in pppoe_pernet tcp: Protect accesses to .ts_recent_stamp with {READ,WRITE}_ONCE() tcp: tighten acceptance of ACKs not matching a child socket tcp: fix rejected syncookies due to stale timestamps lpc_eth: kernel BUG on remove tcp: md5: fix potential overestimation of TCP option space net: sched: allow indirect blocks to bind to clsact in TC net: core: rename indirect block ingress cb function net-sysfs: Call dev_hold always in netdev_queue_add_kobject net: dsa: fix flow dissection on Tx path net/tls: Fix return values to avoid ENOTSUPP net: avoid an indirect call in ____sys_recvmsg() ...
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/net/qeth_core.h4
-rw-r--r--drivers/s390/net/qeth_core_main.c158
-rw-r--r--drivers/s390/net/qeth_core_mpc.h14
-rw-r--r--drivers/s390/net/qeth_ethtool.c1
-rw-r--r--drivers/s390/net/qeth_l2_main.c12
-rw-r--r--drivers/s390/net/qeth_l3_main.c13
6 files changed, 119 insertions, 83 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 293dd99b7fef..871d44746f5c 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -480,6 +480,7 @@ struct qeth_card_stats {
u64 rx_dropped_nomem;
u64 rx_dropped_notsupp;
+ u64 rx_dropped_runt;
/* rtnl_link_stats64 */
u64 rx_packets;
@@ -627,6 +628,7 @@ struct qeth_ipato {
struct qeth_channel {
struct ccw_device *ccwdev;
+ struct qeth_cmd_buffer *active_cmd;
enum qeth_channel_states state;
atomic_t irq_pending;
};
@@ -1037,6 +1039,8 @@ int qeth_do_run_thread(struct qeth_card *, unsigned long);
void qeth_clear_thread_start_bit(struct qeth_card *, unsigned long);
void qeth_clear_thread_running_bit(struct qeth_card *, unsigned long);
int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok);
+int qeth_stop_channel(struct qeth_channel *channel);
+
void qeth_print_status_message(struct qeth_card *);
int qeth_init_qdio_queues(struct qeth_card *);
int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index efcbe60220d1..b9a2349e4b90 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -515,7 +515,9 @@ static int __qeth_issue_next_read(struct qeth_card *card)
QETH_CARD_TEXT(card, 6, "noirqpnd");
rc = ccw_device_start(channel->ccwdev, ccw, (addr_t) iob, 0, 0);
- if (rc) {
+ if (!rc) {
+ channel->active_cmd = iob;
+ } else {
QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n",
rc, CARD_DEVID(card));
atomic_set(&channel->irq_pending, 0);
@@ -986,8 +988,21 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
QETH_CARD_TEXT(card, 5, "data");
}
- if (qeth_intparm_is_iob(intparm))
- iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
+ if (intparm == 0) {
+ QETH_CARD_TEXT(card, 5, "irqunsol");
+ } else if ((addr_t)intparm != (addr_t)channel->active_cmd) {
+ QETH_CARD_TEXT(card, 5, "irqunexp");
+
+ dev_err(&cdev->dev,
+ "Received IRQ with intparm %lx, expected %px\n",
+ intparm, channel->active_cmd);
+ if (channel->active_cmd)
+ qeth_cancel_cmd(channel->active_cmd, -EIO);
+ } else {
+ iob = (struct qeth_cmd_buffer *) (addr_t)intparm;
+ }
+
+ channel->active_cmd = NULL;
rc = qeth_check_irb_error(card, cdev, irb);
if (rc) {
@@ -1007,15 +1022,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
channel->state = CH_STATE_HALTED;
- if (intparm == QETH_CLEAR_CHANNEL_PARM) {
- QETH_CARD_TEXT(card, 6, "clrchpar");
- /* we don't have to handle this further */
- intparm = 0;
- }
- if (intparm == QETH_HALT_CHANNEL_PARM) {
- QETH_CARD_TEXT(card, 6, "hltchpar");
- /* we don't have to handle this further */
- intparm = 0;
+ if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC |
+ SCSW_FCTL_HALT_FUNC))) {
+ qeth_cancel_cmd(iob, -ECANCELED);
+ iob = NULL;
}
cstat = irb->scsw.cmd.cstat;
@@ -1408,7 +1418,7 @@ static int qeth_clear_channel(struct qeth_card *card,
QETH_CARD_TEXT(card, 3, "clearch");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
- rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM);
+ rc = ccw_device_clear(channel->ccwdev, (addr_t)channel->active_cmd);
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc)
@@ -1430,7 +1440,7 @@ static int qeth_halt_channel(struct qeth_card *card,
QETH_CARD_TEXT(card, 3, "haltch");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
- rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM);
+ rc = ccw_device_halt(channel->ccwdev, (addr_t)channel->active_cmd);
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc)
@@ -1444,6 +1454,25 @@ static int qeth_halt_channel(struct qeth_card *card,
return 0;
}
+int qeth_stop_channel(struct qeth_channel *channel)
+{
+ struct ccw_device *cdev = channel->ccwdev;
+ int rc;
+
+ rc = ccw_device_set_offline(cdev);
+
+ spin_lock_irq(get_ccwdev_lock(cdev));
+ if (channel->active_cmd) {
+ dev_err(&cdev->dev, "Stopped channel while cmd %px was still active\n",
+ channel->active_cmd);
+ channel->active_cmd = NULL;
+ }
+ spin_unlock_irq(get_ccwdev_lock(cdev));
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_stop_channel);
+
static int qeth_halt_channels(struct qeth_card *card)
{
int rc1 = 0, rc2 = 0, rc3 = 0;
@@ -1746,6 +1775,8 @@ static int qeth_send_control_data(struct qeth_card *card,
spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob),
(addr_t) iob, 0, 0, timeout);
+ if (!rc)
+ channel->active_cmd = iob;
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) {
QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n",
@@ -4667,12 +4698,12 @@ EXPORT_SYMBOL_GPL(qeth_vm_request_mac);
static void qeth_determine_capabilities(struct qeth_card *card)
{
+ struct qeth_channel *channel = &card->data;
+ struct ccw_device *ddev = channel->ccwdev;
int rc;
- struct ccw_device *ddev;
int ddev_offline = 0;
QETH_CARD_TEXT(card, 2, "detcapab");
- ddev = CARD_DDEV(card);
if (!ddev->online) {
ddev_offline = 1;
rc = ccw_device_set_online(ddev);
@@ -4711,7 +4742,7 @@ static void qeth_determine_capabilities(struct qeth_card *card)
out_offline:
if (ddev_offline == 1)
- ccw_device_set_offline(ddev);
+ qeth_stop_channel(channel);
out:
return;
}
@@ -4911,9 +4942,9 @@ retry:
QETH_DBF_MESSAGE(2, "Retrying to do IDX activates on device %x.\n",
CARD_DEVID(card));
rc = qeth_qdio_clear_card(card, !IS_IQD(card));
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
+ qeth_stop_channel(&card->data);
+ qeth_stop_channel(&card->write);
+ qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card));
rc = ccw_device_set_online(CARD_RDEV(card));
if (rc)
@@ -5028,27 +5059,15 @@ out:
}
EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
-static void qeth_create_skb_frag(struct qdio_buffer_element *element,
- struct sk_buff *skb, int offset, int data_len)
+static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len)
{
- struct page *page = virt_to_page(element->addr);
+ struct page *page = virt_to_page(data);
unsigned int next_frag;
- /* first fill the linear space */
- if (!skb->len) {
- unsigned int linear = min(data_len, skb_tailroom(skb));
-
- skb_put_data(skb, element->addr + offset, linear);
- data_len -= linear;
- if (!data_len)
- return;
- offset += linear;
- /* fall through to add page frag for remaining data */
- }
-
next_frag = skb_shinfo(skb)->nr_frags;
get_page(page);
- skb_add_rx_frag(skb, next_frag, page, offset, data_len, data_len);
+ skb_add_rx_frag(skb, next_frag, page, offset_in_page(data), data_len,
+ data_len);
}
static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale)
@@ -5063,13 +5082,12 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
{
struct qdio_buffer_element *element = *__element;
struct qdio_buffer *buffer = qethbuffer->buffer;
+ unsigned int linear_len = 0;
int offset = *__offset;
bool use_rx_sg = false;
unsigned int headroom;
struct sk_buff *skb;
int skb_len = 0;
- void *data_ptr;
- int data_len;
next_packet:
/* qeth_hdr must not cross element boundaries */
@@ -5082,29 +5100,41 @@ next_packet:
*hdr = element->addr + offset;
offset += sizeof(struct qeth_hdr);
+ skb = NULL;
+
switch ((*hdr)->hdr.l2.id) {
case QETH_HEADER_TYPE_LAYER2:
skb_len = (*hdr)->hdr.l2.pkt_length;
+ linear_len = ETH_HLEN;
headroom = 0;
break;
case QETH_HEADER_TYPE_LAYER3:
skb_len = (*hdr)->hdr.l3.length;
if (!IS_LAYER3(card)) {
QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
- skb = NULL;
goto walk_packet;
}
+ if ((*hdr)->hdr.l3.flags & QETH_HDR_PASSTHRU) {
+ linear_len = ETH_HLEN;
+ headroom = 0;
+ break;
+ }
+
+ if ((*hdr)->hdr.l3.flags & QETH_HDR_IPV6)
+ linear_len = sizeof(struct ipv6hdr);
+ else
+ linear_len = sizeof(struct iphdr);
headroom = ETH_HLEN;
break;
case QETH_HEADER_TYPE_OSN:
skb_len = (*hdr)->hdr.osn.pdu_length;
if (!IS_OSN(card)) {
QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
- skb = NULL;
goto walk_packet;
}
+ linear_len = skb_len;
headroom = sizeof(struct qeth_hdr);
break;
default:
@@ -5117,8 +5147,10 @@ next_packet:
return NULL;
}
- if (!skb_len)
- return NULL;
+ if (skb_len < linear_len) {
+ QETH_CARD_STAT_INC(card, rx_dropped_runt);
+ goto walk_packet;
+ }
use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) ||
((skb_len >= card->options.rx_sg_cb) &&
@@ -5130,9 +5162,9 @@ next_packet:
skb = qethbuffer->rx_skb;
qethbuffer->rx_skb = NULL;
} else {
- unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len;
-
- skb = napi_alloc_skb(&card->napi, linear + headroom);
+ if (!use_rx_sg)
+ linear_len = skb_len;
+ skb = napi_alloc_skb(&card->napi, linear_len + headroom);
}
if (!skb)
@@ -5141,18 +5173,32 @@ next_packet:
skb_reserve(skb, headroom);
walk_packet:
- data_ptr = element->addr + offset;
while (skb_len) {
- data_len = min(skb_len, (int)(element->length - offset));
+ int data_len = min(skb_len, (int)(element->length - offset));
+ char *data = element->addr + offset;
+
+ skb_len -= data_len;
+ offset += data_len;
+ /* Extract data from current element: */
if (skb && data_len) {
- if (use_rx_sg)
- qeth_create_skb_frag(element, skb, offset,
- data_len);
- else
- skb_put_data(skb, data_ptr, data_len);
+ if (linear_len) {
+ unsigned int copy_len;
+
+ copy_len = min_t(unsigned int, linear_len,
+ data_len);
+
+ skb_put_data(skb, data, copy_len);
+ linear_len -= copy_len;
+ data_len -= copy_len;
+ data += copy_len;
+ }
+
+ if (data_len)
+ qeth_create_skb_frag(skb, data, data_len);
}
- skb_len -= data_len;
+
+ /* Step forward to next element: */
if (skb_len) {
if (qeth_is_last_sbale(element)) {
QETH_CARD_TEXT(card, 4, "unexeob");
@@ -5166,9 +5212,6 @@ walk_packet:
}
element++;
offset = 0;
- data_ptr = element->addr;
- } else {
- offset += data_len;
}
}
@@ -6268,7 +6311,8 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
card->stats.rx_frame_errors +
card->stats.rx_fifo_errors;
stats->rx_dropped = card->stats.rx_dropped_nomem +
- card->stats.rx_dropped_notsupp;
+ card->stats.rx_dropped_notsupp +
+ card->stats.rx_dropped_runt;
stats->multicast = card->stats.rx_multicast;
stats->rx_length_errors = card->stats.rx_length_errors;
stats->rx_frame_errors = card->stats.rx_frame_errors;
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index 53fcf6641154..88f4dc140751 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -29,20 +29,6 @@ extern unsigned char IPA_PDU_HEADER[];
#define QETH_TIMEOUT (10 * HZ)
#define QETH_IPA_TIMEOUT (45 * HZ)
-#define QETH_CLEAR_CHANNEL_PARM -10
-#define QETH_HALT_CHANNEL_PARM -11
-
-static inline bool qeth_intparm_is_iob(unsigned long intparm)
-{
- switch (intparm) {
- case QETH_CLEAR_CHANNEL_PARM:
- case QETH_HALT_CHANNEL_PARM:
- case 0:
- return false;
- }
- return true;
-}
-
/*****************************************************************************/
/* IP Assist related definitions */
/*****************************************************************************/
diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c
index f7485c6dea25..ab59bc975719 100644
--- a/drivers/s390/net/qeth_ethtool.c
+++ b/drivers/s390/net/qeth_ethtool.c
@@ -51,6 +51,7 @@ static const struct qeth_stats card_stats[] = {
QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page),
QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem),
QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp),
+ QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt),
};
#define TXQ_STATS_LEN ARRAY_SIZE(txq_stats)
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 989935d67b31..9086bc04fa6b 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -845,9 +845,9 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev)
out_remove:
qeth_l2_stop_card(card);
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
+ qeth_stop_channel(&card->data);
+ qeth_stop_channel(&card->write);
+ qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card));
mutex_unlock(&card->conf_mutex);
@@ -878,9 +878,9 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
rtnl_unlock();
qeth_l2_stop_card(card);
- rc = ccw_device_set_offline(CARD_DDEV(card));
- rc2 = ccw_device_set_offline(CARD_WDEV(card));
- rc3 = ccw_device_set_offline(CARD_RDEV(card));
+ rc = qeth_stop_channel(&card->data);
+ rc2 = qeth_stop_channel(&card->write);
+ rc3 = qeth_stop_channel(&card->read);
if (!rc)
rc = (rc2) ? rc2 : rc3;
if (rc)
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index e7ce73b9f016..27126330a4b0 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -2259,9 +2259,9 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev)
return 0;
out_remove:
qeth_l3_stop_card(card);
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
+ qeth_stop_channel(&card->data);
+ qeth_stop_channel(&card->write);
+ qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card));
mutex_unlock(&card->conf_mutex);
@@ -2297,9 +2297,10 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,
call_netdevice_notifiers(NETDEV_REBOOT, card->dev);
rtnl_unlock();
}
- rc = ccw_device_set_offline(CARD_DDEV(card));
- rc2 = ccw_device_set_offline(CARD_WDEV(card));
- rc3 = ccw_device_set_offline(CARD_RDEV(card));
+
+ rc = qeth_stop_channel(&card->data);
+ rc2 = qeth_stop_channel(&card->write);
+ rc3 = qeth_stop_channel(&card->read);
if (!rc)
rc = (rc2) ? rc2 : rc3;
if (rc)