这个驱动会把所有octeon的接口注册为host上的网口
1. 结构体
1.1. octdev_props_t
/** Octeon device properties to be used by the NIC module.
Each octeon device in the system will be represented
by this structure in the NIC module. */
struct octdev_props_t {
/** Number of interfaces detected in this octeon device. */
int ifcount;
/* Link status sent by core app is stored in a buffer at this
address. */
oct_link_status_resp_t *ls;
/** Pointer to pre-allocated soft instr used to send link status
request to Octeon app. */
octeon_soft_instruction_t *si_link_status;
/** Flag to indicate if a link status instruction is currently
being processed. */
cavium_atomic_t ls_flag;
/** The last tick at which the link status was checked. The
status is checked every second. */
unsigned long last_check;
/** Each interface in the Octeon device has a network
device pointer (used for OS specific calls). */
octnet_os_devptr_t *pndev[MAX_OCTEON_LINKS]; //是linux net_device
};
1.2. oct_link_status_resp_t
typedef struct {
struct {
int octeon_id;
cavium_wait_channel wc;
int cond;
} s;
uint64_t resp_hdr;
uint64_t link_count;
//一个link_info就是octeon的一个接口, 将来会被注册为host的网口
oct_link_info_t link_info[MAX_OCTEON_LINKS];
uint64_t status;
} oct_link_status_resp_t;
1.3. 被用来做net_dev的priv
/** Octeon per-interface Network Private Data */
typedef struct {
cavium_spinlock_t lock;
/** State of the interface. Rx/Tx happens only in the RUNNING state. */
atomic_t ifstate;
/** Octeon Interface index number. This device will be represented as
oct<ifidx> in the system. */
int ifidx;
/** Octeon Input queue to use to transmit for this network interface. */
int txq; //只有一个txq说明octeon的么个interface只能用一个q
/** Octeon Output queue from which pkts arrive for this network interface.*/
int rxq; //只有一个rxq说明octeon的么个interface只能用一个q
/** Linked list of gather components */
cavium_list_t glist; //比如q的最大深度是n, 则每个格子都一个glist --gather list
/** Pointer to the NIC properties for the Octeon device this network
interface is associated with. */
struct octdev_props_t *octprops;
/** Pointer to the octeon device structure. */
void *oct_dev;
octnet_os_devptr_t *pndev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
struct napi_struct napi;
#endif
/** Link information sent by the core application for this interface. */
oct_link_info_t linfo;
/** Statistics for this interface. */
struct net_device_stats stats;
/** Size of Tx queue for this octeon device. */
uint32_t tx_qsize;
/** Size of Rx queue for this octeon device. */
uint32_t rx_qsize;
/** Copy of netdevice flags. */
uint32_t pndev_flags;
/* Copy of the flags managed by core app & NIC module. */
octnet_ifflags_t core_flags;
} octnet_priv_t;
#define OCTNET_PRIV_SIZE (sizeof(octnet_priv_t))
1.4. glist
/** Structure of a node in list of gather components maintained by
NIC driver for each network device. */
struct octnic_gather {
/** List manipulation. Next and prev pointers. */
cavium_list_t list;
/** Size of the gather component at sg in bytes. */
int sg_size;
/** Number of bytes that sg was adjusted to make it 8B-aligned. */
int adjust;
/** Gather component that can accomodate max sized fragment list
received from the IP layer. */
octeon_sg_entry_t *sg;
};
2. A. init_module
init_module()
octeon_module_handler_t nethandler;
/* Register handlers with the BASE driver. For each octeon device that
runs the NIC core app, the BASE driver would call the functions
below for initialization, reset and shutdown operations. */
//会被base调用, 比如在设备重启后
nethandler.startptr = octnet_init_nic_module; //见A1
nethandler.resetptr = octnet_reset_nic_module;
nethandler.stopptr = octnet_stop_nic_module;
nethandler.app_type = CVM_DRV_NIC_APP;
octeon_register_module_handler(&nethandler)
2.1. A1. octnet_init_nic_module
octnet_init_nic_module(int octeon_id, void *octeon_dev)
oct_link_status_resp_t *ls = NULL;
octeon_soft_instruction_t *si = NULL;
int ifidx, retval = 0;
//见上面结构体octdev_props_t
octprops[octeon_id] = cavium_alloc_virt(sizeof(struct octdev_props_t));
/* Allocate a buffer to collect link status from the core app. */
ls = cavium_malloc_dma(sizeof(oct_link_status_resp_t),
__CAVIUM_MEM_GENERAL);
octprops[octeon_id]->ls = ls;
/* Allocate a soft instruction to be used to send link status requests
to the core app. */
si = (octeon_soft_instruction_t *)
cavium_alloc_buffer(octeon_dev, OCT_SOFT_INSTR_SIZE);
octprops[octeon_id]->si_link_status = si;
//用于link status的命令字
octnet_prepare_ls_soft_instr(octeon_dev, si);
cavium_memset(si, 0, OCT_SOFT_INSTR_SIZE);
si->ih.fsz = 16;
si->ih.tagtype = ORDERED_TAG;
si->ih.tag = 0x11111111;
si->ih.raw = 1;
si->irh.opcode = HOST_NW_INFO_OP;
si->irh.param = 32;
SET_SOFT_INSTR_DMA_MODE(si, OCTEON_DMA_DIRECT);
SET_SOFT_INSTR_RESP_ORDER(si, OCTEON_RESP_ORDERED);
SET_SOFT_INSTR_RESP_MODE(si, OCTEON_RESP_NON_BLOCKING);
SET_SOFT_INSTR_IQ_NO(si, 0);
SET_SOFT_INSTR_TIMEOUT(si, 100);
/* Since this instruction is sent in the poll thread context, if the
doorbell coalescing is > 1, the doorbell will never be rung for
this instruction (this call has to return for poll thread to hit
the doorbell). So enforce the doorbell ring. */
SET_SOFT_INSTR_ALLOCFLAGS(si, OCTEON_SOFT_INSTR_DB_NOW);
si->dptr = NULL;
si->ih.dlengsz = 0;
/* Send an instruction to get the link status information from core. */
//这个函数会阻塞等待, 注意: 这里是典型的调用发送接口的例子
octnet_get_inittime_link_status(octeon_dev, octprops[octeon_id])
struct octdev_props_t *props;
octeon_soft_instruction_t *si;
oct_link_status_resp_t *ls;
octeon_instr_status_t retval;
props = (struct octdev_props_t *)props_ptr;
/* Use the link status soft instruction pre-allocated
for this octeon device. */
si = props->si_link_status;
/* Reset the link status buffer in props for this octeon device. */
ls = props->ls;
cavium_memset(ls, 0, OCT_LINK_STATUS_RESP_SIZE);
cavium_init_wait_channel(&ls->s.wc);
//注意: 这里的rptr是ls的一个成员的地址, ls是上面用cavium_malloc_dma申请的
si->rptr = &(ls->resp_hdr);
//这里的size是octeon回复的size, 从ls->resp_hdr地址开始.
//是oct_link_status_resp_t去掉s的大小
si->irh.rlenssz = ( OCT_LINK_STATUS_RESP_SIZE - sizeof(ls->s) );
//octeon返回的status
si->status_word = (uint64_t *)&(ls->status);
*(si->status_word) = COMPLETION_WORD_INIT;
ls->s.cond = 0;
ls->s.octeon_id = get_octeon_device_id(oct);
SET_SOFT_INSTR_OCTEONID(si, ls->s.octeon_id);
//这个callback是收包(准确的说是收到response)以后的回调
//作用就是把 ls->s.cond = 1;
SET_SOFT_INSTR_CALLBACK(si, octnet_inittime_ls_callback);
SET_SOFT_INSTR_CALLBACK_ARG(si, (void *)ls);
//发送命令字
retval = octeon_process_instruction(oct, si, NULL);
//等待ls->s.cond = 1
/* Sleep on a wait queue till the cond flag indicates that the
response arrived or timed-out. */
cavium_sleep_timeout_cond(&ls->s.wc, (int *)&ls->s.cond, 1000);
return(ls->status);
/* The link count should be swapped on little endian systems. */
octeon_swap_8B_data(&(ls->link_count), 1);
octeon_swap_8B_data((uint64_t *)ls->link_info,
(ls->link_count * (OCT_LINK_INFO_SIZE >> 3)));
//这里是打印从octeon获取到的接口信息
for(ifidx = 0; ifidx < ls->link_count; ifidx++) {
printk("OCTNIC: if%d rxq: %d txq: %d gmx: %d hw_addr: 0x%llx\n",
ifidx, ls->link_info[ifidx].rxpciq, ls->link_info[ifidx].txpciq, ls->link_info[ifidx].gmxport, CVM_CAST64(ls->link_info[ifidx].hw_addr));
}
octprops[octeon_id]->ifcount = ls->link_count;
//注册noresponse的释放buf函数, 见driver篇A14
//这两个free的回调函数见A11
octeon_register_noresp_buf_free_fn(octeon_id, NORESP_BUFTYPE_NET,
octnic_free_netbuf);
octeon_register_noresp_buf_free_fn(octeon_id, NORESP_BUFTYPE_NET_SG,
octnic_free_netsgbuf);
//注册host上的网口, 见A12
for(ifidx = 0; ifidx < ls->link_count; ifidx++)
octnet_setup_nic_device(octeon_id, &ls->link_info[ifidx], ifidx);
cavium_atomic_set(&octprops[octeon_id]->ls_flag, LINK_STATUS_FETCHED);
octprops[octeon_id]->last_check = cavium_jiffies;
/* Register a poll function to run every second to collect and update
link status. */
octeon_poll_ops_t poll_ops;
poll_ops.fn = octnet_get_runtime_link_status;
poll_ops.fn_arg = (unsigned long)octprops[octeon_id];
poll_ops.ticks = CAVIUM_TICKS_PER_SEC;
strcpy(poll_ops.name, "NIC Link Status");
octeon_register_poll_fn(octeon_id, &poll_ops);
cavium_print_msg("OCTNIC: Network interfaces ready for Octeon %d\n",
octeon_id);
2.1.1. A11. octnic_free_netbuf和octnic_free_netsgbuf
octnic_free_netbuf(void *buf)
struct sk_buff *skb;
struct octnet_buf_free_info *finfo;
octnet_priv_t *priv;
//其实是sk_buff的一个成员cb
finfo = (struct octnet_buf_free_info *)buf;
skb = finfo->skb;
priv = finfo->priv;
octeon_unmap_single_buffer(get_octeon_device_id(priv->oct_dev),
finfo->dptr, skb->len,
CAVIUM_PCI_DMA_TODEVICE);
free_recv_buffer((cavium_netbuf_t *)skb);
__check_txq_state(priv);
octnic_free_netsgbuf(void *buf)
struct octnet_buf_free_info *finfo;
struct sk_buff *skb;
octnet_priv_t *priv;
struct octnic_gather *g;
int i, frags;
finfo = (struct octnet_buf_free_info *)buf;
skb = finfo->skb;
priv = finfo->priv;
g = finfo->g;
frags = skb_shinfo(skb)->nr_frags;
octeon_unmap_single_buffer(get_octeon_device_id(priv->oct_dev),
g->sg[0].ptr[0], (skb->len - skb->data_len),
CAVIUM_PCI_DMA_TODEVICE);
i = 1;
while(frags--) {
struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i-1];
octeon_unmap_page(get_octeon_device_id(priv->oct_dev),
g->sg[(i >> 2)].ptr[(i&3)],
frag->size, CAVIUM_PCI_DMA_TODEVICE);
i++;
}
octeon_unmap_single_buffer(get_octeon_device_id(priv->oct_dev),
finfo->dptr, g->sg_size,
CAVIUM_PCI_DMA_TODEVICE);
cavium_spin_lock(&priv->lock);
//把这个g回收再利用
cavium_list_add_tail(&g->list, &priv->glist);
cavium_spin_unlock(&priv->lock);
free_recv_buffer((cavium_netbuf_t *)skb);
__check_txq_state(priv);
2.1.2. A12. octnet_setup_nic_device
octnet_setup_nic_device(int octeon_id, oct_link_info_t *link_info, int ifidx)
octnet_priv_t *priv;
octnet_os_devptr_t *pndev;
uint8_t macaddr[6], i;
pndev = octnet_alloc_netdev(OCTNET_PRIV_SIZE);
octprops[octeon_id]->pndev[ifidx] = pndev;
/* Associate the routines that will handle different netdev tasks. */
//这个网口的操作函数, 见B
pndev->netdev_ops = &octnetdevops;
/* Can checksum all the packets. */
pndev->features = NETIF_F_HW_CSUM;
/* Scatter/gather IO. */
pndev->features |= NETIF_F_SG;
priv = GET_NETDEV_PRIV(pndev);
cavium_memset(priv, 0, sizeof(octnet_priv_t));
priv->ifidx = ifidx;
/* Point to the properties for octeon device to which this interface
belongs. */
priv->oct_dev = get_octeon_device_ptr(octeon_id);
priv->octprops = octprops[octeon_id];
priv->pndev = pndev;
cavium_spin_lock_init(&(priv->lock));
/* Record the ethernet port number on the Octeon target for this
interface. */
priv->linfo.gmxport = link_info->gmxport;
/* Record the pci port that the core app will send and receive packets
from host for this interface. */
priv->linfo.ifidx = link_info->ifidx;
priv->linfo.hw_addr = link_info->hw_addr;
priv->linfo.txpciq = link_info->txpciq;
priv->linfo.rxpciq = link_info->rxpciq;
//默认是关的, napi见wiz笔记<数据包接收系列 — NAPI的原理和实现>
if(OCT_NIC_USE_NAPI)
octnet_setup_napi(priv);
netif_napi_add(priv->pndev, &priv->napi, octnet_napi_poll, 64);
cavium_print(PRINT_DEBUG, "OCTNIC: if%d gmx: %d hw_addr: 0x%llx\n",
ifidx, priv->linfo.gmxport, CVM_CAST64(priv->linfo.hw_addr));
/* 64-bit swap required on LE machines */
octeon_swap_8B_data(&priv->linfo.hw_addr, 1);
for(i = 0; i < 6; i++)
macaddr[i] = *((uint8_t *)(((uint8_t *)&priv->linfo.hw_addr) + 2 + i));
/* Copy MAC Address to OS network device structure */
cavium_memcpy(pndev->dev_addr, &macaddr, ETH_ALEN);
priv->linfo.link.u64 = link_info->link.u64;
//这里的size是指q的深度
priv->tx_qsize = octeon_get_tx_qsize(octeon_id, priv->txq);
priv->rx_qsize = octeon_get_rx_qsize(octeon_id, priv->rxq);
octnet_setup_glist(priv)
int i;
struct octnic_gather *g;
CAVIUM_INIT_LIST_HEAD(&priv->glist);
//这个q的每个格子都有个glist
for(i = 0; i < priv->tx_qsize; i++)
g = cavium_malloc_dma(sizeof(struct octnic_gather),
__CAVIUM_MEM_GENERAL);
//OCTNIC_MAX_SG一般是kernel的MAX_SKB_FRAGS, 此值一般为18
//sg entry参考driver篇C1231
g->sg_size = ((ROUNDUP4(OCTNIC_MAX_SG) >> 2)* OCT_SG_ENTRY_SIZE);
//为sg entry数组分配空间
g->sg = cavium_malloc_dma(g->sg_size + 8, __CAVIUM_MEM_GENERAL);
/* The gather component should be aligned on a 64-bit boundary. */
if( ((unsigned long)g->sg) & 7)
g->adjust = 8 - ( ((unsigned long)g->sg) & 7);
g->sg = (octeon_sg_entry_t *)((unsigned long)g->sg + g->adjust);
//以g的链表元素list为节点, 加到glist链表
cavium_list_add_tail(&g->list, &priv->glist);
OCTNET_IFSTATE_SET(priv, OCT_NIC_IFSTATE_DROQ_OPS);
/* Register the network device with the OS */
register_netdev(pndev)
netif_carrier_off(pndev);
if(priv->linfo.link.s.status)
netif_carrier_on(pndev);
octnet_start_txqueue(pndev);
else
netif_carrier_off(pndev);
/* Register the fast path function pointers after the network device
related activities are completed. We should be ready for Rx at this
point. */
/* By default all interfaces on a single Octeon uses the same tx and rx
queues */
priv->txq = priv->linfo.txpciq;
priv->rxq = priv->linfo.rxpciq;
octnet_setup_net_queues(octeon_id, priv)
octeon_droq_ops_t droq_ops;
memset(&droq_ops, 0, sizeof(octeon_droq_ops_t));
droq_ops.fptr = octnet_push_packet; //收包函数, 见A121
if(OCT_NIC_USE_NAPI)
droq_ops.poll_mode = 1;
droq_ops.napi_fn = octnet_napi_drv_callback;
else
droq_ops.drop_on_max = 1;
/* Register the droq ops structure so that we can start handling packets
* received on the Octeon interfaces. */
//注册fast path, 在收包线程和收包bh里面用到;见A18和A19
octeon_register_droq_ops(octeon_id, priv->rxq, &droq_ops)
if(OCT_NIC_USE_NAPI)
octnet_napi_enable(priv);
OCTNET_IFSTATE_SET(priv, OCT_NIC_IFSTATE_REGISTERED);
octnet_send_rx_ctrl_cmd(priv, 1);
octnic_ctrl_pkt_t nctrl;
memset(&nctrl, 0, sizeof(octnic_ctrl_pkt_t));
nctrl.ncmd.s.cmd = OCTNET_CMD_RX_CTL;
nctrl.ncmd.s.param1 = priv->linfo.ifidx;
nctrl.ncmd.s.param2 = start_stop;
nctrl.netpndev = (unsigned long)priv->pndev;
octnet_send_nic_ctrl_pkt(priv->oct_dev, &nctrl)
si = octnic_alloc_ctrl_pkt_si(oct, nctrl);
retval = octeon_process_instruction(oct, si, NULL);
octnet_print_link_info(pndev);
A121. octnet_push_packet
在收包线程或收包bh里面被调用, 负责把报文向上传递给协议栈
octnet_push_packet(int octeon_id,
void *skbuff,
uint32_t len,
octeon_resp_hdr_t *resp_hdr)
struct sk_buff *skb = (struct sk_buff *)skbuff;
octnet_os_devptr_t *pndev = (octnet_os_devptr_t *)octprops[octeon_id]->pndev[resp_hdr->dest_qport];
if(pndev)
octnet_priv_t *priv = GET_NETDEV_PRIV(pndev);
/* Do not proceed if the interface is not in RUNNING state. */
if( !(cavium_atomic_read(&priv->ifstate) & OCT_NIC_IFSTATE_RUNNING)) {
free_recv_buffer(skb);
priv->stats.rx_dropped++;
return;
}
skb->dev = pndev;
skb->protocol = eth_type_trans(skb, skb->dev);
skb->ip_summed = CHECKSUM_NONE;
//用linux收包接口netif_rx()交到协议栈
if(netif_rx(skb) != NET_RX_DROP)
priv->stats.rx_bytes += len;
priv->stats.rx_packets++;
pndev->last_rx = jiffies;
else
priv->stats.rx_dropped++;
3. B. octnetdevops
const static struct net_device_ops octnetdevops = {
.ndo_open = octnet_open, //见B1
.ndo_stop = octnet_stop,
.ndo_start_xmit = octnet_xmit, //见B2
.ndo_get_stats = octnet_stats,
.ndo_set_mac_address = octnet_set_mac,
.ndo_set_multicast_list = octnet_set_mcast_list,
.ndo_tx_timeout = octnet_tx_timeout,
.ndo_change_mtu = octnet_change_mtu,
};
3.1. B1. octnet_open
octnet_open(struct net_device *pndev)
{
octnet_priv_t *priv = GET_NETDEV_PRIV(pndev);
//告诉上层, 可以往驱动层发包了
netif_start_queue(pndev);
//现在是running态
OCTNET_IFSTATE_SET(priv, OCT_NIC_IFSTATE_RUNNING);
//注册一个poll函数, octnet_poll_check_txq_status
__setup_tx_poll_fn(pndev);
octeon_poll_ops_t poll_ops;
octnet_priv_t *priv = GET_NETDEV_PRIV(pndev);
poll_ops.fn = octnet_poll_check_txq_status;
poll_ops.fn_arg = (unsigned long)priv;
poll_ops.ticks = 1;
poll_ops.rsvd = 0xff;
octeon_register_poll_fn(get_octeon_device_id(priv->oct_dev), &poll_ops);
CVM_MOD_INC_USE_COUNT;
return 0;
}
3.2. B2. octnet_xmit 网口发送函数
/** This structure is used by NIC driver to store information required
to free the sk_buff when the packet has been fetched by Octeon.
Bytes offset below assume worst-case of a 64-bit system. */
struct octnet_buf_free_info {
/** Bytes 1-8. Pointer to network device private structure. */
octnet_priv_t *priv;
/** Bytes 9-16. Pointer to sk_buff. */
struct sk_buff *skb;
/** Bytes 17-24. Pointer to gather list. */
struct octnic_gather *g;
/** Bytes 25-32. Physical address of skb->data or gather list. */
uint64_t dptr;
};
octnet_xmit(struct sk_buff *skb, struct net_device *pndev)
octnet_priv_t *priv;
struct octnet_buf_free_info *finfo;
octnic_cmd_setup_t cmdsetup;
octnic_data_pkt_t ndata;
int status = 0;
priv = GET_NETDEV_PRIV(pndev);
priv->stats.tx_packets++;
if(!OCTNET_IFSTATE_CHECK(priv, OCT_NIC_IFSTATE_TXENABLED))
return OCT_NIC_TX_BUSY;
//检查条件
if( !(cavium_atomic_read(&priv->ifstate) & OCT_NIC_IFSTATE_RUNNING)
|| (!priv->linfo.link.s.status)
|| (octnet_iq_is_full(priv->oct_dev, priv->txq))
|| (skb->len <= 0) ) {
goto oct_xmit_failed;
}
/* Use space in skb->cb to store info used to unmap and free the buffers. */
//这里看不懂; 为什么skb->cb就能是octnet_buf_free_info?? 协议栈按理说不应该知道这个结构体啊????
//解答: 在sk_buff的定义里cb是个48个字节的自由使用区 char cb[48] __aligned(8);
//octnet_buf_free_info的定义见上面, 可见它只用了32个字节---标记:B2i1
finfo = (struct octnet_buf_free_info *)skb->cb;
finfo->priv = priv;
finfo->skb = skb;
/* Prepare the attributes for the data to be passed to OSI. */
ndata.buf = (void *)finfo;
ndata.q_no = priv->txq;
ndata.datasize = skb->len;
cmdsetup.u64 = 0;
cmdsetup.s.ifidx = priv->linfo.ifidx;
if( (is_ipv4(skb) && !is_ip_fragmented(skb) && is_tcpudp(skb)) ||
(is_ipv6(skb) && is_wo_extn_hdr(skb)) ) {
cmdsetup.s.cksum_offset = sizeof(struct ethhdr) + 1;
}
/*重要!:不管有没有frags, 这里都做到了直接使用skb做为DMA的内存*/
//没有frags, 即只有一个buffer
if(skb_shinfo(skb)->nr_frags == 0)
cmdsetup.s.u.datasize = skb->len;
octnet_prepare_pci_cmd(&(ndata.cmd), &cmdsetup);
volatile octeon_instr_ih_t *ih;
volatile octeon_instr_irh_t *irh;
cmd->ih = 0;
ih = (octeon_instr_ih_t *)&cmd->ih;
ih->fsz = 16;
ih->tagtype = ORDERED_TAG;
ih->grp = OCTNET_POW_GRP;
ih->tag = 0x11111111 + setup->s.ifidx;
ih->raw = 1;
if(!setup->s.gather)
ih->dlengsz = setup->s.u.datasize;
else
ih->gather = 1;
ih->dlengsz = setup->s.u.gatherptrs;
//rptr为0说明是noresponse
cmd->rptr = 0;
cmd->irh = 0;
irh = (octeon_instr_irh_t *)&cmd->irh;
if(setup->s.cksum_offset)
irh->rlenssz = setup->s.cksum_offset;
irh->opcode = OCT_NW_PKT_OP;
irh->param = setup->s.ifidx;
/* Offload checksum calculation for TCP/UDP packets */
ndata.cmd.dptr =
octeon_map_single_buffer(get_octeon_device_id(priv->oct_dev),
skb->data, skb->len, CAVIUM_PCI_DMA_TODEVICE);
finfo->dptr = ndata.cmd.dptr;
ndata.buftype = NORESP_BUFTYPE_NET;
//这中情况下是多buffer, 要搞gather链
else
int i, frags;
struct skb_frag_struct *frag;
struct octnic_gather *g;
cavium_spin_lock(&priv->lock);
//从链表头摘一个元素, 发送完毕会再申请一个glist, 见A11
g = (struct octnic_gather *)cavium_list_delete_head(&priv->glist);
cavium_spin_unlock(&priv->lock);
cmdsetup.s.gather = 1;
cmdsetup.s.u.gatherptrs = (skb_shinfo(skb)->nr_frags + 1);
octnet_prepare_pci_cmd(&(ndata.cmd), &cmdsetup); //见上面
memset(g->sg, 0, g->sg_size);
//建立gather list
g->sg[0].ptr[0] =
octeon_map_single_buffer(get_octeon_device_id(priv->oct_dev),
skb->data, (skb->len - skb->data_len),
CAVIUM_PCI_DMA_TODEVICE);
CAVIUM_ADD_SG_SIZE(&(g->sg[0]), (skb->len - skb->data_len), 0);
//一共有多少个frags
frags = skb_shinfo(skb)->nr_frags;
while(frags--) {
frag = &skb_shinfo(skb)->frags[i-1];
//建立剩下的gather list
g->sg[(i >> 2)].ptr[(i&3)] =
octeon_map_page(get_octeon_device_id(priv->oct_dev), frag->page, frag->page_offset,
frag->size,CAVIUM_PCI_DMA_TODEVICE);
CAVIUM_ADD_SG_SIZE(&(g->sg[(i >> 2)]), frag->size, (i&3));
i++;
}
//这个cmd的dptr是给octeon DMA的
ndata.cmd.dptr =
octeon_map_single_buffer(get_octeon_device_id(priv->oct_dev),
g->sg, g->sg_size, CAVIUM_PCI_DMA_TODEVICE);
finfo->dptr = ndata.cmd.dptr;
finfo->g = g;
ndata.buftype = NORESP_BUFTYPE_NET_SG;
//发包, 重要!
status = octnet_send_nic_data_pkt(priv->oct_dev, &ndata);
//这个发送函数更底层, 直接就发送命令字, 不搞什么buffer; buffer在上面已经搞好了
return octeon_send_noresponse_command(oct, ndata->q_no, 1, &ndata->cmd,
ndata->buf, ndata->datasize, ndata->buftype);
pndev->trans_start = jiffies;
return OCT_NIC_TX_OK;