Merge branch 's390-fixes'
Julian Wiedmann says: ==================== s390/qeth: fixes 2019-12-05 please apply the following fixes to your net tree. The first two patches target the RX data path, the third fixes a memory leak when shutting down a qeth device. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b874405263
@ -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 *,
|
||||
|
@ -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;
|
||||
|
||||
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);
|
||||
}
|
||||
skb_len -= data_len;
|
||||
offset += data_len;
|
||||
|
||||
/* Extract data from current element: */
|
||||
if (skb && 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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
@ -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 */
|
||||
/*****************************************************************************/
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user