zynq: improve nic driver error handling

also be more verbose about detected errors

fixes #3320
This commit is contained in:
Johannes Schlatow 2019-05-06 16:54:23 +02:00 committed by Christian Helmuth
parent 739317a83f
commit 9097c80269
4 changed files with 116 additions and 21 deletions

View File

@ -84,6 +84,9 @@ class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
size_t _head_index() const { return _head_idx; }
size_t _tail_index() const { return _tail_idx; }
void _reset_head() { _head_idx = 0; }
void _reset_tail() { _tail_idx = 0; }
private:
/*

View File

@ -145,8 +145,11 @@ namespace Genode
*/
struct Tx_status : Register<0x14, 32>
{
struct Tx_complete : Bitfield<5, 1> {};
struct Tx_go : Bitfield<3, 1> {};
struct Tx_hresp_nok : Bitfield<8, 1> {};
struct Tx_err_underrun : Bitfield<6, 1> {};
struct Tx_complete : Bitfield<5, 1> {};
struct Tx_err_bufexh : Bitfield<4, 1> {};
struct Tx_go : Bitfield<3, 1> {};
};
/**
@ -170,6 +173,7 @@ namespace Genode
*/
struct Rx_status : Register<0x20, 32>
{
struct Rx_hresp_nok : Bitfield<3, 1> {};
struct Rx_overrun : Bitfield<2, 1> {};
struct Frame_received : Bitfield<1, 1> {};
struct Buffer_not_available : Bitfield<0, 1> {};
@ -261,6 +265,22 @@ namespace Genode
struct Counter : Bitfield<0, 16> { };
};
/**
* Counter for tx underrun errors
*/
struct Tx_underrun : Register<0x134, 32>
{
struct Counter : Bitfield<0, 10> { };
};
/**
* Counter for deferred transmission frames
*/
struct Tx_deferred: Register<0x148, 32>
{
struct Counter : Bitfield<0, 18> { };
};
/**
* Counter for the successfully received frames
*/
@ -487,12 +507,12 @@ namespace Genode
virtual void _handle_irq()
{
/* 16.3.9 Receiving Frames */
/* read interrupt status, to detect the interrupt reasone */
/* read interrupt status, to detect the interrupt reason */
const Interrupt_status::access_t status = read<Interrupt_status>();
const Rx_status::access_t rxStatus = read<Rx_status>();
const Tx_status::access_t txStatus = read<Tx_status>();
if ( Interrupt_status::Rx_complete::get(status) ) {
while (_rx_buffer.next_packet()) {
_handle_acks();
@ -512,14 +532,50 @@ namespace Genode
else {
_handle_acks();
}
/* handle Rx/Tx errors */
if ( Tx_status::Tx_hresp_nok::get(txStatus)
|| Rx_status::Rx_hresp_nok::get(rxStatus)) {
write<Control::Tx_en>(0);
write<Control::Rx_en>(0);
_tx_buffer.reset(*_tx.sink());
_rx_buffer.reset();
write<Control::Tx_en>(1);
write<Control::Rx_en>(1);
write<Tx_status>(Tx_status::Tx_hresp_nok::bits(1));
write<Rx_status>(Rx_status::Rx_hresp_nok::bits(1));
Genode::error("Rx/Tx error: resetting both");
}
/* handle Tx errors */
if ( Tx_status::Tx_err_underrun::get(txStatus)
|| Tx_status::Tx_err_bufexh::get(txStatus)) {
write<Control::Tx_en>(0);
_tx_buffer.reset(*_tx.sink());
write<Control::Tx_en>(1);
Genode::error("Tx error: resetting transceiver");
}
/* handle Rx error */
bool print_stats = false;
if (Interrupt_status::Rx_overrun::get(status)) {
write<Control::Tx_pause>(1);
write<Interrupt_status>(Interrupt_status::Rx_overrun::bits(1));
write<Rx_status>(Rx_status::Rx_overrun::bits(1));
/* reset the receiver because this may lead to a deadlock */
write<Control::Rx_en>(0);
_rx_buffer.reset();
write<Control::Rx_en>(1);
print_stats = true;
Genode::error("Rx overrun");
Genode::error("Rx overrun - packet buffer overflow");
}
if (Interrupt_status::Rx_used_read::get(status)) {
/* tried to use buffer descriptor with used bit set */
@ -529,8 +585,9 @@ namespace Genode
write<Control::Tx_pause>(1);
write<Interrupt_status>(Interrupt_status::Rx_used_read::bits(1));
write<Rx_status>(Rx_status::Buffer_not_available::bits(1));
print_stats = true;
Genode::error("Rx used");
Genode::error("Rx used - the Nic client is not fast enough");
}
if (Interrupt_status::Pause_zero::get(status)) {
Genode::warning("Pause ended.");
@ -555,6 +612,8 @@ namespace Genode
const uint32_t tcp_chk = read<Rx_tcp_chksum_errors::Counter>();
const uint32_t transmit = read<Frames_transmitted>();
const uint32_t pause_tx = read<Pause_transmitted::Counter>();
const uint32_t underrun = read<Tx_underrun::Counter>();
const uint32_t deferred = read<Tx_deferred::Counter>();
Genode::warning("Received: ", received);
Genode::warning(" pause frames: ", pause_rx);
@ -566,6 +625,8 @@ namespace Genode
Genode::warning(" TCP chk failed: ", tcp_chk);
Genode::warning("Transmitted: ", transmit);
Genode::warning(" pause frames: ", pause_tx);
Genode::warning(" underrun: ", underrun);
Genode::warning(" deferred: ", deferred);
}
_irq.ack_irq();

View File

@ -96,17 +96,22 @@ class Rx_buffer_descriptor : public Buffer_descriptor
return false;
}
void reset()
{
for (size_t i=0; i <= _max_index(); i++) {
_descriptors[i].status = 0;
Addr::Used::set(_descriptors[i].addr, 0);
}
_reset_head();
}
bool next_packet()
{
/* Find next available descriptor (head) holding a packet. */
for (unsigned int i=0; i < _max_index(); i++) {
if (_head_available())
return true;
if (_head_available())
return true;
_advance_head();
}
return false;
_advance_head();
return _head_available();
}
Nic::Packet_descriptor get_packet_descriptor()

View File

@ -34,7 +34,11 @@ class Tx_buffer_descriptor : public Buffer_descriptor
struct Last_buffer : Bitfield<15, 1> {};
struct Wrap : Bitfield<30, 1> {};
struct Used : Bitfield<31, 1> {};
struct Chksum_err : Bitfield<20, 2> {};
struct Chksum_err : Bitfield<20, 3> {};
struct Crc_present: Bitfield<16, 1> {};
struct Late_collision: Bitfield<26, 1> {};
struct Corrupt: Bitfield<27, 1> {};
struct Retry_limit: Bitfield<29, 1> {};
};
Timer::Connection &_timer;
@ -74,29 +78,51 @@ class Tx_buffer_descriptor : public Buffer_descriptor
}
}
void submit_acks(Nic::Session::Rx::Sink &sink)
void reset(Nic::Session::Rx::Sink &sink)
{
/* ack all packets that are still queued */
submit_acks(sink, true);
/* reset head and tail */
_reset_head();
_reset_tail();
}
void submit_acks(Nic::Session::Rx::Sink &sink, bool force=false)
{
/* the tail marks the descriptor for which we wait to
* be handed over to software */
for (size_t i=0; i <= _queued(); i++) {
for (size_t i=0; i < _queued(); i++) {
/* stop if still in use by hardware */
if (!Status::Used::get(_tail().status))
if (!Status::Used::get(_tail().status) && !force)
break;
/* if descriptor has been configured properly */
if (_tail().addr != 0) {
/* build packet descriptor from buffer descriptor
* and acknowledge packet */
const size_t length = Status::Length::get(_tail().status);
Nic::Packet_descriptor p((addr_t)_tail().addr - _phys_base, length);
if (sink.packet_valid(p))
sink.acknowledge_packet(p);
else
warning("Invalid packet descriptor");
/* erase address so that we don't send an ack again */
_tail().addr = 0;
/* TODO optionally, we may evaluate the Tx status here */
/* evaluate Tx status */
if (Status::Retry_limit::get(_tail().status))
warning("Retry limit exceeded");
if (Status::Corrupt::get(_tail().status))
warning("Transmit frame corruption");
if (Status::Late_collision::get(_tail().status))
warning("Late collision error");
if (Status::Crc_present::get(_tail().status))
warning("CRC already present - this impedes checksum offloading");
}
_advance_tail();