diff options
| author | Alexandre Bounine <alexandre.bounine@idt.com> | 2012-10-04 20:15:48 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:19 -0400 |
| commit | 7c4a6106d6451fc03c491e61df37c044505d843a (patch) | |
| tree | 4eaf5ab00c18973bfe444136919c2dc18b2abaaa | |
| parent | de4ec99c32cca755a11f91abb86ed94ce11f2e60 (diff) | |
rapidio/rionet: fix multicast packet transmit logic
Fix multicast packet transmit logic to account for repetitive transmission
of single skb:
- correct check for available buffers (this bug may produce NULL pointer
crash dump in case of heavy traffic);
- update skb user count (incorrect user counter causes a warning dump from
net_tx_action routine during multicast transfers in systems with three or
more rionet participants).
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | drivers/net/rionet.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index 91d25888a1b9..1470d3e86e3c 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c | |||
| @@ -79,6 +79,7 @@ static int rionet_capable = 1; | |||
| 79 | * on system trade-offs. | 79 | * on system trade-offs. |
| 80 | */ | 80 | */ |
| 81 | static struct rio_dev **rionet_active; | 81 | static struct rio_dev **rionet_active; |
| 82 | static int nact; /* total number of active rionet peers */ | ||
| 82 | 83 | ||
| 83 | #define is_rionet_capable(src_ops, dst_ops) \ | 84 | #define is_rionet_capable(src_ops, dst_ops) \ |
| 84 | ((src_ops & RIO_SRC_OPS_DATA_MSG) && \ | 85 | ((src_ops & RIO_SRC_OPS_DATA_MSG) && \ |
| @@ -175,6 +176,7 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
| 175 | struct ethhdr *eth = (struct ethhdr *)skb->data; | 176 | struct ethhdr *eth = (struct ethhdr *)skb->data; |
| 176 | u16 destid; | 177 | u16 destid; |
| 177 | unsigned long flags; | 178 | unsigned long flags; |
| 179 | int add_num = 1; | ||
| 178 | 180 | ||
| 179 | local_irq_save(flags); | 181 | local_irq_save(flags); |
| 180 | if (!spin_trylock(&rnet->tx_lock)) { | 182 | if (!spin_trylock(&rnet->tx_lock)) { |
| @@ -182,7 +184,10 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
| 182 | return NETDEV_TX_LOCKED; | 184 | return NETDEV_TX_LOCKED; |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 185 | if ((rnet->tx_cnt + 1) > RIONET_TX_RING_SIZE) { | 187 | if (is_multicast_ether_addr(eth->h_dest)) |
| 188 | add_num = nact; | ||
| 189 | |||
| 190 | if ((rnet->tx_cnt + add_num) > RIONET_TX_RING_SIZE) { | ||
| 186 | netif_stop_queue(ndev); | 191 | netif_stop_queue(ndev); |
| 187 | spin_unlock_irqrestore(&rnet->tx_lock, flags); | 192 | spin_unlock_irqrestore(&rnet->tx_lock, flags); |
| 188 | printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n", | 193 | printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n", |
| @@ -191,11 +196,16 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
| 191 | } | 196 | } |
| 192 | 197 | ||
| 193 | if (is_multicast_ether_addr(eth->h_dest)) { | 198 | if (is_multicast_ether_addr(eth->h_dest)) { |
| 199 | int count = 0; | ||
| 194 | for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size); | 200 | for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size); |
| 195 | i++) | 201 | i++) |
| 196 | if (rionet_active[i]) | 202 | if (rionet_active[i]) { |
| 197 | rionet_queue_tx_msg(skb, ndev, | 203 | rionet_queue_tx_msg(skb, ndev, |
| 198 | rionet_active[i]); | 204 | rionet_active[i]); |
| 205 | if (count) | ||
| 206 | atomic_inc(&skb->users); | ||
| 207 | count++; | ||
| 208 | } | ||
| 199 | } else if (RIONET_MAC_MATCH(eth->h_dest)) { | 209 | } else if (RIONET_MAC_MATCH(eth->h_dest)) { |
| 200 | destid = RIONET_GET_DESTID(eth->h_dest); | 210 | destid = RIONET_GET_DESTID(eth->h_dest); |
| 201 | if (rionet_active[destid]) | 211 | if (rionet_active[destid]) |
| @@ -220,14 +230,17 @@ static void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u | |||
| 220 | if (info == RIONET_DOORBELL_JOIN) { | 230 | if (info == RIONET_DOORBELL_JOIN) { |
| 221 | if (!rionet_active[sid]) { | 231 | if (!rionet_active[sid]) { |
| 222 | list_for_each_entry(peer, &rionet_peers, node) { | 232 | list_for_each_entry(peer, &rionet_peers, node) { |
| 223 | if (peer->rdev->destid == sid) | 233 | if (peer->rdev->destid == sid) { |
| 224 | rionet_active[sid] = peer->rdev; | 234 | rionet_active[sid] = peer->rdev; |
| 235 | nact++; | ||
| 236 | } | ||
| 225 | } | 237 | } |
| 226 | rio_mport_send_doorbell(mport, sid, | 238 | rio_mport_send_doorbell(mport, sid, |
| 227 | RIONET_DOORBELL_JOIN); | 239 | RIONET_DOORBELL_JOIN); |
| 228 | } | 240 | } |
| 229 | } else if (info == RIONET_DOORBELL_LEAVE) { | 241 | } else if (info == RIONET_DOORBELL_LEAVE) { |
| 230 | rionet_active[sid] = NULL; | 242 | rionet_active[sid] = NULL; |
| 243 | nact--; | ||
| 231 | } else { | 244 | } else { |
| 232 | if (netif_msg_intr(rnet)) | 245 | if (netif_msg_intr(rnet)) |
| 233 | printk(KERN_WARNING "%s: unhandled doorbell\n", | 246 | printk(KERN_WARNING "%s: unhandled doorbell\n", |
| @@ -523,6 +536,7 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) | |||
| 523 | 536 | ||
| 524 | rc = rionet_setup_netdev(rdev->net->hport, ndev); | 537 | rc = rionet_setup_netdev(rdev->net->hport, ndev); |
| 525 | rionet_check = 1; | 538 | rionet_check = 1; |
| 539 | nact = 0; | ||
| 526 | } | 540 | } |
| 527 | 541 | ||
| 528 | /* | 542 | /* |
