mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-30 16:07:39 +00:00
s390/qeth: implement smarter resizing of the RX buffer pool
The RX buffer pool is allocated in qeth_alloc_qdio_queues().
A subsequent pool resizing is then handled in a very simple way:
first free the current pool, then allocate a new pool of the requested
size.
There's two ways where this can go wrong:
1. if the resize action happens _before_ the initial pool was allocated,
then a subsequent initialization will call qeth_alloc_qdio_queues()
and fill the pool with a second(!) set of pages. We consume twice the
planned amount of memory.
This is easy to fix - just skip the resizing if the queues haven't
been allocated yet.
2. if the initial pool was created by qeth_alloc_qdio_queues() but a
subsequent resizing fails, then the device has no(!) RX buffer pool.
The next initialization will _not_ call qeth_alloc_qdio_queues(), and
attempting to back the RX buffers with pages in
qeth_init_qdio_queues() will fail.
Not very difficult to fix either - instead of re-allocating the whole
pool, just allocate/free as many entries to match the desired size.
Fixes: 4a71df5004
("qeth: new qeth device driver")
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0f75e14929
commit
5d4f78564c
4 changed files with 56 additions and 19 deletions
|
@ -983,7 +983,7 @@ extern const struct attribute_group qeth_device_blkt_group;
|
||||||
extern const struct device_type qeth_generic_devtype;
|
extern const struct device_type qeth_generic_devtype;
|
||||||
|
|
||||||
const char *qeth_get_cardname_short(struct qeth_card *);
|
const char *qeth_get_cardname_short(struct qeth_card *);
|
||||||
int qeth_realloc_buffer_pool(struct qeth_card *, int);
|
int qeth_resize_buffer_pool(struct qeth_card *card, unsigned int count);
|
||||||
int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);
|
int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);
|
||||||
void qeth_core_free_discipline(struct qeth_card *);
|
void qeth_core_free_discipline(struct qeth_card *);
|
||||||
|
|
||||||
|
|
|
@ -275,18 +275,57 @@ static int qeth_alloc_buffer_pool(struct qeth_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
|
int qeth_resize_buffer_pool(struct qeth_card *card, unsigned int count)
|
||||||
{
|
{
|
||||||
|
unsigned int buf_elements = QETH_MAX_BUFFER_ELEMENTS(card);
|
||||||
|
struct qeth_qdio_buffer_pool *pool = &card->qdio.init_pool;
|
||||||
|
struct qeth_buffer_pool_entry *entry, *tmp;
|
||||||
|
int delta = count - pool->buf_count;
|
||||||
|
LIST_HEAD(entries);
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 2, "realcbp");
|
QETH_CARD_TEXT(card, 2, "realcbp");
|
||||||
|
|
||||||
/* TODO: steel/add buffers from/to a running card's buffer pool (?) */
|
/* Defer until queue is allocated: */
|
||||||
qeth_clear_working_pool_list(card);
|
if (!card->qdio.in_q)
|
||||||
qeth_free_buffer_pool(card);
|
goto out;
|
||||||
card->qdio.in_buf_pool.buf_count = bufcnt;
|
|
||||||
card->qdio.init_pool.buf_count = bufcnt;
|
/* Remove entries from the pool: */
|
||||||
return qeth_alloc_buffer_pool(card);
|
while (delta < 0) {
|
||||||
|
entry = list_first_entry(&pool->entry_list,
|
||||||
|
struct qeth_buffer_pool_entry,
|
||||||
|
init_list);
|
||||||
|
list_del(&entry->init_list);
|
||||||
|
qeth_free_pool_entry(entry);
|
||||||
|
|
||||||
|
delta++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate additional entries: */
|
||||||
|
while (delta > 0) {
|
||||||
|
entry = qeth_alloc_pool_entry(buf_elements);
|
||||||
|
if (!entry) {
|
||||||
|
list_for_each_entry_safe(entry, tmp, &entries,
|
||||||
|
init_list) {
|
||||||
|
list_del(&entry->init_list);
|
||||||
|
qeth_free_pool_entry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add(&entry->init_list, &entries);
|
||||||
|
|
||||||
|
delta--;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_splice(&entries, &pool->entry_list);
|
||||||
|
|
||||||
|
out:
|
||||||
|
card->qdio.in_buf_pool.buf_count = count;
|
||||||
|
pool->buf_count = count;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool);
|
EXPORT_SYMBOL_GPL(qeth_resize_buffer_pool);
|
||||||
|
|
||||||
static void qeth_free_qdio_queue(struct qeth_qdio_q *q)
|
static void qeth_free_qdio_queue(struct qeth_qdio_q *q)
|
||||||
{
|
{
|
||||||
|
|
|
@ -247,8 +247,8 @@ static ssize_t qeth_dev_bufcnt_store(struct device *dev,
|
||||||
struct device_attribute *attr, const char *buf, size_t count)
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct qeth_card *card = dev_get_drvdata(dev);
|
struct qeth_card *card = dev_get_drvdata(dev);
|
||||||
|
unsigned int cnt;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
int cnt, old_cnt;
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
mutex_lock(&card->conf_mutex);
|
mutex_lock(&card->conf_mutex);
|
||||||
|
@ -257,13 +257,12 @@ static ssize_t qeth_dev_bufcnt_store(struct device *dev,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_cnt = card->qdio.in_buf_pool.buf_count;
|
|
||||||
cnt = simple_strtoul(buf, &tmp, 10);
|
cnt = simple_strtoul(buf, &tmp, 10);
|
||||||
cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN :
|
cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN :
|
||||||
((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
|
((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
|
||||||
if (old_cnt != cnt) {
|
|
||||||
rc = qeth_realloc_buffer_pool(card, cnt);
|
rc = qeth_resize_buffer_pool(card, cnt);
|
||||||
}
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&card->conf_mutex);
|
mutex_unlock(&card->conf_mutex);
|
||||||
return rc ? rc : count;
|
return rc ? rc : count;
|
||||||
|
|
|
@ -206,12 +206,11 @@ static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
|
||||||
qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
|
qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
|
||||||
if (card->ssqd.qdioac2 & CHSC_AC2_SNIFFER_AVAILABLE) {
|
if (card->ssqd.qdioac2 & CHSC_AC2_SNIFFER_AVAILABLE) {
|
||||||
card->options.sniffer = i;
|
card->options.sniffer = i;
|
||||||
if (card->qdio.init_pool.buf_count !=
|
qeth_resize_buffer_pool(card, QETH_IN_BUF_COUNT_MAX);
|
||||||
QETH_IN_BUF_COUNT_MAX)
|
} else {
|
||||||
qeth_realloc_buffer_pool(card,
|
|
||||||
QETH_IN_BUF_COUNT_MAX);
|
|
||||||
} else
|
|
||||||
rc = -EPERM;
|
rc = -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
|
Loading…
Reference in a new issue