Linux kernel 如何在接收网络驱动程序的IRQ时发送数据包?
对于一个研究项目,我目前正在修改一个linux驱动程序。我对这个话题相当陌生,所以我不确定我在这里是否犯了一个明显的错误 我的目标是在驱动程序IRQ期间捕获传入的ICMP数据包,并立即向发送方发送回复。现在,这没关系,如果校验和或任何东西都是正确的,我只想尽快发回一些东西。为此,我在IRQ期间运行以下代码:Linux kernel 如何在接收网络驱动程序的IRQ时发送数据包?,linux-kernel,network-programming,linux-device-driver,kernel-module,Linux Kernel,Network Programming,Linux Device Driver,Kernel Module,对于一个研究项目,我目前正在修改一个linux驱动程序。我对这个话题相当陌生,所以我不确定我在这里是否犯了一个明显的错误 我的目标是在驱动程序IRQ期间捕获传入的ICMP数据包,并立即向发送方发送回复。现在,这没关系,如果校验和或任何东西都是正确的,我只想尽快发回一些东西。为此,我在IRQ期间运行以下代码: data = skb-> data; /* we received an ICMP request. Send custom reply. */ if(data[9] == 1){/
data = skb-> data;
/* we received an ICMP request. Send custom reply. */
if(data[9] == 1){// && data[20] == 8){
unsigned long start = jiffies;
unsigned long end;
u8 * newdata;
u16 ippacketLength;
sk_quickreply = dev_alloc_skb(128);
struct ethhdr * mac_skb_quick;
struct iphdr * iph_skb_quick;
struct ethhdr * mac_skb = eth_hdr(skb);
struct iphdr * iph_skb = data;
quicktransmit = 1;
/* Put icmp packet */
skb_push(sk_quickreply, 26);
/* Put IP header */
skb_push(sk_quickreply, sizeof(struct iphdr));
skb_reset_network_header(sk_quickreply);
iph_skb_quick = ip_hdr(sk_quickreply);
iph_skb_quick->version = 4;
iph_skb_quick->ihl = 5;
iph_skb_quick->ttl = 64;
iph_skb_quick->daddr = iph_skb->saddr;
iph_skb_quick->saddr = iph_skb->daddr;
iph_skb_quick->protocol = 1; //ICMP
ip_send_check(iph_skb_quick);
/* Put MAC header */
eth_header(sk_quickreply, dev, ETH_P_802_3, mac_skb->h_source, mac_skb->h_dest, 14);
skb_reset_mac_header(sk_quickreply);
mac_skb_quick = eth_hdr(sk_quickreply);
rtl8168_mystart_xmit(sk_quickreply,dev);
}
其中rtl8168_mystart_xmit是驱动程序最初提供的硬启动功能,但我删除了自旋锁:
static int rtl8168_mystart_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct rtl8168_private *tp = netdev_priv(dev);
unsigned int frags, entry;
struct TxDesc *txd;
void __iomem *ioaddr = tp->mmio_addr;
dma_addr_t mapping;
u32 len;
u32 opts1;
u32 opts2;
int ret = NETDEV_TX_OK;
unsigned long flags, large_send;
printk(KERN_INFO "Starting transmission of fast reply.\n");
if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) {
if (netif_msg_drv(tp)) {
printk(KERN_ERR
"%s: BUG! Tx Ring full when queue awake!\n",
dev->name);
}
goto err_stop;
}
entry = tp->cur_tx % NUM_TX_DESC;
txd = tp->TxDescArray + entry;
if (unlikely(le32_to_cpu(txd->opts1) & DescOwn))
goto err_stop;
opts1 = DescOwn;
opts2 = rtl8168_tx_vlan_tag(tp, skb);
large_send = 0;
if (dev->features & NETIF_F_TSO) {
u32 mss = skb_shinfo(skb)->gso_size;
printk(KERN_INFO "Large send.\n");
/* TCP Segmentation Offload (or TCP Large Send) */
if (mss) {
if ((tp->mcfg == CFG_METHOD_1) ||
(tp->mcfg == CFG_METHOD_2) ||
(tp->mcfg == CFG_METHOD_3)) {
opts1 |= LargeSend | ((mss & MSSMask) << 16);
} else if ((tp->mcfg == CFG_METHOD_11) ||
(tp->mcfg == CFG_METHOD_12) ||
(tp->mcfg == CFG_METHOD_13)) {
opts2 |= LargeSend_DP | ((mss & MSSMask) << 18);
} else {
opts1 |= LargeSend;
opts2 |= (mss & MSSMask) << 18;
}
large_send = 1;
}
}
if (large_send == 0) {
printk(KERN_INFO "No large send.\n");
if (dev->features & NETIF_F_IP_CSUM) {
if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3))
opts1 |= rtl8168_tx_csum(skb, dev);
else
opts2 |= rtl8168_tx_csum(skb, dev);
}
}
frags = rtl8168_xmit_frags(tp, skb, opts1, opts2);
printk(KERN_INFO "Frags: %u\n", frags);
if (frags) {
len = skb_headlen(skb);
opts1 |= FirstFrag;
} else {
len = skb->len;
tp->tx_skb[entry].skb = skb;
if (tp->UseSwPaddingShortPkt && len < 60) {
printk(KERN_INFO "Packet uses padding\n");
rtl8168_sw_padding_short_pkt(tp, skb, opts1, opts2);
opts1 |= FirstFrag;
frags++;
} else {
printk(KERN_INFO "Packet doesn't use padding\n");
//This code means, the fragment is the first and the last fragment to be send
opts1 |= FirstFrag | LastFrag;
}
}
printk(KERN_INFO "Length: %u\n", len);
opts1 |= len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE);
tp->tx_skb[entry].len = len;
txd->addr = cpu_to_le64(mapping);
txd->opts2 = cpu_to_le32(opts2);
txd->opts1 = cpu_to_le32(opts1&~DescOwn);
wmb();
txd->opts1 = cpu_to_le32(opts1);
dev->trans_start = jiffies;
tp->cur_tx += frags + 1;
wmb();
RTL_W8(TxPoll, HPQ); /* set polling bit */
out:
return ret;
err_stop:
printk(KERN_INFO "Problem: Jumped to err_stop\n");
//netif_stop_queue(dev);
//ret = NETDEV_TX_BUSY;
//RTLDEV->stats.tx_dropped++;
spin_unlock_irqrestore(&tp->lock, flags);
goto out;
}
static int rtl8168_mystart_xmit(struct sk_buff*skb,
结构网络(设备*dev)
{
结构rtl8168_private*tp=netdev_priv(dev);
无符号整数框,条目;
结构TxDesc*txd;
void\uuu iomem*ioaddr=tp->mmio\u addr;
dma地址映射;
u32-len;
u32 opts1;
u32 opts2;
int-ret=NETDEV_-TX_-OK;
未签名的长标志,大_发送;
printk(KERN_INFO“开始传输快速回复。\n”);
如果(不太可能(不太可能){
如果(netif_msg_drv(tp)){
printk(KERN_ERR
“%s:BUG!队列唤醒时Tx已满!\n”,
开发人员->名称);
}
去错误的停止;
}
条目=tp->cur\u tx%NUM\u tx\u DESC;
txd=tp->TxDescArray+条目;
如果(不太可能(le32到cpu(txd->opts1)和描述))
去错误的停止;
opts1=下降;
opts2=rtl8168_tx_vlan_标签(tp,skb);
大发送=0;
如果(开发->功能和网络){
u32 mss=skb_shinfo(skb)->gso_尺寸;
printk(KERN_INFO“大发送。\n”);
/*TCP分段卸载(或TCP大发送)*/
中频(mss){
如果((tp->mcfg==CFG_方法_1)||
(tp->mcfg==CFG_方法_2)||
(tp->mcfg==CFG_方法_3)){
opts1 |=慷慨(mss和MSSMask)mcfg==CFG|U方法|11)||
(tp->mcfg==CFG_方法_12)||
(tp->mcfg==CFG_方法_13)){
opts2 |=慷慨的| DP |((mss和MSSMask)mcfg==CFG方法| 1)|(tp->mcfg==CFG方法| 2)|(tp->mcfg==CFG方法| 3))
opts1 |=rtl8168_tx_csum(skb,dev);
其他的
opts2 |=rtl8168_tx_csum(skb,dev);
}
}
frags=rtl8168_xmit_frags(tp、skb、opts1、opts2);
printk(内核信息“碎片:%u\n”,碎片);
如果(碎片){
len=skb_车顶(skb);
opts1 |=第一帧;
}否则{
len=skb->len;
tp->tx_skb[输入].skb=skb;
如果(tp->useswappingshortpkt&&len<60){
printk(KERN_INFO“数据包使用填充\n”);
rtl8168_sw_padding_short_pkt(tp、skb、opts1、opts2);
opts1 |=第一帧;
frags++;
}否则{
printk(KERN_INFO“数据包不使用填充\n”);
//这段代码意味着,该片段是要发送的第一个也是最后一个片段
opts1 |=第一帧|最后帧;
}
}
printk(内核信息“长度:%u\n”,len);
opts1 |=len |(RingEnd*!((entry+1)%NUM_TX_DESC));
mapping=pci\U map\u single(tp->pci\U dev,skb->data,len,pci\U DMA\u TODEVICE);
tp->tx_skb[输入].len=len;
txd->addr=cpu_至_le64(映射);
txd->opts2=cpu_至_le32(opts2);
txd->opts1=cpu_至_le32(opts1&~DescOwn);
wmb();
txd->opts1=cpu_至_le32(opts1);
dev->trans_start=jiffies;
tp->cur_tx+=frags+1;
wmb();
RTL_W8(TxPoll,HPQ);/*设置轮询位*/
输出:
返回ret;
错误停止:
printk(内核信息“问题:跳到错误停止\n”);
//netif_停止队列(dev);
//ret=NETDEV_TX_BUSY;
//RTLDEV->stats.tx_++;
旋转\u解锁\u irqrestore(&tp->lock,flags);
出去;
}
我正在从第二台计算机发送ping请求,该计算机是我设置为混杂模式的nic。似乎从一开始就不会发送回复。我做错了什么,或者在IRQ期间可能会发生这种传输
提前感谢您的帮助。您不应该这样做。您应该尽快处理IRQ,这不包括参与其他netwrok活动。感谢ejp的评论。我知道IRQ可能会变得不可接受的长。我正在尝试将日程安排对包处理和处理的影响降至最低这是一种幼稚的方法。我的目标只是想弄清楚这是否基本有效。你不应该这样做。你应该尽快处理IRQ,这不包括参与其他netwrok活动。感谢ejp的评论。我知道IRQ可能会变得不可接受的长。我正在尝试将影响最小化日程安排对包处理有影响,这是一种幼稚的方法。我的目标是弄清楚这是否基本可行。