#include "e1000g_sw.h"
#include "e1000g_debug.h"
static boolean_t e1000g_send(struct e1000g *, mblk_t *);
static int e1000g_tx_copy(e1000g_tx_ring_t *,
p_tx_sw_packet_t, mblk_t *, boolean_t);
static int e1000g_tx_bind(e1000g_tx_ring_t *,
p_tx_sw_packet_t, mblk_t *);
static boolean_t e1000g_retrieve_context(mblk_t *, context_data_t *, size_t);
static boolean_t e1000g_check_context(e1000g_tx_ring_t *, context_data_t *);
static int e1000g_fill_tx_ring(e1000g_tx_ring_t *, LIST_DESCRIBER *,
context_data_t *);
static void e1000g_fill_context_descriptor(context_data_t *,
struct e1000_context_desc *);
static int e1000g_fill_tx_desc(e1000g_tx_ring_t *,
p_tx_sw_packet_t, uint64_t, size_t);
static uint32_t e1000g_fill_82544_desc(uint64_t Address, size_t Length,
p_desc_array_t desc_array);
static int e1000g_tx_workaround_PCIX_82544(p_tx_sw_packet_t, uint64_t, size_t);
static int e1000g_tx_workaround_jumbo_82544(p_tx_sw_packet_t, uint64_t, size_t);
static void e1000g_82547_timeout(void *);
static void e1000g_82547_tx_move_tail(e1000g_tx_ring_t *);
static void e1000g_82547_tx_move_tail_work(e1000g_tx_ring_t *);
void
e1000g_free_tx_swpkt(register p_tx_sw_packet_t packet)
{
switch (packet->data_transfer_type) {
case USE_BCOPY:
packet->tx_buf->len = 0;
break;
#ifdef __sparc
case USE_DVMA:
dvma_unload(packet->tx_dma_handle, 0, -1);
break;
#endif
case USE_DMA:
(void) ddi_dma_unbind_handle(packet->tx_dma_handle);
break;
default:
break;
}
ASSERT(packet->mp == NULL);
packet->data_transfer_type = USE_NONE;
packet->num_mblk_frag = 0;
packet->num_desc = 0;
}
mblk_t *
e1000g_m_tx(void *arg, mblk_t *mp)
{
struct e1000g *Adapter = (struct e1000g *)arg;
mblk_t *next;
rw_enter(&Adapter->chip_lock, RW_READER);
if ((Adapter->e1000g_state & E1000G_SUSPENDED) ||
!(Adapter->e1000g_state & E1000G_STARTED) ||
(Adapter->link_state != LINK_STATE_UP)) {
freemsgchain(mp);
mp = NULL;
}
while (mp != NULL) {
next = mp->b_next;
mp->b_next = NULL;
if (!e1000g_send(Adapter, mp)) {
mp->b_next = next;
break;
}
mp = next;
}
rw_exit(&Adapter->chip_lock);
return (mp);
}
static boolean_t
e1000g_send(struct e1000g *Adapter, mblk_t *mp)
{
p_tx_sw_packet_t packet;
LIST_DESCRIBER pending_list;
size_t len;
size_t msg_size;
uint32_t frag_count;
int desc_count;
uint32_t desc_total;
uint32_t bcopy_thresh;
uint32_t hdr_frag_len;
boolean_t tx_undersize_flag;
mblk_t *nmp;
mblk_t *tmp;
mblk_t *new_mp;
mblk_t *pre_mp;
mblk_t *next_mp;
e1000g_tx_ring_t *tx_ring;
context_data_t cur_context;
tx_ring = Adapter->tx_ring;
bcopy_thresh = Adapter->tx_bcopy_thresh;
tx_undersize_flag = B_FALSE;
frag_count = 0;
msg_size = 0;
for (nmp = mp; nmp; nmp = nmp->b_cont) {
frag_count++;
msg_size += MBLKL(nmp);
}
if (!e1000g_retrieve_context(mp, &cur_context, msg_size)) {
freemsg(mp);
return (B_TRUE);
}
if (!cur_context.lso_flag &&
(msg_size > Adapter->max_frame_size - ETHERFCSL)) {
E1000G_DEBUGLOG_1(Adapter, E1000G_WARN_LEVEL,
"Tx packet out of bound. length = %d \n", msg_size);
E1000G_STAT(tx_ring->stat_over_size);
freemsg(mp);
return (B_TRUE);
}
if (tx_ring->tbd_avail < DEFAULT_TX_NO_RESOURCE) {
(void) e1000g_recycle(tx_ring);
E1000G_DEBUG_STAT(tx_ring->stat_recycle);
if (tx_ring->tbd_avail < DEFAULT_TX_NO_RESOURCE) {
E1000G_DEBUG_STAT(tx_ring->stat_lack_desc);
goto tx_no_resource;
}
}
if (msg_size < ETHERMIN) {
E1000G_DEBUG_STAT(tx_ring->stat_under_size);
tx_undersize_flag = B_TRUE;
}
desc_count = 1;
desc_total = 0;
new_mp = NULL;
QUEUE_INIT_LIST(&pending_list);
if (cur_context.lso_flag) {
len = MBLKL(mp);
ASSERT(len > 0);
next_mp = mp;
pre_mp = NULL;
while (len < cur_context.hdr_len) {
pre_mp = next_mp;
next_mp = next_mp->b_cont;
len += MBLKL(next_mp);
}
if (len == cur_context.hdr_len)
goto adjust_threshold;
hdr_frag_len = cur_context.hdr_len - (len - MBLKL(next_mp));
if ((next_mp != mp) ||
(P2NPHASE((uintptr_t)next_mp->b_rptr,
E1000_LSO_FIRST_DESC_ALIGNMENT_BOUNDARY_4K)
< E1000_LSO_FIRST_DESC_ALIGNMENT)) {
E1000G_DEBUG_STAT(tx_ring->stat_lso_header_fail);
new_mp = allocb(hdr_frag_len, 0);
if (!new_mp)
return (B_FALSE);
bcopy(next_mp->b_rptr, new_mp->b_rptr, hdr_frag_len);
new_mp->b_wptr = new_mp->b_rptr + hdr_frag_len;
new_mp->b_cont = next_mp;
if (pre_mp)
pre_mp->b_cont = new_mp;
else
mp = new_mp;
next_mp->b_rptr += hdr_frag_len;
frag_count++;
}
adjust_threshold:
if (bcopy_thresh < cur_context.hdr_len)
bcopy_thresh = cur_context.hdr_len;
}
packet = NULL;
nmp = mp;
while (nmp) {
tmp = nmp->b_cont;
len = MBLKL(nmp);
if (len == 0) {
E1000G_DEBUG_STAT(tx_ring->stat_empty_frags);
if (desc_count > 0) {
nmp = tmp;
continue;
}
}
if (desc_count > 0) {
mutex_enter(&tx_ring->freelist_lock);
packet = (p_tx_sw_packet_t)
QUEUE_POP_HEAD(&tx_ring->free_list);
mutex_exit(&tx_ring->freelist_lock);
if (packet == NULL) {
E1000G_DEBUGLOG_0(Adapter, E1000G_INFO_LEVEL,
"No Tx SwPacket available\n");
E1000G_STAT(tx_ring->stat_no_swpkt);
goto tx_send_failed;
}
QUEUE_PUSH_TAIL(&pending_list, &packet->Link);
}
ASSERT(packet);
if ((len <= bcopy_thresh) || tx_undersize_flag) {
desc_count =
e1000g_tx_copy(tx_ring, packet, nmp,
tx_undersize_flag);
E1000G_DEBUG_STAT(tx_ring->stat_copy);
} else {
desc_count =
e1000g_tx_bind(tx_ring, packet, nmp);
E1000G_DEBUG_STAT(tx_ring->stat_bind);
}
if (desc_count > 0)
desc_total += desc_count;
else if (desc_count < 0)
goto tx_send_failed;
nmp = tmp;
}
ASSERT(packet);
ASSERT(packet->mp == NULL);
packet->mp = mp;
if (tx_ring->tbd_avail < (desc_total + 3)) {
E1000G_DEBUG_STAT(tx_ring->stat_recycle_retry);
(void) e1000g_recycle(tx_ring);
}
mutex_enter(&tx_ring->tx_lock);
if (tx_ring->tbd_avail < (desc_total + 3)) {
E1000G_DEBUGLOG_0(Adapter, E1000G_INFO_LEVEL,
"No Enough Tx descriptors\n");
E1000G_STAT(tx_ring->stat_no_desc);
mutex_exit(&tx_ring->tx_lock);
goto tx_send_failed;
}
desc_count = e1000g_fill_tx_ring(tx_ring, &pending_list, &cur_context);
mutex_exit(&tx_ring->tx_lock);
ASSERT(desc_count > 0);
return (B_TRUE);
tx_send_failed:
if (new_mp) {
if (pre_mp) {
pre_mp->b_cont = next_mp;
}
new_mp->b_cont = NULL;
freemsg(new_mp);
next_mp->b_rptr -= hdr_frag_len;
}
tx_ring->resched_timestamp = ddi_get_lbolt();
tx_ring->resched_needed = B_TRUE;
if (!Adapter->tx_intr_enable)
e1000g_mask_tx_interrupt(Adapter);
packet = (p_tx_sw_packet_t)QUEUE_GET_HEAD(&pending_list);
while (packet) {
packet->mp = NULL;
e1000g_free_tx_swpkt(packet);
packet = (p_tx_sw_packet_t)
QUEUE_GET_NEXT(&pending_list, &packet->Link);
}
mutex_enter(&tx_ring->freelist_lock);
QUEUE_APPEND(&tx_ring->free_list, &pending_list);
mutex_exit(&tx_ring->freelist_lock);
E1000G_STAT(tx_ring->stat_send_fail);
return (B_FALSE);
tx_no_resource:
tx_ring->resched_timestamp = ddi_get_lbolt();
tx_ring->resched_needed = B_TRUE;
if (!Adapter->tx_intr_enable)
e1000g_mask_tx_interrupt(Adapter);
return (B_FALSE);
}
static boolean_t
e1000g_retrieve_context(mblk_t *mp, context_data_t *cur_context,
size_t msg_size)
{
uintptr_t ip_start;
uintptr_t tcp_start;
mblk_t *nmp;
uint32_t lsoflags;
uint32_t mss;
bzero(cur_context, sizeof (context_data_t));
mac_lso_get(mp, &mss, &lsoflags);
mac_hcksum_get(mp, &cur_context->cksum_start,
&cur_context->cksum_stuff, NULL, NULL, &cur_context->cksum_flags);
if (((struct ether_vlan_header *)(uintptr_t)mp->b_rptr)->ether_tpid ==
htons(ETHERTYPE_VLAN))
cur_context->ether_header_size =
sizeof (struct ether_vlan_header);
else
cur_context->ether_header_size =
sizeof (struct ether_header);
if (lsoflags & HW_LSO) {
ASSERT(mss != 0);
if (mss == 0 ||
!((cur_context->cksum_flags & HCK_PARTIALCKSUM) &&
(cur_context->cksum_flags & HCK_IPV4_HDRCKSUM))) {
return (B_FALSE);
}
cur_context->mss = (uint16_t)mss;
cur_context->lso_flag = B_TRUE;
nmp = mp;
ip_start = (uintptr_t)(nmp->b_rptr)
+ cur_context->ether_header_size;
if (ip_start >= (uintptr_t)(nmp->b_wptr)) {
ip_start = (uintptr_t)nmp->b_cont->b_rptr
+ (ip_start - (uintptr_t)(nmp->b_wptr));
nmp = nmp->b_cont;
}
tcp_start = ip_start +
IPH_HDR_LENGTH((ipha_t *)ip_start);
if (tcp_start >= (uintptr_t)(nmp->b_wptr)) {
tcp_start = (uintptr_t)nmp->b_cont->b_rptr
+ (tcp_start - (uintptr_t)(nmp->b_wptr));
nmp = nmp->b_cont;
}
cur_context->hdr_len = cur_context->ether_header_size
+ IPH_HDR_LENGTH((ipha_t *)ip_start)
+ TCP_HDR_LENGTH((tcph_t *)tcp_start);
((ipha_t *)ip_start)->ipha_length = 0;
((ipha_t *)ip_start)->ipha_hdr_checksum = 0;
cur_context->pay_len = msg_size - cur_context->hdr_len;
}
return (B_TRUE);
}
static boolean_t
e1000g_check_context(e1000g_tx_ring_t *tx_ring, context_data_t *cur_context)
{
boolean_t context_reload;
context_data_t *pre_context;
struct e1000g *Adapter;
context_reload = B_FALSE;
pre_context = &tx_ring->pre_context;
Adapter = tx_ring->adapter;
if (Adapter->lso_premature_issue &&
Adapter->lso_enable &&
(cur_context->cksum_flags != 0)) {
context_reload = B_TRUE;
} else if (cur_context->lso_flag) {
if ((cur_context->lso_flag != pre_context->lso_flag) ||
(cur_context->cksum_flags != pre_context->cksum_flags) ||
(cur_context->pay_len != pre_context->pay_len) ||
(cur_context->mss != pre_context->mss) ||
(cur_context->hdr_len != pre_context->hdr_len) ||
(cur_context->cksum_stuff != pre_context->cksum_stuff) ||
(cur_context->cksum_start != pre_context->cksum_start) ||
(cur_context->ether_header_size !=
pre_context->ether_header_size)) {
context_reload = B_TRUE;
}
} else if (cur_context->cksum_flags != 0) {
if ((cur_context->lso_flag != pre_context->lso_flag) ||
(cur_context->cksum_flags != pre_context->cksum_flags) ||
(cur_context->cksum_stuff != pre_context->cksum_stuff) ||
(cur_context->cksum_start != pre_context->cksum_start) ||
(cur_context->ether_header_size !=
pre_context->ether_header_size)) {
context_reload = B_TRUE;
}
}
return (context_reload);
}
static int
e1000g_fill_tx_ring(e1000g_tx_ring_t *tx_ring, LIST_DESCRIBER *pending_list,
context_data_t *cur_context)
{
struct e1000g *Adapter;
struct e1000_hw *hw;
p_tx_sw_packet_t first_packet;
p_tx_sw_packet_t packet;
p_tx_sw_packet_t previous_packet;
boolean_t context_reload;
struct e1000_tx_desc *first_data_desc;
struct e1000_tx_desc *next_desc;
struct e1000_tx_desc *descriptor;
struct e1000_data_desc zeroed;
int desc_count;
boolean_t buff_overrun_flag;
int i;
Adapter = tx_ring->adapter;
hw = &Adapter->shared;
desc_count = 0;
first_packet = NULL;
first_data_desc = NULL;
descriptor = NULL;
first_packet = NULL;
packet = NULL;
buff_overrun_flag = B_FALSE;
zeroed.upper.data = 0;
next_desc = tx_ring->tbd_next;
context_reload = e1000g_check_context(tx_ring, cur_context);
if (context_reload) {
first_packet = (p_tx_sw_packet_t)QUEUE_GET_HEAD(pending_list);
descriptor = next_desc;
e1000g_fill_context_descriptor(cur_context,
(struct e1000_context_desc *)descriptor);
if (descriptor == tx_ring->tbd_last)
next_desc = tx_ring->tbd_first;
else
next_desc++;
desc_count++;
}
first_data_desc = next_desc;
if (hw->subsystem_vendor_id == 0x1af4 &&
hw->subsystem_device_id == 0x1100 &&
cur_context->cksum_flags) {
if (cur_context->cksum_flags & HCK_IPV4_HDRCKSUM)
zeroed.upper.fields.popts |= E1000_TXD_POPTS_IXSM;
if (cur_context->cksum_flags & HCK_PARTIALCKSUM)
zeroed.upper.fields.popts |= E1000_TXD_POPTS_TXSM;
}
packet = (p_tx_sw_packet_t)QUEUE_GET_HEAD(pending_list);
while (packet) {
ASSERT(packet->num_desc);
for (i = 0; i < packet->num_desc; i++) {
ASSERT(tx_ring->tbd_avail > 0);
descriptor = next_desc;
descriptor->buffer_addr =
packet->desc[i].address;
descriptor->lower.data =
packet->desc[i].length;
descriptor->upper.data = zeroed.upper.data;
descriptor->lower.data |=
E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
descriptor->lower.data |=
E1000_TXD_CMD_RS;
if (cur_context->lso_flag)
descriptor->lower.data |= E1000_TXD_CMD_TSE;
if (descriptor == tx_ring->tbd_last)
next_desc = tx_ring->tbd_first;
else
next_desc++;
desc_count++;
if (hw->bus.type == e1000_bus_type_pcix &&
descriptor == first_data_desc &&
((descriptor->lower.data & E1000G_TBD_LENGTH_MASK)
> E1000_TX_BUFFER_OEVRRUN_THRESHOLD)) {
descriptor->lower.data &=
~E1000G_TBD_LENGTH_MASK;
descriptor->lower.flags.length =
E1000_TX_BUFFER_OEVRRUN_THRESHOLD;
ASSERT(tx_ring->tbd_avail > 0);
next_desc->buffer_addr =
packet->desc[0].address +
E1000_TX_BUFFER_OEVRRUN_THRESHOLD;
next_desc->lower.data =
packet->desc[0].length -
E1000_TX_BUFFER_OEVRRUN_THRESHOLD;
next_desc->upper.data = zeroed.upper.data;
next_desc->lower.data |=
E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
next_desc->lower.data |=
E1000_TXD_CMD_RS;
if (cur_context->lso_flag)
next_desc->lower.data |=
E1000_TXD_CMD_TSE;
descriptor = next_desc;
if (next_desc == tx_ring->tbd_last)
next_desc = tx_ring->tbd_first;
else
next_desc++;
desc_count++;
buff_overrun_flag = B_TRUE;
}
}
if (buff_overrun_flag) {
packet->num_desc++;
buff_overrun_flag = B_FALSE;
}
if (first_packet != NULL) {
first_packet->num_desc++;
first_packet = NULL;
}
packet->tickstamp = ddi_get_lbolt64();
previous_packet = packet;
packet = (p_tx_sw_packet_t)
QUEUE_GET_NEXT(pending_list, &packet->Link);
}
if (Adapter->lso_premature_issue && cur_context->lso_flag &&
((descriptor->lower.data & E1000G_TBD_LENGTH_MASK) > 8)) {
descriptor->lower.data -= 4;
ASSERT(tx_ring->tbd_avail > 0);
next_desc->buffer_addr =
descriptor->buffer_addr +
(descriptor->lower.data & E1000G_TBD_LENGTH_MASK);
next_desc->lower.data = 4;
next_desc->upper.data = zeroed.upper.data;
next_desc->lower.data |=
E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D |
E1000_TXD_CMD_RS | E1000_TXD_CMD_TSE;
descriptor = next_desc;
if (descriptor == tx_ring->tbd_last)
next_desc = tx_ring->tbd_first;
else
next_desc++;
desc_count++;
previous_packet->num_desc++;
}
ASSERT(descriptor);
if (cur_context->cksum_flags) {
if (cur_context->cksum_flags & HCK_IPV4_HDRCKSUM)
((struct e1000_data_desc *)first_data_desc)->
upper.fields.popts |= E1000_TXD_POPTS_IXSM;
if (cur_context->cksum_flags & HCK_PARTIALCKSUM)
((struct e1000_data_desc *)first_data_desc)->
upper.fields.popts |= E1000_TXD_POPTS_TXSM;
}
if (Adapter->tx_intr_delay) {
descriptor->lower.data |= E1000_TXD_CMD_IDE |
E1000_TXD_CMD_EOP;
} else {
descriptor->lower.data |= E1000_TXD_CMD_EOP;
}
if (cur_context->lso_flag) {
first_data_desc->lower.data |= E1000_TXD_CMD_IFCS;
} else {
descriptor->lower.data |= E1000_TXD_CMD_IFCS;
}
(void) ddi_dma_sync(tx_ring->tbd_dma_handle,
0, 0, DDI_DMA_SYNC_FORDEV);
tx_ring->tbd_next = next_desc;
if (hw->mac.type == e1000_82547)
e1000g_82547_tx_move_tail(tx_ring);
else
E1000_WRITE_REG(hw, E1000_TDT(0),
(uint32_t)(next_desc - tx_ring->tbd_first));
if (e1000g_check_acc_handle(Adapter->osdep.reg_handle) != DDI_FM_OK) {
ddi_fm_service_impact(Adapter->dip, DDI_SERVICE_DEGRADED);
Adapter->e1000g_state |= E1000G_ERROR;
}
mutex_enter(&tx_ring->usedlist_lock);
QUEUE_APPEND(&tx_ring->used_list, pending_list);
tx_ring->tbd_avail -= desc_count;
mutex_exit(&tx_ring->usedlist_lock);
if (context_reload)
tx_ring->pre_context = *cur_context;
return (desc_count);
}
void
e1000g_tx_setup(struct e1000g *Adapter)
{
struct e1000_hw *hw;
p_tx_sw_packet_t packet;
uint32_t i;
uint32_t buf_high;
uint32_t buf_low;
uint32_t reg_tipg;
uint32_t reg_tctl;
int size;
e1000g_tx_ring_t *tx_ring;
hw = &Adapter->shared;
tx_ring = Adapter->tx_ring;
QUEUE_INIT_LIST(&tx_ring->used_list);
QUEUE_INIT_LIST(&tx_ring->free_list);
packet = tx_ring->packet_area;
for (i = 0; i < Adapter->tx_freelist_num; i++, packet++) {
e1000g_free_tx_swpkt(packet);
QUEUE_PUSH_TAIL(&tx_ring->free_list,
&packet->Link);
}
tx_ring->tbd_next = tx_ring->tbd_first;
tx_ring->tbd_oldest = tx_ring->tbd_first;
reg_tctl = E1000_READ_REG(hw, E1000_TCTL);
reg_tctl |= E1000_TCTL_PSP | E1000_TCTL_EN |
(E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) |
(E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT) |
E1000_TCTL_RTLC;
if (hw->bus.type == e1000_bus_type_pci_express)
reg_tctl |= E1000_TCTL_MULR;
E1000_WRITE_REG(hw, E1000_TCTL, reg_tctl);
size = (Adapter->tx_desc_num * sizeof (struct e1000_tx_desc));
E1000_WRITE_REG(hw, E1000_TDLEN(0), size);
size = E1000_READ_REG(hw, E1000_TDLEN(0));
buf_low = (uint32_t)tx_ring->tbd_dma_addr;
buf_high = (uint32_t)(tx_ring->tbd_dma_addr >> 32);
E1000_WRITE_REG(hw, E1000_TDBAH(0), buf_high);
E1000_WRITE_REG(hw, E1000_TDBAL(0), buf_low);
E1000_WRITE_REG(hw, E1000_TDH(0), 0);
E1000_WRITE_REG(hw, E1000_TDT(0), 0);
if ((hw->mac.type == e1000_82542) &&
((hw->revision_id == E1000_REVISION_2) ||
(hw->revision_id == E1000_REVISION_3))) {
reg_tipg = DEFAULT_82542_TIPG_IPGT;
reg_tipg |=
DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT;
reg_tipg |=
DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
} else if (hw->mac.type == e1000_80003es2lan) {
reg_tipg = DEFAULT_82543_TIPG_IPGR1;
reg_tipg |= DEFAULT_80003ES2LAN_TIPG_IPGR2 <<
E1000_TIPG_IPGR2_SHIFT;
} else {
if (hw->phy.media_type == e1000_media_type_fiber)
reg_tipg = DEFAULT_82543_TIPG_IPGT_FIBER;
else
reg_tipg = DEFAULT_82543_TIPG_IPGT_COPPER;
reg_tipg |=
DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT;
reg_tipg |=
DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT;
}
E1000_WRITE_REG(hw, E1000_TIPG, reg_tipg);
E1000_WRITE_REG(hw, E1000_TIDV, Adapter->tx_intr_delay);
E1000G_DEBUGLOG_1(Adapter, E1000G_INFO_LEVEL,
"E1000_TIDV: 0x%x\n", Adapter->tx_intr_delay);
if (hw->mac.type >= e1000_82540) {
E1000_WRITE_REG(&Adapter->shared, E1000_TADV,
Adapter->tx_intr_abs_delay);
E1000G_DEBUGLOG_1(Adapter, E1000G_INFO_LEVEL,
"E1000_TADV: 0x%x\n", Adapter->tx_intr_abs_delay);
}
tx_ring->tbd_avail = Adapter->tx_desc_num;
bzero(&(tx_ring->pre_context), sizeof (context_data_t));
}
int
e1000g_recycle(e1000g_tx_ring_t *tx_ring)
{
struct e1000g *Adapter;
LIST_DESCRIBER pending_list;
p_tx_sw_packet_t packet;
mblk_t *mp;
mblk_t *nmp;
struct e1000_tx_desc *descriptor;
int desc_count;
int64_t delta;
Adapter = tx_ring->adapter;
delta = 0;
packet = (p_tx_sw_packet_t)QUEUE_GET_HEAD(&tx_ring->used_list);
if (packet == NULL) {
Adapter->stall_flag = B_FALSE;
return (0);
}
desc_count = 0;
QUEUE_INIT_LIST(&pending_list);
(void) ddi_dma_sync(tx_ring->tbd_dma_handle,
0, 0, DDI_DMA_SYNC_FORKERNEL);
if (e1000g_check_dma_handle(
tx_ring->tbd_dma_handle) != DDI_FM_OK) {
ddi_fm_service_impact(Adapter->dip, DDI_SERVICE_DEGRADED);
Adapter->e1000g_state |= E1000G_ERROR;
return (0);
}
mutex_enter(&tx_ring->usedlist_lock);
while ((packet =
(p_tx_sw_packet_t)QUEUE_GET_HEAD(&tx_ring->used_list)) != NULL) {
ASSERT(packet->num_desc);
descriptor = tx_ring->tbd_oldest + (packet->num_desc - 1);
if (descriptor > tx_ring->tbd_last)
descriptor -= Adapter->tx_desc_num;
if (descriptor->upper.fields.status & E1000_TXD_STAT_DD) {
QUEUE_POP_HEAD(&tx_ring->used_list);
QUEUE_PUSH_TAIL(&pending_list, &packet->Link);
if (descriptor == tx_ring->tbd_last)
tx_ring->tbd_oldest =
tx_ring->tbd_first;
else
tx_ring->tbd_oldest =
descriptor + 1;
desc_count += packet->num_desc;
} else {
delta = ddi_get_lbolt64() - packet->tickstamp;
break;
}
}
tx_ring->tbd_avail += desc_count;
Adapter->tx_pkt_cnt += desc_count;
mutex_exit(&tx_ring->usedlist_lock);
if (desc_count == 0) {
E1000G_DEBUG_STAT(tx_ring->stat_recycle_none);
if ((delta > Adapter->stall_threshold) &&
!(E1000_READ_REG(&Adapter->shared,
E1000_STATUS) & E1000_STATUS_TXOFF)) {
Adapter->stall_flag = B_TRUE;
}
return (0);
}
Adapter->stall_flag = B_FALSE;
mp = NULL;
nmp = NULL;
packet = (p_tx_sw_packet_t)QUEUE_GET_HEAD(&pending_list);
ASSERT(packet != NULL);
while (packet != NULL) {
if (packet->mp != NULL) {
ASSERT(packet->mp->b_next == NULL);
if (mp == NULL) {
mp = packet->mp;
nmp = packet->mp;
} else {
nmp->b_next = packet->mp;
nmp = packet->mp;
}
packet->mp = NULL;
}
e1000g_free_tx_swpkt(packet);
packet = (p_tx_sw_packet_t)
QUEUE_GET_NEXT(&pending_list, &packet->Link);
}
mutex_enter(&tx_ring->freelist_lock);
QUEUE_APPEND(&tx_ring->free_list, &pending_list);
mutex_exit(&tx_ring->freelist_lock);
if (mp != NULL)
freemsgchain(mp);
return (desc_count);
}
static uint32_t
e1000g_fill_82544_desc(uint64_t address,
size_t length, p_desc_array_t desc_array)
{
uint32_t safe_terminator;
if (length <= 4) {
desc_array->descriptor[0].address = address;
desc_array->descriptor[0].length = (uint32_t)length;
desc_array->elements = 1;
return (desc_array->elements);
}
safe_terminator =
(uint32_t)((((uint32_t)address & 0x7) +
(length & 0xF)) & 0xF);
if (safe_terminator == 0 ||
(safe_terminator > 4 && safe_terminator < 9) ||
(safe_terminator > 0xC && safe_terminator <= 0xF)) {
desc_array->descriptor[0].address = address;
desc_array->descriptor[0].length = (uint32_t)length;
desc_array->elements = 1;
return (desc_array->elements);
}
desc_array->descriptor[0].address = address;
desc_array->descriptor[0].length = length - 4;
desc_array->descriptor[1].address = address + (length - 4);
desc_array->descriptor[1].length = 4;
desc_array->elements = 2;
return (desc_array->elements);
}
static int
e1000g_tx_copy(e1000g_tx_ring_t *tx_ring, p_tx_sw_packet_t packet,
mblk_t *mp, boolean_t tx_undersize_flag)
{
size_t len;
size_t len1;
dma_buffer_t *tx_buf;
mblk_t *nmp;
boolean_t finished;
int desc_count;
desc_count = 0;
tx_buf = packet->tx_buf;
len = MBLKL(mp);
ASSERT((tx_buf->len + len) <= tx_buf->size);
if (len > 0) {
bcopy(mp->b_rptr,
tx_buf->address + tx_buf->len,
len);
tx_buf->len += len;
packet->num_mblk_frag++;
}
nmp = mp->b_cont;
if (nmp == NULL) {
finished = B_TRUE;
} else {
len1 = MBLKL(nmp);
if ((tx_buf->len + len1) > tx_buf->size)
finished = B_TRUE;
else if (tx_undersize_flag)
finished = B_FALSE;
else if (len1 > tx_ring->adapter->tx_bcopy_thresh)
finished = B_TRUE;
else
finished = B_FALSE;
}
if (finished) {
E1000G_DEBUG_STAT_COND(tx_ring->stat_multi_copy,
(tx_buf->len > len));
if (tx_undersize_flag) {
ASSERT(tx_buf->len < ETHERMIN);
bzero(tx_buf->address + tx_buf->len,
ETHERMIN - tx_buf->len);
tx_buf->len = ETHERMIN;
}
#ifdef __sparc
if (packet->dma_type == USE_DVMA)
dvma_sync(tx_buf->dma_handle, 0, DDI_DMA_SYNC_FORDEV);
else
(void) ddi_dma_sync(tx_buf->dma_handle, 0,
tx_buf->len, DDI_DMA_SYNC_FORDEV);
#else
(void) ddi_dma_sync(tx_buf->dma_handle, 0,
tx_buf->len, DDI_DMA_SYNC_FORDEV);
#endif
packet->data_transfer_type = USE_BCOPY;
desc_count = e1000g_fill_tx_desc(tx_ring,
packet,
tx_buf->dma_address,
tx_buf->len);
if (desc_count <= 0)
return (-1);
}
return (desc_count);
}
static int
e1000g_tx_bind(e1000g_tx_ring_t *tx_ring, p_tx_sw_packet_t packet, mblk_t *mp)
{
int j;
int mystat;
size_t len;
ddi_dma_cookie_t dma_cookie;
uint_t ncookies;
int desc_count;
uint32_t desc_total;
desc_total = 0;
len = MBLKL(mp);
switch (packet->dma_type) {
#ifdef __sparc
case USE_DVMA:
dvma_kaddr_load(packet->tx_dma_handle,
(caddr_t)mp->b_rptr, len, 0, &dma_cookie);
dvma_sync(packet->tx_dma_handle, 0,
DDI_DMA_SYNC_FORDEV);
ncookies = 1;
packet->data_transfer_type = USE_DVMA;
break;
#endif
case USE_DMA:
if ((mystat = ddi_dma_addr_bind_handle(
packet->tx_dma_handle, NULL,
(caddr_t)mp->b_rptr, len,
DDI_DMA_WRITE | DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, &dma_cookie,
&ncookies)) != DDI_DMA_MAPPED) {
e1000g_log(tx_ring->adapter, CE_WARN,
"Couldn't bind mblk buffer to Tx DMA handle: "
"return: %X, Pkt: %X\n",
mystat, packet);
return (-1);
}
ASSERT(ncookies);
E1000G_DEBUG_STAT_COND(tx_ring->stat_multi_cookie,
(ncookies > 1));
packet->data_transfer_type = USE_DMA;
break;
default:
ASSERT(B_FALSE);
break;
}
packet->num_mblk_frag++;
for (j = ncookies; j != 0; j--) {
desc_count = e1000g_fill_tx_desc(tx_ring,
packet,
dma_cookie.dmac_laddress,
dma_cookie.dmac_size);
if (desc_count <= 0)
return (-1);
desc_total += desc_count;
if (j > 1) {
ddi_dma_nextcookie(packet->tx_dma_handle,
&dma_cookie);
}
}
return (desc_total);
}
static void
e1000g_fill_context_descriptor(context_data_t *cur_context,
struct e1000_context_desc *context_desc)
{
if (cur_context->cksum_flags & HCK_IPV4_HDRCKSUM) {
context_desc->lower_setup.ip_fields.ipcss =
cur_context->ether_header_size;
context_desc->lower_setup.ip_fields.ipcso =
cur_context->ether_header_size +
offsetof(struct ip, ip_sum);
context_desc->lower_setup.ip_fields.ipcse =
cur_context->ether_header_size +
cur_context->cksum_start - 1;
} else
context_desc->lower_setup.ip_config = 0;
if (cur_context->cksum_flags & HCK_PARTIALCKSUM) {
context_desc->upper_setup.tcp_fields.tucss =
cur_context->cksum_start + cur_context->ether_header_size;
context_desc->upper_setup.tcp_fields.tucso =
cur_context->cksum_stuff + cur_context->ether_header_size;
context_desc->upper_setup.tcp_fields.tucse = 0;
} else
context_desc->upper_setup.tcp_config = 0;
if (cur_context->lso_flag) {
context_desc->tcp_seg_setup.fields.mss = cur_context->mss;
context_desc->tcp_seg_setup.fields.hdr_len =
cur_context->hdr_len;
context_desc->cmd_and_length = E1000_TXD_CMD_DEXT
| E1000_TXD_CMD_TSE | E1000_TXD_CMD_IP | E1000_TXD_CMD_TCP
| E1000_TXD_DTYP_C | cur_context->pay_len;
} else {
context_desc->cmd_and_length = E1000_TXD_CMD_DEXT
| E1000_TXD_DTYP_C;
context_desc->tcp_seg_setup.data = 0;
}
}
static int
e1000g_fill_tx_desc(e1000g_tx_ring_t *tx_ring,
p_tx_sw_packet_t packet, uint64_t address, size_t size)
{
struct e1000_hw *hw = &tx_ring->adapter->shared;
p_sw_desc_t desc;
if (hw->mac.type == e1000_82544) {
if (hw->bus.type == e1000_bus_type_pcix)
return (e1000g_tx_workaround_PCIX_82544(packet,
address, size));
if (size > JUMBO_FRAG_LENGTH)
return (e1000g_tx_workaround_jumbo_82544(packet,
address, size));
}
ASSERT(packet->num_desc < MAX_TX_DESC_PER_PACKET);
desc = &packet->desc[packet->num_desc];
desc->address = address;
desc->length = (uint32_t)size;
packet->num_desc++;
return (1);
}
static int
e1000g_tx_workaround_PCIX_82544(p_tx_sw_packet_t packet,
uint64_t address, size_t size)
{
p_sw_desc_t desc;
int desc_count;
long size_left;
size_t len;
uint32_t counter;
uint32_t array_elements;
desc_array_t desc_array;
size_left = size;
desc_count = 0;
while (size_left > 0) {
if (size_left > MAX_TX_BUF_SIZE)
len = MAX_TX_BUF_SIZE;
else
len = size_left;
array_elements = e1000g_fill_82544_desc(address,
len, &desc_array);
for (counter = 0; counter < array_elements; counter++) {
ASSERT(packet->num_desc < MAX_TX_DESC_PER_PACKET);
desc = &packet->desc[packet->num_desc];
desc->address =
desc_array.descriptor[counter].address;
desc->length =
desc_array.descriptor[counter].length;
packet->num_desc++;
desc_count++;
}
address += MAX_TX_BUF_SIZE;
size_left -= MAX_TX_BUF_SIZE;
}
return (desc_count);
}
static int
e1000g_tx_workaround_jumbo_82544(p_tx_sw_packet_t packet,
uint64_t address, size_t size)
{
p_sw_desc_t desc;
int desc_count;
long size_left;
uint32_t offset;
size_left = size;
desc_count = 0;
offset = 0;
while (size_left > 0) {
ASSERT(packet->num_desc < MAX_TX_DESC_PER_PACKET);
desc = &packet->desc[packet->num_desc];
desc->address = address + offset;
if (size_left > JUMBO_FRAG_LENGTH)
desc->length = JUMBO_FRAG_LENGTH;
else
desc->length = (uint32_t)size_left;
packet->num_desc++;
desc_count++;
offset += desc->length;
size_left -= JUMBO_FRAG_LENGTH;
}
return (desc_count);
}
static void
e1000g_82547_tx_move_tail_work(e1000g_tx_ring_t *tx_ring)
{
struct e1000_hw *hw;
uint16_t hw_tdt;
uint16_t sw_tdt;
struct e1000_tx_desc *tx_desc;
uint16_t length = 0;
boolean_t eop = B_FALSE;
struct e1000g *Adapter;
Adapter = tx_ring->adapter;
hw = &Adapter->shared;
hw_tdt = E1000_READ_REG(hw, E1000_TDT(0));
sw_tdt = tx_ring->tbd_next - tx_ring->tbd_first;
while (hw_tdt != sw_tdt) {
tx_desc = &(tx_ring->tbd_first[hw_tdt]);
length += tx_desc->lower.flags.length;
eop = tx_desc->lower.data & E1000_TXD_CMD_EOP;
if (++hw_tdt == Adapter->tx_desc_num)
hw_tdt = 0;
if (eop) {
if ((Adapter->link_duplex == HALF_DUPLEX) &&
(e1000_fifo_workaround_82547(hw, length)
!= E1000_SUCCESS)) {
if (tx_ring->timer_enable_82547) {
ASSERT(tx_ring->timer_id_82547 == 0);
tx_ring->timer_id_82547 =
timeout(e1000g_82547_timeout,
(void *)tx_ring,
drv_usectohz(10000));
}
return;
} else {
E1000_WRITE_REG(hw, E1000_TDT(0), hw_tdt);
e1000_update_tx_fifo_head_82547(hw, length);
length = 0;
}
}
}
}
static void
e1000g_82547_timeout(void *arg)
{
e1000g_tx_ring_t *tx_ring;
tx_ring = (e1000g_tx_ring_t *)arg;
mutex_enter(&tx_ring->tx_lock);
tx_ring->timer_id_82547 = 0;
e1000g_82547_tx_move_tail_work(tx_ring);
mutex_exit(&tx_ring->tx_lock);
}
static void
e1000g_82547_tx_move_tail(e1000g_tx_ring_t *tx_ring)
{
timeout_id_t tid;
ASSERT(MUTEX_HELD(&tx_ring->tx_lock));
tid = tx_ring->timer_id_82547;
tx_ring->timer_id_82547 = 0;
if (tid != 0) {
tx_ring->timer_enable_82547 = B_FALSE;
mutex_exit(&tx_ring->tx_lock);
(void) untimeout(tid);
mutex_enter(&tx_ring->tx_lock);
}
tx_ring->timer_enable_82547 = B_TRUE;
e1000g_82547_tx_move_tail_work(tx_ring);
}
void
e1000g_flush_tx_ring(struct e1000g *Adapter)
{
struct e1000_hw *hw = &Adapter->shared;
e1000g_tx_ring_t *tx_ring = &Adapter->tx_ring[0];
uint32_t tctl, txd_lower = E1000_TXD_CMD_IFCS;
uint16_t size = 512;
struct e1000_tx_desc *desc;
tctl = E1000_READ_REG(hw, E1000_TCTL);
E1000_WRITE_REG(hw, E1000_TCTL, tctl | E1000_TCTL_EN);
desc = tx_ring->tbd_next;
if (tx_ring->tbd_next == tx_ring->tbd_last)
tx_ring->tbd_next = tx_ring->tbd_first;
else
tx_ring->tbd_next++;
desc->buffer_addr = tx_ring->tbd_dma_addr;
desc->lower.data = LE_32(txd_lower | size);
desc->upper.data = 0;
(void) ddi_dma_sync(tx_ring->tbd_dma_handle,
0, 0, DDI_DMA_SYNC_FORDEV);
E1000_WRITE_REG(hw, E1000_TDT(0),
(uint32_t)(tx_ring->tbd_next - tx_ring->tbd_first));
(void) E1000_READ_REG(hw, E1000_STATUS);
usec_delay(250);
}