内核网络驱动程序中netdev_alloc和netdev_priv的问题

时间:2014-03-07 23:20:41

标签: c linux-kernel network-programming linux-device-driver

我有一个自定义的FPGA逻辑器,我已经为它实现了一个正常运行的char驱动程序,我现在也试图让它作为网络驱动程序工作。我正在尽可能地使用LDD书籍,snull代码和loopback.c,dummy.c以及内核中的其他驱动程序作为示例。

我下面的代码(显然是从较大的文件中删除)是我到目前为止的网络驱动程序代码;我可以成功insmod,“ifconfig optical0”显示我的MTU&标志是正确的所以我知道至少它已被注册。

现在我正在尝试实现struct net_device的私有字段(void * priv)来存储统计信息和其他内容,这导致了segfaults。这是迄今为止的代码:

struct serdes_device {
    struct serdes_regs __iomem *regs;
    struct serdes_dma *tx_dma,
                      *rx_dma;

    struct device *dev;
    struct net_device *netdev;
    struct resource serdes_res; 
    struct cdev cdev;
    int major, minor;

    int tx_kbps;
    int rx_kbps;
};  

struct optical_priv {
    struct serdes_device *serdes_dev;
    struct net_device_stats stats;
};

static const struct net_device_ops optical_netdev_ops = {
    .ndo_get_stats = optical_stats,
    .ndo_start_xmit = optical_xmit,
    /* and so on, not including the ops functions for brevity */
};

void optical_setup(struct net_device *dev)
{
    dev->netdev_ops = &optical_netdev_ops;
    dev->destructor = free_netdev;

    dev->tx_queue_len = 1;
    dev->type = ARPHRD_NONE;
    dev->hard_header_len = SERDES_FRAME_HEADER_LENGTH;
    dev->mtu = SERDES_FRAME_TOTAL_LENGTH;
    dev->addr_len = 0;

    dev->flags &= ~IFF_BROADCAST;
    /* more flags & features cut */
}

static int optical_init(struct net_device *netdev)
{
    int err;
    struct optical_priv *priv;

    netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);
    if (!netdev)
        return -ENOMEM;

    err = register_netdev(netdev);
    if (err < 0)
        goto err;

    priv = netdev_priv(netdev);
    printk(KERN_WARNING "priv is at address 0x%p\n", priv);

    return 0;

err:
    free_netdev(netdev);
    return err;
}

static int serdes_of_probe(struct platform_device *op)
{
    struct serdes_device *serdes_dev;
    struct optical_priv *priv;
    int err = 0;

    serdes_dev = kzalloc(sizeof(struct serdes_device), GFP_KERNEL);

    /* A bunch of unrelated openfirmware & cdev code removed. */

    err = optical_init(serdes_dev->netdev);
    if (err < 0)
        dev_err(serdes_dev->dev, "Error %d initing optical link\n", err);

priv = netdev_priv(serdes_dev->netdev);
    if (!priv)
        dev_err(serdes_dev->dev, "priv is null... \n");

    dev_info(serdes_dev->dev, "priv is at 0x%p\n", priv);
    dev_info(&op->dev, "Done probing.\n");

    return 0;

out_free_serdes:
    kfree(serdes_dev);

out_return:
    return err;
}

据我了解,我正在分配空间并初始化我的serdes设备(我知道它可以作为一个char驱动程序)。然后我调用optical_init来分配,注册和配置serdes_dev-&gt; netdev。 alloc_netdev应该为(void *)priv字段分配空间,这就是传入sizeof(struct optical_priv)的原因。

两个打印语句的insmod之后的输出如下所示:

priv is at address 0xdd2de500
priv is at 0x00000500
Done probing.

显然尝试以任何方式访问priv会导致段错误。我想我的困惑是为什么netdev_priv()在从两个不同的函数调用时返回两个不同的值 - 在alloc_netdev之后的任何时候都不应该分配和更正吗?

e:我想通过在太多的驱动程序中寻找示例来困惑自己,从太多的时代开始,沉浸在硬件和内核API的历史中......如果有人对我有一个很好的基本驱动程序建议首先,我非常乐意阅读代码来学习。

1 个答案:

答案 0 :(得分:0)

我认为问题在于:

err = optical_init(serdes_dev->netdev);

您正在将无效指针传递给netdev结构。然后在optical_init()中使用以下命令更改指针的本地值:

netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);

现在你有一个指向struct net_device的有效指针。但是此指针值不适用于serdes_dev->netdev变量,而只适用于optical_init()中的本地。因此,指向priv的指针也是无效的。这里有一个显示问题的小例子:

#include <stdio.h>
#include <stdlib.h>

struct test {
    int first;
    int *second;
};

void allocate_memory(int *ptr)
{
    ptr = malloc(10);
    printf("while allocating %p\n", ptr);
}

int main(void) {
    struct test *p1;

    p1= malloc(sizeof(struct test));

    printf("before %p\n", p1->second);
    allocate_memory(p1->second);
    printf("after %p\n", p1->second);

    free(p1);
    free(p2);

    return EXIT_SUCCESS;
 }

输出结果为:

before (nil)
while allocating 0x23ee030
after (nil)