Commit be92e538 authored by David S. Miller's avatar David S. Miller

Merge branch 'bnx2x-fix-page-allocation-failure'

Jason Baron says:

====================
bnx2x: page allocation failure

While configuring ~500 multicast addrs, we ran into high order
page allocation failures. They don't need to be high order, and
thus I'm proposing to split them into at most PAGE_SIZE allocations.

Below is a sample failure.

[1201902.617882] bnx2x: [bnx2x_set_mc_list:12374(eth0)]Failed to create multicast MACs list: -12
[1207325.695021] kworker/1:0: page allocation failure: order:2, mode:0xc020
[1207325.702059] CPU: 1 PID: 15805 Comm: kworker/1:0 Tainted: G        W
[1207325.712940] Hardware name: SYNNEX CORPORATION 1x8-X4i SSD 10GE/S5512LE, BIOS V8.810 05/16/2013
[1207325.722284] Workqueue: events bnx2x_sp_rtnl_task [bnx2x]
[1207325.728206]  0000000000000000 ffff88012d873a78 ffffffff8267f7c7 000000000000c020
[1207325.736754]  0000000000000000 ffff88012d873b08 ffffffff8212f8e0 fffffffc00000003
[1207325.745301]  ffff88041ffecd80 ffff880400000030 0000000000000002 0000c0206800da13
[1207325.753846] Call Trace:
[1207325.756789]  [<ffffffff8267f7c7>] dump_stack+0x4d/0x63
[1207325.762426]  [<ffffffff8212f8e0>] warn_alloc_failed+0xe0/0x130
[1207325.768756]  [<ffffffff8213c898>] ? wakeup_kswapd+0x48/0x140
[1207325.774914]  [<ffffffff82132afc>] __alloc_pages_nodemask+0x2bc/0x970
[1207325.781761]  [<ffffffff82173691>] alloc_pages_current+0x91/0x100
[1207325.788260]  [<ffffffff8212fa1e>] alloc_kmem_pages+0xe/0x10
[1207325.794329]  [<ffffffff8214c9c8>] kmalloc_order+0x18/0x50
[1207325.800227]  [<ffffffff8214ca26>] kmalloc_order_trace+0x26/0xb0
[1207325.806642]  [<ffffffff82451c68>] ? _xfer_secondary_pool+0xa8/0x1a0
[1207325.813404]  [<ffffffff8217cfda>] __kmalloc+0x19a/0x1b0
[1207325.819142]  [<ffffffffa02fe975>] bnx2x_set_rx_mode_inner+0x3d5/0x590 [bnx2x]
[1207325.827000]  [<ffffffffa02ff52d>] bnx2x_sp_rtnl_task+0x28d/0x760 [bnx2x]
[1207325.834197]  [<ffffffff820695d4>] process_one_work+0x134/0x3c0
[1207325.840522]  [<ffffffff82069981>] worker_thread+0x121/0x460
[1207325.846585]  [<ffffffff82069860>] ? process_one_work+0x3c0/0x3c0
[1207325.853089]  [<ffffffff8206f039>] kthread+0xc9/0xe0
[1207325.858459]  [<ffffffff82070000>] ? notify_die+0x10/0x40
[1207325.864263]  [<ffffffff8206ef70>] ? kthread_create_on_node+0x180/0x180
[1207325.871288]  [<ffffffff826852d2>] ret_from_fork+0x42/0x70
[1207325.877183]  [<ffffffff8206ef70>] ? kthread_create_on_node+0x180/0x180

v2:
 -make use of list_next_entry()
 -only use PAGE_SIZE allocations
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b47c62c5 3129e159
...@@ -12563,43 +12563,64 @@ static int bnx2x_close(struct net_device *dev) ...@@ -12563,43 +12563,64 @@ static int bnx2x_close(struct net_device *dev)
return 0; return 0;
} }
static int bnx2x_init_mcast_macs_list(struct bnx2x *bp, struct bnx2x_mcast_list_elem_group
struct bnx2x_mcast_ramrod_params *p)
{ {
int mc_count = netdev_mc_count(bp->dev); struct list_head mcast_group_link;
struct bnx2x_mcast_list_elem *mc_mac = struct bnx2x_mcast_list_elem mcast_elems[];
kcalloc(mc_count, sizeof(*mc_mac), GFP_ATOMIC); };
struct netdev_hw_addr *ha;
if (!mc_mac) { #define MCAST_ELEMS_PER_PG \
BNX2X_ERR("Failed to allocate mc MAC list\n"); ((PAGE_SIZE - sizeof(struct bnx2x_mcast_list_elem_group)) / \
return -ENOMEM; sizeof(struct bnx2x_mcast_list_elem))
static void bnx2x_free_mcast_macs_list(struct list_head *mcast_group_list)
{
struct bnx2x_mcast_list_elem_group *current_mcast_group;
while (!list_empty(mcast_group_list)) {
current_mcast_group = list_first_entry(mcast_group_list,
struct bnx2x_mcast_list_elem_group,
mcast_group_link);
list_del(&current_mcast_group->mcast_group_link);
free_page((unsigned long)current_mcast_group);
} }
}
INIT_LIST_HEAD(&p->mcast_list); static int bnx2x_init_mcast_macs_list(struct bnx2x *bp,
struct bnx2x_mcast_ramrod_params *p,
struct list_head *mcast_group_list)
{
struct bnx2x_mcast_list_elem *mc_mac;
struct netdev_hw_addr *ha;
struct bnx2x_mcast_list_elem_group *current_mcast_group = NULL;
int mc_count = netdev_mc_count(bp->dev);
int offset = 0;
INIT_LIST_HEAD(&p->mcast_list);
netdev_for_each_mc_addr(ha, bp->dev) { netdev_for_each_mc_addr(ha, bp->dev) {
if (!offset) {
current_mcast_group =
(struct bnx2x_mcast_list_elem_group *)
__get_free_page(GFP_ATOMIC);
if (!current_mcast_group) {
bnx2x_free_mcast_macs_list(mcast_group_list);
BNX2X_ERR("Failed to allocate mc MAC list\n");
return -ENOMEM;
}
list_add(&current_mcast_group->mcast_group_link,
mcast_group_list);
}
mc_mac = &current_mcast_group->mcast_elems[offset];
mc_mac->mac = bnx2x_mc_addr(ha); mc_mac->mac = bnx2x_mc_addr(ha);
list_add_tail(&mc_mac->link, &p->mcast_list); list_add_tail(&mc_mac->link, &p->mcast_list);
mc_mac++; offset++;
if (offset == MCAST_ELEMS_PER_PG)
offset = 0;
} }
p->mcast_list_len = mc_count; p->mcast_list_len = mc_count;
return 0; return 0;
} }
static void bnx2x_free_mcast_macs_list(
struct bnx2x_mcast_ramrod_params *p)
{
struct bnx2x_mcast_list_elem *mc_mac =
list_first_entry(&p->mcast_list, struct bnx2x_mcast_list_elem,
link);
WARN_ON(!mc_mac);
kfree(mc_mac);
}
/** /**
* bnx2x_set_uc_list - configure a new unicast MACs list. * bnx2x_set_uc_list - configure a new unicast MACs list.
* *
...@@ -12647,6 +12668,7 @@ static int bnx2x_set_uc_list(struct bnx2x *bp) ...@@ -12647,6 +12668,7 @@ static int bnx2x_set_uc_list(struct bnx2x *bp)
static int bnx2x_set_mc_list_e1x(struct bnx2x *bp) static int bnx2x_set_mc_list_e1x(struct bnx2x *bp)
{ {
LIST_HEAD(mcast_group_list);
struct net_device *dev = bp->dev; struct net_device *dev = bp->dev;
struct bnx2x_mcast_ramrod_params rparam = {NULL}; struct bnx2x_mcast_ramrod_params rparam = {NULL};
int rc = 0; int rc = 0;
...@@ -12662,7 +12684,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp) ...@@ -12662,7 +12684,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp)
/* then, configure a new MACs list */ /* then, configure a new MACs list */
if (netdev_mc_count(dev)) { if (netdev_mc_count(dev)) {
rc = bnx2x_init_mcast_macs_list(bp, &rparam); rc = bnx2x_init_mcast_macs_list(bp, &rparam, &mcast_group_list);
if (rc) if (rc)
return rc; return rc;
...@@ -12673,7 +12695,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp) ...@@ -12673,7 +12695,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp)
BNX2X_ERR("Failed to set a new multicast configuration: %d\n", BNX2X_ERR("Failed to set a new multicast configuration: %d\n",
rc); rc);
bnx2x_free_mcast_macs_list(&rparam); bnx2x_free_mcast_macs_list(&mcast_group_list);
} }
return rc; return rc;
...@@ -12681,6 +12703,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp) ...@@ -12681,6 +12703,7 @@ static int bnx2x_set_mc_list_e1x(struct bnx2x *bp)
static int bnx2x_set_mc_list(struct bnx2x *bp) static int bnx2x_set_mc_list(struct bnx2x *bp)
{ {
LIST_HEAD(mcast_group_list);
struct bnx2x_mcast_ramrod_params rparam = {NULL}; struct bnx2x_mcast_ramrod_params rparam = {NULL};
struct net_device *dev = bp->dev; struct net_device *dev = bp->dev;
int rc = 0; int rc = 0;
...@@ -12692,7 +12715,7 @@ static int bnx2x_set_mc_list(struct bnx2x *bp) ...@@ -12692,7 +12715,7 @@ static int bnx2x_set_mc_list(struct bnx2x *bp)
rparam.mcast_obj = &bp->mcast_obj; rparam.mcast_obj = &bp->mcast_obj;
if (netdev_mc_count(dev)) { if (netdev_mc_count(dev)) {
rc = bnx2x_init_mcast_macs_list(bp, &rparam); rc = bnx2x_init_mcast_macs_list(bp, &rparam, &mcast_group_list);
if (rc) if (rc)
return rc; return rc;
...@@ -12703,7 +12726,7 @@ static int bnx2x_set_mc_list(struct bnx2x *bp) ...@@ -12703,7 +12726,7 @@ static int bnx2x_set_mc_list(struct bnx2x *bp)
BNX2X_ERR("Failed to set a new multicast configuration: %d\n", BNX2X_ERR("Failed to set a new multicast configuration: %d\n",
rc); rc);
bnx2x_free_mcast_macs_list(&rparam); bnx2x_free_mcast_macs_list(&mcast_group_list);
} else { } else {
/* If no mc addresses are required, flush the configuration */ /* If no mc addresses are required, flush the configuration */
rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_DEL); rc = bnx2x_config_mcast(bp, &rparam, BNX2X_MCAST_CMD_DEL);
......
...@@ -2606,8 +2606,23 @@ struct bnx2x_mcast_bin_elem { ...@@ -2606,8 +2606,23 @@ struct bnx2x_mcast_bin_elem {
int type; /* BNX2X_MCAST_CMD_SET_{ADD, DEL} */ int type; /* BNX2X_MCAST_CMD_SET_{ADD, DEL} */
}; };
union bnx2x_mcast_elem {
struct bnx2x_mcast_bin_elem bin_elem;
struct bnx2x_mcast_mac_elem mac_elem;
};
struct bnx2x_mcast_elem_group {
struct list_head mcast_group_link;
union bnx2x_mcast_elem mcast_elems[];
};
#define MCAST_MAC_ELEMS_PER_PG \
((PAGE_SIZE - sizeof(struct bnx2x_mcast_elem_group)) / \
sizeof(union bnx2x_mcast_elem))
struct bnx2x_pending_mcast_cmd { struct bnx2x_pending_mcast_cmd {
struct list_head link; struct list_head link;
struct list_head group_head;
int type; /* BNX2X_MCAST_CMD_X */ int type; /* BNX2X_MCAST_CMD_X */
union { union {
struct list_head macs_head; struct list_head macs_head;
...@@ -2638,16 +2653,29 @@ static int bnx2x_mcast_wait(struct bnx2x *bp, ...@@ -2638,16 +2653,29 @@ static int bnx2x_mcast_wait(struct bnx2x *bp,
return 0; return 0;
} }
static void bnx2x_free_groups(struct list_head *mcast_group_list)
{
struct bnx2x_mcast_elem_group *current_mcast_group;
while (!list_empty(mcast_group_list)) {
current_mcast_group = list_first_entry(mcast_group_list,
struct bnx2x_mcast_elem_group,
mcast_group_link);
list_del(&current_mcast_group->mcast_group_link);
free_page((unsigned long)current_mcast_group);
}
}
static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp, static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
struct bnx2x_mcast_obj *o, struct bnx2x_mcast_obj *o,
struct bnx2x_mcast_ramrod_params *p, struct bnx2x_mcast_ramrod_params *p,
enum bnx2x_mcast_cmd cmd) enum bnx2x_mcast_cmd cmd)
{ {
int total_sz;
struct bnx2x_pending_mcast_cmd *new_cmd; struct bnx2x_pending_mcast_cmd *new_cmd;
struct bnx2x_mcast_mac_elem *cur_mac = NULL;
struct bnx2x_mcast_list_elem *pos; struct bnx2x_mcast_list_elem *pos;
int macs_list_len = 0, macs_list_len_size; struct bnx2x_mcast_elem_group *elem_group;
struct bnx2x_mcast_mac_elem *mac_elem;
int total_elems = 0, macs_list_len = 0, offset = 0;
/* When adding MACs we'll need to store their values */ /* When adding MACs we'll need to store their values */
if (cmd == BNX2X_MCAST_CMD_ADD || cmd == BNX2X_MCAST_CMD_SET) if (cmd == BNX2X_MCAST_CMD_ADD || cmd == BNX2X_MCAST_CMD_SET)
...@@ -2657,50 +2685,61 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp, ...@@ -2657,50 +2685,61 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
if (!p->mcast_list_len) if (!p->mcast_list_len)
return 0; return 0;
/* For a set command, we need to allocate sufficient memory for all
* the bins, since we can't analyze at this point how much memory would
* be required.
*/
macs_list_len_size = macs_list_len *
sizeof(struct bnx2x_mcast_mac_elem);
if (cmd == BNX2X_MCAST_CMD_SET) {
int bin_size = BNX2X_MCAST_BINS_NUM *
sizeof(struct bnx2x_mcast_bin_elem);
if (bin_size > macs_list_len_size)
macs_list_len_size = bin_size;
}
total_sz = sizeof(*new_cmd) + macs_list_len_size;
/* Add mcast is called under spin_lock, thus calling with GFP_ATOMIC */ /* Add mcast is called under spin_lock, thus calling with GFP_ATOMIC */
new_cmd = kzalloc(total_sz, GFP_ATOMIC); new_cmd = kzalloc(sizeof(*new_cmd), GFP_ATOMIC);
if (!new_cmd) if (!new_cmd)
return -ENOMEM; return -ENOMEM;
DP(BNX2X_MSG_SP, "About to enqueue a new %d command. macs_list_len=%d\n",
cmd, macs_list_len);
INIT_LIST_HEAD(&new_cmd->data.macs_head); INIT_LIST_HEAD(&new_cmd->data.macs_head);
INIT_LIST_HEAD(&new_cmd->group_head);
new_cmd->type = cmd; new_cmd->type = cmd;
new_cmd->done = false; new_cmd->done = false;
DP(BNX2X_MSG_SP, "About to enqueue a new %d command. macs_list_len=%d\n",
cmd, macs_list_len);
switch (cmd) { switch (cmd) {
case BNX2X_MCAST_CMD_ADD: case BNX2X_MCAST_CMD_ADD:
case BNX2X_MCAST_CMD_SET: case BNX2X_MCAST_CMD_SET:
cur_mac = (struct bnx2x_mcast_mac_elem *) /* For a set command, we need to allocate sufficient memory for
((u8 *)new_cmd + sizeof(*new_cmd)); * all the bins, since we can't analyze at this point how much
* memory would be required.
/* Push the MACs of the current command into the pending command
* MACs list: FIFO
*/ */
total_elems = macs_list_len;
if (cmd == BNX2X_MCAST_CMD_SET) {
if (total_elems < BNX2X_MCAST_BINS_NUM)
total_elems = BNX2X_MCAST_BINS_NUM;
}
while (total_elems > 0) {
elem_group = (struct bnx2x_mcast_elem_group *)
__get_free_page(GFP_ATOMIC | __GFP_ZERO);
if (!elem_group) {
kfree(new_cmd);
bnx2x_free_groups(&new_cmd->group_head);
return -ENOMEM;
}
total_elems -= MCAST_MAC_ELEMS_PER_PG;
list_add_tail(&elem_group->mcast_group_link,
&new_cmd->group_head);
}
elem_group = list_first_entry(&new_cmd->group_head,
struct bnx2x_mcast_elem_group,
mcast_group_link);
list_for_each_entry(pos, &p->mcast_list, link) { list_for_each_entry(pos, &p->mcast_list, link) {
memcpy(cur_mac->mac, pos->mac, ETH_ALEN); mac_elem = &elem_group->mcast_elems[offset].mac_elem;
list_add_tail(&cur_mac->link, &new_cmd->data.macs_head); memcpy(mac_elem->mac, pos->mac, ETH_ALEN);
cur_mac++; /* Push the MACs of the current command into the pending
* command MACs list: FIFO
*/
list_add_tail(&mac_elem->link,
&new_cmd->data.macs_head);
offset++;
if (offset == MCAST_MAC_ELEMS_PER_PG) {
offset = 0;
elem_group = list_next_entry(elem_group,
mcast_group_link);
}
} }
break; break;
case BNX2X_MCAST_CMD_DEL: case BNX2X_MCAST_CMD_DEL:
...@@ -2978,7 +3017,8 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp, ...@@ -2978,7 +3017,8 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp,
u64 cur[BNX2X_MCAST_VEC_SZ], req[BNX2X_MCAST_VEC_SZ]; u64 cur[BNX2X_MCAST_VEC_SZ], req[BNX2X_MCAST_VEC_SZ];
struct bnx2x_mcast_mac_elem *pmac_pos, *pmac_pos_n; struct bnx2x_mcast_mac_elem *pmac_pos, *pmac_pos_n;
struct bnx2x_mcast_bin_elem *p_item; struct bnx2x_mcast_bin_elem *p_item;
int i, cnt = 0, mac_cnt = 0; struct bnx2x_mcast_elem_group *elem_group;
int cnt = 0, mac_cnt = 0, offset = 0, i;
memset(req, 0, sizeof(u64) * BNX2X_MCAST_VEC_SZ); memset(req, 0, sizeof(u64) * BNX2X_MCAST_VEC_SZ);
memcpy(cur, o->registry.aprox_match.vec, memcpy(cur, o->registry.aprox_match.vec,
...@@ -3001,9 +3041,10 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp, ...@@ -3001,9 +3041,10 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp,
* a list that will be used to configure bins. * a list that will be used to configure bins.
*/ */
cmd_pos->set_convert = true; cmd_pos->set_convert = true;
p_item = (struct bnx2x_mcast_bin_elem *)(cmd_pos + 1);
INIT_LIST_HEAD(&cmd_pos->data.macs_head); INIT_LIST_HEAD(&cmd_pos->data.macs_head);
elem_group = list_first_entry(&cmd_pos->group_head,
struct bnx2x_mcast_elem_group,
mcast_group_link);
for (i = 0; i < BNX2X_MCAST_BINS_NUM; i++) { for (i = 0; i < BNX2X_MCAST_BINS_NUM; i++) {
bool b_current = !!BIT_VEC64_TEST_BIT(cur, i); bool b_current = !!BIT_VEC64_TEST_BIT(cur, i);
bool b_required = !!BIT_VEC64_TEST_BIT(req, i); bool b_required = !!BIT_VEC64_TEST_BIT(req, i);
...@@ -3011,12 +3052,18 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp, ...@@ -3011,12 +3052,18 @@ bnx2x_mcast_hdl_pending_set_e2_convert(struct bnx2x *bp,
if (b_current == b_required) if (b_current == b_required)
continue; continue;
p_item = &elem_group->mcast_elems[offset].bin_elem;
p_item->bin = i; p_item->bin = i;
p_item->type = b_required ? BNX2X_MCAST_CMD_SET_ADD p_item->type = b_required ? BNX2X_MCAST_CMD_SET_ADD
: BNX2X_MCAST_CMD_SET_DEL; : BNX2X_MCAST_CMD_SET_DEL;
list_add_tail(&p_item->link , &cmd_pos->data.macs_head); list_add_tail(&p_item->link , &cmd_pos->data.macs_head);
p_item++;
cnt++; cnt++;
offset++;
if (offset == MCAST_MAC_ELEMS_PER_PG) {
offset = 0;
elem_group = list_next_entry(elem_group,
mcast_group_link);
}
} }
/* We now definitely know how many commands are hiding here. /* We now definitely know how many commands are hiding here.
...@@ -3103,6 +3150,7 @@ static inline int bnx2x_mcast_handle_pending_cmds_e2(struct bnx2x *bp, ...@@ -3103,6 +3150,7 @@ static inline int bnx2x_mcast_handle_pending_cmds_e2(struct bnx2x *bp,
*/ */
if (cmd_pos->done) { if (cmd_pos->done) {
list_del(&cmd_pos->link); list_del(&cmd_pos->link);
bnx2x_free_groups(&cmd_pos->group_head);
kfree(cmd_pos); kfree(cmd_pos);
} }
...@@ -3741,6 +3789,7 @@ static inline int bnx2x_mcast_handle_pending_cmds_e1( ...@@ -3741,6 +3789,7 @@ static inline int bnx2x_mcast_handle_pending_cmds_e1(
} }
list_del(&cmd_pos->link); list_del(&cmd_pos->link);
bnx2x_free_groups(&cmd_pos->group_head);
kfree(cmd_pos); kfree(cmd_pos);
return cnt; return cnt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment