Problems with netdev_alloc and netdev_priv in kernel network driver

1

I've got a custom piece of FPGA logic which I've implemented a functioning char driver for, and I'm trying to get it to work as a network driver as well now. I'm following along as best I can using the LDD book, snull code, and loopback.c, dummy.c, and other drivers from the kernel as examples.

The code I've got below (obviously ripped out of the larger file) is the networking driver code I have so far; I can successfully insmod, and "ifconfig optical0" shows that my MTU & flags are correct so I know at least it's being registered.

Now I'm trying to implement the struct net_device's private field (void* priv) to store stats and other things in, and it's resulting in segfaults. Here's the code so far:

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;
}

So as I understand it, I'm allocating space for and initializing my serdes device (which I know works as a char driver). Then I call optical_init which allocs, registers, and configures serdes_dev->netdev. alloc_netdev is supposed to allocate room for the (void *)priv field as well which is why sizeof(struct optical_priv) is passed in.

The output after insmod of the two print statements in there look like this:

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

and obviously trying to access priv in any way after that causes a segfault. I think my confusion is about why netdev_priv() returns the two different values when called from the two different functions-- shouldn't it be allocated and correct anytime after alloc_netdev?

e: I think I'm also confusing myself by looking for examples in too many drivers, from too many eras, steeped in the history of hardware and kernel APIs past... if anyone has a good basic driver recommendation for me to start with, I'm more than happy reading code to learn.

c
linux-kernel
network-programming
linux-device-driver
asked on Stack Overflow Mar 7, 2014 by user1598590 • edited Mar 7, 2014 by user1598590

1 Answer

0

I think that the problem is here:

err = optical_init(serdes_dev->netdev);

You are passing an invalid pointer to a netdev structure. Then in optical_init() you change the local value of the pointer with:

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

Now you have a valid pointer to a struct net_device. But this pointer value does not apply to the serdes_dev->netdev variable but only locally in optical_init(). In consequence of this, also the pointer to priv is invalid. Here a little example that show the issue:

#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;
 }

The output is:

before (nil)
while allocating 0x23ee030
after (nil)
answered on Stack Overflow Mar 8, 2014 by Federico

User contributions licensed under CC BY-SA 3.0