diff -urN linux-2.4.2-uml/arch/um/config.in linux-2.4.2-uml-net/arch/um/config.in --- linux-2.4.2-uml/arch/um/config.in Sat Mar 3 21:26:44 2001 +++ linux-2.4.2-uml-net/arch/um/config.in Sat Mar 3 22:20:03 2001 @@ -58,6 +58,7 @@ if [ "$CONFIG_NETDEVICES" = "y" ]; then bool 'Virtual ethernet device' CONFIG_NET_UM_ETH if [ "$CONFIG_NET_UM_ETH" = "y" ]; then + bool ' Use fast protocol' CONFIG_NET_UM_ETH_FAST if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_NETLINK" = "y" ]; then tristate 'Ethertap network tap (EXPERIMENTAL)' CONFIG_ETHERTAP diff -urN linux-2.4.2-uml/arch/um/drivers/Makefile linux-2.4.2-uml-net/arch/um/drivers/Makefile --- linux-2.4.2-uml/arch/um/drivers/Makefile Sat Mar 3 21:26:44 2001 +++ linux-2.4.2-uml-net/arch/um/drivers/Makefile Sat Mar 3 22:20:03 2001 @@ -25,6 +25,8 @@ CFLAGS_umn_user.o := $(USER_CFLAGS) CFLAGS_eth_kern.o := $(CFLAGS) CFLAGS_eth_user.o := $(USER_CFLAGS) +CFLAGS_eth_kern_fast.o := $(CFLAGS) +CFLAGS_eth_user_fast.o := $(USER_CFLAGS) obj-$(CONFIG_STDIO_CONSOLE) += stdio_console.o stdio_console_user.o obj-$(CONFIG_SSL) += ssl.o @@ -32,7 +34,11 @@ obj-$(CONFIG_STDIO_CONSOLE) += chan_user.o chan_kern.o obj-$(CONFIG_BLK_DEV_UBD) += ubd.o ubd_user.o obj-$(CONFIG_NET_UMN) += umn_user.o umn_kern.o -obj-$(CONFIG_NET_UM_ETH) += eth_kern.o eth_user.o +ifeq ($(CONFIG_NET_UM_ETH_FAST), y) + obj-y += eth_kern_fast.o eth_user_fast.o +else + obj-y += eth_kern.o eth_user.o +endif override CFLAGS = diff -urN linux-2.4.2-uml/arch/um/drivers/eth_kern_fast.c linux-2.4.2-uml-net/arch/um/drivers/eth_kern_fast.c --- linux-2.4.2-uml/arch/um/drivers/eth_kern_fast.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.2-uml-net/arch/um/drivers/eth_kern_fast.c Sat Mar 3 22:28:47 2001 @@ -0,0 +1,312 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "user_util.h" + +extern int uml_net_user_close(void *); +extern void uml_net_user_open(void *, int, void *, char *, void *); +extern void uml_net_user_set_mac(unsigned char *); +extern void uml_net_user_timer_expire(unsigned long); +extern void uml_net_user_tx(void *, char *, int); +extern int uml_net_user_rx(void *, char *, int); + +static unsigned char broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static struct net_device *uml_net_dev = NULL; +static int uml_net_count = 0; + +#define MAX_PACKET 1536 +#define MAX_UNITS 8 + +struct uml_net_private { + spinlock_t lock; + void *user; + struct timer_list tl; + unsigned int net; + struct net_device_stats stats; + struct net_device *next; +}; + +static int uml_net_rx(struct net_device *); +static int uml_net_open(struct net_device *); +static int uml_net_close(struct net_device *); +void uml_net_interrupt(int, void *, struct pt_regs *); +static void uml_net_tx_timeout(struct net_device *dev); +static void uml_net_set_multicast_list(struct net_device *); +static int uml_net_start_xmit(struct sk_buff *, struct net_device *); +static struct net_device_stats *uml_net_get_stats(struct net_device *); + +int __init uml_net_probe(void) +{ + struct net_device *dev = NULL; + struct uml_net_private *lp; + char *priv; + int i; + + for (i = 0; i < 4; i++) { + dev = init_etherdev(NULL, 0); + + if (dev == NULL) + return -ENOMEM; + + printk("User-mode Linux fast network interface 0.001 (%s)\n", + dev->name); + + dev->irq = UM_ETH_IRQ; + uml_net_count++; + + if (uml_net_count >= MAX_UNITS) { + printk("ERROR: increase UML MAX_UNITS\n"); + return -ENOMEM; + } + + if ((priv = kmalloc(sizeof(struct uml_net_private) + 15, + GFP_KERNEL)) == NULL) + return -ENOMEM; + + lp = (struct uml_net_private *)(((unsigned long)priv + + 15) & ~15); + + memset(lp, 0, sizeof(struct uml_net_private)); + init_timer(&lp->tl); + lp->tl.function = uml_net_user_timer_expire; + + uml_net_user_set_mac(dev->dev_addr); + + dev->priv = lp; + dev->open = uml_net_open; + dev->hard_start_xmit = uml_net_start_xmit; + dev->stop = uml_net_close; + dev->get_stats = uml_net_get_stats; + dev->set_multicast_list = uml_net_set_multicast_list; + dev->tx_timeout = uml_net_tx_timeout; + dev->watchdog_timeo = (HZ >> 1); + lp->next = uml_net_dev; + uml_net_dev = dev; + spin_lock_init(&lp->lock); + + /* Fill in the generic fields of the device structure. */ + ether_setup(dev); + } + + return 0; +} + +static int uml_net_open(struct net_device *dev) +{ + char hostname[32]; + char ifname[32]; + char tapname[64]; + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + if (request_irq(dev->irq, uml_net_interrupt, SA_INTERRUPT | SA_SHIRQ, + "um-eth", dev)) { + printk("uml_net_open: couldn't reserve IRQ %i\n", dev->irq); + return -1; + } + + strncpy(hostname, system_utsname.nodename, 32); + strncpy(ifname, dev->name, 32); + hostname[31] = 0; + ifname[31] = 0; + sprintf(tapname, "%s.%s", hostname, ifname); + + uml_net_user_open((void *)dev, dev->irq, dev, tapname, &lp->user); + if (lp->user == NULL) { + free_irq(dev->irq, dev); + printk("uml_net_open: failed\n"); + return -1; + } + + lp->tl.data = (unsigned long)lp->user; + netif_start_queue(dev); + MOD_INC_USE_COUNT; + + return 0; +} + +static void uml_net_tx_timeout(struct net_device *dev) +{ + printk("uml_net_tx_timeout enter\n"); + + dev->trans_start = jiffies; + netif_wake_queue(dev); + + printk("uml_net_tx_timeout exit\n"); +} + +static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + unsigned long flags; + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + uml_net_user_tx(lp->user, skb->data, skb->len); + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + netif_wake_queue(dev); + spin_unlock_irqrestore(&lp->lock, flags); + + kfree_skb(skb); + + return 0; +} + +void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + +#if 0 + printk("uml_net_interrupt [%i, %p]\n", irq, dev_id); +#endif + + if (netif_running(dev)) { + spin_lock(&lp->lock); + while (uml_net_rx(dev)) + ; + spin_unlock(&lp->lock); + } +} + +/* Look at the device flags and the frame's destination MAC address to + * determine whether this frame is for us. + */ +static int __packet_for_us(struct net_device *dev, struct sk_buff *skb) +{ + /* Are we in promiscuous mode? */ + if (dev->flags & IFF_PROMISC) + return 1; + + /* Is this frame destined to our MAC address? */ + if (!memcmp(skb->mac.raw, dev->dev_addr, ETH_ALEN)) + return 1; + + /* Is this a broadcast packet? */ + if (!memcmp(skb->mac.raw, broadcast, ETH_ALEN)) + return 1; + + /* Is this a multicast packet? */ + if (skb->mac.raw[0] & 1) { + struct dev_mc_list *dmi; + int i; + + /* Are we accepting all multicast frames? */ + if (dev->flags & IFF_ALLMULTI) + return 1; + + /* Check the table of multicast addresses handed to us by + * the upper layers. */ + dmi = dev->mc_list; + for (i=0;imc_count;i++) { + if (!memcmp(skb->mac.raw, dmi->dmi_addr, ETH_ALEN)) + return 1; + dmi = dmi->next; + } + } + + /* No match. */ + return 0; +} + +static int uml_net_rx(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + int pkt_len; + struct sk_buff *skb; + + /* If we can't allocate memory, try again next round. */ + if ((skb = dev_alloc_skb(MAX_PACKET)) == NULL) { + lp->stats.rx_dropped++; + return 0; + } + + skb->dev = dev; + skb_reserve(skb, 2); + skb_put(skb, MAX_PACKET - 2); + skb->mac.raw = skb->data; + pkt_len = uml_net_user_rx(lp->user, skb->mac.raw, MAX_PACKET - 2); + if (pkt_len >= 0 && __packet_for_us(dev, skb)) { + skb_trim(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + return 1; + } + + kfree_skb(skb); + lp->stats.rx_dropped++; + return 0; +} + +static int uml_net_close(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + netif_stop_queue(dev); + + uml_net_user_close(lp->user); + lp->user = NULL; + dev->flags &= (~IFF_UP); + free_irq(dev->irq, dev); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct net_device_stats *uml_net_get_stats(struct net_device *dev) +{ + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + return &lp->stats; +} + +static void uml_net_set_multicast_list(struct net_device *dev) +{ + if (dev->flags & IFF_PROMISC) { + printk("%s: Promiscuous mode enabled.\n", dev->name); + } +} + +void uml_net_kern_start_timer(void *kern, int seconds) +{ + struct net_device *dev = kern; + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + mod_timer(&lp->tl, jiffies + (seconds*HZ)); +} + +void uml_net_kern_stop_timer(void *kern) +{ + struct net_device *dev = kern; + struct uml_net_private *lp = (struct uml_net_private *)dev->priv; + + del_timer(&lp->tl); +} + +void uml_net_kern_set_carrier(void *kern, int carrier) +{ + struct net_device *dev = kern; + + if (carrier) { + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + } else { + netif_carrier_off(dev); + } +} diff -urN linux-2.4.2-uml/arch/um/drivers/eth_user_fast.c linux-2.4.2-uml-net/arch/um/drivers/eth_user_fast.c --- linux-2.4.2-uml/arch/um/drivers/eth_user_fast.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.2-uml-net/arch/um/drivers/eth_user_fast.c Sat Mar 3 22:20:03 2001 @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kern_util.h" + +extern int printk(char *format, ...); +#if 0 +#define dprintk printk +#else +#define dprintk +#endif + +#include "user_util.h" + +extern void uml_net_kern_start_timer(void *, int); +extern void uml_net_kern_stop_timer (void *); +extern void uml_net_kern_set_carrier(void *, int); + +struct connection +{ + void *kern; + void *dev; + int irq; + int tap_fd; + char tap_name[32]; +}; + + +void uml_net_user_set_mac(unsigned char *addr) +{ + int fd; + + addr[0] = 0xfe; + addr[1] = 0xfd; + if ((fd = open("/dev/urandom", O_RDONLY)) > 0) { + read(fd, addr+2, 4); + close(fd); + } else { + addr[2] = 0x11; + addr[3] = 0x22; + addr[4] = 0x33; + addr[5] = 0x44; + } +} + +static void start_reconnect_timer(struct connection *conn) +{ + dprintk("start_reconnect_timer [%p]\n", conn); + + conn->tap_fd = 0; + uml_net_kern_start_timer(conn->kern, 1); +} + +static void do_connect(struct connection *conn) +{ + int fd; + struct ifreq ifr; + + dprintk("do_connect [%p]\n", conn); + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + dprintk("do_connect 1 [%p]\n", conn); + goto fail; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, conn->tap_name, sizeof(ifr.ifr_name)); + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + dprintk("do_connect 2 [%p]\n", conn); + close(fd); + goto fail; + } + + conn->tap_fd = fd; + activate_fd(conn->irq, conn->tap_fd, conn->dev); + return; + +fail: + start_reconnect_timer(conn); +} + +void uml_net_user_open(void *dev, int irq, void *kern, char *tap_name, void **user) +{ + struct connection *conn; + + dprintk("uml_net_user_open [%s]\n", tap_name); + uml_net_kern_set_carrier(kern, 0); + + if ((conn = malloc(sizeof(struct connection))) == NULL) { + *user = NULL; + return; + } + + conn->kern = kern; + conn->dev = dev; + conn->irq = irq; + conn->tap_fd = 0; + strncpy(conn->tap_name, tap_name, 32); + conn->tap_name[31] = 0; + *user = conn; + + do_connect(conn); +} + +void uml_net_user_timer_expire(unsigned long _conn) +{ + struct connection *conn = (struct connection *)_conn; + + dprintk("uml_net_user_timer_expire [%p]\n", conn); + do_connect(conn); +} + +int uml_net_user_tx(void *user, char *buffer, int len) +{ + struct connection *conn = user; + int retval; + + dprintk("uml_net_user_tx [%p]\n", conn); + + if (!conn->tap_fd) + return len; + + do { + retval = write(conn->tap_fd, buffer, len); + } while (retval < 0 && errno == EINTR); + + if (retval < 0) { + dprintk("uml_net_user_tx: error %i\n", errno); + close(conn->tap_fd); + start_reconnect_timer(conn); + } + + return retval; +} + +int uml_net_user_rx(void *user, char *buffer, int len) +{ + struct connection *conn = user; + int retval; + + dprintk("uml_net_user_rx [%p]\n", conn); + + do { + retval = read(conn->tap_fd, buffer, len); + } while (retval < 0 && errno == EINTR); + + if (retval > 0) { + reactivate_fd(conn->tap_fd); + return retval; + } + + reactivate_fd(conn->tap_fd); + return -1; +} + +int __inline__ uml_net_user_close(void *user) +{ + struct connection *conn = user; + + dprintk("uml_net_user_close [%p]\n", conn); + uml_net_kern_stop_timer(conn->kern); + uml_net_kern_set_carrier(conn->kern, 0); + free_irq_fd(conn->dev); + if (conn->tap_fd) + close(conn->tap_fd); + free(conn); + + return 0; +}