mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-25 19:54:26 +00:00
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:
parent
99ae463e5c
commit
dae8ca2952
@ -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);
|
||||||
|
@ -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
|
||||||
@ -125,6 +125,11 @@ namespace Nic {
|
|||||||
|
|
||||||
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)
|
||||||
|
@ -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,11 +595,11 @@ 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
|
||||||
*/
|
*/
|
||||||
@ -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) {
|
|
||||||
l = l->next;
|
|
||||||
|
|
||||||
if (l == skb) {
|
|
||||||
l->prev->next = l->next;
|
|
||||||
l->next->prev = l->prev;
|
|
||||||
list->qlen--;
|
|
||||||
return;
|
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)
|
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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user