diff options
author | Paul Janzen <pcj@linux.sez.to> | 2006-01-16 18:52:13 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2006-01-17 07:23:37 -0500 |
commit | f7ea333765438232ac346a2f23cfec3e2012758f (patch) | |
tree | 9a1f685e3220e9b6b4170586e7f4b4d19fc6e753 /drivers/net | |
parent | b44cd572623cb6a931a947d9108595517fd945f8 (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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/mv643xx_eth.c | 54 |
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 | |||
1100 | static 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) { |
1144 | linear: | ||
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; |