diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/tx.c')
-rw-r--r-- | drivers/net/wimax/i2400m/tx.c | 75 |
1 files changed, 69 insertions, 6 deletions
diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c index 613a88ffd651..fa16ccf8e26a 100644 --- a/drivers/net/wimax/i2400m/tx.c +++ b/drivers/net/wimax/i2400m/tx.c | |||
@@ -278,6 +278,48 @@ enum { | |||
278 | #define TAIL_FULL ((void *)~(unsigned long)NULL) | 278 | #define TAIL_FULL ((void *)~(unsigned long)NULL) |
279 | 279 | ||
280 | /* | 280 | /* |
281 | * Calculate how much tail room is available | ||
282 | * | ||
283 | * Note the trick here. This path is ONLY caleed for Case A (see | ||
284 | * i2400m_tx_fifo_push() below), where we have: | ||
285 | * | ||
286 | * Case A | ||
287 | * N ___________ | ||
288 | * | tail room | | ||
289 | * | | | ||
290 | * |<- IN ->| | ||
291 | * | | | ||
292 | * | data | | ||
293 | * | | | ||
294 | * |<- OUT ->| | ||
295 | * | | | ||
296 | * | head room | | ||
297 | * 0 ----------- | ||
298 | * | ||
299 | * When calculating the tail_room, tx_in might get to be zero if | ||
300 | * i2400m->tx_in is right at the end of the buffer (really full | ||
301 | * buffer) if there is no head room. In this case, tail_room would be | ||
302 | * I2400M_TX_BUF_SIZE, although it is actually zero. Hence the final | ||
303 | * mod (%) operation. However, when doing this kind of optimization, | ||
304 | * i2400m->tx_in being zero would fail, so we treat is an a special | ||
305 | * case. | ||
306 | */ | ||
307 | static inline | ||
308 | size_t __i2400m_tx_tail_room(struct i2400m *i2400m) | ||
309 | { | ||
310 | size_t tail_room; | ||
311 | size_t tx_in; | ||
312 | |||
313 | if (unlikely(i2400m->tx_in) == 0) | ||
314 | return I2400M_TX_BUF_SIZE; | ||
315 | tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; | ||
316 | tail_room = I2400M_TX_BUF_SIZE - tx_in; | ||
317 | tail_room %= I2400M_TX_BUF_SIZE; | ||
318 | return tail_room; | ||
319 | } | ||
320 | |||
321 | |||
322 | /* | ||
281 | * Allocate @size bytes in the TX fifo, return a pointer to it | 323 | * Allocate @size bytes in the TX fifo, return a pointer to it |
282 | * | 324 | * |
283 | * @i2400m: device descriptor | 325 | * @i2400m: device descriptor |
@@ -338,7 +380,7 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) | |||
338 | return NULL; | 380 | return NULL; |
339 | } | 381 | } |
340 | /* Is there space at the tail? */ | 382 | /* Is there space at the tail? */ |
341 | tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE; | 383 | tail_room = __i2400m_tx_tail_room(i2400m); |
342 | if (tail_room < needed_size) { | 384 | if (tail_room < needed_size) { |
343 | if (i2400m->tx_out % I2400M_TX_BUF_SIZE | 385 | if (i2400m->tx_out % I2400M_TX_BUF_SIZE |
344 | < i2400m->tx_in % I2400M_TX_BUF_SIZE) { | 386 | < i2400m->tx_in % I2400M_TX_BUF_SIZE) { |
@@ -367,17 +409,29 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) | |||
367 | * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the | 409 | * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the |
368 | * header). | 410 | * header). |
369 | * | 411 | * |
412 | * Tail room can get to be zero if a message was opened when there was | ||
413 | * space only for a header. _tx_close() will mark it as to-skip (as it | ||
414 | * will have no payloads) and there will be no more space to flush, so | ||
415 | * nothing has to be done here. This is probably cheaper than ensuring | ||
416 | * in _tx_new() that there is some space for payloads...as we could | ||
417 | * always possibly hit the same problem if the payload wouldn't fit. | ||
418 | * | ||
370 | * Note: | 419 | * Note: |
371 | * | 420 | * |
372 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier | 421 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier |
422 | * | ||
423 | * This path is only taken for Case A FIFO situations [see | ||
424 | * i2400m_tx_fifo_push()] | ||
373 | */ | 425 | */ |
374 | static | 426 | static |
375 | void i2400m_tx_skip_tail(struct i2400m *i2400m) | 427 | void i2400m_tx_skip_tail(struct i2400m *i2400m) |
376 | { | 428 | { |
377 | struct device *dev = i2400m_dev(i2400m); | 429 | struct device *dev = i2400m_dev(i2400m); |
378 | size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; | 430 | size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; |
379 | size_t tail_room = I2400M_TX_BUF_SIZE - tx_in; | 431 | size_t tail_room = __i2400m_tx_tail_room(i2400m); |
380 | struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; | 432 | struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; |
433 | if (unlikely(tail_room == 0)) | ||
434 | return; | ||
381 | BUG_ON(tail_room < sizeof(*msg)); | 435 | BUG_ON(tail_room < sizeof(*msg)); |
382 | msg->size = tail_room | I2400M_TX_SKIP; | 436 | msg->size = tail_room | I2400M_TX_SKIP; |
383 | d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n", | 437 | d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n", |
@@ -474,10 +528,18 @@ void i2400m_tx_close(struct i2400m *i2400m) | |||
474 | struct i2400m_msg_hdr *tx_msg_moved; | 528 | struct i2400m_msg_hdr *tx_msg_moved; |
475 | size_t aligned_size, padding, hdr_size; | 529 | size_t aligned_size, padding, hdr_size; |
476 | void *pad_buf; | 530 | void *pad_buf; |
531 | unsigned num_pls; | ||
477 | 532 | ||
478 | if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ | 533 | if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ |
479 | goto out; | 534 | goto out; |
480 | 535 | num_pls = le16_to_cpu(tx_msg->num_pls); | |
536 | /* We can get this situation when a new message was started | ||
537 | * and there was no space to add payloads before hitting the | ||
538 | tail (and taking padding into consideration). */ | ||
539 | if (num_pls == 0) { | ||
540 | tx_msg->size |= I2400M_TX_SKIP; | ||
541 | goto out; | ||
542 | } | ||
481 | /* Relocate the message header | 543 | /* Relocate the message header |
482 | * | 544 | * |
483 | * Find the current header size, align it to 16 and if we need | 545 | * Find the current header size, align it to 16 and if we need |
@@ -491,7 +553,7 @@ void i2400m_tx_close(struct i2400m *i2400m) | |||
491 | */ | 553 | */ |
492 | hdr_size = sizeof(*tx_msg) | 554 | hdr_size = sizeof(*tx_msg) |
493 | + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); | 555 | + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); |
494 | hdr_size = ALIGN(hdr_size, I2400M_PL_PAD); | 556 | hdr_size = ALIGN(hdr_size, I2400M_PL_ALIGN); |
495 | tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; | 557 | tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; |
496 | tx_msg_moved = (void *) tx_msg + tx_msg->offset; | 558 | tx_msg_moved = (void *) tx_msg + tx_msg->offset; |
497 | memmove(tx_msg_moved, tx_msg, hdr_size); | 559 | memmove(tx_msg_moved, tx_msg, hdr_size); |
@@ -574,7 +636,7 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, | |||
574 | 636 | ||
575 | d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", | 637 | d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", |
576 | i2400m, buf, buf_len, pl_type); | 638 | i2400m, buf, buf_len, pl_type); |
577 | padded_len = ALIGN(buf_len, I2400M_PL_PAD); | 639 | padded_len = ALIGN(buf_len, I2400M_PL_ALIGN); |
578 | d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); | 640 | d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); |
579 | /* If there is no current TX message, create one; if the | 641 | /* If there is no current TX message, create one; if the |
580 | * current one is out of payload slots or we have a singleton, | 642 | * current one is out of payload slots or we have a singleton, |
@@ -591,6 +653,8 @@ try_new: | |||
591 | i2400m_tx_close(i2400m); | 653 | i2400m_tx_close(i2400m); |
592 | i2400m_tx_new(i2400m); | 654 | i2400m_tx_new(i2400m); |
593 | } | 655 | } |
656 | if (i2400m->tx_msg == NULL) | ||
657 | goto error_tx_new; | ||
594 | if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { | 658 | if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { |
595 | d_printf(2, dev, "TX: message too big, going new\n"); | 659 | d_printf(2, dev, "TX: message too big, going new\n"); |
596 | i2400m_tx_close(i2400m); | 660 | i2400m_tx_close(i2400m); |
@@ -773,7 +837,6 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m) | |||
773 | n = i2400m->tx_out / I2400M_TX_BUF_SIZE; | 837 | n = i2400m->tx_out / I2400M_TX_BUF_SIZE; |
774 | i2400m->tx_out %= I2400M_TX_BUF_SIZE; | 838 | i2400m->tx_out %= I2400M_TX_BUF_SIZE; |
775 | i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; | 839 | i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; |
776 | netif_start_queue(i2400m->wimax_dev.net_dev); | ||
777 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | 840 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); |
778 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | 841 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); |
779 | } | 842 | } |