From dae8ca2952c019c17bf7f29ee829fe103ed84d19 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Mon, 15 Jul 2013 16:21:10 +0200 Subject: [PATCH] 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. --- dde_linux/src/lib/usb/include/lx_emul.h | 5 -- dde_linux/src/lib/usb/include/nic/component.h | 40 +++++++--- dde_linux/src/lib/usb/nic/nic.cc | 80 +++++++++---------- 3 files changed, 70 insertions(+), 55 deletions(-) diff --git a/dde_linux/src/lib/usb/include/lx_emul.h b/dde_linux/src/lib/usb/include/lx_emul.h index 5144ca122f..e9dd5a8852 100644 --- a/dde_linux/src/lib/usb/include/lx_emul.h +++ b/dde_linux/src/lib/usb/include/lx_emul.h @@ -2949,11 +2949,6 @@ struct sk_buff_head skb != (struct sk_buff *)(queue); \ 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 sk_buff *alloc_skb(unsigned int, gfp_t); unsigned char *skb_push(struct sk_buff *, unsigned int); diff --git a/dde_linux/src/lib/usb/include/nic/component.h b/dde_linux/src/lib/usb/include/nic/component.h index d35b00efd5..a2e2beb8f4 100644 --- a/dde_linux/src/lib/usb/include/nic/component.h +++ b/dde_linux/src/lib/usb/include/nic/component.h @@ -71,7 +71,7 @@ namespace Nic { /** * 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 @@ -123,8 +123,13 @@ namespace Nic { { private: - Device *_device; /* device this session is using */ - Tx::Sink *_tx_sink; /* client packet sink */ + Device *_device; /* device this session is using */ + 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: @@ -141,7 +146,7 @@ namespace Nic { /* submit received packets to lower layer */ 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); if (_device->burst()) { @@ -156,6 +161,16 @@ namespace Nic { /* alloc new SKB */ skb = _device->alloc_skb(); + + if (!skb) { + _send_packet_avail_signal(); + _tx_alloc = false; + _tx_packet = packet; + return; + } + + _tx_alloc = true; + ptr = skb->data; work_skb.data = 0; _device->skb_fill(&work_skb, ptr, packet.size(), skb->end); @@ -169,17 +184,22 @@ namespace Nic { ptr = work_skb.end; skb->len += work_skb.truesize; } else { + /* 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++; } counter.inc(packet.size()); - if (!_tx_sink->ready_to_ack()) { - _wait_event(_tx_sink->ready_to_ack()); - } - /* acknowledge to client */ _tx_sink->acknowledge_packet(packet); @@ -201,7 +221,7 @@ namespace Nic { _rx_ack(false); if (_tx_sink->packet_avail()) - Signal_transmitter(_tx.sigh_packet_avail()).submit(); + _send_packet_avail_signal(); } void _rx_ack(bool block = true) diff --git a/dde_linux/src/lib/usb/nic/nic.cc b/dde_linux/src/lib/usb/nic/nic.cc index c5f78fa17f..5f943d64c6 100644 --- a/dde_linux/src/lib/usb/nic/nic.cc +++ b/dde_linux/src/lib/usb/nic/nic.cc @@ -55,9 +55,8 @@ class Skb sk_buff *_buf; unsigned *_free; unsigned _idx; - bool _wait_free; - enum { ENTRY_ELEMENT_SIZE = sizeof(_free[0]) * 8 }; + enum { ENTRY_ELEMENT_SIZE = sizeof(unsigned) * 8 }; public: @@ -65,12 +64,12 @@ class Skb : _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]; - _free = new (Genode::env()->heap()) unsigned[size]; + _buf = (sk_buff *)kmalloc(sizeof(sk_buff) * _entries, GFP_KERNEL); + _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++) _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 */ - _wait_free = false; - //PDBG("wait for free skbs ..."); - _wait_event(_wait_free); - - return alloc(); + return 0; } void free(sk_buff *buf) @@ -111,8 +105,6 @@ class Skb if (&_buf[0] > buf || entry > _entries) return; - /* unblock waiting skb allocs */ - _wait_free = true; _idx = 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 */ - 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->data += HEAD_ROOM; Genode::memcpy(skb->data, (void *)virt, skb->len); tx_skb(skb); + return true; } /** @@ -209,7 +206,11 @@ class Nic_device : public Nic::Device sk_buff *alloc_skb() { 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; 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(); + if (!skb) + return 0; + size = (size + 3) & ~(0x3); 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 *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->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) { - sk_buff *c = alloc_skb(0, 0); + sk_buff *c; + + if (!(c = alloc_skb(0,0))) + return 0; + unsigned char *start = c->start; *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) { + static int count_x = 0; list->prev = list->next = (sk_buff *)list; list->qlen = 0; } - /** * Add to tail of queue */ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) { - newsk->next = (sk_buff *)list; - newsk->prev = list->prev; + newsk->next = (sk_buff *)list; + newsk->prev = list->prev; list->prev->next = newsk; - list->prev = newsk; + list->prev = newsk; 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) { - sk_buff *l = (sk_buff *)list; - while (l->next != l) { - l = l->next; + if (!list->qlen) + return; - if (l == skb) { - l->prev->next = l->next; - l->next->prev = l->prev; - list->qlen--; - return; - } - } - - PERR("SKB not found in __skb_unlink"); + skb->prev->next = skb->next; + skb->next->prev = skb->prev; + skb->next = skb->prev = 0; + list->qlen--; } @@ -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) { - if (list->next == (sk_buff *)list) + if (list->qlen == 0) return 0; - + sk_buff *skb = list->next; - list->next = skb->next; - list->next->prev = (sk_buff *)list; - list->qlen--; + __skb_unlink(skb, list); return skb; }