Re: [PROBLEM] sleeping function called from invalid context at mm/slab.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 10 Jun 2007 14:13:58 +0400, "Denis Kirjanov" <kirjanov@xxxxxxxxx> wrote:
> On 6/10/07, Lukas Razik <linux@xxxxxxxxxx> wrote:
>> Hello!
>>
>> I'm new to kernel programming and I try to develop a fake ethernet
> driver for
>> v2.6.21.3 which I really need based on netdev but I ran into the
> following
>> issue:
>> If the kernel calls the 'dev->hard_start_xmit' function of my driver
> then this
>> calls another one which try to create a socket in the following "normal
> way":
>>
>> struct socket *ssock = NULL;
>> ...
>> sock_create(family, SOCK_STREAM, IPPROTO_TCP, &ssock);
>>
>> But at this point I get an error message from the kernel as appended
> below.
>> So what have I done wrong?
>>
>> Regards and many thanks for any help!
>> Lukas
> Show full source code
>

Hi Dennis!

Here it is (it's based on this one http://www.cs.fsu.edu/~baker/devices/lxr/http/source/ldd-examples/snull/snull.c ).
I'll send you the full code as a file. For all others the important part is below.
The error occurs in ethos_socket_tx().

Regards,
Lukas

...

static int timeout = ETHOS_TIMEOUT;
module_param(timeout, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);

static int family = AF_INET;
module_param(family, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);

struct ethos_addr_map {
	u32 ethos_addr;
	u32 real_addr;
};

static const struct ethos_addr_map ethos_addr_maps[] = {
	/* ethos_addr <-> real_addr */
	/* 192.168.10.1 <-> 192.168.0.1 */
	{__constant_htonl(0xc0a80a01),__constant_htonl(0xc0a80001)}
};
#define ETHOS_ADDR_MAPS_NUM 1

...

static void ethos_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	int statusword;
	struct ethos_priv *priv;
	struct ethos_packet *pkt = NULL;
	/*
	 * As usual, check the "device" pointer to be sure it is
	 * really interrupting.
	 * Then assign "struct device *dev"
	 */
	struct net_device *dev = (struct net_device *)dev_id;
	/* ... and check with hw if it's really ours */

	/* paranoid */
	if (!dev)
		return;

	/* Lock the device */
	priv = netdev_priv(dev);
	spin_lock(&priv->lock);

	/* retrieve statusword: real netdevices use I/O instructions */
	statusword = priv->status;
	priv->status = 0;
	if (statusword & ETHOS_RX_INTR) {
		/* send it to ethos_rx for handling */
		pkt = priv->rx_queue;
		if (pkt) {
			priv->rx_queue = pkt->next;
			ethos_rx(dev, pkt);
		}
	}
	if (statusword & ETHOS_TX_INTR) {
		/* If packet was sent... */
		if(priv->tx_packetlen) {
			priv->stats.tx_packets++;
			priv->stats.tx_bytes += priv->tx_packetlen;
		}
		else
		PNOTICE("packet dropped\n");
		// TODO: else info about rejected packet
		/* a transmission is over: free the skb */
		dev_kfree_skb(priv->skb);
	}

	/* Unlock the device and we are done */
	spin_unlock(&priv->lock);
	// TODO: maybe if(pkt) kfree(pkt);
//	if (pkt) ethos_release_buffer(pkt); /* Do this outside the lock! */
	return;
}


static void ethos_socket_tx(char *buf, int len, struct net_device *dev)
	struct iphdr *ih;
	struct ethos_priv *priv;
	struct ethos_packet pkt;

	struct sockaddr_in saddr;
	struct socket *ssock = NULL;
	int ret = -1, i;

	/* I am paranoid. Ain't I? */
	if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
		printk("ethos: Hmm... packet too short (%i octets)\n",
				len);
		// TODO: buf won't be freed by ethos_interrupt()
		return;
	}

	/*
	 * Ethhdr is 14 bytes, but the kernel arranges for iphdr
	 * to be aligned (i.e., ethhdr is unaligned)
	 */
	ih = (struct iphdr *)(buf+sizeof(struct ethhdr));

	//TODO:
	// set pkt

	/* Trying to map ethos address to real address */
	for(i = 0; i < ETHOS_ADDR_MAPS_NUM; i++)
		if(ethos_addr_maps[i].ethos_addr == ih->daddr)
			break;

	/* Didn't find ethos address in the map table */
	if(ETHOS_ADDR_MAPS_NUM == i) {
		PNOTICE("ethos: couldn't map ethos addr %08x\n", ntohl(ih->daddr));
		len = 0;
		goto err_map;
	}

	PDEBUG("mapped %08x to %08x\n", ntohl(ih->daddr), ntohl(ethos_addr_maps[i].real_addr));

	/* Create send socket */
	ret = sock_create(family, SOCK_STREAM, IPPROTO_TCP, &ssock);
	if(ret < 0) {
		PERR("couldn't create send socket (ret=%d)\n", ret);
		len = 0;
		goto err_sock;
	}

	PDEBUG("created send socket (ssock=%0x, family=%d, ret=%d)\n", (unsigned int)ssock, family, ret);
	len = 0;
	goto err_connect;

	/* Connect through send socket */
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = family;
	saddr.sin_port   = htons(ETHOS_SOCKET_PORT);
	saddr.sin_addr.s_addr = ethos_addr_maps[i].real_addr;
	ret = kernel_connect(ssock, (struct sockaddr *) &saddr, sizeof(saddr), O_RDWR);
	if(ret && (ret != -EINPROGRESS)) {
		PERR("couldn't connect to %08x:%05d (ret=%d)\n",
			 saddr.sin_addr.s_addr, saddr.sin_port, ret);
		len = 0;
		goto err_connect;
	}

	/* Receive an answere from pong program */
	char *recv_msg = kmalloc(ETHOS_RCVBUF, GFP_KERNEL);
	read_msg(ssock, recv_msg);
	printk(KERN_INFO "ethos: server said:  %s\n", recv_msg);

err_connect:
	sock_release(ssock);
	PDEBUG("released send socket (ssock=%0x)\n", (unsigned int)ssock);
err_sock:
err_map:
	priv = netdev_priv(dev);
	priv->tx_packetlen = len;
	priv->tx_packetdata = buf;
	priv->status |= ETHOS_TX_INTR;

	ethos_interrupt(0, dev, NULL);
}

...

/*
 * Transmit a packet (called by the kernel)
 */
int ethos_tx(struct sk_buff *skb, struct net_device *dev)
{
	int len;
	char *data, shortpkt[ETH_ZLEN];
	struct ethos_priv *priv = netdev_priv(dev);

	data = skb->data;
	len = skb->len;
	if (len < ETH_ZLEN) {
		memset(shortpkt, 0, ETH_ZLEN);
		memcpy(shortpkt, skb->data, skb->len);
		len = ETH_ZLEN;
		data = shortpkt;
	}
	dev->trans_start = jiffies; /* save the timestamp */

	/* Remember the skb, so we can free it at interrupt time */
	priv->skb = skb;

	/* actual deliver of data is device-specific, and not shown here */
	ethos_socket_tx(data, len, dev);

	return 0; /* Our simple device can not fail */
}

...

void ethos_init(struct net_device *dev)
{
	struct ethos_priv *priv;

	/*
	 * Then, assign other fields in dev, using ether_setup() and some
	 * hand assignments
	 */
	ether_setup(dev); /* assign some of the fields */

	dev->open            = ethos_open;
	dev->stop            = ethos_release;
	dev->set_config      = ethos_config;
	dev->hard_start_xmit = ethos_tx;
	dev->do_ioctl        = ethos_ioctl;
	dev->get_stats       = ethos_stats;
	dev->change_mtu      = ethos_change_mtu;
	dev->rebuild_header  = ethos_rebuild_header;
	dev->hard_header     = ethos_header;
	dev->tx_timeout      = ethos_tx_timeout;
	dev->watchdog_timeo = timeout;
	/* keep the default flags, just add NOARP */
	dev->flags           |= IFF_NOARP;
	dev->features        |= NETIF_F_NO_CSUM;
	dev->hard_header_cache = NULL;      /* Disable caching */

	/*
	 * Then, initialize the priv field. This encloses the statistics
	 * and a few private fields.
	 */
	priv = netdev_priv(dev);
	memset(priv, 0, sizeof(struct ethos_priv));
	spin_lock_init(&priv->lock);
	ethos_rx_ints(dev, 1);		/* enable receive interrupts */
}

...

int ethos_init_module(void)
{
	int result, i, ret = -ENOMEM;

	/* Allocate the devices */
	ethos_devs[0] = alloc_netdev(sizeof(struct ethos_priv), "eth%d",
			ethos_init);
	if (ethos_devs[0] == NULL)
		goto out;

	ret = -ENODEV;
	for (i = 0; i < ETHOS_NUM;  i++)
		if ((result = register_netdev(ethos_devs[i])))
			PERR("ethos: error %i registering device \"%s\"\n",
					result, ethos_devs[i]->name);
		else
			ret = 0;
out:
	if (ret)
		ethos_cleanup_module();
	return ret;
}


--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux