usb_drv: Remove wait events for Nic

'alloc_skb' might now fail, the Nic component will then send a 'packet
available' signal and return. Fix broken SKB list implementation as well as
completely bogus initialization of SKBs.

Related to #778.
This commit is contained in:
Sebastian Sumpf 2013-07-15 16:21:10 +02:00 committed by Christian Helmuth
parent 99ae463e5c
commit dae8ca2952
3 changed files with 70 additions and 55 deletions

View File

@ -2949,11 +2949,6 @@ struct sk_buff_head
skb != (struct sk_buff *)(queue); \ skb != (struct sk_buff *)(queue); \
skb = skb->next) skb = skb->next)
#define skb_queue_walk_safe(queue, skb, tmp) \
for (skb = (queue)->next, tmp = skb->next; \
skb != (struct sk_buff *)(queue); \
skb = tmp, tmp = skb->next)
struct skb_shared_info *skb_shinfo(struct sk_buff *); struct skb_shared_info *skb_shinfo(struct sk_buff *);
struct sk_buff *alloc_skb(unsigned int, gfp_t); struct sk_buff *alloc_skb(unsigned int, gfp_t);
unsigned char *skb_push(struct sk_buff *, unsigned int); unsigned char *skb_push(struct sk_buff *, unsigned int);

View File

@ -71,7 +71,7 @@ namespace Nic {
/** /**
* Transmit data to driver * Transmit data to driver
*/ */
virtual void tx(addr_t virt, size_t size) = 0; virtual bool tx(addr_t virt, size_t size) = 0;
/** /**
* Return mac address of device * Return mac address of device
@ -123,8 +123,13 @@ namespace Nic {
{ {
private: private:
Device *_device; /* device this session is using */ Device *_device; /* device this session is using */
Tx::Sink *_tx_sink; /* client packet sink */ Tx::Sink *_tx_sink; /* client packet sink */
bool _tx_alloc;
Packet_descriptor _tx_packet;
void _send_packet_avail_signal() {
Signal_transmitter(_tx.sigh_packet_avail()).submit(); }
protected: protected:
@ -141,7 +146,7 @@ namespace Nic {
/* submit received packets to lower layer */ /* submit received packets to lower layer */
while (_tx_sink->packet_avail()) while (_tx_sink->packet_avail())
{ {
Packet_descriptor packet = _tx_sink->get_packet(); Packet_descriptor packet = _tx_alloc ? _tx_sink->get_packet() : _tx_packet;
addr_t virt = (addr_t)_tx_sink->packet_content(packet); addr_t virt = (addr_t)_tx_sink->packet_content(packet);
if (_device->burst()) { if (_device->burst()) {
@ -156,6 +161,16 @@ namespace Nic {
/* alloc new SKB */ /* alloc new SKB */
skb = _device->alloc_skb(); skb = _device->alloc_skb();
if (!skb) {
_send_packet_avail_signal();
_tx_alloc = false;
_tx_packet = packet;
return;
}
_tx_alloc = true;
ptr = skb->data; ptr = skb->data;
work_skb.data = 0; work_skb.data = 0;
_device->skb_fill(&work_skb, ptr, packet.size(), skb->end); _device->skb_fill(&work_skb, ptr, packet.size(), skb->end);
@ -169,17 +184,22 @@ namespace Nic {
ptr = work_skb.end; ptr = work_skb.end;
skb->len += work_skb.truesize; skb->len += work_skb.truesize;
} else { } else {
/* send to driver */ /* send to driver */
_device->tx(virt, packet.size()); if (!(_device->tx(virt, packet.size()))) {
_send_packet_avail_signal();
_tx_alloc = false;
_tx_packet = packet;
return;
}
_tx_alloc = true;
tx_cnt++; tx_cnt++;
} }
counter.inc(packet.size()); counter.inc(packet.size());
if (!_tx_sink->ready_to_ack()) {
_wait_event(_tx_sink->ready_to_ack());
}
/* acknowledge to client */ /* acknowledge to client */
_tx_sink->acknowledge_packet(packet); _tx_sink->acknowledge_packet(packet);
@ -201,7 +221,7 @@ namespace Nic {
_rx_ack(false); _rx_ack(false);
if (_tx_sink->packet_avail()) if (_tx_sink->packet_avail())
Signal_transmitter(_tx.sigh_packet_avail()).submit(); _send_packet_avail_signal();
} }
void _rx_ack(bool block = true) void _rx_ack(bool block = true)

View File

@ -55,9 +55,8 @@ class Skb
sk_buff *_buf; sk_buff *_buf;
unsigned *_free; unsigned *_free;
unsigned _idx; unsigned _idx;
bool _wait_free;
enum { ENTRY_ELEMENT_SIZE = sizeof(_free[0]) * 8 }; enum { ENTRY_ELEMENT_SIZE = sizeof(unsigned) * 8 };
public: public:
@ -65,12 +64,12 @@ class Skb
: :
_entries(entries), _idx(0) _entries(entries), _idx(0)
{ {
unsigned const size = _entries / sizeof(_free[0]); unsigned const size = _entries / sizeof(unsigned);
_buf = new (Genode::env()->heap()) sk_buff[_entries]; _buf = (sk_buff *)kmalloc(sizeof(sk_buff) * _entries, GFP_KERNEL);
_free = new (Genode::env()->heap()) unsigned[size]; _free = (unsigned *)kmalloc(sizeof(unsigned) * size, GFP_KERNEL);
Genode::memset(_free, 0xff, size * sizeof(_free[0])); Genode::memset(_free, 0xff, size * sizeof(unsigned));
for (unsigned i = 0; i < _entries; i++) for (unsigned i = 0; i < _entries; i++)
_buf[i].start = (unsigned char *)kmalloc(buffer_size + NET_IP_ALIGN, GFP_NOIO); _buf[i].start = (unsigned char *)kmalloc(buffer_size + NET_IP_ALIGN, GFP_NOIO);
@ -97,12 +96,7 @@ class Skb
} }
/* wait until some SKBs are freed */ return 0;
_wait_free = false;
//PDBG("wait for free skbs ...");
_wait_event(_wait_free);
return alloc();
} }
void free(sk_buff *buf) void free(sk_buff *buf)
@ -111,8 +105,6 @@ class Skb
if (&_buf[0] > buf || entry > _entries) if (&_buf[0] > buf || entry > _entries)
return; return;
/* unblock waiting skb allocs */
_wait_free = true;
_idx = entry / ENTRY_ELEMENT_SIZE; _idx = entry / ENTRY_ELEMENT_SIZE;
_free[_idx] |= (1 << (entry % ENTRY_ELEMENT_SIZE)); _free[_idx] |= (1 << (entry % ENTRY_ELEMENT_SIZE));
} }
@ -193,14 +185,19 @@ class Nic_device : public Nic::Device
/** /**
* Submit packet to driver * Submit packet to driver
*/ */
void tx(Genode::addr_t virt, Genode::size_t size) bool tx(Genode::addr_t virt, Genode::size_t size)
{ {
sk_buff *skb = _alloc_skb(size + HEAD_ROOM); sk_buff *skb;
if (!(skb = _alloc_skb(size + HEAD_ROOM)))
return false;
skb->len = size; skb->len = size;
skb->data += HEAD_ROOM; skb->data += HEAD_ROOM;
Genode::memcpy(skb->data, (void *)virt, skb->len); Genode::memcpy(skb->data, (void *)virt, skb->len);
tx_skb(skb); tx_skb(skb);
return true;
} }
/** /**
@ -209,7 +206,11 @@ class Nic_device : public Nic::Device
sk_buff *alloc_skb() sk_buff *alloc_skb()
{ {
struct usbnet *dev = (usbnet *)netdev_priv(_ndev); struct usbnet *dev = (usbnet *)netdev_priv(_ndev);
sk_buff *skb = _alloc_skb(dev->rx_urb_size); sk_buff *skb;
if (!(skb = _alloc_skb(dev->rx_urb_size)))
return 0;
skb->len = 0; skb->len = 0;
return skb; return skb;
} }
@ -383,6 +384,9 @@ struct sk_buff *_alloc_skb(unsigned int size, bool tx)
{ {
sk_buff *skb = tx ? skb_tx()->alloc() : skb_rx()->alloc(); sk_buff *skb = tx ? skb_tx()->alloc() : skb_rx()->alloc();
if (!skb)
return 0;
size = (size + 3) & ~(0x3); size = (size + 3) & ~(0x3);
skb->end = skb->start + size; skb->end = skb->start + size;
@ -405,7 +409,7 @@ struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, unsigned int length) struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, unsigned int length)
{ {
struct sk_buff *s = _alloc_skb(length + NET_IP_ALIGN, false); struct sk_buff *s = _alloc_skb(length + NET_IP_ALIGN, false);
if (dev->net_ip_align) { if (s && dev->net_ip_align) {
s->data += NET_IP_ALIGN; s->data += NET_IP_ALIGN;
s->tail += NET_IP_ALIGN; s->tail += NET_IP_ALIGN;
} }
@ -543,7 +547,11 @@ void skb_trim(struct sk_buff *skb, unsigned int len)
*/ */
struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{ {
sk_buff *c = alloc_skb(0, 0); sk_buff *c;
if (!(c = alloc_skb(0,0)))
return 0;
unsigned char *start = c->start; unsigned char *start = c->start;
*c = *skb; *c = *skb;
@ -587,20 +595,20 @@ struct skb_shared_info *skb_shinfo(struct sk_buff * /* skb */)
*/ */
void skb_queue_head_init(struct sk_buff_head *list) void skb_queue_head_init(struct sk_buff_head *list)
{ {
static int count_x = 0;
list->prev = list->next = (sk_buff *)list; list->prev = list->next = (sk_buff *)list;
list->qlen = 0; list->qlen = 0;
} }
/** /**
* Add to tail of queue * Add to tail of queue
*/ */
void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{ {
newsk->next = (sk_buff *)list; newsk->next = (sk_buff *)list;
newsk->prev = list->prev; newsk->prev = list->prev;
list->prev->next = newsk; list->prev->next = newsk;
list->prev = newsk; list->prev = newsk;
list->qlen++; list->qlen++;
} }
@ -614,19 +622,13 @@ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) {
*/ */
void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
{ {
sk_buff *l = (sk_buff *)list; if (!list->qlen)
while (l->next != l) { return;
l = l->next;
if (l == skb) { skb->prev->next = skb->next;
l->prev->next = l->next; skb->next->prev = skb->prev;
l->next->prev = l->prev; skb->next = skb->prev = 0;
list->qlen--; list->qlen--;
return;
}
}
PERR("SKB not found in __skb_unlink");
} }
@ -635,13 +637,11 @@ void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
*/ */
struct sk_buff *skb_dequeue(struct sk_buff_head *list) struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{ {
if (list->next == (sk_buff *)list) if (list->qlen == 0)
return 0; return 0;
sk_buff *skb = list->next; sk_buff *skb = list->next;
list->next = skb->next; __skb_unlink(skb, list);
list->next->prev = (sk_buff *)list;
list->qlen--;
return skb; return skb;
} }