Merge branch 's390-qeth-fixes'
Julian Wiedmann says: ==================== s390/qeth: fixes 2021-03-09 please apply the following patch series to netdev's net tree. This brings one fix for a memleak in an error path of the setup code. Also several fixes for dealing with pending TX buffers - two for old bugs in their completion handling, and one recent regression in a teardown path. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>rM2-mainline
commit
8515455720
|
@ -436,7 +436,7 @@ struct qeth_qdio_out_buffer {
|
||||||
int is_header[QDIO_MAX_ELEMENTS_PER_BUFFER];
|
int is_header[QDIO_MAX_ELEMENTS_PER_BUFFER];
|
||||||
|
|
||||||
struct qeth_qdio_out_q *q;
|
struct qeth_qdio_out_q *q;
|
||||||
struct qeth_qdio_out_buffer *next_pending;
|
struct list_head list_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qeth_card;
|
struct qeth_card;
|
||||||
|
@ -500,6 +500,7 @@ struct qeth_qdio_out_q {
|
||||||
struct qdio_buffer *qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
|
struct qdio_buffer *qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
|
||||||
struct qeth_qdio_out_buffer *bufs[QDIO_MAX_BUFFERS_PER_Q];
|
struct qeth_qdio_out_buffer *bufs[QDIO_MAX_BUFFERS_PER_Q];
|
||||||
struct qdio_outbuf_state *bufstates; /* convenience pointer */
|
struct qdio_outbuf_state *bufstates; /* convenience pointer */
|
||||||
|
struct list_head pending_bufs;
|
||||||
struct qeth_out_q_stats stats;
|
struct qeth_out_q_stats stats;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
unsigned int priority;
|
unsigned int priority;
|
||||||
|
|
|
@ -73,8 +73,6 @@ static void qeth_free_qdio_queues(struct qeth_card *card);
|
||||||
static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
|
static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
|
||||||
struct qeth_qdio_out_buffer *buf,
|
struct qeth_qdio_out_buffer *buf,
|
||||||
enum iucv_tx_notify notification);
|
enum iucv_tx_notify notification);
|
||||||
static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error,
|
|
||||||
int budget);
|
|
||||||
|
|
||||||
static void qeth_close_dev_handler(struct work_struct *work)
|
static void qeth_close_dev_handler(struct work_struct *work)
|
||||||
{
|
{
|
||||||
|
@ -465,41 +463,6 @@ static enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15,
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx,
|
|
||||||
int forced_cleanup)
|
|
||||||
{
|
|
||||||
if (q->card->options.cq != QETH_CQ_ENABLED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (q->bufs[bidx]->next_pending != NULL) {
|
|
||||||
struct qeth_qdio_out_buffer *head = q->bufs[bidx];
|
|
||||||
struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending;
|
|
||||||
|
|
||||||
while (c) {
|
|
||||||
if (forced_cleanup ||
|
|
||||||
atomic_read(&c->state) == QETH_QDIO_BUF_EMPTY) {
|
|
||||||
struct qeth_qdio_out_buffer *f = c;
|
|
||||||
|
|
||||||
QETH_CARD_TEXT(f->q->card, 5, "fp");
|
|
||||||
QETH_CARD_TEXT_(f->q->card, 5, "%lx", (long) f);
|
|
||||||
/* release here to avoid interleaving between
|
|
||||||
outbound tasklet and inbound tasklet
|
|
||||||
regarding notifications and lifecycle */
|
|
||||||
qeth_tx_complete_buf(c, forced_cleanup, 0);
|
|
||||||
|
|
||||||
c = f->next_pending;
|
|
||||||
WARN_ON_ONCE(head->next_pending != f);
|
|
||||||
head->next_pending = c;
|
|
||||||
kmem_cache_free(qeth_qdio_outbuf_cache, f);
|
|
||||||
} else {
|
|
||||||
head = c;
|
|
||||||
c = c->next_pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qeth_qdio_handle_aob(struct qeth_card *card,
|
static void qeth_qdio_handle_aob(struct qeth_card *card,
|
||||||
unsigned long phys_aob_addr)
|
unsigned long phys_aob_addr)
|
||||||
{
|
{
|
||||||
|
@ -507,6 +470,7 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
|
||||||
struct qaob *aob;
|
struct qaob *aob;
|
||||||
struct qeth_qdio_out_buffer *buffer;
|
struct qeth_qdio_out_buffer *buffer;
|
||||||
enum iucv_tx_notify notification;
|
enum iucv_tx_notify notification;
|
||||||
|
struct qeth_qdio_out_q *queue;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
aob = (struct qaob *) phys_to_virt(phys_aob_addr);
|
aob = (struct qaob *) phys_to_virt(phys_aob_addr);
|
||||||
|
@ -537,7 +501,7 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
|
||||||
qeth_notify_skbs(buffer->q, buffer, notification);
|
qeth_notify_skbs(buffer->q, buffer, notification);
|
||||||
|
|
||||||
/* Free dangling allocations. The attached skbs are handled by
|
/* Free dangling allocations. The attached skbs are handled by
|
||||||
* qeth_cleanup_handled_pending().
|
* qeth_tx_complete_pending_bufs().
|
||||||
*/
|
*/
|
||||||
for (i = 0;
|
for (i = 0;
|
||||||
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
|
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
|
||||||
|
@ -549,7 +513,9 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
|
||||||
buffer->is_header[i] = 0;
|
buffer->is_header[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queue = buffer->q;
|
||||||
atomic_set(&buffer->state, QETH_QDIO_BUF_EMPTY);
|
atomic_set(&buffer->state, QETH_QDIO_BUF_EMPTY);
|
||||||
|
napi_schedule(&queue->napi);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
@ -1424,9 +1390,6 @@ static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error,
|
||||||
struct qeth_qdio_out_q *queue = buf->q;
|
struct qeth_qdio_out_q *queue = buf->q;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING)
|
|
||||||
qeth_notify_skbs(queue, buf, TX_NOTIFY_GENERALERROR);
|
|
||||||
|
|
||||||
/* Empty buffer? */
|
/* Empty buffer? */
|
||||||
if (buf->next_element_to_fill == 0)
|
if (buf->next_element_to_fill == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -1488,14 +1451,38 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
|
||||||
atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
|
atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qeth_tx_complete_pending_bufs(struct qeth_card *card,
|
||||||
|
struct qeth_qdio_out_q *queue,
|
||||||
|
bool drain)
|
||||||
|
{
|
||||||
|
struct qeth_qdio_out_buffer *buf, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(buf, tmp, &queue->pending_bufs, list_entry) {
|
||||||
|
if (drain || atomic_read(&buf->state) == QETH_QDIO_BUF_EMPTY) {
|
||||||
|
QETH_CARD_TEXT(card, 5, "fp");
|
||||||
|
QETH_CARD_TEXT_(card, 5, "%lx", (long) buf);
|
||||||
|
|
||||||
|
if (drain)
|
||||||
|
qeth_notify_skbs(queue, buf,
|
||||||
|
TX_NOTIFY_GENERALERROR);
|
||||||
|
qeth_tx_complete_buf(buf, drain, 0);
|
||||||
|
|
||||||
|
list_del(&buf->list_entry);
|
||||||
|
kmem_cache_free(qeth_qdio_outbuf_cache, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void qeth_drain_output_queue(struct qeth_qdio_out_q *q, bool free)
|
static void qeth_drain_output_queue(struct qeth_qdio_out_q *q, bool free)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
qeth_tx_complete_pending_bufs(q->card, q, true);
|
||||||
|
|
||||||
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
|
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
|
||||||
if (!q->bufs[j])
|
if (!q->bufs[j])
|
||||||
continue;
|
continue;
|
||||||
qeth_cleanup_handled_pending(q, j, 1);
|
|
||||||
qeth_clear_output_buffer(q, q->bufs[j], true, 0);
|
qeth_clear_output_buffer(q, q->bufs[j], true, 0);
|
||||||
if (free) {
|
if (free) {
|
||||||
kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]);
|
kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]);
|
||||||
|
@ -2615,7 +2602,6 @@ static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx)
|
||||||
skb_queue_head_init(&newbuf->skb_list);
|
skb_queue_head_init(&newbuf->skb_list);
|
||||||
lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key);
|
lockdep_set_class(&newbuf->skb_list.lock, &qdio_out_skb_queue_key);
|
||||||
newbuf->q = q;
|
newbuf->q = q;
|
||||||
newbuf->next_pending = q->bufs[bidx];
|
|
||||||
atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY);
|
atomic_set(&newbuf->state, QETH_QDIO_BUF_EMPTY);
|
||||||
q->bufs[bidx] = newbuf;
|
q->bufs[bidx] = newbuf;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2634,15 +2620,28 @@ static void qeth_free_output_queue(struct qeth_qdio_out_q *q)
|
||||||
static struct qeth_qdio_out_q *qeth_alloc_output_queue(void)
|
static struct qeth_qdio_out_q *qeth_alloc_output_queue(void)
|
||||||
{
|
{
|
||||||
struct qeth_qdio_out_q *q = kzalloc(sizeof(*q), GFP_KERNEL);
|
struct qeth_qdio_out_q *q = kzalloc(sizeof(*q), GFP_KERNEL);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
if (!q)
|
if (!q)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) {
|
if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q))
|
||||||
kfree(q);
|
goto err_qdio_bufs;
|
||||||
return NULL;
|
|
||||||
|
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
|
||||||
|
if (qeth_init_qdio_out_buf(q, i))
|
||||||
|
goto err_out_bufs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return q;
|
return q;
|
||||||
|
|
||||||
|
err_out_bufs:
|
||||||
|
while (i > 0)
|
||||||
|
kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[--i]);
|
||||||
|
qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q);
|
||||||
|
err_qdio_bufs:
|
||||||
|
kfree(q);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qeth_tx_completion_timer(struct timer_list *timer)
|
static void qeth_tx_completion_timer(struct timer_list *timer)
|
||||||
|
@ -2655,7 +2654,7 @@ static void qeth_tx_completion_timer(struct timer_list *timer)
|
||||||
|
|
||||||
static int qeth_alloc_qdio_queues(struct qeth_card *card)
|
static int qeth_alloc_qdio_queues(struct qeth_card *card)
|
||||||
{
|
{
|
||||||
int i, j;
|
unsigned int i;
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 2, "allcqdbf");
|
QETH_CARD_TEXT(card, 2, "allcqdbf");
|
||||||
|
|
||||||
|
@ -2684,18 +2683,12 @@ static int qeth_alloc_qdio_queues(struct qeth_card *card)
|
||||||
card->qdio.out_qs[i] = queue;
|
card->qdio.out_qs[i] = queue;
|
||||||
queue->card = card;
|
queue->card = card;
|
||||||
queue->queue_no = i;
|
queue->queue_no = i;
|
||||||
|
INIT_LIST_HEAD(&queue->pending_bufs);
|
||||||
spin_lock_init(&queue->lock);
|
spin_lock_init(&queue->lock);
|
||||||
timer_setup(&queue->timer, qeth_tx_completion_timer, 0);
|
timer_setup(&queue->timer, qeth_tx_completion_timer, 0);
|
||||||
queue->coalesce_usecs = QETH_TX_COALESCE_USECS;
|
queue->coalesce_usecs = QETH_TX_COALESCE_USECS;
|
||||||
queue->max_coalesced_frames = QETH_TX_MAX_COALESCED_FRAMES;
|
queue->max_coalesced_frames = QETH_TX_MAX_COALESCED_FRAMES;
|
||||||
queue->priority = QETH_QIB_PQUE_PRIO_DEFAULT;
|
queue->priority = QETH_QIB_PQUE_PRIO_DEFAULT;
|
||||||
|
|
||||||
/* give outbound qeth_qdio_buffers their qdio_buffers */
|
|
||||||
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
|
|
||||||
WARN_ON(queue->bufs[j]);
|
|
||||||
if (qeth_init_qdio_out_buf(queue, j))
|
|
||||||
goto out_freeoutqbufs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* completion */
|
/* completion */
|
||||||
|
@ -2704,13 +2697,6 @@ static int qeth_alloc_qdio_queues(struct qeth_card *card)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_freeoutqbufs:
|
|
||||||
while (j > 0) {
|
|
||||||
--j;
|
|
||||||
kmem_cache_free(qeth_qdio_outbuf_cache,
|
|
||||||
card->qdio.out_qs[i]->bufs[j]);
|
|
||||||
card->qdio.out_qs[i]->bufs[j] = NULL;
|
|
||||||
}
|
|
||||||
out_freeoutq:
|
out_freeoutq:
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
qeth_free_output_queue(card->qdio.out_qs[--i]);
|
qeth_free_output_queue(card->qdio.out_qs[--i]);
|
||||||
|
@ -6107,6 +6093,8 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,
|
||||||
qeth_schedule_recovery(card);
|
qeth_schedule_recovery(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add(&buffer->list_entry,
|
||||||
|
&queue->pending_bufs);
|
||||||
/* Skip clearing the buffer: */
|
/* Skip clearing the buffer: */
|
||||||
return;
|
return;
|
||||||
case QETH_QDIO_BUF_QAOB_OK:
|
case QETH_QDIO_BUF_QAOB_OK:
|
||||||
|
@ -6162,6 +6150,8 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget)
|
||||||
unsigned int bytes = 0;
|
unsigned int bytes = 0;
|
||||||
int completed;
|
int completed;
|
||||||
|
|
||||||
|
qeth_tx_complete_pending_bufs(card, queue, false);
|
||||||
|
|
||||||
if (qeth_out_queue_is_empty(queue)) {
|
if (qeth_out_queue_is_empty(queue)) {
|
||||||
napi_complete(napi);
|
napi_complete(napi);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -6194,7 +6184,6 @@ static int qeth_tx_poll(struct napi_struct *napi, int budget)
|
||||||
|
|
||||||
qeth_handle_send_error(card, buffer, error);
|
qeth_handle_send_error(card, buffer, error);
|
||||||
qeth_iqd_tx_complete(queue, bidx, error, budget);
|
qeth_iqd_tx_complete(queue, bidx, error, budget);
|
||||||
qeth_cleanup_handled_pending(queue, bidx, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
netdev_tx_completed_queue(txq, packets, bytes);
|
netdev_tx_completed_queue(txq, packets, bytes);
|
||||||
|
@ -7249,9 +7238,7 @@ int qeth_open(struct net_device *dev)
|
||||||
card->data.state = CH_STATE_UP;
|
card->data.state = CH_STATE_UP;
|
||||||
netif_tx_start_all_queues(dev);
|
netif_tx_start_all_queues(dev);
|
||||||
|
|
||||||
napi_enable(&card->napi);
|
|
||||||
local_bh_disable();
|
local_bh_disable();
|
||||||
napi_schedule(&card->napi);
|
|
||||||
if (IS_IQD(card)) {
|
if (IS_IQD(card)) {
|
||||||
struct qeth_qdio_out_q *queue;
|
struct qeth_qdio_out_q *queue;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -7263,8 +7250,12 @@ int qeth_open(struct net_device *dev)
|
||||||
napi_schedule(&queue->napi);
|
napi_schedule(&queue->napi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
napi_enable(&card->napi);
|
||||||
|
napi_schedule(&card->napi);
|
||||||
/* kick-start the NAPI softirq: */
|
/* kick-start the NAPI softirq: */
|
||||||
local_bh_enable();
|
local_bh_enable();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_open);
|
EXPORT_SYMBOL_GPL(qeth_open);
|
||||||
|
@ -7274,6 +7265,11 @@ int qeth_stop(struct net_device *dev)
|
||||||
struct qeth_card *card = dev->ml_priv;
|
struct qeth_card *card = dev->ml_priv;
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 4, "qethstop");
|
QETH_CARD_TEXT(card, 4, "qethstop");
|
||||||
|
|
||||||
|
napi_disable(&card->napi);
|
||||||
|
cancel_delayed_work_sync(&card->buffer_reclaim_work);
|
||||||
|
qdio_stop_irq(CARD_DDEV(card));
|
||||||
|
|
||||||
if (IS_IQD(card)) {
|
if (IS_IQD(card)) {
|
||||||
struct qeth_qdio_out_q *queue;
|
struct qeth_qdio_out_q *queue;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -7294,10 +7290,6 @@ int qeth_stop(struct net_device *dev)
|
||||||
netif_tx_disable(dev);
|
netif_tx_disable(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
napi_disable(&card->napi);
|
|
||||||
cancel_delayed_work_sync(&card->buffer_reclaim_work);
|
|
||||||
qdio_stop_irq(CARD_DDEV(card));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_stop);
|
EXPORT_SYMBOL_GPL(qeth_stop);
|
||||||
|
|
Loading…
Reference in New Issue