aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Janzen <pcj@linux.sez.to>2006-01-16 18:52:13 -0500
committerJeff Garzik <jgarzik@pobox.com>2006-01-17 07:23:37 -0500
commitf7ea333765438232ac346a2f23cfec3e2012758f (patch)
tree9a1f685e3220e9b6b4170586e7f4b4d19fc6e753
parentb44cd572623cb6a931a947d9108595517fd945f8 (diff)
[PATCH] mv643xx_eth: Fix handling of small, unaligned fragments
Fix handling of small, unaligned fragments. It also solves a potential deadlock if skb_linearize() returns -ENOMEM. Signed-off-by: Paul Janzen <pcj@linux.sez.to> Signed-off-by: Dale Farnsworth <dale@farnsworth.org> mv643xx_eth.c | 54 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 23 deletions(-) Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
-rw-r--r--drivers/net/mv643xx_eth.c54
1 files changed, 31 insertions, 23 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index 4afb954092a6..e01b03c7615e 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -1093,6 +1093,25 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
1093} 1093}
1094#endif 1094#endif
1095 1095
1096/* Hardware can't handle unaligned fragments smaller than 9 bytes.
1097 * This helper function detects that case.
1098 */
1099
1100static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
1101{
1102 unsigned int frag;
1103 skb_frag_t *fragp;
1104
1105 for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
1106 fragp = &skb_shinfo(skb)->frags[frag];
1107 if (fragp->size <= 8 && fragp->page_offset & 0x7)
1108 return 1;
1109
1110 }
1111 return 0;
1112}
1113
1114
1096/* 1115/*
1097 * mv643xx_eth_start_xmit 1116 * mv643xx_eth_start_xmit
1098 * 1117 *
@@ -1136,12 +1155,19 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
1136 return 1; 1155 return 1;
1137 } 1156 }
1138 1157
1158#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
1159 if (has_tiny_unaligned_frags(skb)) {
1160 if ((skb_linearize(skb, GFP_ATOMIC) != 0)) {
1161 stats->tx_dropped++;
1162 printk(KERN_DEBUG "%s: failed to linearize tiny "
1163 "unaligned fragment\n", dev->name);
1164 return 1;
1165 }
1166 }
1167
1139 spin_lock_irqsave(&mp->lock, flags); 1168 spin_lock_irqsave(&mp->lock, flags);
1140 1169
1141 /* Update packet info data structure -- DMA owned, first last */
1142#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
1143 if (!skb_shinfo(skb)->nr_frags) { 1170 if (!skb_shinfo(skb)->nr_frags) {
1144linear:
1145 if (skb->ip_summed != CHECKSUM_HW) { 1171 if (skb->ip_summed != CHECKSUM_HW) {
1146 /* Errata BTS #50, IHL must be 5 if no HW checksum */ 1172 /* Errata BTS #50, IHL must be 5 if no HW checksum */
1147 pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | 1173 pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
@@ -1183,26 +1209,6 @@ linear:
1183 } else { 1209 } else {
1184 unsigned int frag; 1210 unsigned int frag;
1185 1211
1186 /* Since hardware can't handle unaligned fragments smaller
1187 * than 9 bytes, if we find any, we linearize the skb
1188 * and start again. When I've seen it, it's always been
1189 * the first frag (probably near the end of the page),
1190 * but we check all frags to be safe.
1191 */
1192 for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
1193 skb_frag_t *fragp;
1194
1195 fragp = &skb_shinfo(skb)->frags[frag];
1196 if (fragp->size <= 8 && fragp->page_offset & 0x7) {
1197 skb_linearize(skb, GFP_ATOMIC);
1198 printk(KERN_DEBUG "%s: unaligned tiny fragment"
1199 "%d of %d, fixed\n",
1200 dev->name, frag,
1201 skb_shinfo(skb)->nr_frags);
1202 goto linear;
1203 }
1204 }
1205
1206 /* first frag which is skb header */ 1212 /* first frag which is skb header */
1207 pkt_info.byte_cnt = skb_headlen(skb); 1213 pkt_info.byte_cnt = skb_headlen(skb);
1208 pkt_info.buf_ptr = dma_map_single(NULL, skb->data, 1214 pkt_info.buf_ptr = dma_map_single(NULL, skb->data,
@@ -1288,6 +1294,8 @@ linear:
1288 } 1294 }
1289 } 1295 }
1290#else 1296#else
1297 spin_lock_irqsave(&mp->lock, flags);
1298
1291 pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC | 1299 pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC |
1292 ETH_TX_LAST_DESC; 1300 ETH_TX_LAST_DESC;
1293 pkt_info.l4i_chk = 0; 1301 pkt_info.l4i_chk = 0;