[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [linux patches] Update patches for linux-2.6.16.29.
# HG changeset patch # User Christian Limpach <Christian.Limpach@xxxxxxxxxxxxx> # Node ID 041be3f6b38e05f904d240630c18cadb1259317b # Parent c5d4d47bbeb8a489a2160174c08a04aaebef3d6f [linux patches] Update patches for linux-2.6.16.29. Signed-off-by: Christian Limpach <Christian.Limpach@xxxxxxxxxxxxx> --- patches/linux-2.6.16.13/blktap-aio-16_03_06.patch | 274 - patches/linux-2.6.16.13/device_bind.patch | 14 patches/linux-2.6.16.13/fix-hz-suspend.patch | 25 patches/linux-2.6.16.13/fix-ide-cd-pio-mode.patch | 13 patches/linux-2.6.16.13/i386-mach-io-check-nmi.patch | 35 patches/linux-2.6.16.13/ipv6-no-autoconf.patch | 22 patches/linux-2.6.16.13/net-csum.patch | 57 patches/linux-2.6.16.13/net-gso-0-base.patch | 2510 ---------- patches/linux-2.6.16.13/net-gso-1-check-dodgy.patch | 22 patches/linux-2.6.16.13/net-gso-2-checksum-fix.patch | 400 - patches/linux-2.6.16.13/net-gso-3-fix-errorcheck.patch | 13 patches/linux-2.6.16.13/net-gso-4-kill-warnon.patch | 18 patches/linux-2.6.16.13/pmd-shared.patch | 100 patches/linux-2.6.16.13/rcu_needs_cpu.patch | 31 patches/linux-2.6.16.13/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch | 26 patches/linux-2.6.16.13/series | 23 patches/linux-2.6.16.13/smp-alts.patch | 540 -- patches/linux-2.6.16.13/tpm_plugin_2.6.17.patch | 1381 ----- patches/linux-2.6.16.13/x86-elfnote-as-preprocessor-macro.patch | 28 patches/linux-2.6.16.13/x86-increase-interrupt-vector-range.patch | 73 patches/linux-2.6.16.13/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch | 168 patches/linux-2.6.16.13/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch | 71 patches/linux-2.6.16.13/xen-hotplug.patch | 9 patches/linux-2.6.16.13/xenoprof-generic.patch | 568 -- patches/linux-2.6.16.29/blktap-aio-16_03_06.patch | 161 patches/linux-2.6.16.29/device_bind.patch | 9 patches/linux-2.6.16.29/fix-hz-suspend.patch | 9 patches/linux-2.6.16.29/fix-ide-cd-pio-mode.patch | 13 patches/linux-2.6.16.29/i386-mach-io-check-nmi.patch | 30 patches/linux-2.6.16.29/ipv6-no-autoconf.patch | 16 patches/linux-2.6.16.29/net-csum.patch | 40 patches/linux-2.6.16.29/net-gso-0-base.patch | 1906 +++++++ patches/linux-2.6.16.29/net-gso-1-check-dodgy.patch | 16 patches/linux-2.6.16.29/net-gso-2-checksum-fix.patch | 311 + patches/linux-2.6.16.29/net-gso-3-fix-errorcheck.patch | 13 patches/linux-2.6.16.29/net-gso-4-kill-warnon.patch | 26 patches/linux-2.6.16.29/pmd-shared.patch | 57 patches/linux-2.6.16.29/rcu_needs_cpu.patch | 18 patches/linux-2.6.16.29/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch | 26 patches/linux-2.6.16.29/series | 23 patches/linux-2.6.16.29/smp-alts.patch | 330 + patches/linux-2.6.16.29/tpm_plugin_2.6.17.patch | 703 ++ patches/linux-2.6.16.29/x86-elfnote-as-preprocessor-macro.patch | 25 patches/linux-2.6.16.29/x86-increase-interrupt-vector-range.patch | 73 patches/linux-2.6.16.29/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch | 39 patches/linux-2.6.16.29/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch | 63 patches/linux-2.6.16.29/xen-hotplug.patch | 10 patches/linux-2.6.16.29/xenoprof-generic.patch | 268 + 48 files changed, 4185 insertions(+), 6421 deletions(-) diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/blktap-aio-16_03_06.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/blktap-aio-16_03_06.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,294 @@ +diff -pruN ../orig-linux-2.6.16.29/fs/aio.c ./fs/aio.c +--- ../orig-linux-2.6.16.29/fs/aio.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./fs/aio.c 2006-09-19 13:58:49.000000000 +0100 +@@ -34,6 +34,11 @@ + #include <asm/uaccess.h> + #include <asm/mmu_context.h> + ++#ifdef CONFIG_EPOLL ++#include <linux/poll.h> ++#include <linux/eventpoll.h> ++#endif ++ + #if DEBUG > 1 + #define dprintk printk + #else +@@ -1016,6 +1021,10 @@ put_rq: + if (waitqueue_active(&ctx->wait)) + wake_up(&ctx->wait); + ++#ifdef CONFIG_EPOLL ++ if (ctx->file && waitqueue_active(&ctx->poll_wait)) ++ wake_up(&ctx->poll_wait); ++#endif + if (ret) + put_ioctx(ctx); + +@@ -1025,6 +1034,8 @@ put_rq: + /* aio_read_evt + * Pull an event off of the ioctx's event ring. Returns the number of + * events fetched (0 or 1 ;-) ++ * If ent parameter is 0, just returns the number of events that would ++ * be fetched. + * FIXME: make this use cmpxchg. + * TODO: make the ringbuffer user mmap()able (requires FIXME). + */ +@@ -1047,13 +1058,18 @@ static int aio_read_evt(struct kioctx *i + + head = ring->head % info->nr; + if (head != ring->tail) { +- struct io_event *evp = aio_ring_event(info, head, KM_USER1); +- *ent = *evp; +- head = (head + 1) % info->nr; +- smp_mb(); /* finish reading the event before updatng the head */ +- ring->head = head; +- ret = 1; +- put_aio_ring_event(evp, KM_USER1); ++ if (ent) { /* event requested */ ++ struct io_event *evp = ++ aio_ring_event(info, head, KM_USER1); ++ *ent = *evp; ++ head = (head + 1) % info->nr; ++ /* finish reading the event before updatng the head */ ++ smp_mb(); ++ ring->head = head; ++ ret = 1; ++ put_aio_ring_event(evp, KM_USER1); ++ } else /* only need to know availability */ ++ ret = 1; + } + spin_unlock(&info->ring_lock); + +@@ -1236,9 +1252,78 @@ static void io_destroy(struct kioctx *io + + aio_cancel_all(ioctx); + wait_for_all_aios(ioctx); ++#ifdef CONFIG_EPOLL ++ /* forget the poll file, but it's up to the user to close it */ ++ if (ioctx->file) { ++ ioctx->file->private_data = 0; ++ ioctx->file = 0; ++ } ++#endif + put_ioctx(ioctx); /* once for the lookup */ + } + ++#ifdef CONFIG_EPOLL ++ ++static int aio_queue_fd_close(struct inode *inode, struct file *file) ++{ ++ struct kioctx *ioctx = file->private_data; ++ if (ioctx) { ++ file->private_data = 0; ++ spin_lock_irq(&ioctx->ctx_lock); ++ ioctx->file = 0; ++ spin_unlock_irq(&ioctx->ctx_lock); ++ } ++ return 0; ++} ++ ++static unsigned int aio_queue_fd_poll(struct file *file, poll_table *wait) ++{ unsigned int pollflags = 0; ++ struct kioctx *ioctx = file->private_data; ++ ++ if (ioctx) { ++ ++ spin_lock_irq(&ioctx->ctx_lock); ++ /* Insert inside our poll wait queue */ ++ poll_wait(file, &ioctx->poll_wait, wait); ++ ++ /* Check our condition */ ++ if (aio_read_evt(ioctx, 0)) ++ pollflags = POLLIN | POLLRDNORM; ++ spin_unlock_irq(&ioctx->ctx_lock); ++ } ++ ++ return pollflags; ++} ++ ++static struct file_operations aioq_fops = { ++ .release = aio_queue_fd_close, ++ .poll = aio_queue_fd_poll ++}; ++ ++/* make_aio_fd: ++ * Create a file descriptor that can be used to poll the event queue. ++ * Based and piggybacked on the excellent epoll code. ++ */ ++ ++static int make_aio_fd(struct kioctx *ioctx) ++{ ++ int error, fd; ++ struct inode *inode; ++ struct file *file; ++ ++ error = ep_getfd(&fd, &inode, &file, NULL, &aioq_fops); ++ if (error) ++ return error; ++ ++ /* associate the file with the IO context */ ++ file->private_data = ioctx; ++ ioctx->file = file; ++ init_waitqueue_head(&ioctx->poll_wait); ++ return fd; ++} ++#endif ++ ++ + /* sys_io_setup: + * Create an aio_context capable of receiving at least nr_events. + * ctxp must not point to an aio_context that already exists, and +@@ -1251,18 +1336,30 @@ static void io_destroy(struct kioctx *io + * resources are available. May fail with -EFAULT if an invalid + * pointer is passed for ctxp. Will fail with -ENOSYS if not + * implemented. ++ * ++ * To request a selectable fd, the user context has to be initialized ++ * to 1, instead of 0, and the return value is the fd. ++ * This keeps the system call compatible, since a non-zero value ++ * was not allowed so far. + */ + asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp) + { + struct kioctx *ioctx = NULL; + unsigned long ctx; + long ret; ++ int make_fd = 0; + + ret = get_user(ctx, ctxp); + if (unlikely(ret)) + goto out; + + ret = -EINVAL; ++#ifdef CONFIG_EPOLL ++ if (ctx == 1) { ++ make_fd = 1; ++ ctx = 0; ++ } ++#endif + if (unlikely(ctx || nr_events == 0)) { + pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n", + ctx, nr_events); +@@ -1273,8 +1370,12 @@ asmlinkage long sys_io_setup(unsigned nr + ret = PTR_ERR(ioctx); + if (!IS_ERR(ioctx)) { + ret = put_user(ioctx->user_id, ctxp); +- if (!ret) +- return 0; ++#ifdef CONFIG_EPOLL ++ if (make_fd && ret >= 0) ++ ret = make_aio_fd(ioctx); ++#endif ++ if (ret >= 0) ++ return ret; + + get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */ + io_destroy(ioctx); +diff -pruN ../orig-linux-2.6.16.29/fs/eventpoll.c ./fs/eventpoll.c +--- ../orig-linux-2.6.16.29/fs/eventpoll.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./fs/eventpoll.c 2006-09-19 13:58:49.000000000 +0100 +@@ -235,8 +235,6 @@ struct ep_pqueue { + + static void ep_poll_safewake_init(struct poll_safewake *psw); + static void ep_poll_safewake(struct poll_safewake *psw, wait_queue_head_t *wq); +-static int ep_getfd(int *efd, struct inode **einode, struct file **efile, +- struct eventpoll *ep); + static int ep_alloc(struct eventpoll **pep); + static void ep_free(struct eventpoll *ep); + static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd); +@@ -266,7 +264,7 @@ static int ep_events_transfer(struct eve + static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, + int maxevents, long timeout); + static int eventpollfs_delete_dentry(struct dentry *dentry); +-static struct inode *ep_eventpoll_inode(void); ++static struct inode *ep_eventpoll_inode(struct file_operations *fops); + static struct super_block *eventpollfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data); +@@ -525,7 +523,7 @@ asmlinkage long sys_epoll_create(int siz + * Creates all the items needed to setup an eventpoll file. That is, + * a file structure, and inode and a free file descriptor. + */ +- error = ep_getfd(&fd, &inode, &file, ep); ++ error = ep_getfd(&fd, &inode, &file, ep, &eventpoll_fops); + if (error) + goto eexit_2; + +@@ -710,8 +708,8 @@ eexit_1: + /* + * Creates the file descriptor to be used by the epoll interface. + */ +-static int ep_getfd(int *efd, struct inode **einode, struct file **efile, +- struct eventpoll *ep) ++int ep_getfd(int *efd, struct inode **einode, struct file **efile, ++ struct eventpoll *ep, struct file_operations *fops) + { + struct qstr this; + char name[32]; +@@ -727,7 +725,7 @@ static int ep_getfd(int *efd, struct ino + goto eexit_1; + + /* Allocates an inode from the eventpoll file system */ +- inode = ep_eventpoll_inode(); ++ inode = ep_eventpoll_inode(fops); + error = PTR_ERR(inode); + if (IS_ERR(inode)) + goto eexit_2; +@@ -758,7 +756,7 @@ static int ep_getfd(int *efd, struct ino + + file->f_pos = 0; + file->f_flags = O_RDONLY; +- file->f_op = &eventpoll_fops; ++ file->f_op = fops; + file->f_mode = FMODE_READ; + file->f_version = 0; + file->private_data = ep; +@@ -1574,7 +1572,7 @@ static int eventpollfs_delete_dentry(str + } + + +-static struct inode *ep_eventpoll_inode(void) ++static struct inode *ep_eventpoll_inode(struct file_operations *fops) + { + int error = -ENOMEM; + struct inode *inode = new_inode(eventpoll_mnt->mnt_sb); +@@ -1582,7 +1580,7 @@ static struct inode *ep_eventpoll_inode( + if (!inode) + goto eexit_1; + +- inode->i_fop = &eventpoll_fops; ++ inode->i_fop = fops; + + /* + * Mark the inode dirty from the very beginning, +diff -pruN ../orig-linux-2.6.16.29/include/linux/aio.h ./include/linux/aio.h +--- ../orig-linux-2.6.16.29/include/linux/aio.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/aio.h 2006-09-19 13:58:49.000000000 +0100 +@@ -191,6 +191,11 @@ struct kioctx { + struct aio_ring_info ring_info; + + struct work_struct wq; ++#ifdef CONFIG_EPOLL ++ // poll integration ++ wait_queue_head_t poll_wait; ++ struct file *file; ++#endif + }; + + /* prototypes */ +diff -pruN ../orig-linux-2.6.16.29/include/linux/eventpoll.h ./include/linux/eventpoll.h +--- ../orig-linux-2.6.16.29/include/linux/eventpoll.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/eventpoll.h 2006-09-19 13:58:49.000000000 +0100 +@@ -86,6 +86,12 @@ static inline void eventpoll_release(str + } + + ++/* ++ * called by aio code to create fd that can poll the aio event queueQ ++ */ ++struct eventpoll; ++int ep_getfd(int *efd, struct inode **einode, struct file **efile, ++ struct eventpoll *ep, struct file_operations *fops); + #else + + static inline void eventpoll_init_file(struct file *file) {} diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/device_bind.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/device_bind.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,15 @@ +diff -pruN ../orig-linux-2.6.16.29/drivers/base/bus.c ./drivers/base/bus.c +--- ../orig-linux-2.6.16.29/drivers/base/bus.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/base/bus.c 2006-09-19 13:58:54.000000000 +0100 +@@ -188,6 +188,11 @@ static ssize_t driver_bind(struct device + up(&dev->sem); + if (dev->parent) + up(&dev->parent->sem); ++ ++ if (err > 0) /* success */ ++ err = count; ++ else if (err == 0) /* driver didn't accept device */ ++ err = -ENODEV; + } + put_device(dev); + put_bus(bus); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/fix-hz-suspend.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/fix-hz-suspend.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,26 @@ +diff -pruN ../orig-linux-2.6.16.29/kernel/timer.c ./kernel/timer.c +--- ../orig-linux-2.6.16.29/kernel/timer.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./kernel/timer.c 2006-09-19 13:58:58.000000000 +0100 +@@ -555,6 +555,22 @@ found: + } + spin_unlock(&base->t_base.lock); + ++ /* ++ * It can happen that other CPUs service timer IRQs and increment ++ * jiffies, but we have not yet got a local timer tick to process ++ * the timer wheels. In that case, the expiry time can be before ++ * jiffies, but since the high-resolution timer here is relative to ++ * jiffies, the default expression when high-resolution timers are ++ * not active, ++ * ++ * time_before(MAX_JIFFY_OFFSET + jiffies, expires) ++ * ++ * would falsely evaluate to true. If that is the case, just ++ * return jiffies so that we can immediately fire the local timer ++ */ ++ if (time_before(expires, jiffies)) ++ return jiffies; ++ + if (time_before(hr_expires, expires)) + return hr_expires; + diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/fix-ide-cd-pio-mode.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/fix-ide-cd-pio-mode.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,18 @@ +diff -pruN ../orig-linux-2.6.16.29/drivers/ide/ide-lib.c ./drivers/ide/ide-lib.c +--- ../orig-linux-2.6.16.29/drivers/ide/ide-lib.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/ide/ide-lib.c 2006-09-19 13:59:03.000000000 +0100 +@@ -410,10 +410,10 @@ void ide_toggle_bounce(ide_drive_t *driv + { + u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */ + +- if (!PCI_DMA_BUS_IS_PHYS) { +- addr = BLK_BOUNCE_ANY; +- } else if (on && drive->media == ide_disk) { +- if (HWIF(drive)->pci_dev) ++ if (on && drive->media == ide_disk) { ++ if (!PCI_DMA_BUS_IS_PHYS) ++ addr = BLK_BOUNCE_ANY; ++ else if (HWIF(drive)->pci_dev) + addr = HWIF(drive)->pci_dev->dma_mask; + } + diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/i386-mach-io-check-nmi.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/i386-mach-io-check-nmi.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,45 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/traps.c ./arch/i386/kernel/traps.c +--- ../orig-linux-2.6.16.29/arch/i386/kernel/traps.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/traps.c 2006-09-19 13:59:06.000000000 +0100 +@@ -567,18 +567,11 @@ static void mem_parity_error(unsigned ch + + static void io_check_error(unsigned char reason, struct pt_regs * regs) + { +- unsigned long i; +- + printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n"); + show_registers(regs); + + /* Re-enable the IOCK line, wait for a few seconds */ +- reason = (reason & 0xf) | 8; +- outb(reason, 0x61); +- i = 2000; +- while (--i) udelay(1000); +- reason &= ~8; +- outb(reason, 0x61); ++ clear_io_check_error(reason); + } + + static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/mach-default/mach_traps.h ./include/asm-i386/mach-default/mach_traps.h +--- ../orig-linux-2.6.16.29/include/asm-i386/mach-default/mach_traps.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/mach-default/mach_traps.h 2006-09-19 13:59:06.000000000 +0100 +@@ -15,6 +15,18 @@ static inline void clear_mem_error(unsig + outb(reason, 0x61); + } + ++static inline void clear_io_check_error(unsigned char reason) ++{ ++ unsigned long i; ++ ++ reason = (reason & 0xf) | 8; ++ outb(reason, 0x61); ++ i = 2000; ++ while (--i) udelay(1000); ++ reason &= ~8; ++ outb(reason, 0x61); ++} ++ + static inline unsigned char get_nmi_reason(void) + { + return inb(0x61); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/ipv6-no-autoconf.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/ipv6-no-autoconf.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,19 @@ +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/addrconf.c ./net/ipv6/addrconf.c +--- ../orig-linux-2.6.16.29/net/ipv6/addrconf.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv6/addrconf.c 2006-09-19 13:59:11.000000000 +0100 +@@ -2471,6 +2471,7 @@ static void addrconf_dad_start(struct in + spin_lock_bh(&ifp->lock); + + if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || ++ !(dev->flags&IFF_MULTICAST) || + !(ifp->flags&IFA_F_TENTATIVE)) { + ifp->flags &= ~IFA_F_TENTATIVE; + spin_unlock_bh(&ifp->lock); +@@ -2555,6 +2556,7 @@ static void addrconf_dad_completed(struc + if (ifp->idev->cnf.forwarding == 0 && + ifp->idev->cnf.rtr_solicits > 0 && + (dev->flags&IFF_LOOPBACK) == 0 && ++ (dev->flags & IFF_MULTICAST) && + (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { + struct in6_addr all_routers; + diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-csum.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-csum.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,63 @@ +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/netfilter/ip_nat_proto_tcp.c ./net/ipv4/netfilter/ip_nat_proto_tcp.c +--- ../orig-linux-2.6.16.29/net/ipv4/netfilter/ip_nat_proto_tcp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/netfilter/ip_nat_proto_tcp.c 2006-09-19 13:59:15.000000000 +0100 +@@ -129,7 +129,12 @@ tcp_manip_pkt(struct sk_buff **pskb, + if (hdrsize < sizeof(*hdr)) + return 1; + +- hdr->check = ip_nat_cheat_check(~oldip, newip, ++#ifdef CONFIG_XEN ++ if ((*pskb)->proto_csum_blank) ++ hdr->check = ip_nat_cheat_check(oldip, ~newip, hdr->check); ++ else ++#endif ++ hdr->check = ip_nat_cheat_check(~oldip, newip, + ip_nat_cheat_check(oldport ^ 0xFFFF, + newport, + hdr->check)); +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/netfilter/ip_nat_proto_udp.c ./net/ipv4/netfilter/ip_nat_proto_udp.c +--- ../orig-linux-2.6.16.29/net/ipv4/netfilter/ip_nat_proto_udp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/netfilter/ip_nat_proto_udp.c 2006-09-19 13:59:15.000000000 +0100 +@@ -113,11 +113,17 @@ udp_manip_pkt(struct sk_buff **pskb, + newport = tuple->dst.u.udp.port; + portptr = &hdr->dest; + } +- if (hdr->check) /* 0 is a special case meaning no checksum */ +- hdr->check = ip_nat_cheat_check(~oldip, newip, ++ if (hdr->check) { /* 0 is a special case meaning no checksum */ ++#ifdef CONFIG_XEN ++ if ((*pskb)->proto_csum_blank) ++ hdr->check = ip_nat_cheat_check(oldip, ~newip, hdr->check); ++ else ++#endif ++ hdr->check = ip_nat_cheat_check(~oldip, newip, + ip_nat_cheat_check(*portptr ^ 0xFFFF, + newport, + hdr->check)); ++ } + *portptr = newport; + return 1; + } +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c ./net/ipv4/xfrm4_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/xfrm4_output.c 2006-09-19 13:59:15.000000000 +0100 +@@ -17,6 +17,8 @@ + #include <net/xfrm.h> + #include <net/icmp.h> + ++extern int skb_checksum_setup(struct sk_buff *skb); ++ + /* Add encapsulation header. + * + * In transport mode, the IP header will be moved forward to make space +@@ -103,6 +105,10 @@ static int xfrm4_output_one(struct sk_bu + struct xfrm_state *x = dst->xfrm; + int err; + ++ err = skb_checksum_setup(skb); ++ if (err) ++ goto error_nolock; ++ + if (skb->ip_summed == CHECKSUM_HW) { + err = skb_checksum_help(skb, 0); + if (err) diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-gso-0-base.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-gso-0-base.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,2835 @@ +diff -pruN ../orig-linux-2.6.16.29/Documentation/networking/netdevices.txt ./Documentation/networking/netdevices.txt +--- ../orig-linux-2.6.16.29/Documentation/networking/netdevices.txt 2006-09-12 19:02:10.000000000 +0100 ++++ ./Documentation/networking/netdevices.txt 2006-09-19 13:59:20.000000000 +0100 +@@ -42,9 +42,9 @@ dev->get_stats: + Context: nominally process, but don't sleep inside an rwlock + + dev->hard_start_xmit: +- Synchronization: dev->xmit_lock spinlock. ++ Synchronization: netif_tx_lock spinlock. + When the driver sets NETIF_F_LLTX in dev->features this will be +- called without holding xmit_lock. In this case the driver ++ called without holding netif_tx_lock. In this case the driver + has to lock by itself when needed. It is recommended to use a try lock + for this and return -1 when the spin lock fails. + The locking there should also properly protect against +@@ -62,12 +62,12 @@ dev->hard_start_xmit: + Only valid when NETIF_F_LLTX is set. + + dev->tx_timeout: +- Synchronization: dev->xmit_lock spinlock. ++ Synchronization: netif_tx_lock spinlock. + Context: BHs disabled + Notes: netif_queue_stopped() is guaranteed true + + dev->set_multicast_list: +- Synchronization: dev->xmit_lock spinlock. ++ Synchronization: netif_tx_lock spinlock. + Context: BHs disabled + + dev->poll: +diff -pruN ../orig-linux-2.6.16.29/drivers/block/aoe/aoenet.c ./drivers/block/aoe/aoenet.c +--- ../orig-linux-2.6.16.29/drivers/block/aoe/aoenet.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/block/aoe/aoenet.c 2006-09-19 13:59:20.000000000 +0100 +@@ -95,9 +95,8 @@ mac_addr(char addr[6]) + static struct sk_buff * + skb_check(struct sk_buff *skb) + { +- if (skb_is_nonlinear(skb)) + if ((skb = skb_share_check(skb, GFP_ATOMIC))) +- if (skb_linearize(skb, GFP_ATOMIC) < 0) { ++ if (skb_linearize(skb)) { + dev_kfree_skb(skb); + return NULL; + } +diff -pruN ../orig-linux-2.6.16.29/drivers/infiniband/ulp/ipoib/ipoib_multicast.c ./drivers/infiniband/ulp/ipoib/ipoib_multicast.c +--- ../orig-linux-2.6.16.29/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2006-09-19 13:59:20.000000000 +0100 +@@ -821,7 +821,8 @@ void ipoib_mcast_restart_task(void *dev_ + + ipoib_mcast_stop_thread(dev, 0); + +- spin_lock_irqsave(&dev->xmit_lock, flags); ++ local_irq_save(flags); ++ netif_tx_lock(dev); + spin_lock(&priv->lock); + + /* +@@ -896,7 +897,8 @@ void ipoib_mcast_restart_task(void *dev_ + } + + spin_unlock(&priv->lock); +- spin_unlock_irqrestore(&dev->xmit_lock, flags); ++ netif_tx_unlock(dev); ++ local_irq_restore(flags); + + /* We have to cancel outside of the spinlock */ + list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { +diff -pruN ../orig-linux-2.6.16.29/drivers/media/dvb/dvb-core/dvb_net.c ./drivers/media/dvb/dvb-core/dvb_net.c +--- ../orig-linux-2.6.16.29/drivers/media/dvb/dvb-core/dvb_net.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/media/dvb/dvb-core/dvb_net.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1052,7 +1052,7 @@ static void wq_set_multicast_list (void + + dvb_net_feed_stop(dev); + priv->rx_mode = RX_MODE_UNI; +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + + if (dev->flags & IFF_PROMISC) { + dprintk("%s: promiscuous mode\n", dev->name); +@@ -1077,7 +1077,7 @@ static void wq_set_multicast_list (void + } + } + +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + dvb_net_feed_start(dev); + } + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/8139cp.c ./drivers/net/8139cp.c +--- ../orig-linux-2.6.16.29/drivers/net/8139cp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/8139cp.c 2006-09-19 13:59:20.000000000 +0100 +@@ -794,7 +794,7 @@ static int cp_start_xmit (struct sk_buff + entry = cp->tx_head; + eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + if (dev->features & NETIF_F_TSO) +- mss = skb_shinfo(skb)->tso_size; ++ mss = skb_shinfo(skb)->gso_size; + + if (skb_shinfo(skb)->nr_frags == 0) { + struct cp_desc *txd = &cp->tx_ring[entry]; +diff -pruN ../orig-linux-2.6.16.29/drivers/net/bnx2.c ./drivers/net/bnx2.c +--- ../orig-linux-2.6.16.29/drivers/net/bnx2.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/bnx2.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1593,7 +1593,7 @@ bnx2_tx_int(struct bnx2 *bp) + skb = tx_buf->skb; + #ifdef BCM_TSO + /* partial BD completions possible with TSO packets */ +- if (skb_shinfo(skb)->tso_size) { ++ if (skb_shinfo(skb)->gso_size) { + u16 last_idx, last_ring_idx; + + last_idx = sw_cons + +@@ -1948,7 +1948,7 @@ bnx2_poll(struct net_device *dev, int *b + return 1; + } + +-/* Called with rtnl_lock from vlan functions and also dev->xmit_lock ++/* Called with rtnl_lock from vlan functions and also netif_tx_lock + * from set_multicast. + */ + static void +@@ -4403,7 +4403,7 @@ bnx2_vlan_rx_kill_vid(struct net_device + } + #endif + +-/* Called with dev->xmit_lock. ++/* Called with netif_tx_lock. + * hard_start_xmit is pseudo-lockless - a lock is only required when + * the tx queue is full. This way, we get the benefit of lockless + * operations most of the time without the complexities to handle +@@ -4441,7 +4441,7 @@ bnx2_start_xmit(struct sk_buff *skb, str + (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); + } + #ifdef BCM_TSO +- if ((mss = skb_shinfo(skb)->tso_size) && ++ if ((mss = skb_shinfo(skb)->gso_size) && + (skb->len > (bp->dev->mtu + ETH_HLEN))) { + u32 tcp_opt_len, ip_tcp_len; + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/bonding/bond_main.c ./drivers/net/bonding/bond_main.c +--- ../orig-linux-2.6.16.29/drivers/net/bonding/bond_main.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/bonding/bond_main.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1145,8 +1145,7 @@ int bond_sethwaddr(struct net_device *bo + } + + #define BOND_INTERSECT_FEATURES \ +- (NETIF_F_SG|NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM|\ +- NETIF_F_TSO|NETIF_F_UFO) ++ (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_TSO | NETIF_F_UFO) + + /* + * Compute the common dev->feature set available to all slaves. Some +@@ -1164,9 +1163,7 @@ static int bond_compute_features(struct + features &= (slave->dev->features & BOND_INTERSECT_FEATURES); + + if ((features & NETIF_F_SG) && +- !(features & (NETIF_F_IP_CSUM | +- NETIF_F_NO_CSUM | +- NETIF_F_HW_CSUM))) ++ !(features & NETIF_F_ALL_CSUM)) + features &= ~NETIF_F_SG; + + /* +@@ -4147,7 +4144,7 @@ static int bond_init(struct net_device * + */ + bond_dev->features |= NETIF_F_VLAN_CHALLENGED; + +- /* don't acquire bond device's xmit_lock when ++ /* don't acquire bond device's netif_tx_lock when + * transmitting */ + bond_dev->features |= NETIF_F_LLTX; + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/chelsio/sge.c ./drivers/net/chelsio/sge.c +--- ../orig-linux-2.6.16.29/drivers/net/chelsio/sge.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/chelsio/sge.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1419,7 +1419,7 @@ int t1_start_xmit(struct sk_buff *skb, s + struct cpl_tx_pkt *cpl; + + #ifdef NETIF_F_TSO +- if (skb_shinfo(skb)->tso_size) { ++ if (skb_shinfo(skb)->gso_size) { + int eth_type; + struct cpl_tx_pkt_lso *hdr; + +@@ -1434,7 +1434,7 @@ int t1_start_xmit(struct sk_buff *skb, s + hdr->ip_hdr_words = skb->nh.iph->ihl; + hdr->tcp_hdr_words = skb->h.th->doff; + hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type, +- skb_shinfo(skb)->tso_size)); ++ skb_shinfo(skb)->gso_size)); + hdr->len = htonl(skb->len - sizeof(*hdr)); + cpl = (struct cpl_tx_pkt *)hdr; + sge->stats.tx_lso_pkts++; +diff -pruN ../orig-linux-2.6.16.29/drivers/net/e1000/e1000_main.c ./drivers/net/e1000/e1000_main.c +--- ../orig-linux-2.6.16.29/drivers/net/e1000/e1000_main.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/e1000/e1000_main.c 2006-09-19 13:59:20.000000000 +0100 +@@ -2526,7 +2526,7 @@ e1000_tso(struct e1000_adapter *adapter, + uint8_t ipcss, ipcso, tucss, tucso, hdr_len; + int err; + +- if (skb_shinfo(skb)->tso_size) { ++ if (skb_shinfo(skb)->gso_size) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) +@@ -2534,7 +2534,7 @@ e1000_tso(struct e1000_adapter *adapter, + } + + hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); +- mss = skb_shinfo(skb)->tso_size; ++ mss = skb_shinfo(skb)->gso_size; + if (skb->protocol == ntohs(ETH_P_IP)) { + skb->nh.iph->tot_len = 0; + skb->nh.iph->check = 0; +@@ -2651,7 +2651,7 @@ e1000_tx_map(struct e1000_adapter *adapt + * tso gets written back prematurely before the data is fully + * DMAd to the controller */ + if (!skb->data_len && tx_ring->last_tx_tso && +- !skb_shinfo(skb)->tso_size) { ++ !skb_shinfo(skb)->gso_size) { + tx_ring->last_tx_tso = 0; + size -= 4; + } +@@ -2893,7 +2893,7 @@ e1000_xmit_frame(struct sk_buff *skb, st + } + + #ifdef NETIF_F_TSO +- mss = skb_shinfo(skb)->tso_size; ++ mss = skb_shinfo(skb)->gso_size; + /* The controller does a simple calculation to + * make sure there is enough room in the FIFO before + * initiating the DMA for each buffer. The calc is: +@@ -2935,7 +2935,7 @@ e1000_xmit_frame(struct sk_buff *skb, st + #ifdef NETIF_F_TSO + /* Controller Erratum workaround */ + if (!skb->data_len && tx_ring->last_tx_tso && +- !skb_shinfo(skb)->tso_size) ++ !skb_shinfo(skb)->gso_size) + count++; + #endif + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/forcedeth.c ./drivers/net/forcedeth.c +--- ../orig-linux-2.6.16.29/drivers/net/forcedeth.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/forcedeth.c 2006-09-19 13:59:20.000000000 +0100 +@@ -482,9 +482,9 @@ typedef union _ring_type { + * critical parts: + * - rx is (pseudo-) lockless: it relies on the single-threading provided + * by the arch code for interrupts. +- * - tx setup is lockless: it relies on dev->xmit_lock. Actual submission ++ * - tx setup is lockless: it relies on netif_tx_lock. Actual submission + * needs dev->priv->lock :-( +- * - set_multicast_list: preparation lockless, relies on dev->xmit_lock. ++ * - set_multicast_list: preparation lockless, relies on netif_tx_lock. + */ + + /* in dev: base, irq */ +@@ -1016,7 +1016,7 @@ static void drain_ring(struct net_device + + /* + * nv_start_xmit: dev->hard_start_xmit function +- * Called with dev->xmit_lock held. ++ * Called with netif_tx_lock held. + */ + static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) + { +@@ -1105,8 +1105,8 @@ static int nv_start_xmit(struct sk_buff + np->tx_skbuff[nr] = skb; + + #ifdef NETIF_F_TSO +- if (skb_shinfo(skb)->tso_size) +- tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->tso_size << NV_TX2_TSO_SHIFT); ++ if (skb_shinfo(skb)->gso_size) ++ tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); + else + #endif + tx_flags_extra = (skb->ip_summed == CHECKSUM_HW ? (NV_TX2_CHECKSUM_L3|NV_TX2_CHECKSUM_L4) : 0); +@@ -1203,7 +1203,7 @@ static void nv_tx_done(struct net_device + + /* + * nv_tx_timeout: dev->tx_timeout function +- * Called with dev->xmit_lock held. ++ * Called with netif_tx_lock held. + */ + static void nv_tx_timeout(struct net_device *dev) + { +@@ -1524,7 +1524,7 @@ static int nv_change_mtu(struct net_devi + * Changing the MTU is a rare event, it shouldn't matter. + */ + disable_irq(dev->irq); +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + spin_lock(&np->lock); + /* stop engines */ + nv_stop_rx(dev); +@@ -1559,7 +1559,7 @@ static int nv_change_mtu(struct net_devi + nv_start_rx(dev); + nv_start_tx(dev); + spin_unlock(&np->lock); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + enable_irq(dev->irq); + } + return 0; +@@ -1594,7 +1594,7 @@ static int nv_set_mac_address(struct net + memcpy(dev->dev_addr, macaddr->sa_data, ETH_ALEN); + + if (netif_running(dev)) { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + spin_lock_irq(&np->lock); + + /* stop rx engine */ +@@ -1606,7 +1606,7 @@ static int nv_set_mac_address(struct net + /* restart rx engine */ + nv_start_rx(dev); + spin_unlock_irq(&np->lock); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } else { + nv_copy_mac_to_hw(dev); + } +@@ -1615,7 +1615,7 @@ static int nv_set_mac_address(struct net + + /* + * nv_set_multicast: dev->set_multicast function +- * Called with dev->xmit_lock held. ++ * Called with netif_tx_lock held. + */ + static void nv_set_multicast(struct net_device *dev) + { +diff -pruN ../orig-linux-2.6.16.29/drivers/net/hamradio/6pack.c ./drivers/net/hamradio/6pack.c +--- ../orig-linux-2.6.16.29/drivers/net/hamradio/6pack.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/hamradio/6pack.c 2006-09-19 13:59:20.000000000 +0100 +@@ -308,9 +308,9 @@ static int sp_set_mac_address(struct net + { + struct sockaddr_ax25 *sa = addr; + +- spin_lock_irq(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); +- spin_unlock_irq(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + + return 0; + } +@@ -767,9 +767,9 @@ static int sixpack_ioctl(struct tty_stru + break; + } + +- spin_lock_irq(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN); +- spin_unlock_irq(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + + err = 0; + break; +diff -pruN ../orig-linux-2.6.16.29/drivers/net/hamradio/mkiss.c ./drivers/net/hamradio/mkiss.c +--- ../orig-linux-2.6.16.29/drivers/net/hamradio/mkiss.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/hamradio/mkiss.c 2006-09-19 13:59:20.000000000 +0100 +@@ -357,9 +357,9 @@ static int ax_set_mac_address(struct net + { + struct sockaddr_ax25 *sa = addr; + +- spin_lock_irq(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); +- spin_unlock_irq(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + + return 0; + } +@@ -886,9 +886,9 @@ static int mkiss_ioctl(struct tty_struct + break; + } + +- spin_lock_irq(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + memcpy(dev->dev_addr, addr, AX25_ADDR_LEN); +- spin_unlock_irq(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + + err = 0; + break; +diff -pruN ../orig-linux-2.6.16.29/drivers/net/ifb.c ./drivers/net/ifb.c +--- ../orig-linux-2.6.16.29/drivers/net/ifb.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/ifb.c 2006-09-19 13:59:20.000000000 +0100 +@@ -76,13 +76,13 @@ static void ri_tasklet(unsigned long dev + dp->st_task_enter++; + if ((skb = skb_peek(&dp->tq)) == NULL) { + dp->st_txq_refl_try++; +- if (spin_trylock(&_dev->xmit_lock)) { ++ if (netif_tx_trylock(_dev)) { + dp->st_rxq_enter++; + while ((skb = skb_dequeue(&dp->rq)) != NULL) { + skb_queue_tail(&dp->tq, skb); + dp->st_rx2tx_tran++; + } +- spin_unlock(&_dev->xmit_lock); ++ netif_tx_unlock(_dev); + } else { + /* reschedule */ + dp->st_rxq_notenter++; +@@ -110,7 +110,7 @@ static void ri_tasklet(unsigned long dev + } + } + +- if (spin_trylock(&_dev->xmit_lock)) { ++ if (netif_tx_trylock(_dev)) { + dp->st_rxq_check++; + if ((skb = skb_peek(&dp->rq)) == NULL) { + dp->tasklet_pending = 0; +@@ -118,10 +118,10 @@ static void ri_tasklet(unsigned long dev + netif_wake_queue(_dev); + } else { + dp->st_rxq_rsch++; +- spin_unlock(&_dev->xmit_lock); ++ netif_tx_unlock(_dev); + goto resched; + } +- spin_unlock(&_dev->xmit_lock); ++ netif_tx_unlock(_dev); + } else { + resched: + dp->tasklet_pending = 1; +diff -pruN ../orig-linux-2.6.16.29/drivers/net/irda/vlsi_ir.c ./drivers/net/irda/vlsi_ir.c +--- ../orig-linux-2.6.16.29/drivers/net/irda/vlsi_ir.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/irda/vlsi_ir.c 2006-09-19 13:59:20.000000000 +0100 +@@ -959,7 +959,7 @@ static int vlsi_hard_start_xmit(struct s + || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) + break; + udelay(100); +- /* must not sleep here - we are called under xmit_lock! */ ++ /* must not sleep here - called under netif_tx_lock! */ + } + } + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/ixgb/ixgb_main.c ./drivers/net/ixgb/ixgb_main.c +--- ../orig-linux-2.6.16.29/drivers/net/ixgb/ixgb_main.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/ixgb/ixgb_main.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1163,7 +1163,7 @@ ixgb_tso(struct ixgb_adapter *adapter, s + uint16_t ipcse, tucse, mss; + int err; + +- if(likely(skb_shinfo(skb)->tso_size)) { ++ if(likely(skb_shinfo(skb)->gso_size)) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) +@@ -1171,7 +1171,7 @@ ixgb_tso(struct ixgb_adapter *adapter, s + } + + hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); +- mss = skb_shinfo(skb)->tso_size; ++ mss = skb_shinfo(skb)->gso_size; + skb->nh.iph->tot_len = 0; + skb->nh.iph->check = 0; + skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, +diff -pruN ../orig-linux-2.6.16.29/drivers/net/loopback.c ./drivers/net/loopback.c +--- ../orig-linux-2.6.16.29/drivers/net/loopback.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/loopback.c 2006-09-19 13:59:20.000000000 +0100 +@@ -74,7 +74,7 @@ static void emulate_large_send_offload(s + struct iphdr *iph = skb->nh.iph; + struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4)); + unsigned int doffset = (iph->ihl + th->doff) * 4; +- unsigned int mtu = skb_shinfo(skb)->tso_size + doffset; ++ unsigned int mtu = skb_shinfo(skb)->gso_size + doffset; + unsigned int offset = 0; + u32 seq = ntohl(th->seq); + u16 id = ntohs(iph->id); +@@ -139,7 +139,7 @@ static int loopback_xmit(struct sk_buff + #endif + + #ifdef LOOPBACK_TSO +- if (skb_shinfo(skb)->tso_size) { ++ if (skb_shinfo(skb)->gso_size) { + BUG_ON(skb->protocol != htons(ETH_P_IP)); + BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/mv643xx_eth.c ./drivers/net/mv643xx_eth.c +--- ../orig-linux-2.6.16.29/drivers/net/mv643xx_eth.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/mv643xx_eth.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1107,7 +1107,7 @@ static int mv643xx_eth_start_xmit(struct + + #ifdef MV643XX_CHECKSUM_OFFLOAD_TX + if (has_tiny_unaligned_frags(skb)) { +- if ((skb_linearize(skb, GFP_ATOMIC) != 0)) { ++ if (__skb_linearize(skb)) { + stats->tx_dropped++; + printk(KERN_DEBUG "%s: failed to linearize tiny " + "unaligned fragment\n", dev->name); +diff -pruN ../orig-linux-2.6.16.29/drivers/net/natsemi.c ./drivers/net/natsemi.c +--- ../orig-linux-2.6.16.29/drivers/net/natsemi.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/natsemi.c 2006-09-19 13:59:20.000000000 +0100 +@@ -323,12 +323,12 @@ performance critical codepaths: + The rx process only runs in the interrupt handler. Access from outside + the interrupt handler is only permitted after disable_irq(). + +-The rx process usually runs under the dev->xmit_lock. If np->intr_tx_reap ++The rx process usually runs under the netif_tx_lock. If np->intr_tx_reap + is set, then access is permitted under spin_lock_irq(&np->lock). + + Thus configuration functions that want to access everything must call + disable_irq(dev->irq); +- spin_lock_bh(dev->xmit_lock); ++ netif_tx_lock_bh(dev); + spin_lock_irq(&np->lock); + + IV. Notes +diff -pruN ../orig-linux-2.6.16.29/drivers/net/r8169.c ./drivers/net/r8169.c +--- ../orig-linux-2.6.16.29/drivers/net/r8169.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/r8169.c 2006-09-19 13:59:20.000000000 +0100 +@@ -2171,7 +2171,7 @@ static int rtl8169_xmit_frags(struct rtl + static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev) + { + if (dev->features & NETIF_F_TSO) { +- u32 mss = skb_shinfo(skb)->tso_size; ++ u32 mss = skb_shinfo(skb)->gso_size; + + if (mss) + return LargeSend | ((mss & MSSMask) << MSSShift); +diff -pruN ../orig-linux-2.6.16.29/drivers/net/s2io.c ./drivers/net/s2io.c +--- ../orig-linux-2.6.16.29/drivers/net/s2io.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/s2io.c 2006-09-19 13:59:20.000000000 +0100 +@@ -3522,8 +3522,8 @@ static int s2io_xmit(struct sk_buff *skb + txdp->Control_1 = 0; + txdp->Control_2 = 0; + #ifdef NETIF_F_TSO +- mss = skb_shinfo(skb)->tso_size; +- if (mss) { ++ mss = skb_shinfo(skb)->gso_size; ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) { + txdp->Control_1 |= TXD_TCP_LSO_EN; + txdp->Control_1 |= TXD_TCP_LSO_MSS(mss); + } +@@ -3543,10 +3543,10 @@ static int s2io_xmit(struct sk_buff *skb + } + + frg_len = skb->len - skb->data_len; +- if (skb_shinfo(skb)->ufo_size) { ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) { + int ufo_size; + +- ufo_size = skb_shinfo(skb)->ufo_size; ++ ufo_size = skb_shinfo(skb)->gso_size; + ufo_size &= ~7; + txdp->Control_1 |= TXD_UFO_EN; + txdp->Control_1 |= TXD_UFO_MSS(ufo_size); +@@ -3572,7 +3572,7 @@ static int s2io_xmit(struct sk_buff *skb + txdp->Host_Control = (unsigned long) skb; + txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); + +- if (skb_shinfo(skb)->ufo_size) ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) + txdp->Control_1 |= TXD_UFO_EN; + + frg_cnt = skb_shinfo(skb)->nr_frags; +@@ -3587,12 +3587,12 @@ static int s2io_xmit(struct sk_buff *skb + (sp->pdev, frag->page, frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size); +- if (skb_shinfo(skb)->ufo_size) ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) + txdp->Control_1 |= TXD_UFO_EN; + } + txdp->Control_1 |= TXD_GATHER_CODE_LAST; + +- if (skb_shinfo(skb)->ufo_size) ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) + frg_cnt++; /* as Txd0 was used for inband header */ + + tx_fifo = mac_control->tx_FIFO_start[queue]; +@@ -3606,7 +3606,7 @@ static int s2io_xmit(struct sk_buff *skb + if (mss) + val64 |= TX_FIFO_SPECIAL_FUNC; + #endif +- if (skb_shinfo(skb)->ufo_size) ++ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) + val64 |= TX_FIFO_SPECIAL_FUNC; + writeq(val64, &tx_fifo->List_Control); + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/sky2.c ./drivers/net/sky2.c +--- ../orig-linux-2.6.16.29/drivers/net/sky2.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/sky2.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1125,7 +1125,7 @@ static unsigned tx_le_req(const struct s + count = sizeof(dma_addr_t) / sizeof(u32); + count += skb_shinfo(skb)->nr_frags * count; + +- if (skb_shinfo(skb)->tso_size) ++ if (skb_shinfo(skb)->gso_size) + ++count; + + if (skb->ip_summed == CHECKSUM_HW) +@@ -1197,7 +1197,7 @@ static int sky2_xmit_frame(struct sk_buf + } + + /* Check for TCP Segmentation Offload */ +- mss = skb_shinfo(skb)->tso_size; ++ mss = skb_shinfo(skb)->gso_size; + if (mss != 0) { + /* just drop the packet if non-linear expansion fails */ + if (skb_header_cloned(skb) && +diff -pruN ../orig-linux-2.6.16.29/drivers/net/tg3.c ./drivers/net/tg3.c +--- ../orig-linux-2.6.16.29/drivers/net/tg3.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/tg3.c 2006-09-19 13:59:20.000000000 +0100 +@@ -3664,7 +3664,7 @@ static int tg3_start_xmit(struct sk_buff + #if TG3_TSO_SUPPORT != 0 + mss = 0; + if (skb->len > (tp->dev->mtu + ETH_HLEN) && +- (mss = skb_shinfo(skb)->tso_size) != 0) { ++ (mss = skb_shinfo(skb)->gso_size) != 0) { + int tcp_opt_len, ip_tcp_len; + + if (skb_header_cloned(skb) && +diff -pruN ../orig-linux-2.6.16.29/drivers/net/tulip/winbond-840.c ./drivers/net/tulip/winbond-840.c +--- ../orig-linux-2.6.16.29/drivers/net/tulip/winbond-840.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/tulip/winbond-840.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1605,11 +1605,11 @@ static void __devexit w840_remove1 (stru + * - get_stats: + * spin_lock_irq(np->lock), doesn't touch hw if not present + * - hard_start_xmit: +- * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); ++ * synchronize_irq + netif_tx_disable; + * - tx_timeout: +- * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); ++ * netif_device_detach + netif_tx_disable; + * - set_multicast_list +- * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); ++ * netif_device_detach + netif_tx_disable; + * - interrupt handler + * doesn't touch hw if not present, synchronize_irq waits for + * running instances of the interrupt handler. +@@ -1635,11 +1635,10 @@ static int w840_suspend (struct pci_dev + netif_device_detach(dev); + update_csr6(dev, 0); + iowrite32(0, ioaddr + IntrEnable); +- netif_stop_queue(dev); + spin_unlock_irq(&np->lock); + +- spin_unlock_wait(&dev->xmit_lock); + synchronize_irq(dev->irq); ++ netif_tx_disable(dev); + + np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/typhoon.c ./drivers/net/typhoon.c +--- ../orig-linux-2.6.16.29/drivers/net/typhoon.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/typhoon.c 2006-09-19 13:59:20.000000000 +0100 +@@ -340,7 +340,7 @@ enum state_values { + #endif + + #if defined(NETIF_F_TSO) +-#define skb_tso_size(x) (skb_shinfo(x)->tso_size) ++#define skb_tso_size(x) (skb_shinfo(x)->gso_size) + #define TSO_NUM_DESCRIPTORS 2 + #define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT + #else +diff -pruN ../orig-linux-2.6.16.29/drivers/net/via-velocity.c ./drivers/net/via-velocity.c +--- ../orig-linux-2.6.16.29/drivers/net/via-velocity.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/via-velocity.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1899,6 +1899,13 @@ static int velocity_xmit(struct sk_buff + + int pktlen = skb->len; + ++#ifdef VELOCITY_ZERO_COPY_SUPPORT ++ if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { ++ kfree_skb(skb); ++ return 0; ++ } ++#endif ++ + spin_lock_irqsave(&vptr->lock, flags); + + index = vptr->td_curr[qnum]; +@@ -1914,8 +1921,6 @@ static int velocity_xmit(struct sk_buff + */ + if (pktlen < ETH_ZLEN) { + /* Cannot occur until ZC support */ +- if(skb_linearize(skb, GFP_ATOMIC)) +- return 0; + pktlen = ETH_ZLEN; + memcpy(tdinfo->buf, skb->data, skb->len); + memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len); +@@ -1933,7 +1938,6 @@ static int velocity_xmit(struct sk_buff + int nfrags = skb_shinfo(skb)->nr_frags; + tdinfo->skb = skb; + if (nfrags > 6) { +- skb_linearize(skb, GFP_ATOMIC); + memcpy(tdinfo->buf, skb->data, skb->len); + tdinfo->skb_dma[0] = tdinfo->buf_dma; + td_ptr->tdesc0.pktsize = +diff -pruN ../orig-linux-2.6.16.29/drivers/net/wireless/orinoco.c ./drivers/net/wireless/orinoco.c +--- ../orig-linux-2.6.16.29/drivers/net/wireless/orinoco.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/net/wireless/orinoco.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1835,7 +1835,9 @@ static int __orinoco_program_rids(struct + /* Set promiscuity / multicast*/ + priv->promiscuous = 0; + priv->mc_count = 0; +- __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ ++ ++ /* FIXME: what about netif_tx_lock */ ++ __orinoco_set_multicast_list(dev); + + return 0; + } +diff -pruN ../orig-linux-2.6.16.29/drivers/s390/net/qeth_eddp.c ./drivers/s390/net/qeth_eddp.c +--- ../orig-linux-2.6.16.29/drivers/s390/net/qeth_eddp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/s390/net/qeth_eddp.c 2006-09-19 13:59:20.000000000 +0100 +@@ -421,7 +421,7 @@ __qeth_eddp_fill_context_tcp(struct qeth + } + tcph = eddp->skb->h.th; + while (eddp->skb_offset < eddp->skb->len) { +- data_len = min((int)skb_shinfo(eddp->skb)->tso_size, ++ data_len = min((int)skb_shinfo(eddp->skb)->gso_size, + (int)(eddp->skb->len - eddp->skb_offset)); + /* prepare qdio hdr */ + if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){ +@@ -516,20 +516,20 @@ qeth_eddp_calc_num_pages(struct qeth_edd + + QETH_DBF_TEXT(trace, 5, "eddpcanp"); + /* can we put multiple skbs in one page? */ +- skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len); ++ skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->gso_size + hdr_len); + if (skbs_per_page > 1){ +- ctx->num_pages = (skb_shinfo(skb)->tso_segs + 1) / ++ ctx->num_pages = (skb_shinfo(skb)->gso_segs + 1) / + skbs_per_page + 1; + ctx->elements_per_skb = 1; + } else { + /* no -> how many elements per skb? */ +- ctx->elements_per_skb = (skb_shinfo(skb)->tso_size + hdr_len + ++ ctx->elements_per_skb = (skb_shinfo(skb)->gso_size + hdr_len + + PAGE_SIZE) >> PAGE_SHIFT; + ctx->num_pages = ctx->elements_per_skb * +- (skb_shinfo(skb)->tso_segs + 1); ++ (skb_shinfo(skb)->gso_segs + 1); + } + ctx->num_elements = ctx->elements_per_skb * +- (skb_shinfo(skb)->tso_segs + 1); ++ (skb_shinfo(skb)->gso_segs + 1); + } + + static inline struct qeth_eddp_context * +diff -pruN ../orig-linux-2.6.16.29/drivers/s390/net/qeth_main.c ./drivers/s390/net/qeth_main.c +--- ../orig-linux-2.6.16.29/drivers/s390/net/qeth_main.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/s390/net/qeth_main.c 2006-09-19 13:59:20.000000000 +0100 +@@ -4454,7 +4454,7 @@ qeth_send_packet(struct qeth_card *card, + queue = card->qdio.out_qs + [qeth_get_priority_queue(card, skb, ipv, cast_type)]; + +- if (skb_shinfo(skb)->tso_size) ++ if (skb_shinfo(skb)->gso_size) + large_send = card->options.large_send; + + /*are we able to do TSO ? If so ,prepare and send it from here */ +@@ -4501,7 +4501,7 @@ qeth_send_packet(struct qeth_card *card, + card->stats.tx_packets++; + card->stats.tx_bytes += skb->len; + #ifdef CONFIG_QETH_PERF_STATS +- if (skb_shinfo(skb)->tso_size && ++ if (skb_shinfo(skb)->gso_size && + !(large_send == QETH_LARGE_SEND_NO)) { + card->perf_stats.large_send_bytes += skb->len; + card->perf_stats.large_send_cnt++; +diff -pruN ../orig-linux-2.6.16.29/drivers/s390/net/qeth_tso.h ./drivers/s390/net/qeth_tso.h +--- ../orig-linux-2.6.16.29/drivers/s390/net/qeth_tso.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/s390/net/qeth_tso.h 2006-09-19 13:59:20.000000000 +0100 +@@ -51,7 +51,7 @@ qeth_tso_fill_header(struct qeth_card *c + hdr->ext.hdr_version = 1; + hdr->ext.hdr_len = 28; + /*insert non-fix values */ +- hdr->ext.mss = skb_shinfo(skb)->tso_size; ++ hdr->ext.mss = skb_shinfo(skb)->gso_size; + hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); + hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - + sizeof(struct qeth_hdr_tso)); +diff -pruN ../orig-linux-2.6.16.29/include/linux/ethtool.h ./include/linux/ethtool.h +--- ../orig-linux-2.6.16.29/include/linux/ethtool.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/ethtool.h 2006-09-19 13:59:20.000000000 +0100 +@@ -408,6 +408,8 @@ struct ethtool_ops { + #define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ + #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ + #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ ++#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ ++#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ + + /* compatibility with older code */ + #define SPARC_ETH_GSET ETHTOOL_GSET +diff -pruN ../orig-linux-2.6.16.29/include/linux/netdevice.h ./include/linux/netdevice.h +--- ../orig-linux-2.6.16.29/include/linux/netdevice.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/netdevice.h 2006-09-19 13:59:20.000000000 +0100 +@@ -230,7 +230,8 @@ enum netdev_state_t + __LINK_STATE_SCHED, + __LINK_STATE_NOCARRIER, + __LINK_STATE_RX_SCHED, +- __LINK_STATE_LINKWATCH_PENDING ++ __LINK_STATE_LINKWATCH_PENDING, ++ __LINK_STATE_QDISC_RUNNING, + }; + + +@@ -306,9 +307,17 @@ struct net_device + #define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */ + #define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */ + #define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */ +-#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */ ++#define NETIF_F_GSO 2048 /* Enable software GSO. */ + #define NETIF_F_LLTX 4096 /* LockLess TX */ +-#define NETIF_F_UFO 8192 /* Can offload UDP Large Send*/ ++ ++ /* Segmentation offload features */ ++#define NETIF_F_GSO_SHIFT 16 ++#define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) ++#define NETIF_F_UFO (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT) ++#define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) ++ ++#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) ++#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) + + struct net_device *next_sched; + +@@ -394,6 +403,9 @@ struct net_device + struct list_head qdisc_list; + unsigned long tx_queue_len; /* Max frames per queue allowed */ + ++ /* Partially transmitted GSO packet. */ ++ struct sk_buff *gso_skb; ++ + /* ingress path synchronizer */ + spinlock_t ingress_lock; + struct Qdisc *qdisc_ingress; +@@ -402,7 +414,7 @@ struct net_device + * One part is mostly used on xmit path (device) + */ + /* hard_start_xmit synchronizer */ +- spinlock_t xmit_lock ____cacheline_aligned_in_smp; ++ spinlock_t _xmit_lock ____cacheline_aligned_in_smp; + /* cpu id of processor entered to hard_start_xmit or -1, + if nobody entered there. + */ +@@ -527,6 +539,8 @@ struct packet_type { + struct net_device *, + struct packet_type *, + struct net_device *); ++ struct sk_buff *(*gso_segment)(struct sk_buff *skb, ++ int features); + void *af_packet_priv; + struct list_head list; + }; +@@ -693,7 +707,8 @@ extern int dev_change_name(struct net_d + extern int dev_set_mtu(struct net_device *, int); + extern int dev_set_mac_address(struct net_device *, + struct sockaddr *); +-extern void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); ++extern int dev_hard_start_xmit(struct sk_buff *skb, ++ struct net_device *dev); + + extern void dev_init(void); + +@@ -900,11 +915,43 @@ static inline void __netif_rx_complete(s + clear_bit(__LINK_STATE_RX_SCHED, &dev->state); + } + ++static inline void netif_tx_lock(struct net_device *dev) ++{ ++ spin_lock(&dev->_xmit_lock); ++ dev->xmit_lock_owner = smp_processor_id(); ++} ++ ++static inline void netif_tx_lock_bh(struct net_device *dev) ++{ ++ spin_lock_bh(&dev->_xmit_lock); ++ dev->xmit_lock_owner = smp_processor_id(); ++} ++ ++static inline int netif_tx_trylock(struct net_device *dev) ++{ ++ int err = spin_trylock(&dev->_xmit_lock); ++ if (!err) ++ dev->xmit_lock_owner = smp_processor_id(); ++ return err; ++} ++ ++static inline void netif_tx_unlock(struct net_device *dev) ++{ ++ dev->xmit_lock_owner = -1; ++ spin_unlock(&dev->_xmit_lock); ++} ++ ++static inline void netif_tx_unlock_bh(struct net_device *dev) ++{ ++ dev->xmit_lock_owner = -1; ++ spin_unlock_bh(&dev->_xmit_lock); ++} ++ + static inline void netif_tx_disable(struct net_device *dev) + { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + netif_stop_queue(dev); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } + + /* These functions live elsewhere (drivers/net/net_init.c, but related) */ +@@ -932,6 +979,7 @@ extern int netdev_max_backlog; + extern int weight_p; + extern int netdev_set_master(struct net_device *dev, struct net_device *master); + extern int skb_checksum_help(struct sk_buff *skb, int inward); ++extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features); + #ifdef CONFIG_BUG + extern void netdev_rx_csum_fault(struct net_device *dev); + #else +@@ -951,6 +999,18 @@ extern void dev_seq_stop(struct seq_file + + extern void linkwatch_run_queue(void); + ++static inline int skb_gso_ok(struct sk_buff *skb, int features) ++{ ++ int feature = skb_shinfo(skb)->gso_size ? ++ skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0; ++ return (features & feature) == feature; ++} ++ ++static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) ++{ ++ return !skb_gso_ok(skb, dev->features); ++} ++ + #endif /* __KERNEL__ */ + + #endif /* _LINUX_DEV_H */ +diff -pruN ../orig-linux-2.6.16.29/include/linux/skbuff.h ./include/linux/skbuff.h +--- ../orig-linux-2.6.16.29/include/linux/skbuff.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/skbuff.h 2006-09-19 13:59:20.000000000 +0100 +@@ -134,9 +134,10 @@ struct skb_frag_struct { + struct skb_shared_info { + atomic_t dataref; + unsigned short nr_frags; +- unsigned short tso_size; +- unsigned short tso_segs; +- unsigned short ufo_size; ++ unsigned short gso_size; ++ /* Warning: this field is not always filled in (UFO)! */ ++ unsigned short gso_segs; ++ unsigned short gso_type; + unsigned int ip6_frag_id; + struct sk_buff *frag_list; + skb_frag_t frags[MAX_SKB_FRAGS]; +@@ -168,6 +169,14 @@ enum { + SKB_FCLONE_CLONE, + }; + ++enum { ++ SKB_GSO_TCPV4 = 1 << 0, ++ SKB_GSO_UDPV4 = 1 << 1, ++ ++ /* This indicates the skb is from an untrusted source. */ ++ SKB_GSO_DODGY = 1 << 2, ++}; ++ + /** + * struct sk_buff - socket buffer + * @next: Next buffer in list +@@ -1148,18 +1157,34 @@ static inline int skb_can_coalesce(struc + return 0; + } + ++static inline int __skb_linearize(struct sk_buff *skb) ++{ ++ return __pskb_pull_tail(skb, skb->data_len) ? 0 : -ENOMEM; ++} ++ + /** + * skb_linearize - convert paged skb to linear one + * @skb: buffer to linarize +- * @gfp: allocation mode + * + * If there is no free memory -ENOMEM is returned, otherwise zero + * is returned and the old skb data released. + */ +-extern int __skb_linearize(struct sk_buff *skb, gfp_t gfp); +-static inline int skb_linearize(struct sk_buff *skb, gfp_t gfp) ++static inline int skb_linearize(struct sk_buff *skb) ++{ ++ return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; ++} ++ ++/** ++ * skb_linearize_cow - make sure skb is linear and writable ++ * @skb: buffer to process ++ * ++ * If there is no free memory -ENOMEM is returned, otherwise zero ++ * is returned and the old skb data released. ++ */ ++static inline int skb_linearize_cow(struct sk_buff *skb) + { +- return __skb_linearize(skb, gfp); ++ return skb_is_nonlinear(skb) || skb_cloned(skb) ? ++ __skb_linearize(skb) : 0; + } + + /** +@@ -1254,6 +1279,7 @@ extern void skb_split(struct sk_b + struct sk_buff *skb1, const u32 len); + + extern void skb_release_data(struct sk_buff *skb); ++extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); + + static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, + int len, void *buffer) +diff -pruN ../orig-linux-2.6.16.29/include/net/pkt_sched.h ./include/net/pkt_sched.h +--- ../orig-linux-2.6.16.29/include/net/pkt_sched.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/net/pkt_sched.h 2006-09-19 13:59:20.000000000 +0100 +@@ -218,12 +218,13 @@ extern struct qdisc_rate_table *qdisc_ge + struct rtattr *tab); + extern void qdisc_put_rtab(struct qdisc_rate_table *tab); + +-extern int qdisc_restart(struct net_device *dev); ++extern void __qdisc_run(struct net_device *dev); + + static inline void qdisc_run(struct net_device *dev) + { +- while (!netif_queue_stopped(dev) && qdisc_restart(dev) < 0) +- /* NOTHING */; ++ if (!netif_queue_stopped(dev) && ++ !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) ++ __qdisc_run(dev); + } + + extern int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, +diff -pruN ../orig-linux-2.6.16.29/include/net/protocol.h ./include/net/protocol.h +--- ../orig-linux-2.6.16.29/include/net/protocol.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/net/protocol.h 2006-09-19 13:59:20.000000000 +0100 +@@ -37,6 +37,8 @@ + struct net_protocol { + int (*handler)(struct sk_buff *skb); + void (*err_handler)(struct sk_buff *skb, u32 info); ++ struct sk_buff *(*gso_segment)(struct sk_buff *skb, ++ int features); + int no_policy; + }; + +diff -pruN ../orig-linux-2.6.16.29/include/net/sock.h ./include/net/sock.h +--- ../orig-linux-2.6.16.29/include/net/sock.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/net/sock.h 2006-09-19 13:59:20.000000000 +0100 +@@ -1064,9 +1064,13 @@ static inline void sk_setup_caps(struct + { + __sk_dst_set(sk, dst); + sk->sk_route_caps = dst->dev->features; ++ if (sk->sk_route_caps & NETIF_F_GSO) ++ sk->sk_route_caps |= NETIF_F_TSO; + if (sk->sk_route_caps & NETIF_F_TSO) { + if (sock_flag(sk, SOCK_NO_LARGESEND) || dst->header_len) + sk->sk_route_caps &= ~NETIF_F_TSO; ++ else ++ sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; + } + } + +diff -pruN ../orig-linux-2.6.16.29/include/net/tcp.h ./include/net/tcp.h +--- ../orig-linux-2.6.16.29/include/net/tcp.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/net/tcp.h 2006-09-19 13:59:20.000000000 +0100 +@@ -552,13 +552,13 @@ struct tcp_skb_cb { + */ + static inline int tcp_skb_pcount(const struct sk_buff *skb) + { +- return skb_shinfo(skb)->tso_segs; ++ return skb_shinfo(skb)->gso_segs; + } + + /* This is valid iff tcp_skb_pcount() > 1. */ + static inline int tcp_skb_mss(const struct sk_buff *skb) + { +- return skb_shinfo(skb)->tso_size; ++ return skb_shinfo(skb)->gso_size; + } + + static inline void tcp_dec_pcount_approx(__u32 *count, +@@ -1063,6 +1063,8 @@ extern struct request_sock_ops tcp_reque + + extern int tcp_v4_destroy_sock(struct sock *sk); + ++extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); ++ + #ifdef CONFIG_PROC_FS + extern int tcp4_proc_init(void); + extern void tcp4_proc_exit(void); +diff -pruN ../orig-linux-2.6.16.29/net/atm/clip.c ./net/atm/clip.c +--- ../orig-linux-2.6.16.29/net/atm/clip.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/atm/clip.c 2006-09-19 13:59:20.000000000 +0100 +@@ -101,7 +101,7 @@ static void unlink_clip_vcc(struct clip_ + printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc); + return; + } +- spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */ ++ netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ + entry->neigh->used = jiffies; + for (walk = &entry->vccs; *walk; walk = &(*walk)->next) + if (*walk == clip_vcc) { +@@ -125,7 +125,7 @@ static void unlink_clip_vcc(struct clip_ + printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc " + "0x%p)\n",entry,clip_vcc); + out: +- spin_unlock_bh(&entry->neigh->dev->xmit_lock); ++ netif_tx_unlock_bh(entry->neigh->dev); + } + + /* The neighbour entry n->lock is held. */ +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_device.c ./net/bridge/br_device.c +--- ../orig-linux-2.6.16.29/net/bridge/br_device.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/bridge/br_device.c 2006-09-19 13:59:20.000000000 +0100 +@@ -146,9 +146,9 @@ static int br_set_tx_csum(struct net_dev + struct net_bridge *br = netdev_priv(dev); + + if (data) +- br->feature_mask |= NETIF_F_IP_CSUM; ++ br->feature_mask |= NETIF_F_NO_CSUM; + else +- br->feature_mask &= ~NETIF_F_IP_CSUM; ++ br->feature_mask &= ~NETIF_F_ALL_CSUM; + + br_features_recompute(br); + return 0; +@@ -185,6 +185,6 @@ void br_dev_setup(struct net_device *dev + dev->set_mac_address = br_set_mac_address; + dev->priv_flags = IFF_EBRIDGE; + +- dev->features = NETIF_F_SG | NETIF_F_FRAGLIST +- | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM; ++ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | ++ NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST; + } +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_forward.c ./net/bridge/br_forward.c +--- ../orig-linux-2.6.16.29/net/bridge/br_forward.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/bridge/br_forward.c 2006-09-19 13:59:20.000000000 +0100 +@@ -32,7 +32,7 @@ static inline int should_deliver(const s + int br_dev_queue_push_xmit(struct sk_buff *skb) + { + /* drop mtu oversized packets except tso */ +- if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->tso_size) ++ if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->gso_size) + kfree_skb(skb); + else { + #ifdef CONFIG_BRIDGE_NETFILTER +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_if.c ./net/bridge/br_if.c +--- ../orig-linux-2.6.16.29/net/bridge/br_if.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/bridge/br_if.c 2006-09-19 13:59:20.000000000 +0100 +@@ -385,17 +385,28 @@ void br_features_recompute(struct net_br + struct net_bridge_port *p; + unsigned long features, checksum; + +- features = br->feature_mask &~ NETIF_F_IP_CSUM; +- checksum = br->feature_mask & NETIF_F_IP_CSUM; ++ checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0; ++ features = br->feature_mask & ~NETIF_F_ALL_CSUM; + + list_for_each_entry(p, &br->port_list, list) { +- if (!(p->dev->features +- & (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM))) ++ unsigned long feature = p->dev->features; ++ ++ if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) ++ checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; ++ if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) ++ checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; ++ if (!(feature & NETIF_F_IP_CSUM)) + checksum = 0; +- features &= p->dev->features; ++ ++ if (feature & NETIF_F_GSO) ++ feature |= NETIF_F_TSO; ++ feature |= NETIF_F_GSO; ++ ++ features &= feature; + } + +- br->dev->features = features | checksum | NETIF_F_LLTX; ++ br->dev->features = features | checksum | NETIF_F_LLTX | ++ NETIF_F_GSO_ROBUST; + } + + /* called with RTNL */ +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_netfilter.c ./net/bridge/br_netfilter.c +--- ../orig-linux-2.6.16.29/net/bridge/br_netfilter.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/bridge/br_netfilter.c 2006-09-19 13:59:20.000000000 +0100 +@@ -743,7 +743,7 @@ static int br_nf_dev_queue_xmit(struct s + { + if (skb->protocol == htons(ETH_P_IP) && + skb->len > skb->dev->mtu && +- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) ++ !skb_shinfo(skb)->gso_size) + return ip_fragment(skb, br_dev_queue_push_xmit); + else + return br_dev_queue_push_xmit(skb); +diff -pruN ../orig-linux-2.6.16.29/net/core/dev.c ./net/core/dev.c +--- ../orig-linux-2.6.16.29/net/core/dev.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/dev.c 2006-09-19 13:59:20.000000000 +0100 +@@ -115,6 +115,7 @@ + #include <net/iw_handler.h> + #endif /* CONFIG_NET_RADIO */ + #include <asm/current.h> ++#include <linux/err.h> + + /* + * The list of packet types we will receive (as opposed to discard) +@@ -1032,7 +1033,7 @@ static inline void net_timestamp(struct + * taps currently in use. + */ + +-void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) ++static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) + { + struct packet_type *ptype; + +@@ -1106,6 +1107,45 @@ out: + return ret; + } + ++/** ++ * skb_gso_segment - Perform segmentation on skb. ++ * @skb: buffer to segment ++ * @features: features for the output path (see dev->features) ++ * ++ * This function segments the given skb and returns a list of segments. ++ * ++ * It may return NULL if the skb requires no segmentation. This is ++ * only possible when GSO is used for verifying header integrity. ++ */ ++struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) ++{ ++ struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); ++ struct packet_type *ptype; ++ int type = skb->protocol; ++ ++ BUG_ON(skb_shinfo(skb)->frag_list); ++ BUG_ON(skb->ip_summed != CHECKSUM_HW); ++ ++ skb->mac.raw = skb->data; ++ skb->mac_len = skb->nh.raw - skb->data; ++ __skb_pull(skb, skb->mac_len); ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { ++ if (ptype->type == type && !ptype->dev && ptype->gso_segment) { ++ segs = ptype->gso_segment(skb, features); ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ ++ __skb_push(skb, skb->data - skb->mac.raw); ++ ++ return segs; ++} ++ ++EXPORT_SYMBOL(skb_gso_segment); ++ + /* Take action when hardware reception checksum errors are detected. */ + #ifdef CONFIG_BUG + void netdev_rx_csum_fault(struct net_device *dev) +@@ -1142,75 +1182,108 @@ static inline int illegal_highdma(struct + #define illegal_highdma(dev, skb) (0) + #endif + +-/* Keep head the same: replace data */ +-int __skb_linearize(struct sk_buff *skb, gfp_t gfp_mask) ++struct dev_gso_cb { ++ void (*destructor)(struct sk_buff *skb); ++}; ++ ++#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)->cb) ++ ++static void dev_gso_skb_destructor(struct sk_buff *skb) ++{ ++ struct dev_gso_cb *cb; ++ ++ do { ++ struct sk_buff *nskb = skb->next; ++ ++ skb->next = nskb->next; ++ nskb->next = NULL; ++ kfree_skb(nskb); ++ } while (skb->next); ++ ++ cb = DEV_GSO_CB(skb); ++ if (cb->destructor) ++ cb->destructor(skb); ++} ++ ++/** ++ * dev_gso_segment - Perform emulated hardware segmentation on skb. ++ * @skb: buffer to segment ++ * ++ * This function segments the given skb and stores the list of segments ++ * in skb->next. ++ */ ++static int dev_gso_segment(struct sk_buff *skb) + { +- unsigned int size; +- u8 *data; +- long offset; +- struct skb_shared_info *ninfo; +- int headerlen = skb->data - skb->head; +- int expand = (skb->tail + skb->data_len) - skb->end; +- +- if (skb_shared(skb)) +- BUG(); +- +- if (expand <= 0) +- expand = 0; +- +- size = skb->end - skb->head + expand; +- size = SKB_DATA_ALIGN(size); +- data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); +- if (!data) +- return -ENOMEM; +- +- /* Copy entire thing */ +- if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len)) +- BUG(); +- +- /* Set up shinfo */ +- ninfo = (struct skb_shared_info*)(data + size); +- atomic_set(&ninfo->dataref, 1); +- ninfo->tso_size = skb_shinfo(skb)->tso_size; +- ninfo->tso_segs = skb_shinfo(skb)->tso_segs; +- ninfo->nr_frags = 0; +- ninfo->frag_list = NULL; +- +- /* Offset between the two in bytes */ +- offset = data - skb->head; +- +- /* Free old data. */ +- skb_release_data(skb); +- +- skb->head = data; +- skb->end = data + size; +- +- /* Set up new pointers */ +- skb->h.raw += offset; +- skb->nh.raw += offset; +- skb->mac.raw += offset; +- skb->tail += offset; +- skb->data += offset; ++ struct net_device *dev = skb->dev; ++ struct sk_buff *segs; ++ int features = dev->features & ~(illegal_highdma(dev, skb) ? ++ NETIF_F_SG : 0); ++ ++ segs = skb_gso_segment(skb, features); ++ ++ /* Verifying header integrity only. */ ++ if (!segs) ++ return 0; + +- /* We are no longer a clone, even if we were. */ +- skb->cloned = 0; ++ if (unlikely(IS_ERR(segs))) ++ return PTR_ERR(segs); ++ ++ skb->next = segs; ++ DEV_GSO_CB(skb)->destructor = skb->destructor; ++ skb->destructor = dev_gso_skb_destructor; + +- skb->tail += skb->data_len; +- skb->data_len = 0; ++ return 0; ++} ++ ++int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ if (likely(!skb->next)) { ++ if (netdev_nit) ++ dev_queue_xmit_nit(skb, dev); ++ ++ if (netif_needs_gso(dev, skb)) { ++ if (unlikely(dev_gso_segment(skb))) ++ goto out_kfree_skb; ++ if (skb->next) ++ goto gso; ++ } ++ ++ return dev->hard_start_xmit(skb, dev); ++ } ++ ++gso: ++ do { ++ struct sk_buff *nskb = skb->next; ++ int rc; ++ ++ skb->next = nskb->next; ++ nskb->next = NULL; ++ rc = dev->hard_start_xmit(nskb, dev); ++ if (unlikely(rc)) { ++ nskb->next = skb->next; ++ skb->next = nskb; ++ return rc; ++ } ++ if (unlikely(netif_queue_stopped(dev) && skb->next)) ++ return NETDEV_TX_BUSY; ++ } while (skb->next); ++ ++ skb->destructor = DEV_GSO_CB(skb)->destructor; ++ ++out_kfree_skb: ++ kfree_skb(skb); + return 0; + } + + #define HARD_TX_LOCK(dev, cpu) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ +- spin_lock(&dev->xmit_lock); \ +- dev->xmit_lock_owner = cpu; \ ++ netif_tx_lock(dev); \ + } \ + } + + #define HARD_TX_UNLOCK(dev) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ +- dev->xmit_lock_owner = -1; \ +- spin_unlock(&dev->xmit_lock); \ ++ netif_tx_unlock(dev); \ + } \ + } + +@@ -1246,9 +1319,13 @@ int dev_queue_xmit(struct sk_buff *skb) + struct Qdisc *q; + int rc = -ENOMEM; + ++ /* GSO will handle the following emulations directly. */ ++ if (netif_needs_gso(dev, skb)) ++ goto gso; ++ + if (skb_shinfo(skb)->frag_list && + !(dev->features & NETIF_F_FRAGLIST) && +- __skb_linearize(skb, GFP_ATOMIC)) ++ __skb_linearize(skb)) + goto out_kfree_skb; + + /* Fragmented skb is linearized if device does not support SG, +@@ -1257,25 +1334,26 @@ int dev_queue_xmit(struct sk_buff *skb) + */ + if (skb_shinfo(skb)->nr_frags && + (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) && +- __skb_linearize(skb, GFP_ATOMIC)) ++ __skb_linearize(skb)) + goto out_kfree_skb; + + /* If packet is not checksummed and device does not support + * checksumming for this protocol, complete checksumming here. + */ + if (skb->ip_summed == CHECKSUM_HW && +- (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) && ++ (!(dev->features & NETIF_F_GEN_CSUM) && + (!(dev->features & NETIF_F_IP_CSUM) || + skb->protocol != htons(ETH_P_IP)))) + if (skb_checksum_help(skb, 0)) + goto out_kfree_skb; + ++gso: + spin_lock_prefetch(&dev->queue_lock); + + /* Disable soft irqs for various locks below. Also + * stops preemption for RCU. + */ +- local_bh_disable(); ++ rcu_read_lock_bh(); + + /* Updates of qdisc are serialized by queue_lock. + * The struct Qdisc which is pointed to by qdisc is now a +@@ -1309,8 +1387,8 @@ int dev_queue_xmit(struct sk_buff *skb) + /* The device has no queue. Common case for software devices: + loopback, all the sorts of tunnels... + +- Really, it is unlikely that xmit_lock protection is necessary here. +- (f.e. loopback and IP tunnels are clean ignoring statistics ++ Really, it is unlikely that netif_tx_lock protection is necessary ++ here. (f.e. loopback and IP tunnels are clean ignoring statistics + counters.) + However, it is possible, that they rely on protection + made by us here. +@@ -1326,11 +1404,8 @@ int dev_queue_xmit(struct sk_buff *skb) + HARD_TX_LOCK(dev, cpu); + + if (!netif_queue_stopped(dev)) { +- if (netdev_nit) +- dev_queue_xmit_nit(skb, dev); +- + rc = 0; +- if (!dev->hard_start_xmit(skb, dev)) { ++ if (!dev_hard_start_xmit(skb, dev)) { + HARD_TX_UNLOCK(dev); + goto out; + } +@@ -1349,13 +1424,13 @@ int dev_queue_xmit(struct sk_buff *skb) + } + + rc = -ENETDOWN; +- local_bh_enable(); ++ rcu_read_unlock_bh(); + + out_kfree_skb: + kfree_skb(skb); + return rc; + out: +- local_bh_enable(); ++ rcu_read_unlock_bh(); + return rc; + } + +@@ -2670,7 +2745,7 @@ int register_netdevice(struct net_device + BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); + + spin_lock_init(&dev->queue_lock); +- spin_lock_init(&dev->xmit_lock); ++ spin_lock_init(&dev->_xmit_lock); + dev->xmit_lock_owner = -1; + #ifdef CONFIG_NET_CLS_ACT + spin_lock_init(&dev->ingress_lock); +@@ -2714,9 +2789,7 @@ int register_netdevice(struct net_device + + /* Fix illegal SG+CSUM combinations. */ + if ((dev->features & NETIF_F_SG) && +- !(dev->features & (NETIF_F_IP_CSUM | +- NETIF_F_NO_CSUM | +- NETIF_F_HW_CSUM))) { ++ !(dev->features & NETIF_F_ALL_CSUM)) { + printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", + dev->name); + dev->features &= ~NETIF_F_SG; +@@ -3268,7 +3341,6 @@ subsys_initcall(net_dev_init); + EXPORT_SYMBOL(__dev_get_by_index); + EXPORT_SYMBOL(__dev_get_by_name); + EXPORT_SYMBOL(__dev_remove_pack); +-EXPORT_SYMBOL(__skb_linearize); + EXPORT_SYMBOL(dev_valid_name); + EXPORT_SYMBOL(dev_add_pack); + EXPORT_SYMBOL(dev_alloc_name); +diff -pruN ../orig-linux-2.6.16.29/net/core/dev_mcast.c ./net/core/dev_mcast.c +--- ../orig-linux-2.6.16.29/net/core/dev_mcast.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/dev_mcast.c 2006-09-19 13:59:20.000000000 +0100 +@@ -62,7 +62,7 @@ + * Device mc lists are changed by bh at least if IPv6 is enabled, + * so that it must be bh protected. + * +- * We block accesses to device mc filters with dev->xmit_lock. ++ * We block accesses to device mc filters with netif_tx_lock. + */ + + /* +@@ -93,9 +93,9 @@ static void __dev_mc_upload(struct net_d + + void dev_mc_upload(struct net_device *dev) + { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + __dev_mc_upload(dev); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } + + /* +@@ -107,7 +107,7 @@ int dev_mc_delete(struct net_device *dev + int err = 0; + struct dev_mc_list *dmi, **dmip; + +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + + for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { + /* +@@ -139,13 +139,13 @@ int dev_mc_delete(struct net_device *dev + */ + __dev_mc_upload(dev); + +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + return 0; + } + } + err = -ENOENT; + done: +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + return err; + } + +@@ -160,7 +160,7 @@ int dev_mc_add(struct net_device *dev, v + + dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); + +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { + if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && + dmi->dmi_addrlen == alen) { +@@ -176,7 +176,7 @@ int dev_mc_add(struct net_device *dev, v + } + + if ((dmi = dmi1) == NULL) { +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + return -ENOMEM; + } + memcpy(dmi->dmi_addr, addr, alen); +@@ -189,11 +189,11 @@ int dev_mc_add(struct net_device *dev, v + + __dev_mc_upload(dev); + +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + return 0; + + done: +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + kfree(dmi1); + return err; + } +@@ -204,7 +204,7 @@ done: + + void dev_mc_discard(struct net_device *dev) + { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + + while (dev->mc_list != NULL) { + struct dev_mc_list *tmp = dev->mc_list; +@@ -215,7 +215,7 @@ void dev_mc_discard(struct net_device *d + } + dev->mc_count = 0; + +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } + + #ifdef CONFIG_PROC_FS +@@ -250,7 +250,7 @@ static int dev_mc_seq_show(struct seq_fi + struct dev_mc_list *m; + struct net_device *dev = v; + +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + for (m = dev->mc_list; m; m = m->next) { + int i; + +@@ -262,7 +262,7 @@ static int dev_mc_seq_show(struct seq_fi + + seq_putc(seq, '\n'); + } +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + return 0; + } + +diff -pruN ../orig-linux-2.6.16.29/net/core/ethtool.c ./net/core/ethtool.c +--- ../orig-linux-2.6.16.29/net/core/ethtool.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/ethtool.c 2006-09-19 13:59:20.000000000 +0100 +@@ -30,7 +30,7 @@ u32 ethtool_op_get_link(struct net_devic + + u32 ethtool_op_get_tx_csum(struct net_device *dev) + { +- return (dev->features & (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM)) != 0; ++ return (dev->features & NETIF_F_ALL_CSUM) != 0; + } + + int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) +@@ -551,9 +551,7 @@ static int ethtool_set_sg(struct net_dev + return -EFAULT; + + if (edata.data && +- !(dev->features & (NETIF_F_IP_CSUM | +- NETIF_F_NO_CSUM | +- NETIF_F_HW_CSUM))) ++ !(dev->features & NETIF_F_ALL_CSUM)) + return -EINVAL; + + return __ethtool_set_sg(dev, edata.data); +@@ -561,7 +559,7 @@ static int ethtool_set_sg(struct net_dev + + static int ethtool_get_tso(struct net_device *dev, char __user *useraddr) + { +- struct ethtool_value edata = { ETHTOOL_GTSO }; ++ struct ethtool_value edata = { ETHTOOL_GUFO }; + + if (!dev->ethtool_ops->get_tso) + return -EOPNOTSUPP; +@@ -616,6 +614,29 @@ static int ethtool_set_ufo(struct net_de + return dev->ethtool_ops->set_ufo(dev, edata.data); + } + ++static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) ++{ ++ struct ethtool_value edata = { ETHTOOL_GGSO }; ++ ++ edata.data = dev->features & NETIF_F_GSO; ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) ++{ ++ struct ethtool_value edata; ++ ++ if (copy_from_user(&edata, useraddr, sizeof(edata))) ++ return -EFAULT; ++ if (edata.data) ++ dev->features |= NETIF_F_GSO; ++ else ++ dev->features &= ~NETIF_F_GSO; ++ return 0; ++} ++ + static int ethtool_self_test(struct net_device *dev, char __user *useraddr) + { + struct ethtool_test test; +@@ -907,6 +928,12 @@ int dev_ethtool(struct ifreq *ifr) + case ETHTOOL_SUFO: + rc = ethtool_set_ufo(dev, useraddr); + break; ++ case ETHTOOL_GGSO: ++ rc = ethtool_get_gso(dev, useraddr); ++ break; ++ case ETHTOOL_SGSO: ++ rc = ethtool_set_gso(dev, useraddr); ++ break; + default: + rc = -EOPNOTSUPP; + } +diff -pruN ../orig-linux-2.6.16.29/net/core/netpoll.c ./net/core/netpoll.c +--- ../orig-linux-2.6.16.29/net/core/netpoll.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/netpoll.c 2006-09-19 13:59:20.000000000 +0100 +@@ -273,24 +273,21 @@ static void netpoll_send_skb(struct netp + + do { + npinfo->tries--; +- spin_lock(&np->dev->xmit_lock); +- np->dev->xmit_lock_owner = smp_processor_id(); ++ netif_tx_lock(np->dev); + + /* + * network drivers do not expect to be called if the queue is + * stopped. + */ + if (netif_queue_stopped(np->dev)) { +- np->dev->xmit_lock_owner = -1; +- spin_unlock(&np->dev->xmit_lock); ++ netif_tx_unlock(np->dev); + netpoll_poll(np); + udelay(50); + continue; + } + + status = np->dev->hard_start_xmit(skb, np->dev); +- np->dev->xmit_lock_owner = -1; +- spin_unlock(&np->dev->xmit_lock); ++ netif_tx_unlock(np->dev); + + /* success */ + if(!status) { +diff -pruN ../orig-linux-2.6.16.29/net/core/pktgen.c ./net/core/pktgen.c +--- ../orig-linux-2.6.16.29/net/core/pktgen.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/pktgen.c 2006-09-19 13:59:20.000000000 +0100 +@@ -2586,7 +2586,7 @@ static __inline__ void pktgen_xmit(struc + } + } + +- spin_lock_bh(&odev->xmit_lock); ++ netif_tx_lock_bh(odev); + if (!netif_queue_stopped(odev)) { + + atomic_inc(&(pkt_dev->skb->users)); +@@ -2631,7 +2631,7 @@ retry_now: + pkt_dev->next_tx_ns = 0; + } + +- spin_unlock_bh(&odev->xmit_lock); ++ netif_tx_unlock_bh(odev); + + /* If pkt_dev->count is zero, then run forever */ + if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { +diff -pruN ../orig-linux-2.6.16.29/net/core/skbuff.c ./net/core/skbuff.c +--- ../orig-linux-2.6.16.29/net/core/skbuff.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/core/skbuff.c 2006-09-19 13:59:20.000000000 +0100 +@@ -164,9 +164,9 @@ struct sk_buff *__alloc_skb(unsigned int + shinfo = skb_shinfo(skb); + atomic_set(&shinfo->dataref, 1); + shinfo->nr_frags = 0; +- shinfo->tso_size = 0; +- shinfo->tso_segs = 0; +- shinfo->ufo_size = 0; ++ shinfo->gso_size = 0; ++ shinfo->gso_segs = 0; ++ shinfo->gso_type = 0; + shinfo->ip6_frag_id = 0; + shinfo->frag_list = NULL; + +@@ -230,8 +230,9 @@ struct sk_buff *alloc_skb_from_cache(kme + + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; +- skb_shinfo(skb)->tso_size = 0; +- skb_shinfo(skb)->tso_segs = 0; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_segs = 0; ++ skb_shinfo(skb)->gso_type = 0; + skb_shinfo(skb)->frag_list = NULL; + out: + return skb; +@@ -501,8 +502,9 @@ static void copy_skb_header(struct sk_bu + new->tc_index = old->tc_index; + #endif + atomic_set(&new->users, 1); +- skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size; +- skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs; ++ skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; ++ skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; ++ skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; + } + + /** +@@ -1777,6 +1779,133 @@ int skb_append_datato_frags(struct sock + return 0; + } + ++/** ++ * skb_segment - Perform protocol segmentation on skb. ++ * @skb: buffer to segment ++ * @features: features for the output path (see dev->features) ++ * ++ * This function performs segmentation on the given skb. It returns ++ * the segment at the given position. It returns NULL if there are ++ * no more segments to generate, or when an error is encountered. ++ */ ++struct sk_buff *skb_segment(struct sk_buff *skb, int features) ++{ ++ struct sk_buff *segs = NULL; ++ struct sk_buff *tail = NULL; ++ unsigned int mss = skb_shinfo(skb)->gso_size; ++ unsigned int doffset = skb->data - skb->mac.raw; ++ unsigned int offset = doffset; ++ unsigned int headroom; ++ unsigned int len; ++ int sg = features & NETIF_F_SG; ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ int err = -ENOMEM; ++ int i = 0; ++ int pos; ++ ++ __skb_push(skb, doffset); ++ headroom = skb_headroom(skb); ++ pos = skb_headlen(skb); ++ ++ do { ++ struct sk_buff *nskb; ++ skb_frag_t *frag; ++ int hsize, nsize; ++ int k; ++ int size; ++ ++ len = skb->len - offset; ++ if (len > mss) ++ len = mss; ++ ++ hsize = skb_headlen(skb) - offset; ++ if (hsize < 0) ++ hsize = 0; ++ nsize = hsize + doffset; ++ if (nsize > len + doffset || !sg) ++ nsize = len + doffset; ++ ++ nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); ++ if (unlikely(!nskb)) ++ goto err; ++ ++ if (segs) ++ tail->next = nskb; ++ else ++ segs = nskb; ++ tail = nskb; ++ ++ nskb->dev = skb->dev; ++ nskb->priority = skb->priority; ++ nskb->protocol = skb->protocol; ++ nskb->dst = dst_clone(skb->dst); ++ memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); ++ nskb->pkt_type = skb->pkt_type; ++ nskb->mac_len = skb->mac_len; ++ ++ skb_reserve(nskb, headroom); ++ nskb->mac.raw = nskb->data; ++ nskb->nh.raw = nskb->data + skb->mac_len; ++ nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); ++ memcpy(skb_put(nskb, doffset), skb->data, doffset); ++ ++ if (!sg) { ++ nskb->csum = skb_copy_and_csum_bits(skb, offset, ++ skb_put(nskb, len), ++ len, 0); ++ continue; ++ } ++ ++ frag = skb_shinfo(nskb)->frags; ++ k = 0; ++ ++ nskb->ip_summed = CHECKSUM_HW; ++ nskb->csum = skb->csum; ++ memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); ++ ++ while (pos < offset + len) { ++ BUG_ON(i >= nfrags); ++ ++ *frag = skb_shinfo(skb)->frags[i]; ++ get_page(frag->page); ++ size = frag->size; ++ ++ if (pos < offset) { ++ frag->page_offset += offset - pos; ++ frag->size -= offset - pos; ++ } ++ ++ k++; ++ ++ if (pos + size <= offset + len) { ++ i++; ++ pos += size; ++ } else { ++ frag->size -= pos + size - (offset + len); ++ break; ++ } ++ ++ frag++; ++ } ++ ++ skb_shinfo(nskb)->nr_frags = k; ++ nskb->data_len = len - hsize; ++ nskb->len += nskb->data_len; ++ nskb->truesize += nskb->data_len; ++ } while ((offset += len) < skb->len); ++ ++ return segs; ++ ++err: ++ while ((skb = segs)) { ++ segs = skb->next; ++ kfree(skb); ++ } ++ return ERR_PTR(err); ++} ++ ++EXPORT_SYMBOL_GPL(skb_segment); ++ + void __init skb_init(void) + { + skbuff_head_cache = kmem_cache_create("skbuff_head_cache", +diff -pruN ../orig-linux-2.6.16.29/net/decnet/dn_nsp_in.c ./net/decnet/dn_nsp_in.c +--- ../orig-linux-2.6.16.29/net/decnet/dn_nsp_in.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/decnet/dn_nsp_in.c 2006-09-19 13:59:20.000000000 +0100 +@@ -801,8 +801,7 @@ got_it: + * We linearize everything except data segments here. + */ + if (cb->nsp_flags & ~0x60) { +- if (unlikely(skb_is_nonlinear(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) ++ if (unlikely(skb_linearize(skb))) + goto free_out; + } + +diff -pruN ../orig-linux-2.6.16.29/net/decnet/dn_route.c ./net/decnet/dn_route.c +--- ../orig-linux-2.6.16.29/net/decnet/dn_route.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/decnet/dn_route.c 2006-09-19 13:59:20.000000000 +0100 +@@ -629,8 +629,7 @@ int dn_route_rcv(struct sk_buff *skb, st + padlen); + + if (flags & DN_RT_PKT_CNTL) { +- if (unlikely(skb_is_nonlinear(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) ++ if (unlikely(skb_linearize(skb))) + goto dump_it; + + switch(flags & DN_RT_CNTL_MSK) { +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/af_inet.c ./net/ipv4/af_inet.c +--- ../orig-linux-2.6.16.29/net/ipv4/af_inet.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/af_inet.c 2006-09-19 13:59:20.000000000 +0100 +@@ -68,6 +68,7 @@ + */ + + #include <linux/config.h> ++#include <linux/err.h> + #include <linux/errno.h> + #include <linux/types.h> + #include <linux/socket.h> +@@ -1084,6 +1085,54 @@ int inet_sk_rebuild_header(struct sock * + + EXPORT_SYMBOL(inet_sk_rebuild_header); + ++static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) ++{ ++ struct sk_buff *segs = ERR_PTR(-EINVAL); ++ struct iphdr *iph; ++ struct net_protocol *ops; ++ int proto; ++ int ihl; ++ int id; ++ ++ if (!pskb_may_pull(skb, sizeof(*iph))) ++ goto out; ++ ++ iph = skb->nh.iph; ++ ihl = iph->ihl * 4; ++ if (ihl < sizeof(*iph)) ++ goto out; ++ ++ if (!pskb_may_pull(skb, ihl)) ++ goto out; ++ ++ skb->h.raw = __skb_pull(skb, ihl); ++ iph = skb->nh.iph; ++ id = ntohs(iph->id); ++ proto = iph->protocol & (MAX_INET_PROTOS - 1); ++ segs = ERR_PTR(-EPROTONOSUPPORT); ++ ++ rcu_read_lock(); ++ ops = rcu_dereference(inet_protos[proto]); ++ if (ops && ops->gso_segment) ++ segs = ops->gso_segment(skb, features); ++ rcu_read_unlock(); ++ ++ if (!segs || unlikely(IS_ERR(segs))) ++ goto out; ++ ++ skb = segs; ++ do { ++ iph = skb->nh.iph; ++ iph->id = htons(id++); ++ iph->tot_len = htons(skb->len - skb->mac_len); ++ iph->check = 0; ++ iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); ++ } while ((skb = skb->next)); ++ ++out: ++ return segs; ++} ++ + #ifdef CONFIG_IP_MULTICAST + static struct net_protocol igmp_protocol = { + .handler = igmp_rcv, +@@ -1093,6 +1142,7 @@ static struct net_protocol igmp_protocol + static struct net_protocol tcp_protocol = { + .handler = tcp_v4_rcv, + .err_handler = tcp_v4_err, ++ .gso_segment = tcp_tso_segment, + .no_policy = 1, + }; + +@@ -1138,6 +1188,7 @@ static int ipv4_proc_init(void); + static struct packet_type ip_packet_type = { + .type = __constant_htons(ETH_P_IP), + .func = ip_rcv, ++ .gso_segment = inet_gso_segment, + }; + + static int __init inet_init(void) +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/ip_output.c ./net/ipv4/ip_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/ip_output.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/ip_output.c 2006-09-19 13:59:20.000000000 +0100 +@@ -210,8 +210,7 @@ static inline int ip_finish_output(struc + return dst_output(skb); + } + #endif +- if (skb->len > dst_mtu(skb->dst) && +- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) ++ if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) + return ip_fragment(skb, ip_finish_output2); + else + return ip_finish_output2(skb); +@@ -362,7 +361,7 @@ packet_routed: + } + + ip_select_ident_more(iph, &rt->u.dst, sk, +- (skb_shinfo(skb)->tso_segs ?: 1) - 1); ++ (skb_shinfo(skb)->gso_segs ?: 1) - 1); + + /* Add an IP checksum. */ + ip_send_check(iph); +@@ -743,7 +742,8 @@ static inline int ip_ufo_append_data(str + (length - transhdrlen)); + if (!err) { + /* specify the length of each IP datagram fragment*/ +- skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); ++ skb_shinfo(skb)->gso_size = mtu - fragheaderlen; ++ skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; + __skb_queue_tail(&sk->sk_write_queue, skb); + + return 0; +@@ -839,7 +839,7 @@ int ip_append_data(struct sock *sk, + */ + if (transhdrlen && + length + fragheaderlen <= mtu && +- rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) && ++ rt->u.dst.dev->features & NETIF_F_ALL_CSUM && + !exthdrlen) + csummode = CHECKSUM_HW; + +@@ -1086,14 +1086,16 @@ ssize_t ip_append_page(struct sock *sk, + + inet->cork.length += size; + if ((sk->sk_protocol == IPPROTO_UDP) && +- (rt->u.dst.dev->features & NETIF_F_UFO)) +- skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); ++ (rt->u.dst.dev->features & NETIF_F_UFO)) { ++ skb_shinfo(skb)->gso_size = mtu - fragheaderlen; ++ skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; ++ } + + + while (size > 0) { + int i; + +- if (skb_shinfo(skb)->ufo_size) ++ if (skb_shinfo(skb)->gso_size) + len = size; + else { + +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/ipcomp.c ./net/ipv4/ipcomp.c +--- ../orig-linux-2.6.16.29/net/ipv4/ipcomp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/ipcomp.c 2006-09-19 13:59:20.000000000 +0100 +@@ -84,7 +84,7 @@ static int ipcomp_input(struct xfrm_stat + struct xfrm_decap_state *decap, struct sk_buff *skb) + { + u8 nexthdr; +- int err = 0; ++ int err = -ENOMEM; + struct iphdr *iph; + union { + struct iphdr iph; +@@ -92,11 +92,8 @@ static int ipcomp_input(struct xfrm_stat + } tmp_iph; + + +- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) { +- err = -ENOMEM; ++ if (skb_linearize_cow(skb)) + goto out; +- } + + skb->ip_summed = CHECKSUM_NONE; + +@@ -171,10 +168,8 @@ static int ipcomp_output(struct xfrm_sta + goto out_ok; + } + +- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) { ++ if (skb_linearize_cow(skb)) + goto out_ok; +- } + + err = ipcomp_compress(x, skb); + iph = skb->nh.iph; +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/tcp.c ./net/ipv4/tcp.c +--- ../orig-linux-2.6.16.29/net/ipv4/tcp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/tcp.c 2006-09-19 13:59:20.000000000 +0100 +@@ -257,6 +257,7 @@ + #include <linux/fs.h> + #include <linux/random.h> + #include <linux/bootmem.h> ++#include <linux/err.h> + + #include <net/icmp.h> + #include <net/tcp.h> +@@ -570,7 +571,7 @@ new_segment: + skb->ip_summed = CHECKSUM_HW; + tp->write_seq += copy; + TCP_SKB_CB(skb)->end_seq += copy; +- skb_shinfo(skb)->tso_segs = 0; ++ skb_shinfo(skb)->gso_segs = 0; + + if (!copied) + TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH; +@@ -621,14 +622,10 @@ ssize_t tcp_sendpage(struct socket *sock + ssize_t res; + struct sock *sk = sock->sk; + +-#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) +- + if (!(sk->sk_route_caps & NETIF_F_SG) || +- !(sk->sk_route_caps & TCP_ZC_CSUM_FLAGS)) ++ !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) + return sock_no_sendpage(sock, page, offset, size, flags); + +-#undef TCP_ZC_CSUM_FLAGS +- + lock_sock(sk); + TCP_CHECK_TIMER(sk); + res = do_tcp_sendpages(sk, &page, offset, size, flags); +@@ -725,9 +722,7 @@ new_segment: + /* + * Check whether we can use HW checksum. + */ +- if (sk->sk_route_caps & +- (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | +- NETIF_F_HW_CSUM)) ++ if (sk->sk_route_caps & NETIF_F_ALL_CSUM) + skb->ip_summed = CHECKSUM_HW; + + skb_entail(sk, tp, skb); +@@ -823,7 +818,7 @@ new_segment: + + tp->write_seq += copy; + TCP_SKB_CB(skb)->end_seq += copy; +- skb_shinfo(skb)->tso_segs = 0; ++ skb_shinfo(skb)->gso_segs = 0; + + from += copy; + copied += copy; +@@ -2026,6 +2021,71 @@ int tcp_getsockopt(struct sock *sk, int + } + + ++struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features) ++{ ++ struct sk_buff *segs = ERR_PTR(-EINVAL); ++ struct tcphdr *th; ++ unsigned thlen; ++ unsigned int seq; ++ unsigned int delta; ++ unsigned int oldlen; ++ unsigned int len; ++ ++ if (!pskb_may_pull(skb, sizeof(*th))) ++ goto out; ++ ++ th = skb->h.th; ++ thlen = th->doff * 4; ++ if (thlen < sizeof(*th)) ++ goto out; ++ ++ if (!pskb_may_pull(skb, thlen)) ++ goto out; ++ ++ segs = NULL; ++ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) ++ goto out; ++ ++ oldlen = (u16)~skb->len; ++ __skb_pull(skb, thlen); ++ ++ segs = skb_segment(skb, features); ++ if (IS_ERR(segs)) ++ goto out; ++ ++ len = skb_shinfo(skb)->gso_size; ++ delta = htonl(oldlen + (thlen + len)); ++ ++ skb = segs; ++ th = skb->h.th; ++ seq = ntohl(th->seq); ++ ++ do { ++ th->fin = th->psh = 0; ++ ++ th->check = ~csum_fold(th->check + delta); ++ if (skb->ip_summed != CHECKSUM_HW) ++ th->check = csum_fold(csum_partial(skb->h.raw, thlen, ++ skb->csum)); ++ ++ seq += len; ++ skb = skb->next; ++ th = skb->h.th; ++ ++ th->seq = htonl(seq); ++ th->cwr = 0; ++ } while (skb->next); ++ ++ delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len); ++ th->check = ~csum_fold(th->check + delta); ++ if (skb->ip_summed != CHECKSUM_HW) ++ th->check = csum_fold(csum_partial(skb->h.raw, thlen, ++ skb->csum)); ++ ++out: ++ return segs; ++} ++ + extern void __skb_cb_too_small_for_tcp(int, int); + extern struct tcp_congestion_ops tcp_reno; + +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/tcp_input.c ./net/ipv4/tcp_input.c +--- ../orig-linux-2.6.16.29/net/ipv4/tcp_input.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/tcp_input.c 2006-09-19 13:59:20.000000000 +0100 +@@ -1072,7 +1072,7 @@ tcp_sacktag_write_queue(struct sock *sk, + else + pkt_len = (end_seq - + TCP_SKB_CB(skb)->seq); +- if (tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->tso_size)) ++ if (tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->gso_size)) + break; + pcount = tcp_skb_pcount(skb); + } +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/tcp_output.c ./net/ipv4/tcp_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/tcp_output.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/tcp_output.c 2006-09-19 13:59:20.000000000 +0100 +@@ -497,15 +497,17 @@ static void tcp_set_skb_tso_segs(struct + /* Avoid the costly divide in the normal + * non-TSO case. + */ +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + } else { + unsigned int factor; + + factor = skb->len + (mss_now - 1); + factor /= mss_now; +- skb_shinfo(skb)->tso_segs = factor; +- skb_shinfo(skb)->tso_size = mss_now; ++ skb_shinfo(skb)->gso_segs = factor; ++ skb_shinfo(skb)->gso_size = mss_now; ++ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + } + } + +@@ -850,7 +852,7 @@ static int tcp_init_tso_segs(struct sock + + if (!tso_segs || + (tso_segs > 1 && +- skb_shinfo(skb)->tso_size != mss_now)) { ++ tcp_skb_mss(skb) != mss_now)) { + tcp_set_skb_tso_segs(sk, skb, mss_now); + tso_segs = tcp_skb_pcount(skb); + } +@@ -1510,8 +1512,9 @@ int tcp_retransmit_skb(struct sock *sk, + tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { + if (!pskb_trim(skb, 0)) { + TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1; +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + skb->ip_summed = CHECKSUM_NONE; + skb->csum = 0; + } +@@ -1716,8 +1719,9 @@ void tcp_send_fin(struct sock *sk) + skb->csum = 0; + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN); + TCP_SKB_CB(skb)->sacked = 0; +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + + /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ + TCP_SKB_CB(skb)->seq = tp->write_seq; +@@ -1749,8 +1753,9 @@ void tcp_send_active_reset(struct sock * + skb->csum = 0; + TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_RST); + TCP_SKB_CB(skb)->sacked = 0; +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + + /* Send it off. */ + TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk, tp); +@@ -1833,8 +1838,9 @@ struct sk_buff * tcp_make_synack(struct + TCP_SKB_CB(skb)->seq = tcp_rsk(req)->snt_isn; + TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1; + TCP_SKB_CB(skb)->sacked = 0; +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + th->seq = htonl(TCP_SKB_CB(skb)->seq); + th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1); + if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ +@@ -1937,8 +1943,9 @@ int tcp_connect(struct sock *sk) + TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN; + TCP_ECN_send_syn(sk, tp, buff); + TCP_SKB_CB(buff)->sacked = 0; +- skb_shinfo(buff)->tso_segs = 1; +- skb_shinfo(buff)->tso_size = 0; ++ skb_shinfo(buff)->gso_segs = 1; ++ skb_shinfo(buff)->gso_size = 0; ++ skb_shinfo(buff)->gso_type = 0; + buff->csum = 0; + TCP_SKB_CB(buff)->seq = tp->write_seq++; + TCP_SKB_CB(buff)->end_seq = tp->write_seq; +@@ -2042,8 +2049,9 @@ void tcp_send_ack(struct sock *sk) + buff->csum = 0; + TCP_SKB_CB(buff)->flags = TCPCB_FLAG_ACK; + TCP_SKB_CB(buff)->sacked = 0; +- skb_shinfo(buff)->tso_segs = 1; +- skb_shinfo(buff)->tso_size = 0; ++ skb_shinfo(buff)->gso_segs = 1; ++ skb_shinfo(buff)->gso_size = 0; ++ skb_shinfo(buff)->gso_type = 0; + + /* Send it off, this clears delayed acks for us. */ + TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk, tp); +@@ -2078,8 +2086,9 @@ static int tcp_xmit_probe_skb(struct soc + skb->csum = 0; + TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK; + TCP_SKB_CB(skb)->sacked = urgent; +- skb_shinfo(skb)->tso_segs = 1; +- skb_shinfo(skb)->tso_size = 0; ++ skb_shinfo(skb)->gso_segs = 1; ++ skb_shinfo(skb)->gso_size = 0; ++ skb_shinfo(skb)->gso_type = 0; + + /* Use a previous sequence. This should cause the other + * end to send an ack. Don't queue or clone SKB, just +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c ./net/ipv4/xfrm4_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c 2006-09-19 13:59:15.000000000 +0100 ++++ ./net/ipv4/xfrm4_output.c 2006-09-19 13:59:20.000000000 +0100 +@@ -9,6 +9,8 @@ + */ + + #include <linux/compiler.h> ++#include <linux/if_ether.h> ++#include <linux/kernel.h> + #include <linux/skbuff.h> + #include <linux/spinlock.h> + #include <linux/netfilter_ipv4.h> +@@ -158,16 +160,10 @@ error_nolock: + goto out_exit; + } + +-static int xfrm4_output_finish(struct sk_buff *skb) ++static int xfrm4_output_finish2(struct sk_buff *skb) + { + int err; + +-#ifdef CONFIG_NETFILTER +- if (!skb->dst->xfrm) { +- IPCB(skb)->flags |= IPSKB_REROUTED; +- return dst_output(skb); +- } +-#endif + while (likely((err = xfrm4_output_one(skb)) == 0)) { + nf_reset(skb); + +@@ -180,7 +176,7 @@ static int xfrm4_output_finish(struct sk + return dst_output(skb); + + err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL, +- skb->dst->dev, xfrm4_output_finish); ++ skb->dst->dev, xfrm4_output_finish2); + if (unlikely(err != 1)) + break; + } +@@ -188,6 +184,48 @@ static int xfrm4_output_finish(struct sk + return err; + } + ++static int xfrm4_output_finish(struct sk_buff *skb) ++{ ++ struct sk_buff *segs; ++ ++#ifdef CONFIG_NETFILTER ++ if (!skb->dst->xfrm) { ++ IPCB(skb)->flags |= IPSKB_REROUTED; ++ return dst_output(skb); ++ } ++#endif ++ ++ if (!skb_shinfo(skb)->gso_size) ++ return xfrm4_output_finish2(skb); ++ ++ skb->protocol = htons(ETH_P_IP); ++ segs = skb_gso_segment(skb, 0); ++ kfree_skb(skb); ++ if (unlikely(IS_ERR(segs))) ++ return PTR_ERR(segs); ++ ++ do { ++ struct sk_buff *nskb = segs->next; ++ int err; ++ ++ segs->next = NULL; ++ err = xfrm4_output_finish2(segs); ++ ++ if (unlikely(err)) { ++ while ((segs = nskb)) { ++ nskb = segs->next; ++ segs->next = NULL; ++ kfree_skb(segs); ++ } ++ return err; ++ } ++ ++ segs = nskb; ++ } while (segs); ++ ++ return 0; ++} ++ + int xfrm4_output(struct sk_buff *skb) + { + return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/ip6_output.c ./net/ipv6/ip6_output.c +--- ../orig-linux-2.6.16.29/net/ipv6/ip6_output.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv6/ip6_output.c 2006-09-19 13:59:20.000000000 +0100 +@@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *s + + int ip6_output(struct sk_buff *skb) + { +- if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->ufo_size) || ++ if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) || + dst_allfrag(skb->dst)) + return ip6_fragment(skb, ip6_output2); + else +@@ -829,8 +829,9 @@ static inline int ip6_ufo_append_data(st + struct frag_hdr fhdr; + + /* specify the length of each IP datagram fragment*/ +- skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen) - +- sizeof(struct frag_hdr); ++ skb_shinfo(skb)->gso_size = mtu - fragheaderlen - ++ sizeof(struct frag_hdr); ++ skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; + ipv6_select_ident(skb, &fhdr); + skb_shinfo(skb)->ip6_frag_id = fhdr.identification; + __skb_queue_tail(&sk->sk_write_queue, skb); +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/ipcomp6.c ./net/ipv6/ipcomp6.c +--- ../orig-linux-2.6.16.29/net/ipv6/ipcomp6.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv6/ipcomp6.c 2006-09-19 13:59:20.000000000 +0100 +@@ -64,7 +64,7 @@ static LIST_HEAD(ipcomp6_tfms_list); + + static int ipcomp6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) + { +- int err = 0; ++ int err = -ENOMEM; + u8 nexthdr = 0; + int hdr_len = skb->h.raw - skb->nh.raw; + unsigned char *tmp_hdr = NULL; +@@ -75,11 +75,8 @@ static int ipcomp6_input(struct xfrm_sta + struct crypto_tfm *tfm; + int cpu; + +- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) { +- err = -ENOMEM; ++ if (skb_linearize_cow(skb)) + goto out; +- } + + skb->ip_summed = CHECKSUM_NONE; + +@@ -158,10 +155,8 @@ static int ipcomp6_output(struct xfrm_st + goto out_ok; + } + +- if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && +- skb_linearize(skb, GFP_ATOMIC) != 0) { ++ if (skb_linearize_cow(skb)) + goto out_ok; +- } + + /* compression */ + plen = skb->len - hdr_len; +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/xfrm6_output.c ./net/ipv6/xfrm6_output.c +--- ../orig-linux-2.6.16.29/net/ipv6/xfrm6_output.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv6/xfrm6_output.c 2006-09-19 13:59:20.000000000 +0100 +@@ -151,7 +151,7 @@ error_nolock: + goto out_exit; + } + +-static int xfrm6_output_finish(struct sk_buff *skb) ++static int xfrm6_output_finish2(struct sk_buff *skb) + { + int err; + +@@ -167,7 +167,7 @@ static int xfrm6_output_finish(struct sk + return dst_output(skb); + + err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, &skb, NULL, +- skb->dst->dev, xfrm6_output_finish); ++ skb->dst->dev, xfrm6_output_finish2); + if (unlikely(err != 1)) + break; + } +@@ -175,6 +175,41 @@ static int xfrm6_output_finish(struct sk + return err; + } + ++static int xfrm6_output_finish(struct sk_buff *skb) ++{ ++ struct sk_buff *segs; ++ ++ if (!skb_shinfo(skb)->gso_size) ++ return xfrm6_output_finish2(skb); ++ ++ skb->protocol = htons(ETH_P_IP); ++ segs = skb_gso_segment(skb, 0); ++ kfree_skb(skb); ++ if (unlikely(IS_ERR(segs))) ++ return PTR_ERR(segs); ++ ++ do { ++ struct sk_buff *nskb = segs->next; ++ int err; ++ ++ segs->next = NULL; ++ err = xfrm6_output_finish2(segs); ++ ++ if (unlikely(err)) { ++ while ((segs = nskb)) { ++ nskb = segs->next; ++ segs->next = NULL; ++ kfree_skb(segs); ++ } ++ return err; ++ } ++ ++ segs = nskb; ++ } while (segs); ++ ++ return 0; ++} ++ + int xfrm6_output(struct sk_buff *skb) + { + return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev, +diff -pruN ../orig-linux-2.6.16.29/net/sched/sch_generic.c ./net/sched/sch_generic.c +--- ../orig-linux-2.6.16.29/net/sched/sch_generic.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/sched/sch_generic.c 2006-09-19 13:59:20.000000000 +0100 +@@ -72,9 +72,9 @@ void qdisc_unlock_tree(struct net_device + dev->queue_lock serializes queue accesses for this device + AND dev->qdisc pointer itself. + +- dev->xmit_lock serializes accesses to device driver. ++ netif_tx_lock serializes accesses to device driver. + +- dev->queue_lock and dev->xmit_lock are mutually exclusive, ++ dev->queue_lock and netif_tx_lock are mutually exclusive, + if one is grabbed, another must be free. + */ + +@@ -90,14 +90,17 @@ void qdisc_unlock_tree(struct net_device + NOTE: Called under dev->queue_lock with locally disabled BH. + */ + +-int qdisc_restart(struct net_device *dev) ++static inline int qdisc_restart(struct net_device *dev) + { + struct Qdisc *q = dev->qdisc; + struct sk_buff *skb; + + /* Dequeue packet */ +- if ((skb = q->dequeue(q)) != NULL) { ++ if (((skb = dev->gso_skb)) || ((skb = q->dequeue(q)))) { + unsigned nolock = (dev->features & NETIF_F_LLTX); ++ ++ dev->gso_skb = NULL; ++ + /* + * When the driver has LLTX set it does its own locking + * in start_xmit. No need to add additional overhead by +@@ -108,7 +111,7 @@ int qdisc_restart(struct net_device *dev + * will be requeued. + */ + if (!nolock) { +- if (!spin_trylock(&dev->xmit_lock)) { ++ if (!netif_tx_trylock(dev)) { + collision: + /* So, someone grabbed the driver. */ + +@@ -126,8 +129,6 @@ int qdisc_restart(struct net_device *dev + __get_cpu_var(netdev_rx_stat).cpu_collision++; + goto requeue; + } +- /* Remember that the driver is grabbed by us. */ +- dev->xmit_lock_owner = smp_processor_id(); + } + + { +@@ -136,14 +137,11 @@ int qdisc_restart(struct net_device *dev + + if (!netif_queue_stopped(dev)) { + int ret; +- if (netdev_nit) +- dev_queue_xmit_nit(skb, dev); + +- ret = dev->hard_start_xmit(skb, dev); ++ ret = dev_hard_start_xmit(skb, dev); + if (ret == NETDEV_TX_OK) { + if (!nolock) { +- dev->xmit_lock_owner = -1; +- spin_unlock(&dev->xmit_lock); ++ netif_tx_unlock(dev); + } + spin_lock(&dev->queue_lock); + return -1; +@@ -157,8 +155,7 @@ int qdisc_restart(struct net_device *dev + /* NETDEV_TX_BUSY - we need to requeue */ + /* Release the driver */ + if (!nolock) { +- dev->xmit_lock_owner = -1; +- spin_unlock(&dev->xmit_lock); ++ netif_tx_unlock(dev); + } + spin_lock(&dev->queue_lock); + q = dev->qdisc; +@@ -175,7 +172,10 @@ int qdisc_restart(struct net_device *dev + */ + + requeue: +- q->ops->requeue(skb, q); ++ if (skb->next) ++ dev->gso_skb = skb; ++ else ++ q->ops->requeue(skb, q); + netif_schedule(dev); + return 1; + } +@@ -183,11 +183,23 @@ requeue: + return q->q.qlen; + } + ++void __qdisc_run(struct net_device *dev) ++{ ++ if (unlikely(dev->qdisc == &noop_qdisc)) ++ goto out; ++ ++ while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev)) ++ /* NOTHING */; ++ ++out: ++ clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); ++} ++ + static void dev_watchdog(unsigned long arg) + { + struct net_device *dev = (struct net_device *)arg; + +- spin_lock(&dev->xmit_lock); ++ netif_tx_lock(dev); + if (dev->qdisc != &noop_qdisc) { + if (netif_device_present(dev) && + netif_running(dev) && +@@ -201,7 +213,7 @@ static void dev_watchdog(unsigned long a + dev_hold(dev); + } + } +- spin_unlock(&dev->xmit_lock); ++ netif_tx_unlock(dev); + + dev_put(dev); + } +@@ -225,17 +237,17 @@ void __netdev_watchdog_up(struct net_dev + + static void dev_watchdog_up(struct net_device *dev) + { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + __netdev_watchdog_up(dev); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } + + static void dev_watchdog_down(struct net_device *dev) + { +- spin_lock_bh(&dev->xmit_lock); ++ netif_tx_lock_bh(dev); + if (del_timer(&dev->watchdog_timer)) + __dev_put(dev); +- spin_unlock_bh(&dev->xmit_lock); ++ netif_tx_unlock_bh(dev); + } + + void netif_carrier_on(struct net_device *dev) +@@ -577,10 +589,17 @@ void dev_deactivate(struct net_device *d + + dev_watchdog_down(dev); + +- while (test_bit(__LINK_STATE_SCHED, &dev->state)) ++ /* Wait for outstanding dev_queue_xmit calls. */ ++ synchronize_rcu(); ++ ++ /* Wait for outstanding qdisc_run calls. */ ++ while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + yield(); + +- spin_unlock_wait(&dev->xmit_lock); ++ if (dev->gso_skb) { ++ kfree_skb(dev->gso_skb); ++ dev->gso_skb = NULL; ++ } + } + + void dev_init_scheduler(struct net_device *dev) +@@ -622,6 +641,5 @@ EXPORT_SYMBOL(qdisc_create_dflt); + EXPORT_SYMBOL(qdisc_alloc); + EXPORT_SYMBOL(qdisc_destroy); + EXPORT_SYMBOL(qdisc_reset); +-EXPORT_SYMBOL(qdisc_restart); + EXPORT_SYMBOL(qdisc_lock_tree); + EXPORT_SYMBOL(qdisc_unlock_tree); +diff -pruN ../orig-linux-2.6.16.29/net/sched/sch_teql.c ./net/sched/sch_teql.c +--- ../orig-linux-2.6.16.29/net/sched/sch_teql.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/sched/sch_teql.c 2006-09-19 13:59:20.000000000 +0100 +@@ -302,20 +302,17 @@ restart: + + switch (teql_resolve(skb, skb_res, slave)) { + case 0: +- if (spin_trylock(&slave->xmit_lock)) { +- slave->xmit_lock_owner = smp_processor_id(); ++ if (netif_tx_trylock(slave)) { + if (!netif_queue_stopped(slave) && + slave->hard_start_xmit(skb, slave) == 0) { +- slave->xmit_lock_owner = -1; +- spin_unlock(&slave->xmit_lock); ++ netif_tx_unlock(slave); + master->slaves = NEXT_SLAVE(q); + netif_wake_queue(dev); + master->stats.tx_packets++; + master->stats.tx_bytes += len; + return 0; + } +- slave->xmit_lock_owner = -1; +- spin_unlock(&slave->xmit_lock); ++ netif_tx_unlock(slave); + } + if (netif_queue_stopped(dev)) + busy = 1; diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-gso-1-check-dodgy.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-gso-1-check-dodgy.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,27 @@ +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/tcp.c ./net/ipv4/tcp.c +--- ../orig-linux-2.6.16.29/net/ipv4/tcp.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv4/tcp.c 2006-09-19 13:59:42.000000000 +0100 +@@ -2042,13 +2042,19 @@ struct sk_buff *tcp_tso_segment(struct s + if (!pskb_may_pull(skb, thlen)) + goto out; + +- segs = NULL; +- if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) +- goto out; +- + oldlen = (u16)~skb->len; + __skb_pull(skb, thlen); + ++ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { ++ /* Packet is from an untrusted source, reset gso_segs. */ ++ int mss = skb_shinfo(skb)->gso_size; ++ ++ skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss; ++ ++ segs = NULL; ++ goto out; ++ } ++ + segs = skb_segment(skb, features); + if (IS_ERR(segs)) + goto out; diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-gso-2-checksum-fix.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-gso-2-checksum-fix.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,451 @@ +diff -pruN ../orig-linux-2.6.16.29/drivers/net/bnx2.c ./drivers/net/bnx2.c +--- ../orig-linux-2.6.16.29/drivers/net/bnx2.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/bnx2.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1593,7 +1593,7 @@ bnx2_tx_int(struct bnx2 *bp) + skb = tx_buf->skb; + #ifdef BCM_TSO + /* partial BD completions possible with TSO packets */ +- if (skb_shinfo(skb)->gso_size) { ++ if (skb_is_gso(skb)) { + u16 last_idx, last_ring_idx; + + last_idx = sw_cons + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/chelsio/sge.c ./drivers/net/chelsio/sge.c +--- ../orig-linux-2.6.16.29/drivers/net/chelsio/sge.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/chelsio/sge.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1419,7 +1419,7 @@ int t1_start_xmit(struct sk_buff *skb, s + struct cpl_tx_pkt *cpl; + + #ifdef NETIF_F_TSO +- if (skb_shinfo(skb)->gso_size) { ++ if (skb_is_gso(skb)) { + int eth_type; + struct cpl_tx_pkt_lso *hdr; + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/e1000/e1000_main.c ./drivers/net/e1000/e1000_main.c +--- ../orig-linux-2.6.16.29/drivers/net/e1000/e1000_main.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/e1000/e1000_main.c 2006-09-19 13:59:46.000000000 +0100 +@@ -2526,7 +2526,7 @@ e1000_tso(struct e1000_adapter *adapter, + uint8_t ipcss, ipcso, tucss, tucso, hdr_len; + int err; + +- if (skb_shinfo(skb)->gso_size) { ++ if (skb_is_gso(skb)) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) +@@ -2651,7 +2651,7 @@ e1000_tx_map(struct e1000_adapter *adapt + * tso gets written back prematurely before the data is fully + * DMAd to the controller */ + if (!skb->data_len && tx_ring->last_tx_tso && +- !skb_shinfo(skb)->gso_size) { ++ !skb_is_gso(skb)) { + tx_ring->last_tx_tso = 0; + size -= 4; + } +@@ -2934,8 +2934,7 @@ e1000_xmit_frame(struct sk_buff *skb, st + + #ifdef NETIF_F_TSO + /* Controller Erratum workaround */ +- if (!skb->data_len && tx_ring->last_tx_tso && +- !skb_shinfo(skb)->gso_size) ++ if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb)) + count++; + #endif + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/forcedeth.c ./drivers/net/forcedeth.c +--- ../orig-linux-2.6.16.29/drivers/net/forcedeth.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/forcedeth.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1105,7 +1105,7 @@ static int nv_start_xmit(struct sk_buff + np->tx_skbuff[nr] = skb; + + #ifdef NETIF_F_TSO +- if (skb_shinfo(skb)->gso_size) ++ if (skb_is_gso(skb)) + tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); + else + #endif +diff -pruN ../orig-linux-2.6.16.29/drivers/net/ixgb/ixgb_main.c ./drivers/net/ixgb/ixgb_main.c +--- ../orig-linux-2.6.16.29/drivers/net/ixgb/ixgb_main.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/ixgb/ixgb_main.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1163,7 +1163,7 @@ ixgb_tso(struct ixgb_adapter *adapter, s + uint16_t ipcse, tucse, mss; + int err; + +- if(likely(skb_shinfo(skb)->gso_size)) { ++ if (likely(skb_is_gso(skb))) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) +diff -pruN ../orig-linux-2.6.16.29/drivers/net/loopback.c ./drivers/net/loopback.c +--- ../orig-linux-2.6.16.29/drivers/net/loopback.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/loopback.c 2006-09-19 13:59:46.000000000 +0100 +@@ -139,7 +139,7 @@ static int loopback_xmit(struct sk_buff + #endif + + #ifdef LOOPBACK_TSO +- if (skb_shinfo(skb)->gso_size) { ++ if (skb_is_gso(skb)) { + BUG_ON(skb->protocol != htons(ETH_P_IP)); + BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); + +diff -pruN ../orig-linux-2.6.16.29/drivers/net/sky2.c ./drivers/net/sky2.c +--- ../orig-linux-2.6.16.29/drivers/net/sky2.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/sky2.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1125,7 +1125,7 @@ static unsigned tx_le_req(const struct s + count = sizeof(dma_addr_t) / sizeof(u32); + count += skb_shinfo(skb)->nr_frags * count; + +- if (skb_shinfo(skb)->gso_size) ++ if (skb_is_gso(skb)) + ++count; + + if (skb->ip_summed == CHECKSUM_HW) +diff -pruN ../orig-linux-2.6.16.29/drivers/net/typhoon.c ./drivers/net/typhoon.c +--- ../orig-linux-2.6.16.29/drivers/net/typhoon.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/net/typhoon.c 2006-09-19 13:59:46.000000000 +0100 +@@ -805,7 +805,7 @@ typhoon_start_tx(struct sk_buff *skb, st + * If problems develop with TSO, check this first. + */ + numDesc = skb_shinfo(skb)->nr_frags + 1; +- if(skb_tso_size(skb)) ++ if (skb_is_gso(skb)) + numDesc++; + + /* When checking for free space in the ring, we need to also +@@ -845,7 +845,7 @@ typhoon_start_tx(struct sk_buff *skb, st + TYPHOON_TX_PF_VLAN_TAG_SHIFT); + } + +- if(skb_tso_size(skb)) { ++ if (skb_is_gso(skb)) { + first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT; + first_txd->numDesc++; + +diff -pruN ../orig-linux-2.6.16.29/drivers/s390/net/qeth_main.c ./drivers/s390/net/qeth_main.c +--- ../orig-linux-2.6.16.29/drivers/s390/net/qeth_main.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./drivers/s390/net/qeth_main.c 2006-09-19 13:59:46.000000000 +0100 +@@ -4454,7 +4454,7 @@ qeth_send_packet(struct qeth_card *card, + queue = card->qdio.out_qs + [qeth_get_priority_queue(card, skb, ipv, cast_type)]; + +- if (skb_shinfo(skb)->gso_size) ++ if (skb_is_gso(skb)) + large_send = card->options.large_send; + + /*are we able to do TSO ? If so ,prepare and send it from here */ +@@ -4501,8 +4501,7 @@ qeth_send_packet(struct qeth_card *card, + card->stats.tx_packets++; + card->stats.tx_bytes += skb->len; + #ifdef CONFIG_QETH_PERF_STATS +- if (skb_shinfo(skb)->gso_size && +- !(large_send == QETH_LARGE_SEND_NO)) { ++ if (skb_is_gso(skb) && !(large_send == QETH_LARGE_SEND_NO)) { + card->perf_stats.large_send_bytes += skb->len; + card->perf_stats.large_send_cnt++; + } +diff -pruN ../orig-linux-2.6.16.29/include/linux/netdevice.h ./include/linux/netdevice.h +--- ../orig-linux-2.6.16.29/include/linux/netdevice.h 2006-09-19 13:59:20.000000000 +0100 ++++ ./include/linux/netdevice.h 2006-09-19 13:59:46.000000000 +0100 +@@ -541,6 +541,7 @@ struct packet_type { + struct net_device *); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, + int features); ++ int (*gso_send_check)(struct sk_buff *skb); + void *af_packet_priv; + struct list_head list; + }; +@@ -1001,14 +1002,15 @@ extern void linkwatch_run_queue(void); + + static inline int skb_gso_ok(struct sk_buff *skb, int features) + { +- int feature = skb_shinfo(skb)->gso_size ? +- skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0; ++ int feature = skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT; + return (features & feature) == feature; + } + + static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) + { +- return !skb_gso_ok(skb, dev->features); ++ return skb_is_gso(skb) && ++ (!skb_gso_ok(skb, dev->features) || ++ unlikely(skb->ip_summed != CHECKSUM_HW)); + } + + #endif /* __KERNEL__ */ +diff -pruN ../orig-linux-2.6.16.29/include/linux/skbuff.h ./include/linux/skbuff.h +--- ../orig-linux-2.6.16.29/include/linux/skbuff.h 2006-09-19 13:59:20.000000000 +0100 ++++ ./include/linux/skbuff.h 2006-09-19 13:59:46.000000000 +0100 +@@ -1403,5 +1403,10 @@ static inline void nf_bridge_get(struct + static inline void nf_reset(struct sk_buff *skb) {} + #endif /* CONFIG_NETFILTER */ + ++static inline int skb_is_gso(const struct sk_buff *skb) ++{ ++ return skb_shinfo(skb)->gso_size; ++} ++ + #endif /* __KERNEL__ */ + #endif /* _LINUX_SKBUFF_H */ +diff -pruN ../orig-linux-2.6.16.29/include/net/protocol.h ./include/net/protocol.h +--- ../orig-linux-2.6.16.29/include/net/protocol.h 2006-09-19 13:59:20.000000000 +0100 ++++ ./include/net/protocol.h 2006-09-19 13:59:46.000000000 +0100 +@@ -37,6 +37,7 @@ + struct net_protocol { + int (*handler)(struct sk_buff *skb); + void (*err_handler)(struct sk_buff *skb, u32 info); ++ int (*gso_send_check)(struct sk_buff *skb); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, + int features); + int no_policy; +diff -pruN ../orig-linux-2.6.16.29/include/net/tcp.h ./include/net/tcp.h +--- ../orig-linux-2.6.16.29/include/net/tcp.h 2006-09-19 13:59:20.000000000 +0100 ++++ ./include/net/tcp.h 2006-09-19 13:59:46.000000000 +0100 +@@ -1063,6 +1063,7 @@ extern struct request_sock_ops tcp_reque + + extern int tcp_v4_destroy_sock(struct sock *sk); + ++extern int tcp_v4_gso_send_check(struct sk_buff *skb); + extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); + + #ifdef CONFIG_PROC_FS +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_forward.c ./net/bridge/br_forward.c +--- ../orig-linux-2.6.16.29/net/bridge/br_forward.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/bridge/br_forward.c 2006-09-19 13:59:46.000000000 +0100 +@@ -32,7 +32,7 @@ static inline int should_deliver(const s + int br_dev_queue_push_xmit(struct sk_buff *skb) + { + /* drop mtu oversized packets except tso */ +- if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->gso_size) ++ if (skb->len > skb->dev->mtu && !skb_is_gso(skb)) + kfree_skb(skb); + else { + #ifdef CONFIG_BRIDGE_NETFILTER +diff -pruN ../orig-linux-2.6.16.29/net/bridge/br_netfilter.c ./net/bridge/br_netfilter.c +--- ../orig-linux-2.6.16.29/net/bridge/br_netfilter.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/bridge/br_netfilter.c 2006-09-19 13:59:46.000000000 +0100 +@@ -743,7 +743,7 @@ static int br_nf_dev_queue_xmit(struct s + { + if (skb->protocol == htons(ETH_P_IP) && + skb->len > skb->dev->mtu && +- !skb_shinfo(skb)->gso_size) ++ !skb_is_gso(skb)) + return ip_fragment(skb, br_dev_queue_push_xmit); + else + return br_dev_queue_push_xmit(skb); +diff -pruN ../orig-linux-2.6.16.29/net/core/dev.c ./net/core/dev.c +--- ../orig-linux-2.6.16.29/net/core/dev.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/core/dev.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1083,9 +1083,17 @@ int skb_checksum_help(struct sk_buff *sk + unsigned int csum; + int ret = 0, offset = skb->h.raw - skb->data; + +- if (inward) { +- skb->ip_summed = CHECKSUM_NONE; +- goto out; ++ if (inward) ++ goto out_set_summed; ++ ++ if (unlikely(skb_shinfo(skb)->gso_size)) { ++ static int warned; ++ ++ WARN_ON(!warned); ++ warned = 1; ++ ++ /* Let GSO fix up the checksum. */ ++ goto out_set_summed; + } + + if (skb_cloned(skb)) { +@@ -1102,6 +1110,8 @@ int skb_checksum_help(struct sk_buff *sk + BUG_ON(skb->csum + 2 > offset); + + *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum); ++ ++out_set_summed: + skb->ip_summed = CHECKSUM_NONE; + out: + return ret; +@@ -1122,17 +1132,35 @@ struct sk_buff *skb_gso_segment(struct s + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); + struct packet_type *ptype; + int type = skb->protocol; ++ int err; + + BUG_ON(skb_shinfo(skb)->frag_list); +- BUG_ON(skb->ip_summed != CHECKSUM_HW); + + skb->mac.raw = skb->data; + skb->mac_len = skb->nh.raw - skb->data; + __skb_pull(skb, skb->mac_len); + ++ if (unlikely(skb->ip_summed != CHECKSUM_HW)) { ++ static int warned; ++ ++ WARN_ON(!warned); ++ warned = 1; ++ ++ if (skb_header_cloned(skb) && ++ (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) ++ return ERR_PTR(err); ++ } ++ + rcu_read_lock(); + list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { + if (ptype->type == type && !ptype->dev && ptype->gso_segment) { ++ if (unlikely(skb->ip_summed != CHECKSUM_HW)) { ++ err = ptype->gso_send_check(skb); ++ segs = ERR_PTR(err); ++ if (err || skb_gso_ok(skb, features)) ++ break; ++ __skb_push(skb, skb->data - skb->nh.raw); ++ } + segs = ptype->gso_segment(skb, features); + break; + } +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/af_inet.c ./net/ipv4/af_inet.c +--- ../orig-linux-2.6.16.29/net/ipv4/af_inet.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv4/af_inet.c 2006-09-19 13:59:46.000000000 +0100 +@@ -1085,6 +1085,40 @@ int inet_sk_rebuild_header(struct sock * + + EXPORT_SYMBOL(inet_sk_rebuild_header); + ++static int inet_gso_send_check(struct sk_buff *skb) ++{ ++ struct iphdr *iph; ++ struct net_protocol *ops; ++ int proto; ++ int ihl; ++ int err = -EINVAL; ++ ++ if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) ++ goto out; ++ ++ iph = skb->nh.iph; ++ ihl = iph->ihl * 4; ++ if (ihl < sizeof(*iph)) ++ goto out; ++ ++ if (unlikely(!pskb_may_pull(skb, ihl))) ++ goto out; ++ ++ skb->h.raw = __skb_pull(skb, ihl); ++ iph = skb->nh.iph; ++ proto = iph->protocol & (MAX_INET_PROTOS - 1); ++ err = -EPROTONOSUPPORT; ++ ++ rcu_read_lock(); ++ ops = rcu_dereference(inet_protos[proto]); ++ if (likely(ops && ops->gso_send_check)) ++ err = ops->gso_send_check(skb); ++ rcu_read_unlock(); ++ ++out: ++ return err; ++} ++ + static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) + { + struct sk_buff *segs = ERR_PTR(-EINVAL); +@@ -1142,6 +1176,7 @@ static struct net_protocol igmp_protocol + static struct net_protocol tcp_protocol = { + .handler = tcp_v4_rcv, + .err_handler = tcp_v4_err, ++ .gso_send_check = tcp_v4_gso_send_check, + .gso_segment = tcp_tso_segment, + .no_policy = 1, + }; +@@ -1188,6 +1223,7 @@ static int ipv4_proc_init(void); + static struct packet_type ip_packet_type = { + .type = __constant_htons(ETH_P_IP), + .func = ip_rcv, ++ .gso_send_check = inet_gso_send_check, + .gso_segment = inet_gso_segment, + }; + +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/ip_output.c ./net/ipv4/ip_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/ip_output.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv4/ip_output.c 2006-09-19 13:59:46.000000000 +0100 +@@ -210,7 +210,7 @@ static inline int ip_finish_output(struc + return dst_output(skb); + } + #endif +- if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) ++ if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) + return ip_fragment(skb, ip_finish_output2); + else + return ip_finish_output2(skb); +@@ -1095,7 +1095,7 @@ ssize_t ip_append_page(struct sock *sk, + while (size > 0) { + int i; + +- if (skb_shinfo(skb)->gso_size) ++ if (skb_is_gso(skb)) + len = size; + else { + +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/tcp_ipv4.c ./net/ipv4/tcp_ipv4.c +--- ../orig-linux-2.6.16.29/net/ipv4/tcp_ipv4.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./net/ipv4/tcp_ipv4.c 2006-09-19 13:59:46.000000000 +0100 +@@ -495,6 +495,24 @@ void tcp_v4_send_check(struct sock *sk, + } + } + ++int tcp_v4_gso_send_check(struct sk_buff *skb) ++{ ++ struct iphdr *iph; ++ struct tcphdr *th; ++ ++ if (!pskb_may_pull(skb, sizeof(*th))) ++ return -EINVAL; ++ ++ iph = skb->nh.iph; ++ th = skb->h.th; ++ ++ th->check = 0; ++ th->check = ~tcp_v4_check(th, skb->len, iph->saddr, iph->daddr, 0); ++ skb->csum = offsetof(struct tcphdr, check); ++ skb->ip_summed = CHECKSUM_HW; ++ return 0; ++} ++ + /* + * This routine will send an RST to the other tcp. + * +diff -pruN ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c ./net/ipv4/xfrm4_output.c +--- ../orig-linux-2.6.16.29/net/ipv4/xfrm4_output.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv4/xfrm4_output.c 2006-09-19 13:59:46.000000000 +0100 +@@ -195,7 +195,7 @@ static int xfrm4_output_finish(struct sk + } + #endif + +- if (!skb_shinfo(skb)->gso_size) ++ if (!skb_is_gso(skb)) + return xfrm4_output_finish2(skb); + + skb->protocol = htons(ETH_P_IP); +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/ip6_output.c ./net/ipv6/ip6_output.c +--- ../orig-linux-2.6.16.29/net/ipv6/ip6_output.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv6/ip6_output.c 2006-09-19 13:59:46.000000000 +0100 +@@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *s + + int ip6_output(struct sk_buff *skb) + { +- if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) || ++ if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) || + dst_allfrag(skb->dst)) + return ip6_fragment(skb, ip6_output2); + else +diff -pruN ../orig-linux-2.6.16.29/net/ipv6/xfrm6_output.c ./net/ipv6/xfrm6_output.c +--- ../orig-linux-2.6.16.29/net/ipv6/xfrm6_output.c 2006-09-19 13:59:20.000000000 +0100 ++++ ./net/ipv6/xfrm6_output.c 2006-09-19 13:59:46.000000000 +0100 +@@ -179,7 +179,7 @@ static int xfrm6_output_finish(struct sk + { + struct sk_buff *segs; + +- if (!skb_shinfo(skb)->gso_size) ++ if (!skb_is_gso(skb)) + return xfrm6_output_finish2(skb); + + skb->protocol = htons(ETH_P_IP); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-gso-3-fix-errorcheck.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-gso-3-fix-errorcheck.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,17 @@ +diff -pruN ../orig-linux-2.6.16.29/include/linux/netdevice.h ./include/linux/netdevice.h +--- ../orig-linux-2.6.16.29/include/linux/netdevice.h 2006-09-19 13:59:46.000000000 +0100 ++++ ./include/linux/netdevice.h 2006-09-19 14:05:28.000000000 +0100 +@@ -930,10 +930,10 @@ static inline void netif_tx_lock_bh(stru + + static inline int netif_tx_trylock(struct net_device *dev) + { +- int err = spin_trylock(&dev->_xmit_lock); +- if (!err) ++ int ok = spin_trylock(&dev->_xmit_lock); ++ if (likely(ok)) + dev->xmit_lock_owner = smp_processor_id(); +- return err; ++ return ok; + } + + static inline void netif_tx_unlock(struct net_device *dev) diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/net-gso-4-kill-warnon.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/net-gso-4-kill-warnon.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,27 @@ +diff -pruN ../orig-linux-2.6.16.29/net/core/dev.c ./net/core/dev.c +--- ../orig-linux-2.6.16.29/net/core/dev.c 2006-09-19 13:59:46.000000000 +0100 ++++ ./net/core/dev.c 2006-09-19 14:05:32.000000000 +0100 +@@ -1087,11 +1087,6 @@ int skb_checksum_help(struct sk_buff *sk + goto out_set_summed; + + if (unlikely(skb_shinfo(skb)->gso_size)) { +- static int warned; +- +- WARN_ON(!warned); +- warned = 1; +- + /* Let GSO fix up the checksum. */ + goto out_set_summed; + } +@@ -1141,11 +1136,6 @@ struct sk_buff *skb_gso_segment(struct s + __skb_pull(skb, skb->mac_len); + + if (unlikely(skb->ip_summed != CHECKSUM_HW)) { +- static int warned; +- +- WARN_ON(!warned); +- warned = 1; +- + if (skb_header_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + return ERR_PTR(err); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/pmd-shared.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/pmd-shared.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,111 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/mm/pageattr.c ./arch/i386/mm/pageattr.c +--- ../orig-linux-2.6.16.29/arch/i386/mm/pageattr.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/mm/pageattr.c 2006-09-19 14:05:35.000000000 +0100 +@@ -78,7 +78,7 @@ static void set_pmd_pte(pte_t *kpte, uns + unsigned long flags; + + set_pte_atomic(kpte, pte); /* change init_mm */ +- if (PTRS_PER_PMD > 1) ++ if (HAVE_SHARED_KERNEL_PMD) + return; + + spin_lock_irqsave(&pgd_lock, flags); +diff -pruN ../orig-linux-2.6.16.29/arch/i386/mm/pgtable.c ./arch/i386/mm/pgtable.c +--- ../orig-linux-2.6.16.29/arch/i386/mm/pgtable.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/mm/pgtable.c 2006-09-19 14:05:35.000000000 +0100 +@@ -215,9 +215,10 @@ void pgd_ctor(void *pgd, kmem_cache_t *c + spin_lock_irqsave(&pgd_lock, flags); + } + +- clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD, +- swapper_pg_dir + USER_PTRS_PER_PGD, +- KERNEL_PGD_PTRS); ++ if (PTRS_PER_PMD == 1 || HAVE_SHARED_KERNEL_PMD) ++ clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD, ++ swapper_pg_dir + USER_PTRS_PER_PGD, ++ KERNEL_PGD_PTRS); + if (PTRS_PER_PMD > 1) + return; + +@@ -249,6 +250,30 @@ pgd_t *pgd_alloc(struct mm_struct *mm) + goto out_oom; + set_pgd(&pgd[i], __pgd(1 + __pa(pmd))); + } ++ ++ if (!HAVE_SHARED_KERNEL_PMD) { ++ unsigned long flags; ++ ++ for (i = USER_PTRS_PER_PGD; i < PTRS_PER_PGD; i++) { ++ pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); ++ if (!pmd) ++ goto out_oom; ++ set_pgd(&pgd[USER_PTRS_PER_PGD], __pgd(1 + __pa(pmd))); ++ } ++ ++ spin_lock_irqsave(&pgd_lock, flags); ++ for (i = USER_PTRS_PER_PGD; i < PTRS_PER_PGD; i++) { ++ unsigned long v = (unsigned long)i << PGDIR_SHIFT; ++ pgd_t *kpgd = pgd_offset_k(v); ++ pud_t *kpud = pud_offset(kpgd, v); ++ pmd_t *kpmd = pmd_offset(kpud, v); ++ pmd_t *pmd = (void *)__va(pgd_val(pgd[i])-1); ++ memcpy(pmd, kpmd, PAGE_SIZE); ++ } ++ pgd_list_add(pgd); ++ spin_unlock_irqrestore(&pgd_lock, flags); ++ } ++ + return pgd; + + out_oom: +@@ -263,9 +288,23 @@ void pgd_free(pgd_t *pgd) + int i; + + /* in the PAE case user pgd entries are overwritten before usage */ +- if (PTRS_PER_PMD > 1) +- for (i = 0; i < USER_PTRS_PER_PGD; ++i) +- kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); ++ if (PTRS_PER_PMD > 1) { ++ for (i = 0; i < USER_PTRS_PER_PGD; ++i) { ++ pmd_t *pmd = (void *)__va(pgd_val(pgd[i])-1); ++ kmem_cache_free(pmd_cache, pmd); ++ } ++ if (!HAVE_SHARED_KERNEL_PMD) { ++ unsigned long flags; ++ spin_lock_irqsave(&pgd_lock, flags); ++ pgd_list_del(pgd); ++ spin_unlock_irqrestore(&pgd_lock, flags); ++ for (i = USER_PTRS_PER_PGD; i < PTRS_PER_PGD; i++) { ++ pmd_t *pmd = (void *)__va(pgd_val(pgd[i])-1); ++ memset(pmd, 0, PTRS_PER_PMD*sizeof(pmd_t)); ++ kmem_cache_free(pmd_cache, pmd); ++ } ++ } ++ } + /* in the non-PAE case, free_pgtables() clears user pgd entries */ + kmem_cache_free(pgd_cache, pgd); + } +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/pgtable-2level-defs.h ./include/asm-i386/pgtable-2level-defs.h +--- ../orig-linux-2.6.16.29/include/asm-i386/pgtable-2level-defs.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/pgtable-2level-defs.h 2006-09-19 14:05:35.000000000 +0100 +@@ -1,6 +1,8 @@ + #ifndef _I386_PGTABLE_2LEVEL_DEFS_H + #define _I386_PGTABLE_2LEVEL_DEFS_H + ++#define HAVE_SHARED_KERNEL_PMD 0 ++ + /* + * traditional i386 two-level paging structure: + */ +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/pgtable-3level-defs.h ./include/asm-i386/pgtable-3level-defs.h +--- ../orig-linux-2.6.16.29/include/asm-i386/pgtable-3level-defs.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/pgtable-3level-defs.h 2006-09-19 14:05:35.000000000 +0100 +@@ -1,6 +1,8 @@ + #ifndef _I386_PGTABLE_3LEVEL_DEFS_H + #define _I386_PGTABLE_3LEVEL_DEFS_H + ++#define HAVE_SHARED_KERNEL_PMD 1 ++ + /* + * PGDIR_SHIFT determines what a top-level page table entry can map + */ diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/rcu_needs_cpu.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/rcu_needs_cpu.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,35 @@ +diff -pruN ../orig-linux-2.6.16.29/include/linux/rcupdate.h ./include/linux/rcupdate.h +--- ../orig-linux-2.6.16.29/include/linux/rcupdate.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/rcupdate.h 2006-09-19 14:05:39.000000000 +0100 +@@ -134,6 +134,7 @@ static inline void rcu_bh_qsctr_inc(int + } + + extern int rcu_pending(int cpu); ++extern int rcu_needs_cpu(int cpu); + + /** + * rcu_read_lock - mark the beginning of an RCU read-side critical section. +diff -pruN ../orig-linux-2.6.16.29/kernel/rcupdate.c ./kernel/rcupdate.c +--- ../orig-linux-2.6.16.29/kernel/rcupdate.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./kernel/rcupdate.c 2006-09-19 14:05:39.000000000 +0100 +@@ -485,6 +485,20 @@ int rcu_pending(int cpu) + __rcu_pending(&rcu_bh_ctrlblk, &per_cpu(rcu_bh_data, cpu)); + } + ++/* ++ * Check to see if any future RCU-related work will need to be done ++ * by the current CPU, even if none need be done immediately, returning ++ * 1 if so. This function is part of the RCU implementation; it is -not- ++ * an exported member of the RCU API. ++ */ ++int rcu_needs_cpu(int cpu) ++{ ++ struct rcu_data *rdp = &per_cpu(rcu_data, cpu); ++ struct rcu_data *rdp_bh = &per_cpu(rcu_bh_data, cpu); ++ ++ return (!!rdp->curlist || !!rdp_bh->curlist || rcu_pending(cpu)); ++} ++ + void rcu_check_callbacks(int cpu, int user) + { + if (user || diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,30 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/entry.S ./arch/i386/kernel/entry.S +--- ../orig-linux-2.6.16.29/arch/i386/kernel/entry.S 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/entry.S 2006-09-19 14:05:44.000000000 +0100 +@@ -177,7 +177,7 @@ need_resched: + + # sysenter call handler stub + ENTRY(sysenter_entry) +- movl TSS_sysenter_esp0(%esp),%esp ++ movl SYSENTER_stack_esp0(%esp),%esp + sysenter_past_esp: + sti + pushl $(__USER_DS) +@@ -492,7 +492,7 @@ device_not_available_emulate: + * that sets up the real kernel stack. Check here, since we can't + * allow the wrong stack to be used. + * +- * "TSS_sysenter_esp0+12" is because the NMI/debug handler will have ++ * "SYSENTER_stack_esp0+12" is because the NMI/debug handler will have + * already pushed 3 words if it hits on the sysenter instruction: + * eflags, cs and eip. + * +@@ -504,7 +504,7 @@ device_not_available_emulate: + cmpw $__KERNEL_CS,4(%esp); \ + jne ok; \ + label: \ +- movl TSS_sysenter_esp0+offset(%esp),%esp; \ ++ movl SYSENTER_stack_esp0+offset(%esp),%esp; \ + pushfl; \ + pushl $__KERNEL_CS; \ + pushl $sysenter_past_esp diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/series --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/series Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,23 @@ +blktap-aio-16_03_06.patch +device_bind.patch +fix-hz-suspend.patch +fix-ide-cd-pio-mode.patch +i386-mach-io-check-nmi.patch +ipv6-no-autoconf.patch +net-csum.patch +net-gso-0-base.patch +net-gso-1-check-dodgy.patch +net-gso-2-checksum-fix.patch +net-gso-3-fix-errorcheck.patch +net-gso-4-kill-warnon.patch +pmd-shared.patch +rcu_needs_cpu.patch +rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch +smp-alts.patch +tpm_plugin_2.6.17.patch +x86-increase-interrupt-vector-range.patch +xen-hotplug.patch +xenoprof-generic.patch +x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch +x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch +x86-elfnote-as-preprocessor-macro.patch diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/smp-alts.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/smp-alts.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,591 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/Kconfig ./arch/i386/Kconfig +--- ../orig-linux-2.6.16.29/arch/i386/Kconfig 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/Kconfig 2006-09-19 14:05:48.000000000 +0100 +@@ -202,6 +202,19 @@ config SMP + + If you don't know what to do here, say N. + ++config SMP_ALTERNATIVES ++ bool "SMP alternatives support (EXPERIMENTAL)" ++ depends on SMP && EXPERIMENTAL ++ help ++ Try to reduce the overhead of running an SMP kernel on a uniprocessor ++ host slightly by replacing certain key instruction sequences ++ according to whether we currently have more than one CPU available. ++ This should provide a noticeable boost to performance when ++ running SMP kernels on UP machines, and have negligible impact ++ when running on an true SMP host. ++ ++ If unsure, say N. ++ + config NR_CPUS + int "Maximum number of CPUs (2-255)" + range 2 255 +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/Makefile ./arch/i386/kernel/Makefile +--- ../orig-linux-2.6.16.29/arch/i386/kernel/Makefile 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/Makefile 2006-09-19 14:05:48.000000000 +0100 +@@ -37,6 +37,7 @@ obj-$(CONFIG_EFI) += efi.o efi_stub.o + obj-$(CONFIG_DOUBLEFAULT) += doublefault.o + obj-$(CONFIG_VM86) += vm86.o + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ++obj-$(CONFIG_SMP_ALTERNATIVES) += smpalts.o + + EXTRA_AFLAGS := -traditional + +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/smpalts.c ./arch/i386/kernel/smpalts.c +--- ../orig-linux-2.6.16.29/arch/i386/kernel/smpalts.c 1970-01-01 01:00:00.000000000 +0100 ++++ ./arch/i386/kernel/smpalts.c 2006-09-19 14:05:48.000000000 +0100 +@@ -0,0 +1,85 @@ ++#include <linux/kernel.h> ++#include <asm/system.h> ++#include <asm/smp_alt.h> ++#include <asm/processor.h> ++#include <asm/string.h> ++ ++struct smp_replacement_record { ++ unsigned char targ_size; ++ unsigned char smp1_size; ++ unsigned char smp2_size; ++ unsigned char up_size; ++ unsigned char feature; ++ unsigned char data[0]; ++}; ++ ++struct smp_alternative_record { ++ void *targ_start; ++ struct smp_replacement_record *repl; ++}; ++ ++extern struct smp_alternative_record __start_smp_alternatives_table, ++ __stop_smp_alternatives_table; ++extern unsigned long __init_begin, __init_end; ++ ++void prepare_for_smp(void) ++{ ++ struct smp_alternative_record *r; ++ printk(KERN_INFO "Enabling SMP...\n"); ++ for (r = &__start_smp_alternatives_table; ++ r != &__stop_smp_alternatives_table; ++ r++) { ++ BUG_ON(r->repl->targ_size < r->repl->smp1_size); ++ BUG_ON(r->repl->targ_size < r->repl->smp2_size); ++ BUG_ON(r->repl->targ_size < r->repl->up_size); ++ if (system_state == SYSTEM_RUNNING && ++ r->targ_start >= (void *)&__init_begin && ++ r->targ_start < (void *)&__init_end) ++ continue; ++ if (r->repl->feature != (unsigned char)-1 && ++ boot_cpu_has(r->repl->feature)) { ++ memcpy(r->targ_start, ++ r->repl->data + r->repl->smp1_size, ++ r->repl->smp2_size); ++ memset(r->targ_start + r->repl->smp2_size, ++ 0x90, ++ r->repl->targ_size - r->repl->smp2_size); ++ } else { ++ memcpy(r->targ_start, ++ r->repl->data, ++ r->repl->smp1_size); ++ memset(r->targ_start + r->repl->smp1_size, ++ 0x90, ++ r->repl->targ_size - r->repl->smp1_size); ++ } ++ } ++ /* Paranoia */ ++ asm volatile ("jmp 1f\n1:"); ++ mb(); ++} ++ ++void unprepare_for_smp(void) ++{ ++ struct smp_alternative_record *r; ++ printk(KERN_INFO "Disabling SMP...\n"); ++ for (r = &__start_smp_alternatives_table; ++ r != &__stop_smp_alternatives_table; ++ r++) { ++ BUG_ON(r->repl->targ_size < r->repl->smp1_size); ++ BUG_ON(r->repl->targ_size < r->repl->smp2_size); ++ BUG_ON(r->repl->targ_size < r->repl->up_size); ++ if (system_state == SYSTEM_RUNNING && ++ r->targ_start >= (void *)&__init_begin && ++ r->targ_start < (void *)&__init_end) ++ continue; ++ memcpy(r->targ_start, ++ r->repl->data + r->repl->smp1_size + r->repl->smp2_size, ++ r->repl->up_size); ++ memset(r->targ_start + r->repl->up_size, ++ 0x90, ++ r->repl->targ_size - r->repl->up_size); ++ } ++ /* Paranoia */ ++ asm volatile ("jmp 1f\n1:"); ++ mb(); ++} +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/smpboot.c ./arch/i386/kernel/smpboot.c +--- ../orig-linux-2.6.16.29/arch/i386/kernel/smpboot.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/smpboot.c 2006-09-19 14:05:48.000000000 +0100 +@@ -1218,6 +1218,11 @@ static void __init smp_boot_cpus(unsigne + if (max_cpus <= cpucount+1) + continue; + ++#ifdef CONFIG_SMP_ALTERNATIVES ++ if (kicked == 1) ++ prepare_for_smp(); ++#endif ++ + if (((cpu = alloc_cpu_id()) <= 0) || do_boot_cpu(apicid, cpu)) + printk("CPU #%d not responding - cannot use it.\n", + apicid); +@@ -1396,6 +1401,11 @@ int __devinit __cpu_up(unsigned int cpu) + return -EIO; + } + ++#ifdef CONFIG_SMP_ALTERNATIVES ++ if (num_online_cpus() == 1) ++ prepare_for_smp(); ++#endif ++ + local_irq_enable(); + per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; + /* Unleash the CPU! */ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/vmlinux.lds.S ./arch/i386/kernel/vmlinux.lds.S +--- ../orig-linux-2.6.16.29/arch/i386/kernel/vmlinux.lds.S 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/vmlinux.lds.S 2006-09-19 14:05:48.000000000 +0100 +@@ -34,6 +34,13 @@ SECTIONS + __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { *(__ex_table) } + __stop___ex_table = .; + ++ . = ALIGN(16); ++ __start_smp_alternatives_table = .; ++ __smp_alternatives : { *(__smp_alternatives) } ++ __stop_smp_alternatives_table = .; ++ ++ __smp_replacements : { *(__smp_replacements) } ++ + RODATA + + /* writeable */ +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/atomic.h ./include/asm-i386/atomic.h +--- ../orig-linux-2.6.16.29/include/asm-i386/atomic.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/atomic.h 2006-09-19 14:05:48.000000000 +0100 +@@ -4,18 +4,13 @@ + #include <linux/config.h> + #include <linux/compiler.h> + #include <asm/processor.h> ++#include <asm/smp_alt.h> + + /* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +-#ifdef CONFIG_SMP +-#define LOCK "lock ; " +-#else +-#define LOCK "" +-#endif +- + /* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/bitops.h ./include/asm-i386/bitops.h +--- ../orig-linux-2.6.16.29/include/asm-i386/bitops.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/bitops.h 2006-09-19 14:05:48.000000000 +0100 +@@ -7,6 +7,7 @@ + + #include <linux/config.h> + #include <linux/compiler.h> ++#include <asm/smp_alt.h> + + /* + * These have to be done with inline assembly: that way the bit-setting +@@ -16,12 +17,6 @@ + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ + +-#ifdef CONFIG_SMP +-#define LOCK_PREFIX "lock ; " +-#else +-#define LOCK_PREFIX "" +-#endif +- + #define ADDR (*(volatile long *) addr) + + /** +@@ -41,7 +36,7 @@ + */ + static inline void set_bit(int nr, volatile unsigned long * addr) + { +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btsl %1,%0" + :"+m" (ADDR) + :"Ir" (nr)); +@@ -76,7 +71,7 @@ static inline void __set_bit(int nr, vol + */ + static inline void clear_bit(int nr, volatile unsigned long * addr) + { +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btrl %1,%0" + :"+m" (ADDR) + :"Ir" (nr)); +@@ -121,7 +116,7 @@ static inline void __change_bit(int nr, + */ + static inline void change_bit(int nr, volatile unsigned long * addr) + { +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btcl %1,%0" + :"+m" (ADDR) + :"Ir" (nr)); +@@ -140,7 +135,7 @@ static inline int test_and_set_bit(int n + { + int oldbit; + +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btsl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (ADDR) + :"Ir" (nr) : "memory"); +@@ -180,7 +175,7 @@ static inline int test_and_clear_bit(int + { + int oldbit; + +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btrl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (ADDR) + :"Ir" (nr) : "memory"); +@@ -231,7 +226,7 @@ static inline int test_and_change_bit(in + { + int oldbit; + +- __asm__ __volatile__( LOCK_PREFIX ++ __asm__ __volatile__( LOCK + "btcl %2,%1\n\tsbbl %0,%0" + :"=r" (oldbit),"+m" (ADDR) + :"Ir" (nr) : "memory"); +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/futex.h ./include/asm-i386/futex.h +--- ../orig-linux-2.6.16.29/include/asm-i386/futex.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/futex.h 2006-09-19 14:05:48.000000000 +0100 +@@ -28,7 +28,7 @@ + "1: movl %2, %0\n\ + movl %0, %3\n" \ + insn "\n" \ +-"2: " LOCK_PREFIX "cmpxchgl %3, %2\n\ ++"2: " LOCK "cmpxchgl %3, %2\n\ + jnz 1b\n\ + 3: .section .fixup,\"ax\"\n\ + 4: mov %5, %1\n\ +@@ -68,7 +68,7 @@ futex_atomic_op_inuser (int encoded_op, + #endif + switch (op) { + case FUTEX_OP_ADD: +- __futex_atomic_op1(LOCK_PREFIX "xaddl %0, %2", ret, ++ __futex_atomic_op1(LOCK "xaddl %0, %2", ret, + oldval, uaddr, oparg); + break; + case FUTEX_OP_OR: +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/rwsem.h ./include/asm-i386/rwsem.h +--- ../orig-linux-2.6.16.29/include/asm-i386/rwsem.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/rwsem.h 2006-09-19 14:05:48.000000000 +0100 +@@ -40,6 +40,7 @@ + + #include <linux/list.h> + #include <linux/spinlock.h> ++#include <asm/smp_alt.h> + + struct rwsem_waiter; + +@@ -99,7 +100,7 @@ static inline void __down_read(struct rw + { + __asm__ __volatile__( + "# beginning down_read\n\t" +-LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */ ++LOCK " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */ + " js 2f\n\t" /* jump if we weren't granted the lock */ + "1:\n\t" + LOCK_SECTION_START("") +@@ -130,7 +131,7 @@ static inline int __down_read_trylock(st + " movl %1,%2\n\t" + " addl %3,%2\n\t" + " jle 2f\n\t" +-LOCK_PREFIX " cmpxchgl %2,%0\n\t" ++LOCK " cmpxchgl %2,%0\n\t" + " jnz 1b\n\t" + "2:\n\t" + "# ending __down_read_trylock\n\t" +@@ -150,7 +151,7 @@ static inline void __down_write(struct r + tmp = RWSEM_ACTIVE_WRITE_BIAS; + __asm__ __volatile__( + "# beginning down_write\n\t" +-LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the old value */ ++LOCK " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the old value */ + " testl %%edx,%%edx\n\t" /* was the count 0 before? */ + " jnz 2f\n\t" /* jump if we weren't granted the lock */ + "1:\n\t" +@@ -188,7 +189,7 @@ static inline void __up_read(struct rw_s + __s32 tmp = -RWSEM_ACTIVE_READ_BIAS; + __asm__ __volatile__( + "# beginning __up_read\n\t" +-LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtracts 1, returns the old value */ ++LOCK " xadd %%edx,(%%eax)\n\t" /* subtracts 1, returns the old value */ + " js 2f\n\t" /* jump if the lock is being waited upon */ + "1:\n\t" + LOCK_SECTION_START("") +@@ -214,7 +215,7 @@ static inline void __up_write(struct rw_ + __asm__ __volatile__( + "# beginning __up_write\n\t" + " movl %2,%%edx\n\t" +-LOCK_PREFIX " xaddl %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ ++LOCK " xaddl %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */ + " jnz 2f\n\t" /* jump if the lock is being waited upon */ + "1:\n\t" + LOCK_SECTION_START("") +@@ -239,7 +240,7 @@ static inline void __downgrade_write(str + { + __asm__ __volatile__( + "# beginning __downgrade_write\n\t" +-LOCK_PREFIX " addl %2,(%%eax)\n\t" /* transitions 0xZZZZ0001 -> 0xYYYY0001 */ ++LOCK " addl %2,(%%eax)\n\t" /* transitions 0xZZZZ0001 -> 0xYYYY0001 */ + " js 2f\n\t" /* jump if the lock is being waited upon */ + "1:\n\t" + LOCK_SECTION_START("") +@@ -263,7 +264,7 @@ LOCK_PREFIX " addl %2,(%%eax)\n\t" + static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) + { + __asm__ __volatile__( +-LOCK_PREFIX "addl %1,%0" ++LOCK "addl %1,%0" + : "=m"(sem->count) + : "ir"(delta), "m"(sem->count)); + } +@@ -276,7 +277,7 @@ static inline int rwsem_atomic_update(in + int tmp = delta; + + __asm__ __volatile__( +-LOCK_PREFIX "xadd %0,(%2)" ++LOCK "xadd %0,(%2)" + : "+r"(tmp), "=m"(sem->count) + : "r"(sem), "m"(sem->count) + : "memory"); +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/smp_alt.h ./include/asm-i386/smp_alt.h +--- ../orig-linux-2.6.16.29/include/asm-i386/smp_alt.h 1970-01-01 01:00:00.000000000 +0100 ++++ ./include/asm-i386/smp_alt.h 2006-09-19 14:05:48.000000000 +0100 +@@ -0,0 +1,32 @@ ++#ifndef __ASM_SMP_ALT_H__ ++#define __ASM_SMP_ALT_H__ ++ ++#include <linux/config.h> ++ ++#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP_ALTERNATIVES) && !defined(MODULE) ++#define LOCK \ ++ "6677: nop\n" \ ++ ".section __smp_alternatives,\"a\"\n" \ ++ ".long 6677b\n" \ ++ ".long 6678f\n" \ ++ ".previous\n" \ ++ ".section __smp_replacements,\"a\"\n" \ ++ "6678: .byte 1\n" \ ++ ".byte 1\n" \ ++ ".byte 0\n" \ ++ ".byte 1\n" \ ++ ".byte -1\n" \ ++ "lock\n" \ ++ "nop\n" \ ++ ".previous\n" ++void prepare_for_smp(void); ++void unprepare_for_smp(void); ++#else ++#define LOCK "lock ; " ++#endif ++#else ++#define LOCK "" ++#endif ++ ++#endif /* __ASM_SMP_ALT_H__ */ +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/spinlock.h ./include/asm-i386/spinlock.h +--- ../orig-linux-2.6.16.29/include/asm-i386/spinlock.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/spinlock.h 2006-09-19 14:05:48.000000000 +0100 +@@ -6,6 +6,7 @@ + #include <asm/page.h> + #include <linux/config.h> + #include <linux/compiler.h> ++#include <asm/smp_alt.h> + + /* + * Your basic SMP spinlocks, allowing only a single CPU anywhere +@@ -23,7 +24,8 @@ + + #define __raw_spin_lock_string \ + "\n1:\t" \ +- "lock ; decb %0\n\t" \ ++ LOCK \ ++ "decb %0\n\t" \ + "jns 3f\n" \ + "2:\t" \ + "rep;nop\n\t" \ +@@ -34,7 +36,8 @@ + + #define __raw_spin_lock_string_flags \ + "\n1:\t" \ +- "lock ; decb %0\n\t" \ ++ LOCK \ ++ "decb %0\n\t" \ + "jns 4f\n\t" \ + "2:\t" \ + "testl $0x200, %1\n\t" \ +@@ -65,10 +68,34 @@ static inline void __raw_spin_lock_flags + static inline int __raw_spin_trylock(raw_spinlock_t *lock) + { + char oldval; ++#ifdef CONFIG_SMP_ALTERNATIVES + __asm__ __volatile__( +- "xchgb %b0,%1" ++ "1:movb %1,%b0\n" ++ "movb $0,%1\n" ++ "2:" ++ ".section __smp_alternatives,\"a\"\n" ++ ".long 1b\n" ++ ".long 3f\n" ++ ".previous\n" ++ ".section __smp_replacements,\"a\"\n" ++ "3: .byte 2b - 1b\n" ++ ".byte 5f-4f\n" ++ ".byte 0\n" ++ ".byte 6f-5f\n" ++ ".byte -1\n" ++ "4: xchgb %b0,%1\n" ++ "5: movb %1,%b0\n" ++ "movb $0,%1\n" ++ "6:\n" ++ ".previous\n" + :"=q" (oldval), "=m" (lock->slock) + :"0" (0) : "memory"); ++#else ++ __asm__ __volatile__( ++ "xchgb %b0,%1\n" ++ :"=q" (oldval), "=m" (lock->slock) ++ :"0" (0) : "memory"); ++#endif + return oldval > 0; + } + +@@ -178,12 +205,12 @@ static inline int __raw_write_trylock(ra + + static inline void __raw_read_unlock(raw_rwlock_t *rw) + { +- asm volatile("lock ; incl %0" :"=m" (rw->lock) : : "memory"); ++ asm volatile(LOCK "incl %0" :"=m" (rw->lock) : : "memory"); + } + + static inline void __raw_write_unlock(raw_rwlock_t *rw) + { +- asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ", %0" ++ asm volatile(LOCK "addl $" RW_LOCK_BIAS_STR ", %0" + : "=m" (rw->lock) : : "memory"); + } + +diff -pruN ../orig-linux-2.6.16.29/include/asm-i386/system.h ./include/asm-i386/system.h +--- ../orig-linux-2.6.16.29/include/asm-i386/system.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-i386/system.h 2006-09-19 14:05:48.000000000 +0100 +@@ -5,7 +5,7 @@ + #include <linux/kernel.h> + #include <asm/segment.h> + #include <asm/cpufeature.h> +-#include <linux/bitops.h> /* for LOCK_PREFIX */ ++#include <asm/smp_alt.h> + + #ifdef __KERNEL__ + +@@ -271,19 +271,19 @@ static inline unsigned long __cmpxchg(vo + unsigned long prev; + switch (size) { + case 1: +- __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" ++ __asm__ __volatile__(LOCK "cmpxchgb %b1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 2: +- __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" ++ __asm__ __volatile__(LOCK "cmpxchgw %w1,%2" + : "=a"(prev) + : "r"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 4: +- __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" ++ __asm__ __volatile__(LOCK "cmpxchgl %1,%2" + : "=a"(prev) + : "r"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); +@@ -336,7 +336,7 @@ static inline unsigned long long __cmpxc + unsigned long long new) + { + unsigned long long prev; +- __asm__ __volatile__(LOCK_PREFIX "cmpxchg8b %3" ++ __asm__ __volatile__(LOCK "cmpxchg8b %3" + : "=A"(prev) + : "b"((unsigned long)new), + "c"((unsigned long)(new >> 32)), +@@ -503,11 +503,55 @@ struct alt_instr { + #endif + + #ifdef CONFIG_SMP ++#if defined(CONFIG_SMP_ALTERNATIVES) && !defined(MODULE) ++#define smp_alt_mb(instr) \ ++__asm__ __volatile__("6667:\nnop\nnop\nnop\nnop\nnop\nnop\n6668:\n" \ ++ ".section __smp_alternatives,\"a\"\n" \ ++ ".long 6667b\n" \ ++ ".long 6673f\n" \ ++ ".previous\n" \ ++ ".section __smp_replacements,\"a\"\n" \ ++ "6673:.byte 6668b-6667b\n" \ ++ ".byte 6670f-6669f\n" \ ++ ".byte 6671f-6670f\n" \ ++ ".byte 0\n" \ ++ ".byte %c0\n" \ ++ "6669:lock;addl $0,0(%%esp)\n" \ ++ "6670:" instr "\n" \ ++ "6671:\n" \ ++ ".previous\n" \ ++ : \ ++ : "i" (X86_FEATURE_XMM2) \ ++ : "memory") ++#define smp_rmb() smp_alt_mb("lfence") ++#define smp_mb() smp_alt_mb("mfence") ++#define set_mb(var, value) do { \ ++unsigned long __set_mb_temp; \ ++__asm__ __volatile__("6667:movl %1, %0\n6668:\n" \ ++ ".section __smp_alternatives,\"a\"\n" \ ++ ".long 6667b\n" \ ++ ".long 6673f\n" \ ++ ".previous\n" \ ++ ".section __smp_replacements,\"a\"\n" \ ++ "6673: .byte 6668b-6667b\n" \ ++ ".byte 6670f-6669f\n" \ ++ ".byte 0\n" \ ++ ".byte 6671f-6670f\n" \ ++ ".byte -1\n" \ ++ "6669: xchg %1, %0\n" \ ++ "6670:movl %1, %0\n" \ ++ "6671:\n" \ ++ ".previous\n" \ ++ : "=m" (var), "=r" (__set_mb_temp) \ ++ : "1" (value) \ ++ : "memory"); } while (0) ++#else + #define smp_mb() mb() + #define smp_rmb() rmb() ++#define set_mb(var, value) do { (void) xchg(&var, value); } while (0) ++#endif + #define smp_wmb() wmb() + #define smp_read_barrier_depends() read_barrier_depends() +-#define set_mb(var, value) do { (void) xchg(&var, value); } while (0) + #else + #define smp_mb() barrier() + #define smp_rmb() barrier() diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/tpm_plugin_2.6.17.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/tpm_plugin_2.6.17.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,1545 @@ +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_atmel.c ./drivers/char/tpm/tpm_atmel.c +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_atmel.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/char/tpm/tpm_atmel.c 2006-09-19 14:05:52.000000000 +0100 +@@ -47,12 +47,12 @@ static int tpm_atml_recv(struct tpm_chip + return -EIO; + + for (i = 0; i < 6; i++) { +- status = ioread8(chip->vendor->iobase + 1); ++ status = ioread8(chip->vendor.iobase + 1); + if ((status & ATML_STATUS_DATA_AVAIL) == 0) { + dev_err(chip->dev, "error reading header\n"); + return -EIO; + } +- *buf++ = ioread8(chip->vendor->iobase); ++ *buf++ = ioread8(chip->vendor.iobase); + } + + /* size of the data received */ +@@ -63,7 +63,7 @@ static int tpm_atml_recv(struct tpm_chip + dev_err(chip->dev, + "Recv size(%d) less than available space\n", size); + for (; i < size; i++) { /* clear the waiting data anyway */ +- status = ioread8(chip->vendor->iobase + 1); ++ status = ioread8(chip->vendor.iobase + 1); + if ((status & ATML_STATUS_DATA_AVAIL) == 0) { + dev_err(chip->dev, "error reading data\n"); + return -EIO; +@@ -74,16 +74,16 @@ static int tpm_atml_recv(struct tpm_chip + + /* read all the data available */ + for (; i < size; i++) { +- status = ioread8(chip->vendor->iobase + 1); ++ status = ioread8(chip->vendor.iobase + 1); + if ((status & ATML_STATUS_DATA_AVAIL) == 0) { + dev_err(chip->dev, "error reading data\n"); + return -EIO; + } +- *buf++ = ioread8(chip->vendor->iobase); ++ *buf++ = ioread8(chip->vendor.iobase); + } + + /* make sure data available is gone */ +- status = ioread8(chip->vendor->iobase + 1); ++ status = ioread8(chip->vendor.iobase + 1); + + if (status & ATML_STATUS_DATA_AVAIL) { + dev_err(chip->dev, "data available is stuck\n"); +@@ -100,7 +100,7 @@ static int tpm_atml_send(struct tpm_chip + dev_dbg(chip->dev, "tpm_atml_send:\n"); + for (i = 0; i < count; i++) { + dev_dbg(chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); +- iowrite8(buf[i], chip->vendor->iobase); ++ iowrite8(buf[i], chip->vendor.iobase); + } + + return count; +@@ -108,12 +108,12 @@ static int tpm_atml_send(struct tpm_chip + + static void tpm_atml_cancel(struct tpm_chip *chip) + { +- iowrite8(ATML_STATUS_ABORT, chip->vendor->iobase + 1); ++ iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1); + } + + static u8 tpm_atml_status(struct tpm_chip *chip) + { +- return ioread8(chip->vendor->iobase + 1); ++ return ioread8(chip->vendor.iobase + 1); + } + + static struct file_operations atmel_ops = { +@@ -140,7 +140,7 @@ static struct attribute* atmel_attrs[] = + + static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; + +-static struct tpm_vendor_specific tpm_atmel = { ++static const struct tpm_vendor_specific tpm_atmel = { + .recv = tpm_atml_recv, + .send = tpm_atml_send, + .cancel = tpm_atml_cancel, +@@ -159,10 +159,10 @@ static void atml_plat_remove(void) + struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); + + if (chip) { +- if (chip->vendor->have_region) +- atmel_release_region(chip->vendor->base, +- chip->vendor->region_size); +- atmel_put_base_addr(chip->vendor); ++ if (chip->vendor.have_region) ++ atmel_release_region(chip->vendor.base, ++ chip->vendor.region_size); ++ atmel_put_base_addr(chip->vendor.iobase); + tpm_remove_hardware(chip->dev); + platform_device_unregister(pdev); + } +@@ -179,18 +179,22 @@ static struct device_driver atml_drv = { + static int __init init_atmel(void) + { + int rc = 0; ++ void __iomem *iobase = NULL; ++ int have_region, region_size; ++ unsigned long base; ++ struct tpm_chip *chip; + + driver_register(&atml_drv); + +- if ((tpm_atmel.iobase = atmel_get_base_addr(&tpm_atmel)) == NULL) { ++ if ((iobase = atmel_get_base_addr(&base, ®ion_size)) == NULL) { + rc = -ENODEV; + goto err_unreg_drv; + } + +- tpm_atmel.have_region = ++ have_region = + (atmel_request_region +- (tpm_atmel.base, tpm_atmel.region_size, +- "tpm_atmel0") == NULL) ? 0 : 1; ++ (tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1; ++ + + if (IS_ERR + (pdev = +@@ -199,17 +203,25 @@ static int __init init_atmel(void) + goto err_rel_reg; + } + +- if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0) ++ if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_atmel))) { ++ rc = -ENODEV; + goto err_unreg_dev; ++ } ++ ++ chip->vendor.iobase = iobase; ++ chip->vendor.base = base; ++ chip->vendor.have_region = have_region; ++ chip->vendor.region_size = region_size; ++ + return 0; + + err_unreg_dev: + platform_device_unregister(pdev); + err_rel_reg: +- atmel_put_base_addr(&tpm_atmel); +- if (tpm_atmel.have_region) +- atmel_release_region(tpm_atmel.base, +- tpm_atmel.region_size); ++ atmel_put_base_addr(iobase); ++ if (have_region) ++ atmel_release_region(base, ++ region_size); + err_unreg_drv: + driver_unregister(&atml_drv); + return rc; +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_atmel.h ./drivers/char/tpm/tpm_atmel.h +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_atmel.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/char/tpm/tpm_atmel.h 2006-09-19 14:05:52.000000000 +0100 +@@ -28,13 +28,12 @@ + #define atmel_request_region request_mem_region + #define atmel_release_region release_mem_region + +-static inline void atmel_put_base_addr(struct tpm_vendor_specific +- *vendor) ++static inline void atmel_put_base_addr(void __iomem *iobase) + { +- iounmap(vendor->iobase); ++ iounmap(iobase); + } + +-static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific *vendor) ++static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) + { + struct device_node *dn; + unsigned long address, size; +@@ -71,9 +70,9 @@ static void __iomem * atmel_get_base_add + else + size = reg[naddrc]; + +- vendor->base = address; +- vendor->region_size = size; +- return ioremap(vendor->base, vendor->region_size); ++ *base = address; ++ *region_size = size; ++ return ioremap(*base, *region_size); + } + #else + #define atmel_getb(chip, offset) inb(chip->vendor->base + offset) +@@ -106,14 +105,12 @@ static int atmel_verify_tpm11(void) + return 0; + } + +-static inline void atmel_put_base_addr(struct tpm_vendor_specific +- *vendor) ++static inline void atmel_put_base_addr(void __iomem *iobase) + { + } + + /* Determine where to talk to device */ +-static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific +- *vendor) ++static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) + { + int lo, hi; + +@@ -123,9 +120,9 @@ static void __iomem * atmel_get_base_add + lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); + hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); + +- vendor->base = (hi << 8) | lo; +- vendor->region_size = 2; ++ *base = (hi << 8) | lo; ++ *region_size = 2; + +- return ioport_map(vendor->base, vendor->region_size); ++ return ioport_map(*base, *region_size); + } + #endif +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_bios.c ./drivers/char/tpm/tpm_bios.c +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_bios.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/char/tpm/tpm_bios.c 2006-09-19 14:05:52.000000000 +0100 +@@ -29,6 +29,11 @@ + #define MAX_TEXT_EVENT 1000 /* Max event string length */ + #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ + ++enum bios_platform_class { ++ BIOS_CLIENT = 0x00, ++ BIOS_SERVER = 0x01, ++}; ++ + struct tpm_bios_log { + void *bios_event_log; + void *bios_event_log_end; +@@ -36,9 +41,18 @@ struct tpm_bios_log { + + struct acpi_tcpa { + struct acpi_table_header hdr; +- u16 reserved; +- u32 log_max_len __attribute__ ((packed)); +- u32 log_start_addr __attribute__ ((packed)); ++ u16 platform_class; ++ union { ++ struct client_hdr { ++ u32 log_max_len __attribute__ ((packed)); ++ u64 log_start_addr __attribute__ ((packed)); ++ } client; ++ struct server_hdr { ++ u16 reserved; ++ u64 log_max_len __attribute__ ((packed)); ++ u64 log_start_addr __attribute__ ((packed)); ++ } server; ++ }; + }; + + struct tcpa_event { +@@ -91,6 +105,12 @@ static const char* tcpa_event_type_strin + "Non-Host Info" + }; + ++struct tcpa_pc_event { ++ u32 event_id; ++ u32 event_size; ++ u8 event_data[0]; ++}; ++ + enum tcpa_pc_event_ids { + SMBIOS = 1, + BIS_CERT, +@@ -100,14 +120,15 @@ enum tcpa_pc_event_ids { + NVRAM, + OPTION_ROM_EXEC, + OPTION_ROM_CONFIG, +- OPTION_ROM_MICROCODE, ++ OPTION_ROM_MICROCODE = 10, + S_CRTM_VERSION, + S_CRTM_CONTENTS, + POST_CONTENTS, ++ HOST_TABLE_OF_DEVICES, + }; + + static const char* tcpa_pc_event_id_strings[] = { +- "" ++ "", + "SMBIOS", + "BIS Certificate", + "POST BIOS ", +@@ -116,10 +137,12 @@ static const char* tcpa_pc_event_id_stri + "NVRAM", + "Option ROM", + "Option ROM config", +- "Option ROM microcode", ++ "", ++ "Option ROM microcode ", + "S-CRTM Version", +- "S-CRTM Contents", +- "S-CRTM POST Contents", ++ "S-CRTM Contents ", ++ "POST Contents ", ++ "Table of Devices", + }; + + /* returns pointer to start of pos. entry of tcg log */ +@@ -191,7 +214,7 @@ static int get_event_name(char *dest, st + const char *name = ""; + char data[40] = ""; + int i, n_len = 0, d_len = 0; +- u32 event_id; ++ struct tcpa_pc_event *pc_event; + + switch(event->event_type) { + case PREBOOT: +@@ -220,31 +243,32 @@ static int get_event_name(char *dest, st + } + break; + case EVENT_TAG: +- event_id = be32_to_cpu(*((u32 *)event_entry)); ++ pc_event = (struct tcpa_pc_event *)event_entry; + + /* ToDo Row data -> Base64 */ + +- switch (event_id) { ++ switch (pc_event->event_id) { + case SMBIOS: + case BIS_CERT: + case CMOS: + case NVRAM: + case OPTION_ROM_EXEC: + case OPTION_ROM_CONFIG: +- case OPTION_ROM_MICROCODE: + case S_CRTM_VERSION: +- case S_CRTM_CONTENTS: +- case POST_CONTENTS: +- name = tcpa_pc_event_id_strings[event_id]; ++ name = tcpa_pc_event_id_strings[pc_event->event_id]; + n_len = strlen(name); + break; ++ /* hash data */ + case POST_BIOS_ROM: + case ESCD: +- name = tcpa_pc_event_id_strings[event_id]; ++ case OPTION_ROM_MICROCODE: ++ case S_CRTM_CONTENTS: ++ case POST_CONTENTS: ++ name = tcpa_pc_event_id_strings[pc_event->event_id]; + n_len = strlen(name); + for (i = 0; i < 20; i++) +- d_len += sprintf(data, "%02x", +- event_entry[8 + i]); ++ d_len += sprintf(&data[2*i], "%02x", ++ pc_event->event_data[i]); + break; + default: + break; +@@ -260,52 +284,13 @@ static int get_event_name(char *dest, st + + static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) + { ++ struct tcpa_event *event = v; ++ char *data = v; ++ int i; + +- char *eventname; +- char data[4]; +- u32 help; +- int i, len; +- struct tcpa_event *event = (struct tcpa_event *) v; +- unsigned char *event_entry = +- (unsigned char *) (v + sizeof(struct tcpa_event)); +- +- eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); +- if (!eventname) { +- printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", +- __func__); +- return -ENOMEM; +- } +- +- /* 1st: PCR used is in little-endian format (4 bytes) */ +- help = le32_to_cpu(event->pcr_index); +- memcpy(data, &help, 4); +- for (i = 0; i < 4; i++) +- seq_putc(m, data[i]); +- +- /* 2nd: SHA1 (20 bytes) */ +- for (i = 0; i < 20; i++) +- seq_putc(m, event->pcr_value[i]); +- +- /* 3rd: event type identifier (4 bytes) */ +- help = le32_to_cpu(event->event_type); +- memcpy(data, &help, 4); +- for (i = 0; i < 4; i++) ++ for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) + seq_putc(m, data[i]); + +- len = 0; +- +- len += get_event_name(eventname, event, event_entry); +- +- /* 4th: filename <= 255 + \'0' delimiter */ +- if (len > TCG_EVENT_NAME_LEN_MAX) +- len = TCG_EVENT_NAME_LEN_MAX; +- +- for (i = 0; i < len; i++) +- seq_putc(m, eventname[i]); +- +- /* 5th: delimiter */ +- seq_putc(m, '\0'); +- + return 0; + } + +@@ -353,6 +338,7 @@ static int tpm_ascii_bios_measurements_s + /* 4th: eventname <= max + \'0' delimiter */ + seq_printf(m, " %s\n", eventname); + ++ kfree(eventname); + return 0; + } + +@@ -376,6 +362,7 @@ static int read_log(struct tpm_bios_log + struct acpi_tcpa *buff; + acpi_status status; + struct acpi_table_header *virt; ++ u64 len, start; + + if (log->bios_event_log != NULL) { + printk(KERN_ERR +@@ -396,27 +383,37 @@ static int read_log(struct tpm_bios_log + return -EIO; + } + +- if (buff->log_max_len == 0) { ++ switch(buff->platform_class) { ++ case BIOS_SERVER: ++ len = buff->server.log_max_len; ++ start = buff->server.log_start_addr; ++ break; ++ case BIOS_CLIENT: ++ default: ++ len = buff->client.log_max_len; ++ start = buff->client.log_start_addr; ++ break; ++ } ++ if (!len) { + printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); + return -EIO; + } + + /* malloc EventLog space */ +- log->bios_event_log = kmalloc(buff->log_max_len, GFP_KERNEL); ++ log->bios_event_log = kmalloc(len, GFP_KERNEL); + if (!log->bios_event_log) { +- printk +- ("%s: ERROR - Not enough Memory for BIOS measurements\n", +- __func__); ++ printk("%s: ERROR - Not enough Memory for BIOS measurements\n", ++ __func__); + return -ENOMEM; + } + +- log->bios_event_log_end = log->bios_event_log + buff->log_max_len; ++ log->bios_event_log_end = log->bios_event_log + len; + +- acpi_os_map_memory(buff->log_start_addr, buff->log_max_len, (void *) &virt); ++ acpi_os_map_memory(start, len, (void *) &virt); + +- memcpy(log->bios_event_log, virt, buff->log_max_len); ++ memcpy(log->bios_event_log, virt, len); + +- acpi_os_unmap_memory(virt, buff->log_max_len); ++ acpi_os_unmap_memory(virt, len); + return 0; + } + +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_infineon.c ./drivers/char/tpm/tpm_infineon.c +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_infineon.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/char/tpm/tpm_infineon.c 2006-09-19 14:05:52.000000000 +0100 +@@ -15,6 +15,7 @@ + * License. + */ + ++#include <linux/init.h> + #include <linux/pnp.h> + #include "tpm.h" + +@@ -104,7 +105,7 @@ static int empty_fifo(struct tpm_chip *c + + if (clear_wrfifo) { + for (i = 0; i < 4096; i++) { +- status = inb(chip->vendor->base + WRFIFO); ++ status = inb(chip->vendor.base + WRFIFO); + if (status == 0xff) { + if (check == 5) + break; +@@ -124,8 +125,8 @@ static int empty_fifo(struct tpm_chip *c + */ + i = 0; + do { +- status = inb(chip->vendor->base + RDFIFO); +- status = inb(chip->vendor->base + STAT); ++ status = inb(chip->vendor.base + RDFIFO); ++ status = inb(chip->vendor.base + STAT); + i++; + if (i == TPM_MAX_TRIES) + return -EIO; +@@ -138,7 +139,7 @@ static int wait(struct tpm_chip *chip, i + int status; + int i; + for (i = 0; i < TPM_MAX_TRIES; i++) { +- status = inb(chip->vendor->base + STAT); ++ status = inb(chip->vendor.base + STAT); + /* check the status-register if wait_for_bit is set */ + if (status & 1 << wait_for_bit) + break; +@@ -157,7 +158,7 @@ static int wait(struct tpm_chip *chip, i + static void wait_and_send(struct tpm_chip *chip, u8 sendbyte) + { + wait(chip, STAT_XFE); +- outb(sendbyte, chip->vendor->base + WRFIFO); ++ outb(sendbyte, chip->vendor.base + WRFIFO); + } + + /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more +@@ -204,7 +205,7 @@ recv_begin: + ret = wait(chip, STAT_RDA); + if (ret) + return -EIO; +- buf[i] = inb(chip->vendor->base + RDFIFO); ++ buf[i] = inb(chip->vendor.base + RDFIFO); + } + + if (buf[0] != TPM_VL_VER) { +@@ -219,7 +220,7 @@ recv_begin: + + for (i = 0; i < size; i++) { + wait(chip, STAT_RDA); +- buf[i] = inb(chip->vendor->base + RDFIFO); ++ buf[i] = inb(chip->vendor.base + RDFIFO); + } + + if ((size == 0x6D00) && (buf[1] == 0x80)) { +@@ -268,7 +269,7 @@ static int tpm_inf_send(struct tpm_chip + u8 count_high, count_low, count_4, count_3, count_2, count_1; + + /* Disabling Reset, LP and IRQC */ +- outb(RESET_LP_IRQC_DISABLE, chip->vendor->base + CMD); ++ outb(RESET_LP_IRQC_DISABLE, chip->vendor.base + CMD); + + ret = empty_fifo(chip, 1); + if (ret) { +@@ -319,7 +320,7 @@ static void tpm_inf_cancel(struct tpm_ch + + static u8 tpm_inf_status(struct tpm_chip *chip) + { +- return inb(chip->vendor->base + STAT); ++ return inb(chip->vendor.base + STAT); + } + + static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +@@ -346,7 +347,7 @@ static struct file_operations inf_ops = + .release = tpm_release, + }; + +-static struct tpm_vendor_specific tpm_inf = { ++static const struct tpm_vendor_specific tpm_inf = { + .recv = tpm_inf_recv, + .send = tpm_inf_send, + .cancel = tpm_inf_cancel, +@@ -375,6 +376,7 @@ static int __devinit tpm_inf_pnp_probe(s + int version[2]; + int productid[2]; + char chipname[20]; ++ struct tpm_chip *chip; + + /* read IO-ports through PnP */ + if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && +@@ -395,14 +397,13 @@ static int __devinit tpm_inf_pnp_probe(s + goto err_last; + } + /* publish my base address and request region */ +- tpm_inf.base = TPM_INF_BASE; + if (request_region +- (tpm_inf.base, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) { ++ (TPM_INF_BASE, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) { + rc = -EINVAL; + goto err_last; + } +- if (request_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN, +- "tpm_infineon0") == NULL) { ++ if (request_region ++ (TPM_INF_ADDR, TPM_INF_ADDR_LEN, "tpm_infineon0") == NULL) { + rc = -EINVAL; + goto err_last; + } +@@ -442,9 +443,9 @@ static int __devinit tpm_inf_pnp_probe(s + + /* configure TPM with IO-ports */ + outb(IOLIMH, TPM_INF_ADDR); +- outb(((tpm_inf.base >> 8) & 0xff), TPM_INF_DATA); ++ outb(((TPM_INF_BASE >> 8) & 0xff), TPM_INF_DATA); + outb(IOLIML, TPM_INF_ADDR); +- outb((tpm_inf.base & 0xff), TPM_INF_DATA); ++ outb((TPM_INF_BASE & 0xff), TPM_INF_DATA); + + /* control if IO-ports are set correctly */ + outb(IOLIMH, TPM_INF_ADDR); +@@ -452,10 +453,10 @@ static int __devinit tpm_inf_pnp_probe(s + outb(IOLIML, TPM_INF_ADDR); + iol = inb(TPM_INF_DATA); + +- if ((ioh << 8 | iol) != tpm_inf.base) { ++ if ((ioh << 8 | iol) != TPM_INF_BASE) { + dev_err(&dev->dev, +- "Could not set IO-ports to 0x%lx\n", +- tpm_inf.base); ++ "Could not set IO-ports to 0x%x\n", ++ TPM_INF_BASE); + rc = -EIO; + goto err_release_region; + } +@@ -466,15 +467,15 @@ static int __devinit tpm_inf_pnp_probe(s + outb(DISABLE_REGISTER_PAIR, TPM_INF_ADDR); + + /* disable RESET, LP and IRQC */ +- outb(RESET_LP_IRQC_DISABLE, tpm_inf.base + CMD); ++ outb(RESET_LP_IRQC_DISABLE, TPM_INF_BASE + CMD); + + /* Finally, we're done, print some infos */ + dev_info(&dev->dev, "TPM found: " + "config base 0x%x, " + "io base 0x%x, " +- "chip version %02x%02x, " +- "vendor id %x%x (Infineon), " +- "product id %02x%02x" ++ "chip version 0x%02x%02x, " ++ "vendor id 0x%x%x (Infineon), " ++ "product id 0x%02x%02x" + "%s\n", + TPM_INF_ADDR, + TPM_INF_BASE, +@@ -482,11 +483,10 @@ static int __devinit tpm_inf_pnp_probe(s + vendorid[0], vendorid[1], + productid[0], productid[1], chipname); + +- rc = tpm_register_hardware(&dev->dev, &tpm_inf); +- if (rc < 0) { +- rc = -ENODEV; ++ if (!(chip = tpm_register_hardware(&dev->dev, &tpm_inf))) { + goto err_release_region; + } ++ chip->vendor.base = TPM_INF_BASE; + return 0; + } else { + rc = -ENODEV; +@@ -494,7 +494,7 @@ static int __devinit tpm_inf_pnp_probe(s + } + + err_release_region: +- release_region(tpm_inf.base, TPM_INF_PORT_LEN); ++ release_region(TPM_INF_BASE, TPM_INF_PORT_LEN); + release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN); + + err_last: +@@ -506,7 +506,8 @@ static __devexit void tpm_inf_pnp_remove + struct tpm_chip *chip = pnp_get_drvdata(dev); + + if (chip) { +- release_region(chip->vendor->base, TPM_INF_PORT_LEN); ++ release_region(TPM_INF_BASE, TPM_INF_PORT_LEN); ++ release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN); + tpm_remove_hardware(chip->dev); + } + } +@@ -520,7 +521,7 @@ static struct pnp_driver tpm_inf_pnp = { + }, + .id_table = tpm_pnp_tbl, + .probe = tpm_inf_pnp_probe, +- .remove = tpm_inf_pnp_remove, ++ .remove = __devexit_p(tpm_inf_pnp_remove), + }; + + static int __init init_inf(void) +@@ -538,5 +539,5 @@ module_exit(cleanup_inf); + + MODULE_AUTHOR("Marcel Selhorst <selhorst@xxxxxxxxxxxxx>"); + MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); +-MODULE_VERSION("1.7"); ++MODULE_VERSION("1.8"); + MODULE_LICENSE("GPL"); +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_nsc.c ./drivers/char/tpm/tpm_nsc.c +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_nsc.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/char/tpm/tpm_nsc.c 2006-09-19 14:05:52.000000000 +0100 +@@ -71,7 +71,7 @@ static int wait_for_stat(struct tpm_chip + unsigned long stop; + + /* status immediately available check */ +- *data = inb(chip->vendor->base + NSC_STATUS); ++ *data = inb(chip->vendor.base + NSC_STATUS); + if ((*data & mask) == val) + return 0; + +@@ -79,7 +79,7 @@ static int wait_for_stat(struct tpm_chip + stop = jiffies + 10 * HZ; + do { + msleep(TPM_TIMEOUT); +- *data = inb(chip->vendor->base + 1); ++ *data = inb(chip->vendor.base + 1); + if ((*data & mask) == val) + return 0; + } +@@ -94,9 +94,9 @@ static int nsc_wait_for_ready(struct tpm + unsigned long stop; + + /* status immediately available check */ +- status = inb(chip->vendor->base + NSC_STATUS); ++ status = inb(chip->vendor.base + NSC_STATUS); + if (status & NSC_STATUS_OBF) +- status = inb(chip->vendor->base + NSC_DATA); ++ status = inb(chip->vendor.base + NSC_DATA); + if (status & NSC_STATUS_RDY) + return 0; + +@@ -104,9 +104,9 @@ static int nsc_wait_for_ready(struct tpm + stop = jiffies + 100; + do { + msleep(TPM_TIMEOUT); +- status = inb(chip->vendor->base + NSC_STATUS); ++ status = inb(chip->vendor.base + NSC_STATUS); + if (status & NSC_STATUS_OBF) +- status = inb(chip->vendor->base + NSC_DATA); ++ status = inb(chip->vendor.base + NSC_DATA); + if (status & NSC_STATUS_RDY) + return 0; + } +@@ -132,7 +132,7 @@ static int tpm_nsc_recv(struct tpm_chip + return -EIO; + } + if ((data = +- inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) { ++ inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) { + dev_err(chip->dev, "not in normal mode (0x%x)\n", + data); + return -EIO; +@@ -148,7 +148,7 @@ static int tpm_nsc_recv(struct tpm_chip + } + if (data & NSC_STATUS_F0) + break; +- *p = inb(chip->vendor->base + NSC_DATA); ++ *p = inb(chip->vendor.base + NSC_DATA); + } + + if ((data & NSC_STATUS_F0) == 0 && +@@ -156,7 +156,7 @@ static int tpm_nsc_recv(struct tpm_chip + dev_err(chip->dev, "F0 not set\n"); + return -EIO; + } +- if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) { ++ if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) { + dev_err(chip->dev, + "expected end of command(0x%x)\n", data); + return -EIO; +@@ -182,7 +182,7 @@ static int tpm_nsc_send(struct tpm_chip + * fix it. Not sure why this is needed, we followed the flow + * chart in the manual to the letter. + */ +- outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); ++ outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); + + if (nsc_wait_for_ready(chip) != 0) + return -EIO; +@@ -192,7 +192,7 @@ static int tpm_nsc_send(struct tpm_chip + return -EIO; + } + +- outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND); ++ outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND); + if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { + dev_err(chip->dev, "IBR timeout\n"); + return -EIO; +@@ -204,26 +204,26 @@ static int tpm_nsc_send(struct tpm_chip + "IBF timeout (while writing data)\n"); + return -EIO; + } +- outb(buf[i], chip->vendor->base + NSC_DATA); ++ outb(buf[i], chip->vendor.base + NSC_DATA); + } + + if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { + dev_err(chip->dev, "IBF timeout\n"); + return -EIO; + } +- outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND); ++ outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND); + + return count; + } + + static void tpm_nsc_cancel(struct tpm_chip *chip) + { +- outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); ++ outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); + } + + static u8 tpm_nsc_status(struct tpm_chip *chip) + { +- return inb(chip->vendor->base + NSC_STATUS); ++ return inb(chip->vendor.base + NSC_STATUS); + } + + static struct file_operations nsc_ops = { +@@ -250,7 +250,7 @@ static struct attribute * nsc_attrs[] = + + static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs }; + +-static struct tpm_vendor_specific tpm_nsc = { ++static const struct tpm_vendor_specific tpm_nsc = { + .recv = tpm_nsc_recv, + .send = tpm_nsc_send, + .cancel = tpm_nsc_cancel, +@@ -268,7 +268,7 @@ static void __devexit tpm_nsc_remove(str + { + struct tpm_chip *chip = dev_get_drvdata(dev); + if ( chip ) { +- release_region(chip->vendor->base, 2); ++ release_region(chip->vendor.base, 2); + tpm_remove_hardware(chip->dev); + } + } +@@ -286,7 +286,8 @@ static int __init init_nsc(void) + int rc = 0; + int lo, hi; + int nscAddrBase = TPM_ADDR; +- ++ struct tpm_chip *chip; ++ unsigned long base; + + /* verify that it is a National part (SID) */ + if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) { +@@ -300,7 +301,7 @@ static int __init init_nsc(void) + + hi = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_HI); + lo = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_LO); +- tpm_nsc.base = (hi<<8) | lo; ++ base = (hi<<8) | lo; + + /* enable the DPM module */ + tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01); +@@ -320,13 +321,15 @@ static int __init init_nsc(void) + if ((rc = platform_device_register(pdev)) < 0) + goto err_free_dev; + +- if (request_region(tpm_nsc.base, 2, "tpm_nsc0") == NULL ) { ++ if (request_region(base, 2, "tpm_nsc0") == NULL ) { + rc = -EBUSY; + goto err_unreg_dev; + } + +- if ((rc = tpm_register_hardware(&pdev->dev, &tpm_nsc)) < 0) ++ if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) { ++ rc = -ENODEV; + goto err_rel_reg; ++ } + + dev_dbg(&pdev->dev, "NSC TPM detected\n"); + dev_dbg(&pdev->dev, +@@ -361,10 +364,12 @@ static int __init init_nsc(void) + "NSC TPM revision %d\n", + tpm_read_index(nscAddrBase, 0x27) & 0x1F); + ++ chip->vendor.base = base; ++ + return 0; + + err_rel_reg: +- release_region(tpm_nsc.base, 2); ++ release_region(base, 2); + err_unreg_dev: + platform_device_unregister(pdev); + err_free_dev: +diff -pruN ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_tis.c ./drivers/char/tpm/tpm_tis.c +--- ../orig-linux-2.6.16.29/drivers/char/tpm/tpm_tis.c 1970-01-01 01:00:00.000000000 +0100 ++++ ./drivers/char/tpm/tpm_tis.c 2006-09-19 14:05:52.000000000 +0100 +@@ -0,0 +1,665 @@ ++/* ++ * Copyright (C) 2005, 2006 IBM Corporation ++ * ++ * Authors: ++ * Leendert van Doorn <leendert@xxxxxxxxxxxxxx> ++ * Kylene Hall <kjhall@xxxxxxxxxx> ++ * ++ * Device driver for TCG/TCPA TPM (trusted platform module). ++ * Specifications at www.trustedcomputinggroup.org ++ * ++ * This device driver implements the TPM interface as defined in ++ * the TCG TPM Interface Spec version 1.2, revision 1.0. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/pnp.h> ++#include <linux/interrupt.h> ++#include <linux/wait.h> ++#include "tpm.h" ++ ++#define TPM_HEADER_SIZE 10 ++ ++enum tis_access { ++ TPM_ACCESS_VALID = 0x80, ++ TPM_ACCESS_ACTIVE_LOCALITY = 0x20, ++ TPM_ACCESS_REQUEST_PENDING = 0x04, ++ TPM_ACCESS_REQUEST_USE = 0x02, ++}; ++ ++enum tis_status { ++ TPM_STS_VALID = 0x80, ++ TPM_STS_COMMAND_READY = 0x40, ++ TPM_STS_GO = 0x20, ++ TPM_STS_DATA_AVAIL = 0x10, ++ TPM_STS_DATA_EXPECT = 0x08, ++}; ++ ++enum tis_int_flags { ++ TPM_GLOBAL_INT_ENABLE = 0x80000000, ++ TPM_INTF_BURST_COUNT_STATIC = 0x100, ++ TPM_INTF_CMD_READY_INT = 0x080, ++ TPM_INTF_INT_EDGE_FALLING = 0x040, ++ TPM_INTF_INT_EDGE_RISING = 0x020, ++ TPM_INTF_INT_LEVEL_LOW = 0x010, ++ TPM_INTF_INT_LEVEL_HIGH = 0x008, ++ TPM_INTF_LOCALITY_CHANGE_INT = 0x004, ++ TPM_INTF_STS_VALID_INT = 0x002, ++ TPM_INTF_DATA_AVAIL_INT = 0x001, ++}; ++ ++enum tis_defaults { ++ TIS_MEM_BASE = 0xFED40000, ++ TIS_MEM_LEN = 0x5000, ++ TIS_SHORT_TIMEOUT = 750, /* ms */ ++ TIS_LONG_TIMEOUT = 2000, /* 2 sec */ ++}; ++ ++#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) ++#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) ++#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) ++#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) ++#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) ++#define TPM_STS(l) (0x0018 | ((l) << 12)) ++#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) ++ ++#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) ++#define TPM_RID(l) (0x0F04 | ((l) << 12)) ++ ++static LIST_HEAD(tis_chips); ++static DEFINE_SPINLOCK(tis_lock); ++ ++static int check_locality(struct tpm_chip *chip, int l) ++{ ++ if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & ++ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == ++ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ++ return chip->vendor.locality = l; ++ ++ return -1; ++} ++ ++static void release_locality(struct tpm_chip *chip, int l, int force) ++{ ++ if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & ++ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == ++ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ++ iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, ++ chip->vendor.iobase + TPM_ACCESS(l)); ++} ++ ++static int request_locality(struct tpm_chip *chip, int l) ++{ ++ unsigned long stop; ++ long rc; ++ ++ if (check_locality(chip, l) >= 0) ++ return l; ++ ++ iowrite8(TPM_ACCESS_REQUEST_USE, ++ chip->vendor.iobase + TPM_ACCESS(l)); ++ ++ if (chip->vendor.irq) { ++ rc = wait_event_interruptible_timeout(chip->vendor.int_queue, ++ (check_locality ++ (chip, l) >= 0), ++ chip->vendor.timeout_a); ++ if (rc > 0) ++ return l; ++ ++ } else { ++ /* wait for burstcount */ ++ stop = jiffies + chip->vendor.timeout_a; ++ do { ++ if (check_locality(chip, l) >= 0) ++ return l; ++ msleep(TPM_TIMEOUT); ++ } ++ while (time_before(jiffies, stop)); ++ } ++ return -1; ++} ++ ++static u8 tpm_tis_status(struct tpm_chip *chip) ++{ ++ return ioread8(chip->vendor.iobase + ++ TPM_STS(chip->vendor.locality)); ++} ++ ++static void tpm_tis_ready(struct tpm_chip *chip) ++{ ++ /* this causes the current command to be aborted */ ++ iowrite8(TPM_STS_COMMAND_READY, ++ chip->vendor.iobase + TPM_STS(chip->vendor.locality)); ++} ++ ++static int get_burstcount(struct tpm_chip *chip) ++{ ++ unsigned long stop; ++ int burstcnt; ++ ++ /* wait for burstcount */ ++ /* which timeout value, spec has 2 answers (c & d) */ ++ stop = jiffies + chip->vendor.timeout_d; ++ do { ++ burstcnt = ioread8(chip->vendor.iobase + ++ TPM_STS(chip->vendor.locality) + 1); ++ burstcnt += ioread8(chip->vendor.iobase + ++ TPM_STS(chip->vendor.locality) + ++ 2) << 8; ++ if (burstcnt) ++ return burstcnt; ++ msleep(TPM_TIMEOUT); ++ } while (time_before(jiffies, stop)); ++ return -EBUSY; ++} ++ ++static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, ++ wait_queue_head_t *queue) ++{ ++ unsigned long stop; ++ long rc; ++ u8 status; ++ ++ /* check current status */ ++ status = tpm_tis_status(chip); ++ if ((status & mask) == mask) ++ return 0; ++ ++ if (chip->vendor.irq) { ++ rc = wait_event_interruptible_timeout(*queue, ++ ((tpm_tis_status ++ (chip) & mask) == ++ mask), timeout); ++ if (rc > 0) ++ return 0; ++ } else { ++ stop = jiffies + timeout; ++ do { ++ msleep(TPM_TIMEOUT); ++ status = tpm_tis_status(chip); ++ if ((status & mask) == mask) ++ return 0; ++ } while (time_before(jiffies, stop)); ++ } ++ return -ETIME; ++} ++ ++static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) ++{ ++ int size = 0, burstcnt; ++ while (size < count && ++ wait_for_stat(chip, ++ TPM_STS_DATA_AVAIL | TPM_STS_VALID, ++ chip->vendor.timeout_c, ++ &chip->vendor.read_queue) ++ == 0) { ++ burstcnt = get_burstcount(chip); ++ for (; burstcnt > 0 && size < count; burstcnt--) ++ buf[size++] = ioread8(chip->vendor.iobase + ++ TPM_DATA_FIFO(chip->vendor. ++ locality)); ++ } ++ return size; ++} ++ ++static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) ++{ ++ int size = 0; ++ int expected, status; ++ ++ if (count < TPM_HEADER_SIZE) { ++ size = -EIO; ++ goto out; ++ } ++ ++ /* read first 10 bytes, including tag, paramsize, and result */ ++ if ((size = ++ recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) { ++ dev_err(chip->dev, "Unable to read header\n"); ++ goto out; ++ } ++ ++ expected = be32_to_cpu(*(__be32 *) (buf + 2)); ++ if (expected > count) { ++ size = -EIO; ++ goto out; ++ } ++ ++ if ((size += ++ recv_data(chip, &buf[TPM_HEADER_SIZE], ++ expected - TPM_HEADER_SIZE)) < expected) { ++ dev_err(chip->dev, "Unable to read remainder of result\n"); ++ size = -ETIME; ++ goto out; ++ } ++ ++ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, ++ &chip->vendor.int_queue); ++ status = tpm_tis_status(chip); ++ if (status & TPM_STS_DATA_AVAIL) { /* retry? */ ++ dev_err(chip->dev, "Error left over data\n"); ++ size = -EIO; ++ goto out; ++ } ++ ++out: ++ tpm_tis_ready(chip); ++ release_locality(chip, chip->vendor.locality, 0); ++ return size; ++} ++ ++/* ++ * If interrupts are used (signaled by an irq set in the vendor structure) ++ * tpm.c can skip polling for the data to be available as the interrupt is ++ * waited for here ++ */ ++static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) ++{ ++ int rc, status, burstcnt; ++ size_t count = 0; ++ u32 ordinal; ++ ++ if (request_locality(chip, 0) < 0) ++ return -EBUSY; ++ ++ status = tpm_tis_status(chip); ++ if ((status & TPM_STS_COMMAND_READY) == 0) { ++ tpm_tis_ready(chip); ++ if (wait_for_stat ++ (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, ++ &chip->vendor.int_queue) < 0) { ++ rc = -ETIME; ++ goto out_err; ++ } ++ } ++ ++ while (count < len - 1) { ++ burstcnt = get_burstcount(chip); ++ for (; burstcnt > 0 && count < len - 1; burstcnt--) { ++ iowrite8(buf[count], chip->vendor.iobase + ++ TPM_DATA_FIFO(chip->vendor.locality)); ++ count++; ++ } ++ ++ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, ++ &chip->vendor.int_queue); ++ status = tpm_tis_status(chip); ++ if ((status & TPM_STS_DATA_EXPECT) == 0) { ++ rc = -EIO; ++ goto out_err; ++ } ++ } ++ ++ /* write last byte */ ++ iowrite8(buf[count], ++ chip->vendor.iobase + ++ TPM_DATA_FIFO(chip->vendor.locality)); ++ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, ++ &chip->vendor.int_queue); ++ status = tpm_tis_status(chip); ++ if ((status & TPM_STS_DATA_EXPECT) != 0) { ++ rc = -EIO; ++ goto out_err; ++ } ++ ++ /* go and do it */ ++ iowrite8(TPM_STS_GO, ++ chip->vendor.iobase + TPM_STS(chip->vendor.locality)); ++ ++ if (chip->vendor.irq) { ++ ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); ++ if (wait_for_stat ++ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, ++ tpm_calc_ordinal_duration(chip, ordinal), ++ &chip->vendor.read_queue) < 0) { ++ rc = -ETIME; ++ goto out_err; ++ } ++ } ++ return len; ++out_err: ++ tpm_tis_ready(chip); ++ release_locality(chip, chip->vendor.locality, 0); ++ return rc; ++} ++ ++static struct file_operations tis_ops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .open = tpm_open, ++ .read = tpm_read, ++ .write = tpm_write, ++ .release = tpm_release, ++}; ++ ++static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); ++static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); ++static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); ++static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); ++static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); ++static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, ++ NULL); ++static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); ++static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); ++ ++static struct attribute *tis_attrs[] = { ++ &dev_attr_pubek.attr, ++ &dev_attr_pcrs.attr, ++ &dev_attr_enabled.attr, ++ &dev_attr_active.attr, ++ &dev_attr_owned.attr, ++ &dev_attr_temp_deactivated.attr, ++ &dev_attr_caps.attr, ++ &dev_attr_cancel.attr, NULL, ++}; ++ ++static struct attribute_group tis_attr_grp = { ++ .attrs = tis_attrs ++}; ++ ++static struct tpm_vendor_specific tpm_tis = { ++ .status = tpm_tis_status, ++ .recv = tpm_tis_recv, ++ .send = tpm_tis_send, ++ .cancel = tpm_tis_ready, ++ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, ++ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, ++ .req_canceled = TPM_STS_COMMAND_READY, ++ .attr_group = &tis_attr_grp, ++ .miscdev = { ++ .fops = &tis_ops,}, ++}; ++ ++static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct tpm_chip *chip = (struct tpm_chip *) dev_id; ++ u32 interrupt; ++ ++ interrupt = ioread32(chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ ++ if (interrupt == 0) ++ return IRQ_NONE; ++ ++ chip->vendor.irq = irq; ++ ++ /* Clear interrupts handled with TPM_EOI */ ++ iowrite32(interrupt, ++ chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct tpm_chip *chip = (struct tpm_chip *) dev_id; ++ u32 interrupt; ++ int i; ++ ++ interrupt = ioread32(chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ ++ if (interrupt == 0) ++ return IRQ_NONE; ++ ++ if (interrupt & TPM_INTF_DATA_AVAIL_INT) ++ wake_up_interruptible(&chip->vendor.read_queue); ++ if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) ++ for (i = 0; i < 5; i++) ++ if (check_locality(chip, i) >= 0) ++ break; ++ if (interrupt & ++ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | ++ TPM_INTF_CMD_READY_INT)) ++ wake_up_interruptible(&chip->vendor.int_queue); ++ ++ /* Clear interrupts handled with TPM_EOI */ ++ iowrite32(interrupt, ++ chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ return IRQ_HANDLED; ++} ++ ++static int interrupts = 1; ++module_param(interrupts, bool, 0444); ++MODULE_PARM_DESC(interrupts, "Enable interrupts"); ++ ++static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, ++ const struct pnp_device_id *pnp_id) ++{ ++ u32 vendor, intfcaps, intmask; ++ int rc, i; ++ unsigned long start, len; ++ struct tpm_chip *chip; ++ ++ start = pnp_mem_start(pnp_dev, 0); ++ len = pnp_mem_len(pnp_dev, 0); ++ ++ if (!start) ++ start = TIS_MEM_BASE; ++ if (!len) ++ len = TIS_MEM_LEN; ++ ++ if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis))) ++ return -ENODEV; ++ ++ chip->vendor.iobase = ioremap(start, len); ++ if (!chip->vendor.iobase) { ++ rc = -EIO; ++ goto out_err; ++ } ++ ++ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); ++ ++ /* Default timeouts */ ++ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); ++ chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ ++ dev_info(&pnp_dev->dev, ++ "1.2 TPM (device-id 0x%X, rev-id %d)\n", ++ vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); ++ ++ /* Figure out the capabilities */ ++ intfcaps = ++ ioread32(chip->vendor.iobase + ++ TPM_INTF_CAPS(chip->vendor.locality)); ++ dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n", ++ intfcaps); ++ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) ++ dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n"); ++ if (intfcaps & TPM_INTF_CMD_READY_INT) ++ dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n"); ++ if (intfcaps & TPM_INTF_INT_EDGE_FALLING) ++ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n"); ++ if (intfcaps & TPM_INTF_INT_EDGE_RISING) ++ dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n"); ++ if (intfcaps & TPM_INTF_INT_LEVEL_LOW) ++ dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n"); ++ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) ++ dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n"); ++ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) ++ dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n"); ++ if (intfcaps & TPM_INTF_STS_VALID_INT) ++ dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n"); ++ if (intfcaps & TPM_INTF_DATA_AVAIL_INT) ++ dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n"); ++ ++ if (request_locality(chip, 0) != 0) { ++ rc = -ENODEV; ++ goto out_err; ++ } ++ ++ /* INTERRUPT Setup */ ++ init_waitqueue_head(&chip->vendor.read_queue); ++ init_waitqueue_head(&chip->vendor.int_queue); ++ ++ intmask = ++ ioread32(chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ ++ intmask |= TPM_INTF_CMD_READY_INT ++ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT ++ | TPM_INTF_STS_VALID_INT; ++ ++ iowrite32(intmask, ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ if (interrupts) { ++ chip->vendor.irq = ++ ioread8(chip->vendor.iobase + ++ TPM_INT_VECTOR(chip->vendor.locality)); ++ ++ for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { ++ iowrite8(i, chip->vendor.iobase + ++ TPM_INT_VECTOR(chip->vendor.locality)); ++ if (request_irq ++ (i, tis_int_probe, SA_SHIRQ, ++ chip->vendor.miscdev.name, chip) != 0) { ++ dev_info(chip->dev, ++ "Unable to request irq: %d for probe\n", ++ i); ++ continue; ++ } ++ ++ /* Clear all existing */ ++ iowrite32(ioread32 ++ (chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)), ++ chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ ++ /* Turn on */ ++ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ ++ /* Generate Interrupts */ ++ tpm_gen_interrupt(chip); ++ ++ /* Turn off */ ++ iowrite32(intmask, ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ free_irq(i, chip); ++ } ++ } ++ if (chip->vendor.irq) { ++ iowrite8(chip->vendor.irq, ++ chip->vendor.iobase + ++ TPM_INT_VECTOR(chip->vendor.locality)); ++ if (request_irq ++ (chip->vendor.irq, tis_int_handler, SA_SHIRQ, ++ chip->vendor.miscdev.name, chip) != 0) { ++ dev_info(chip->dev, ++ "Unable to request irq: %d for use\n", ++ chip->vendor.irq); ++ chip->vendor.irq = 0; ++ } else { ++ /* Clear all existing */ ++ iowrite32(ioread32 ++ (chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)), ++ chip->vendor.iobase + ++ TPM_INT_STATUS(chip->vendor.locality)); ++ ++ /* Turn on */ ++ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ } ++ } ++ ++ INIT_LIST_HEAD(&chip->vendor.list); ++ spin_lock(&tis_lock); ++ list_add(&chip->vendor.list, &tis_chips); ++ spin_unlock(&tis_lock); ++ ++ tpm_get_timeouts(chip); ++ tpm_continue_selftest(chip); ++ ++ return 0; ++out_err: ++ if (chip->vendor.iobase) ++ iounmap(chip->vendor.iobase); ++ tpm_remove_hardware(chip->dev); ++ return rc; ++} ++ ++static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) ++{ ++ return tpm_pm_suspend(&dev->dev, msg); ++} ++ ++static int tpm_tis_pnp_resume(struct pnp_dev *dev) ++{ ++ return tpm_pm_resume(&dev->dev); ++} ++ ++static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { ++ {"PNP0C31", 0}, /* TPM */ ++ {"ATM1200", 0}, /* Atmel */ ++ {"IFX0102", 0}, /* Infineon */ ++ {"BCM0101", 0}, /* Broadcom */ ++ {"NSC1200", 0}, /* National */ ++ /* Add new here */ ++ {"", 0}, /* User Specified */ ++ {"", 0} /* Terminator */ ++}; ++ ++static struct pnp_driver tis_pnp_driver = { ++ .name = "tpm_tis", ++ .id_table = tpm_pnp_tbl, ++ .probe = tpm_tis_pnp_init, ++ .suspend = tpm_tis_pnp_suspend, ++ .resume = tpm_tis_pnp_resume, ++}; ++ ++#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 ++module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, ++ sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); ++MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); ++ ++static int __init init_tis(void) ++{ ++ return pnp_register_driver(&tis_pnp_driver); ++} ++ ++static void __exit cleanup_tis(void) ++{ ++ struct tpm_vendor_specific *i, *j; ++ struct tpm_chip *chip; ++ spin_lock(&tis_lock); ++ list_for_each_entry_safe(i, j, &tis_chips, list) { ++ chip = to_tpm_chip(i); ++ iowrite32(~TPM_GLOBAL_INT_ENABLE & ++ ioread32(chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor. ++ locality)), ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ release_locality(chip, chip->vendor.locality, 1); ++ if (chip->vendor.irq) ++ free_irq(chip->vendor.irq, chip); ++ iounmap(i->iobase); ++ list_del(&i->list); ++ tpm_remove_hardware(chip->dev); ++ } ++ spin_unlock(&tis_lock); ++ pnp_unregister_driver(&tis_pnp_driver); ++} ++ ++module_init(init_tis); ++module_exit(cleanup_tis); ++MODULE_AUTHOR("Leendert van Doorn (leendert@xxxxxxxxxxxxxx)"); ++MODULE_DESCRIPTION("TPM Driver"); ++MODULE_VERSION("2.0"); ++MODULE_LICENSE("GPL"); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/x86-elfnote-as-preprocessor-macro.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/x86-elfnote-as-preprocessor-macro.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,43 @@ +diff -pruN ../orig-linux-2.6.16.29/include/linux/elfnote.h ./include/linux/elfnote.h +--- ../orig-linux-2.6.16.29/include/linux/elfnote.h 2006-09-19 14:06:10.000000000 +0100 ++++ ./include/linux/elfnote.h 2006-09-19 14:06:20.000000000 +0100 +@@ -31,22 +31,24 @@ + /* + * Generate a structure with the same shape as Elf{32,64}_Nhdr (which + * turn out to be the same size and shape), followed by the name and +- * desc data with appropriate padding. The 'desc' argument includes +- * the assembler pseudo op defining the type of the data: .asciz +- * "hello, world" ++ * desc data with appropriate padding. The 'desctype' argument is the ++ * assembler pseudo op defining the type of the data e.g. .asciz while ++ * 'descdata' is the data itself e.g. "hello, world". ++ * ++ * e.g. ELFNOTE(XYZCo, 42, .asciz, "forty-two") ++ * ELFNOTE(XYZCo, 12, .long, 0xdeadbeef) + */ +-.macro ELFNOTE name type desc:vararg +-.pushsection ".note.\name" +- .align 4 +- .long 2f - 1f /* namesz */ +- .long 4f - 3f /* descsz */ +- .long \type +-1:.asciz "\name" +-2:.align 4 +-3:\desc +-4:.align 4 +-.popsection +-.endm ++#define ELFNOTE(name, type, desctype, descdata) \ ++.pushsection .note.name ; \ ++ .align 4 ; \ ++ .long 2f - 1f /* namesz */ ; \ ++ .long 4f - 3f /* descsz */ ; \ ++ .long type ; \ ++1:.asciz "name" ; \ ++2:.align 4 ; \ ++3:desctype descdata ; \ ++4:.align 4 ; \ ++.popsection ; + #else /* !__ASSEMBLER__ */ + #include <linux/elf.h> + /* diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/x86-increase-interrupt-vector-range.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/x86-increase-interrupt-vector-range.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,89 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/entry.S ./arch/i386/kernel/entry.S +--- ../orig-linux-2.6.16.29/arch/i386/kernel/entry.S 2006-09-19 14:05:44.000000000 +0100 ++++ ./arch/i386/kernel/entry.S 2006-09-19 14:05:56.000000000 +0100 +@@ -406,7 +406,7 @@ vector=0 + ENTRY(irq_entries_start) + .rept NR_IRQS + ALIGN +-1: pushl $vector-256 ++1: pushl $~(vector) + jmp common_interrupt + .data + .long 1b +@@ -423,7 +423,7 @@ common_interrupt: + + #define BUILD_INTERRUPT(name, nr) \ + ENTRY(name) \ +- pushl $nr-256; \ ++ pushl $~(nr); \ + SAVE_ALL \ + movl %esp,%eax; \ + call smp_/**/name; \ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/irq.c ./arch/i386/kernel/irq.c +--- ../orig-linux-2.6.16.29/arch/i386/kernel/irq.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/i386/kernel/irq.c 2006-09-19 14:05:56.000000000 +0100 +@@ -53,8 +53,8 @@ static union irq_ctx *softirq_ctx[NR_CPU + */ + fastcall unsigned int do_IRQ(struct pt_regs *regs) + { +- /* high bits used in ret_from_ code */ +- int irq = regs->orig_eax & 0xff; ++ /* high bit used in ret_from_ code */ ++ int irq = ~regs->orig_eax; + #ifdef CONFIG_4KSTACKS + union irq_ctx *curctx, *irqctx; + u32 *isp; +diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/kernel/entry.S ./arch/x86_64/kernel/entry.S +--- ../orig-linux-2.6.16.29/arch/x86_64/kernel/entry.S 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/x86_64/kernel/entry.S 2006-09-19 14:05:56.000000000 +0100 +@@ -596,7 +596,7 @@ retint_kernel: + */ + .macro apicinterrupt num,func + INTR_FRAME +- pushq $\num-256 ++ pushq $~(\num) + CFI_ADJUST_CFA_OFFSET 8 + interrupt \func + jmp ret_from_intr +diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/kernel/irq.c ./arch/x86_64/kernel/irq.c +--- ../orig-linux-2.6.16.29/arch/x86_64/kernel/irq.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/x86_64/kernel/irq.c 2006-09-19 14:05:56.000000000 +0100 +@@ -96,8 +96,8 @@ skip: + */ + asmlinkage unsigned int do_IRQ(struct pt_regs *regs) + { +- /* high bits used in ret_from_ code */ +- unsigned irq = regs->orig_rax & 0xff; ++ /* high bit used in ret_from_ code */ ++ unsigned irq = ~regs->orig_rax; + + exit_idle(); + irq_enter(); +diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/kernel/smp.c ./arch/x86_64/kernel/smp.c +--- ../orig-linux-2.6.16.29/arch/x86_64/kernel/smp.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/x86_64/kernel/smp.c 2006-09-19 14:05:56.000000000 +0100 +@@ -135,10 +135,10 @@ asmlinkage void smp_invalidate_interrupt + + cpu = smp_processor_id(); + /* +- * orig_rax contains the interrupt vector - 256. ++ * orig_rax contains the negated interrupt vector. + * Use that to determine where the sender put the data. + */ +- sender = regs->orig_rax + 256 - INVALIDATE_TLB_VECTOR_START; ++ sender = ~regs->orig_rax - INVALIDATE_TLB_VECTOR_START; + f = &per_cpu(flush_state, sender); + + if (!cpu_isset(cpu, f->flush_cpumask)) +diff -pruN ../orig-linux-2.6.16.29/include/asm-x86_64/hw_irq.h ./include/asm-x86_64/hw_irq.h +--- ../orig-linux-2.6.16.29/include/asm-x86_64/hw_irq.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-x86_64/hw_irq.h 2006-09-19 14:05:56.000000000 +0100 +@@ -127,7 +127,7 @@ asmlinkage void IRQ_NAME(nr); \ + __asm__( \ + "\n.p2align\n" \ + "IRQ" #nr "_interrupt:\n\t" \ +- "push $" #nr "-256 ; " \ ++ "push $~(" #nr ") ; " \ + "jmp common_interrupt"); + + #if defined(CONFIG_X86_IO_APIC) diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,143 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/i386/kernel/vmlinux.lds.S ./arch/i386/kernel/vmlinux.lds.S +--- ../orig-linux-2.6.16.29/arch/i386/kernel/vmlinux.lds.S 2006-09-19 14:05:48.000000000 +0100 ++++ ./arch/i386/kernel/vmlinux.lds.S 2006-09-19 14:06:10.000000000 +0100 +@@ -12,6 +12,12 @@ OUTPUT_FORMAT("elf32-i386", "elf32-i386" + OUTPUT_ARCH(i386) + ENTRY(phys_startup_32) + jiffies = jiffies_64; ++ ++PHDRS { ++ text PT_LOAD FLAGS(5); /* R_E */ ++ data PT_LOAD FLAGS(7); /* RWE */ ++ note PT_NOTE FLAGS(4); /* R__ */ ++} + SECTIONS + { + . = __KERNEL_START; +@@ -25,7 +31,7 @@ SECTIONS + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) +- } = 0x9090 ++ } :text = 0x9090 + + _etext = .; /* End of text section */ + +@@ -47,7 +53,7 @@ SECTIONS + .data : AT(ADDR(.data) - LOAD_OFFSET) { /* Data */ + *(.data) + CONSTRUCTORS +- } ++ } :data + + . = ALIGN(4096); + __nosave_begin = .; +@@ -154,4 +160,6 @@ SECTIONS + STABS_DEBUG + + DWARF_DEBUG ++ ++ NOTES + } +diff -pruN ../orig-linux-2.6.16.29/include/asm-generic/vmlinux.lds.h ./include/asm-generic/vmlinux.lds.h +--- ../orig-linux-2.6.16.29/include/asm-generic/vmlinux.lds.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/asm-generic/vmlinux.lds.h 2006-09-19 14:06:10.000000000 +0100 +@@ -152,3 +152,6 @@ + .stab.index 0 : { *(.stab.index) } \ + .stab.indexstr 0 : { *(.stab.indexstr) } \ + .comment 0 : { *(.comment) } ++ ++#define NOTES \ ++ .notes : { *(.note.*) } :note +diff -pruN ../orig-linux-2.6.16.29/include/linux/elfnote.h ./include/linux/elfnote.h +--- ../orig-linux-2.6.16.29/include/linux/elfnote.h 1970-01-01 01:00:00.000000000 +0100 ++++ ./include/linux/elfnote.h 2006-09-19 14:06:10.000000000 +0100 +@@ -0,0 +1,88 @@ ++#ifndef _LINUX_ELFNOTE_H ++#define _LINUX_ELFNOTE_H ++/* ++ * Helper macros to generate ELF Note structures, which are put into a ++ * PT_NOTE segment of the final vmlinux image. These are useful for ++ * including name-value pairs of metadata into the kernel binary (or ++ * modules?) for use by external programs. ++ * ++ * Each note has three parts: a name, a type and a desc. The name is ++ * intended to distinguish the note's originator, so it would be a ++ * company, project, subsystem, etc; it must be in a suitable form for ++ * use in a section name. The type is an integer which is used to tag ++ * the data, and is considered to be within the "name" namespace (so ++ * "FooCo"'s type 42 is distinct from "BarProj"'s type 42). The ++ * "desc" field is the actual data. There are no constraints on the ++ * desc field's contents, though typically they're fairly small. ++ * ++ * All notes from a given NAME are put into a section named ++ * .note.NAME. When the kernel image is finally linked, all the notes ++ * are packed into a single .notes section, which is mapped into the ++ * PT_NOTE segment. Because notes for a given name are grouped into ++ * the same section, they'll all be adjacent the output file. ++ * ++ * This file defines macros for both C and assembler use. Their ++ * syntax is slightly different, but they're semantically similar. ++ * ++ * See the ELF specification for more detail about ELF notes. ++ */ ++ ++#ifdef __ASSEMBLER__ ++/* ++ * Generate a structure with the same shape as Elf{32,64}_Nhdr (which ++ * turn out to be the same size and shape), followed by the name and ++ * desc data with appropriate padding. The 'desc' argument includes ++ * the assembler pseudo op defining the type of the data: .asciz ++ * "hello, world" ++ */ ++.macro ELFNOTE name type desc:vararg ++.pushsection ".note.\name" ++ .align 4 ++ .long 2f - 1f /* namesz */ ++ .long 4f - 3f /* descsz */ ++ .long \type ++1:.asciz "\name" ++2:.align 4 ++3:\desc ++4:.align 4 ++.popsection ++.endm ++#else /* !__ASSEMBLER__ */ ++#include <linux/elf.h> ++/* ++ * Use an anonymous structure which matches the shape of ++ * Elf{32,64}_Nhdr, but includes the name and desc data. The size and ++ * type of name and desc depend on the macro arguments. "name" must ++ * be a literal string, and "desc" must be passed by value. You may ++ * only define one note per line, since __LINE__ is used to generate ++ * unique symbols. ++ */ ++#define _ELFNOTE_PASTE(a,b) a##b ++#define _ELFNOTE(size, name, unique, type, desc) \ ++ static const struct { \ ++ struct elf##size##_note _nhdr; \ ++ unsigned char _name[sizeof(name)] \ ++ __attribute__((aligned(sizeof(Elf##size##_Word)))); \ ++ typeof(desc) _desc \ ++ __attribute__((aligned(sizeof(Elf##size##_Word)))); \ ++ } _ELFNOTE_PASTE(_note_, unique) \ ++ __attribute_used__ \ ++ __attribute__((section(".note." name), \ ++ aligned(sizeof(Elf##size##_Word)), \ ++ unused)) = { \ ++ { \ ++ sizeof(name), \ ++ sizeof(desc), \ ++ type, \ ++ }, \ ++ name, \ ++ desc \ ++ } ++#define ELFNOTE(size, name, type, desc) \ ++ _ELFNOTE(size, name, __LINE__, type, desc) ++ ++#define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc) ++#define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc) ++#endif /* __ASSEMBLER__ */ ++ ++#endif /* _LINUX_ELFNOTE_H */ diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,84 @@ +diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/kernel/vmlinux.lds.S ./arch/x86_64/kernel/vmlinux.lds.S +--- ../orig-linux-2.6.16.29/arch/x86_64/kernel/vmlinux.lds.S 2006-09-12 19:02:10.000000000 +0100 ++++ ./arch/x86_64/kernel/vmlinux.lds.S 2006-09-19 14:06:15.000000000 +0100 +@@ -14,6 +14,12 @@ OUTPUT_FORMAT("elf64-x86-64", "elf64-x86 + OUTPUT_ARCH(i386:x86-64) + ENTRY(phys_startup_64) + jiffies_64 = jiffies; ++PHDRS { ++ text PT_LOAD FLAGS(5); /* R_E */ ++ data PT_LOAD FLAGS(7); /* RWE */ ++ user PT_LOAD FLAGS(7); /* RWE */ ++ note PT_NOTE FLAGS(4); /* R__ */ ++} + SECTIONS + { + . = __START_KERNEL; +@@ -26,7 +32,7 @@ SECTIONS + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) +- } = 0x9090 ++ } :text = 0x9090 + /* out-of-line lock text */ + .text.lock : AT(ADDR(.text.lock) - LOAD_OFFSET) { *(.text.lock) } + +@@ -43,17 +49,10 @@ SECTIONS + .data : AT(ADDR(.data) - LOAD_OFFSET) { + *(.data) + CONSTRUCTORS +- } ++ } :data + + _edata = .; /* End of data section */ + +- __bss_start = .; /* BSS */ +- .bss : AT(ADDR(.bss) - LOAD_OFFSET) { +- *(.bss.page_aligned) +- *(.bss) +- } +- __bss_stop = .; +- + . = ALIGN(PAGE_SIZE); + . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); + .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - LOAD_OFFSET) { +@@ -75,7 +74,7 @@ SECTIONS + #define VVIRT(x) (ADDR(x) - VVIRT_OFFSET) + + . = VSYSCALL_ADDR; +- .vsyscall_0 : AT(VSYSCALL_PHYS_ADDR) { *(.vsyscall_0) } ++ .vsyscall_0 : AT(VSYSCALL_PHYS_ADDR) { *(.vsyscall_0) } :user + __vsyscall_0 = VSYSCALL_VIRT_ADDR; + + . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); +@@ -118,7 +117,7 @@ SECTIONS + . = ALIGN(8192); /* init_task */ + .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) { + *(.data.init_task) +- } ++ } :data + + . = ALIGN(4096); + .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) { +@@ -188,6 +187,14 @@ SECTIONS + . = ALIGN(4096); + __nosave_end = .; + ++ __bss_start = .; /* BSS */ ++ . = ALIGN(4096); ++ .bss : AT(ADDR(.bss) - LOAD_OFFSET) { ++ *(.bss.page_aligned) ++ *(.bss) ++ } ++ __bss_stop = .; ++ + _end = . ; + + /* Sections to be discarded */ +@@ -201,4 +208,6 @@ SECTIONS + STABS_DEBUG + + DWARF_DEBUG ++ ++ NOTES + } diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/xen-hotplug.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/xen-hotplug.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,12 @@ +diff -pruN ../orig-linux-2.6.16.29/fs/proc/proc_misc.c ./fs/proc/proc_misc.c +--- ../orig-linux-2.6.16.29/fs/proc/proc_misc.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./fs/proc/proc_misc.c 2006-09-19 14:06:00.000000000 +0100 +@@ -433,7 +433,7 @@ static int show_stat(struct seq_file *p, + (unsigned long long)cputime64_to_clock_t(irq), + (unsigned long long)cputime64_to_clock_t(softirq), + (unsigned long long)cputime64_to_clock_t(steal)); +- for_each_online_cpu(i) { ++ for_each_cpu(i) { + + /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ + user = kstat_cpu(i).cpustat.user; diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.29/xenoprof-generic.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/linux-2.6.16.29/xenoprof-generic.patch Tue Sep 19 14:26:47 2006 +0100 @@ -0,0 +1,615 @@ +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/oprofile/buffer_sync.c +--- ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/buffer_sync.c 2006-09-19 14:06:05.000000000 +0100 +@@ -6,6 +6,10 @@ + * + * @author John Levon <levon@xxxxxxxxxxxxxxxxx> + * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. ++ * + * This is the core of the buffer management. Each + * CPU buffer is processed and entered into the + * global event buffer. Such processing is necessary +@@ -275,15 +279,31 @@ static void add_cpu_switch(int i) + last_cookie = INVALID_COOKIE; + } + +-static void add_kernel_ctx_switch(unsigned int in_kernel) ++static void add_cpu_mode_switch(unsigned int cpu_mode) + { + add_event_entry(ESCAPE_CODE); +- if (in_kernel) +- add_event_entry(KERNEL_ENTER_SWITCH_CODE); +- else +- add_event_entry(KERNEL_EXIT_SWITCH_CODE); ++ switch (cpu_mode) { ++ case CPU_MODE_USER: ++ add_event_entry(USER_ENTER_SWITCH_CODE); ++ break; ++ case CPU_MODE_KERNEL: ++ add_event_entry(KERNEL_ENTER_SWITCH_CODE); ++ break; ++ case CPU_MODE_XEN: ++ add_event_entry(XEN_ENTER_SWITCH_CODE); ++ break; ++ default: ++ break; ++ } + } +- ++ ++static void add_domain_switch(unsigned long domain_id) ++{ ++ add_event_entry(ESCAPE_CODE); ++ add_event_entry(DOMAIN_SWITCH_CODE); ++ add_event_entry(domain_id); ++} ++ + static void + add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) + { +@@ -348,9 +368,9 @@ static int add_us_sample(struct mm_struc + * for later lookup from userspace. + */ + static int +-add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) ++add_sample(struct mm_struct * mm, struct op_sample * s, int cpu_mode) + { +- if (in_kernel) { ++ if (cpu_mode >= CPU_MODE_KERNEL) { + add_sample_entry(s->eip, s->event); + return 1; + } else if (mm) { +@@ -496,10 +516,11 @@ void sync_buffer(int cpu) + struct mm_struct *mm = NULL; + struct task_struct * new; + unsigned long cookie = 0; +- int in_kernel = 1; ++ int cpu_mode = 1; + unsigned int i; + sync_buffer_state state = sb_buffer_start; + unsigned long available; ++ int domain_switch = 0; + + down(&buffer_sem); + +@@ -512,16 +533,18 @@ void sync_buffer(int cpu) + for (i = 0; i < available; ++i) { + struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; + +- if (is_code(s->eip)) { +- if (s->event <= CPU_IS_KERNEL) { +- /* kernel/userspace switch */ +- in_kernel = s->event; ++ if (is_code(s->eip) && !domain_switch) { ++ if (s->event <= CPU_MODE_XEN) { ++ /* xen/kernel/userspace switch */ ++ cpu_mode = s->event; + if (state == sb_buffer_start) + state = sb_sample_start; +- add_kernel_ctx_switch(s->event); ++ add_cpu_mode_switch(s->event); + } else if (s->event == CPU_TRACE_BEGIN) { + state = sb_bt_start; + add_trace_begin(); ++ } else if (s->event == CPU_DOMAIN_SWITCH) { ++ domain_switch = 1; + } else { + struct mm_struct * oldmm = mm; + +@@ -535,11 +558,16 @@ void sync_buffer(int cpu) + add_user_ctx_switch(new, cookie); + } + } else { +- if (state >= sb_bt_start && +- !add_sample(mm, s, in_kernel)) { +- if (state == sb_bt_start) { +- state = sb_bt_ignore; +- atomic_inc(&oprofile_stats.bt_lost_no_mapping); ++ if (domain_switch) { ++ add_domain_switch(s->eip); ++ domain_switch = 0; ++ } else { ++ if (state >= sb_bt_start && ++ !add_sample(mm, s, cpu_mode)) { ++ if (state == sb_bt_start) { ++ state = sb_bt_ignore; ++ atomic_inc(&oprofile_stats.bt_lost_no_mapping); ++ } + } + } + } +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c ./drivers/oprofile/cpu_buffer.c +--- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/cpu_buffer.c 2006-09-19 14:06:05.000000000 +0100 +@@ -6,6 +6,10 @@ + * + * @author John Levon <levon@xxxxxxxxxxxxxxxxx> + * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. ++ * + * Each CPU has a local buffer that stores PC value/event + * pairs. We also log context switches when we notice them. + * Eventually each CPU's buffer is processed into the global +@@ -34,6 +38,8 @@ static void wq_sync_buffer(void *); + #define DEFAULT_TIMER_EXPIRE (HZ / 10) + static int work_enabled; + ++static int32_t current_domain = COORDINATOR_DOMAIN; ++ + void free_cpu_buffers(void) + { + int i; +@@ -58,7 +64,7 @@ int alloc_cpu_buffers(void) + goto fail; + + b->last_task = NULL; +- b->last_is_kernel = -1; ++ b->last_cpu_mode = -1; + b->tracing = 0; + b->buffer_size = buffer_size; + b->tail_pos = 0; +@@ -114,7 +120,7 @@ void cpu_buffer_reset(struct oprofile_cp + * collected will populate the buffer with proper + * values to initialize the buffer + */ +- cpu_buf->last_is_kernel = -1; ++ cpu_buf->last_cpu_mode = -1; + cpu_buf->last_task = NULL; + } + +@@ -164,13 +170,13 @@ add_code(struct oprofile_cpu_buffer * bu + * because of the head/tail separation of the writer and reader + * of the CPU buffer. + * +- * is_kernel is needed because on some architectures you cannot ++ * cpu_mode is needed because on some architectures you cannot + * tell if you are in kernel or user space simply by looking at +- * pc. We tag this in the buffer by generating kernel enter/exit +- * events whenever is_kernel changes ++ * pc. We tag this in the buffer by generating kernel/user (and xen) ++ * enter events whenever cpu_mode changes + */ + static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, +- int is_kernel, unsigned long event) ++ int cpu_mode, unsigned long event) + { + struct task_struct * task; + +@@ -181,18 +187,18 @@ static int log_sample(struct oprofile_cp + return 0; + } + +- is_kernel = !!is_kernel; +- + task = current; + + /* notice a switch from user->kernel or vice versa */ +- if (cpu_buf->last_is_kernel != is_kernel) { +- cpu_buf->last_is_kernel = is_kernel; +- add_code(cpu_buf, is_kernel); ++ if (cpu_buf->last_cpu_mode != cpu_mode) { ++ cpu_buf->last_cpu_mode = cpu_mode; ++ add_code(cpu_buf, cpu_mode); + } +- ++ + /* notice a task switch */ +- if (cpu_buf->last_task != task) { ++ /* if not processing other domain samples */ ++ if ((cpu_buf->last_task != task) && ++ (current_domain == COORDINATOR_DOMAIN)) { + cpu_buf->last_task = task; + add_code(cpu_buf, (unsigned long)task); + } +@@ -269,6 +275,25 @@ void oprofile_add_trace(unsigned long pc + add_sample(cpu_buf, pc, 0); + } + ++int oprofile_add_domain_switch(int32_t domain_id) ++{ ++ struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[smp_processor_id()]; ++ ++ /* should have space for switching into and out of domain ++ (2 slots each) plus one sample and one cpu mode switch */ ++ if (((nr_available_slots(cpu_buf) < 6) && ++ (domain_id != COORDINATOR_DOMAIN)) || ++ (nr_available_slots(cpu_buf) < 2)) ++ return 0; ++ ++ add_code(cpu_buf, CPU_DOMAIN_SWITCH); ++ add_sample(cpu_buf, domain_id, 0); ++ ++ current_domain = domain_id; ++ ++ return 1; ++} ++ + /* + * This serves to avoid cpu buffer overflow, and makes sure + * the task mortuary progresses +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h ./drivers/oprofile/cpu_buffer.h +--- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/cpu_buffer.h 2006-09-19 14:06:05.000000000 +0100 +@@ -36,7 +36,7 @@ struct oprofile_cpu_buffer { + volatile unsigned long tail_pos; + unsigned long buffer_size; + struct task_struct * last_task; +- int last_is_kernel; ++ int last_cpu_mode; + int tracing; + struct op_sample * buffer; + unsigned long sample_received; +@@ -51,7 +51,10 @@ extern struct oprofile_cpu_buffer cpu_bu + void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); + + /* transient events for the CPU buffer -> event buffer */ +-#define CPU_IS_KERNEL 1 +-#define CPU_TRACE_BEGIN 2 ++#define CPU_MODE_USER 0 ++#define CPU_MODE_KERNEL 1 ++#define CPU_MODE_XEN 2 ++#define CPU_TRACE_BEGIN 3 ++#define CPU_DOMAIN_SWITCH 4 + + #endif /* OPROFILE_CPU_BUFFER_H */ +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h ./drivers/oprofile/event_buffer.h +--- ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/event_buffer.h 2006-09-19 14:06:05.000000000 +0100 +@@ -29,15 +29,20 @@ void wake_up_buffer_waiter(void); + #define CPU_SWITCH_CODE 2 + #define COOKIE_SWITCH_CODE 3 + #define KERNEL_ENTER_SWITCH_CODE 4 +-#define KERNEL_EXIT_SWITCH_CODE 5 ++#define USER_ENTER_SWITCH_CODE 5 + #define MODULE_LOADED_CODE 6 + #define CTX_TGID_CODE 7 + #define TRACE_BEGIN_CODE 8 + #define TRACE_END_CODE 9 ++#define XEN_ENTER_SWITCH_CODE 10 ++#define DOMAIN_SWITCH_CODE 11 + + #define INVALID_COOKIE ~0UL + #define NO_COOKIE 0UL + ++/* Constant used to refer to coordinator domain (Xen) */ ++#define COORDINATOR_DOMAIN -1 ++ + /* add data to the event buffer */ + void add_event_entry(unsigned long data); + +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c ./drivers/oprofile/oprof.c +--- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/oprof.c 2006-09-19 14:06:05.000000000 +0100 +@@ -5,6 +5,10 @@ + * @remark Read the file COPYING + * + * @author John Levon <levon@xxxxxxxxxxxxxxxxx> ++ * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. + */ + + #include <linux/kernel.h> +@@ -19,7 +23,7 @@ + #include "cpu_buffer.h" + #include "buffer_sync.h" + #include "oprofile_stats.h" +- ++ + struct oprofile_operations oprofile_ops; + + unsigned long oprofile_started; +@@ -33,6 +37,32 @@ static DECLARE_MUTEX(start_sem); + */ + static int timer = 0; + ++int oprofile_set_active(int active_domains[], unsigned int adomains) ++{ ++ int err; ++ ++ if (!oprofile_ops.set_active) ++ return -EINVAL; ++ ++ down(&start_sem); ++ err = oprofile_ops.set_active(active_domains, adomains); ++ up(&start_sem); ++ return err; ++} ++ ++int oprofile_set_passive(int passive_domains[], unsigned int pdomains) ++{ ++ int err; ++ ++ if (!oprofile_ops.set_passive) ++ return -EINVAL; ++ ++ down(&start_sem); ++ err = oprofile_ops.set_passive(passive_domains, pdomains); ++ up(&start_sem); ++ return err; ++} ++ + int oprofile_setup(void) + { + int err; +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h ./drivers/oprofile/oprof.h +--- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/oprof.h 2006-09-19 14:06:05.000000000 +0100 +@@ -35,5 +35,8 @@ void oprofile_create_files(struct super_ + void oprofile_timer_init(struct oprofile_operations * ops); + + int oprofile_set_backtrace(unsigned long depth); ++ ++int oprofile_set_active(int active_domains[], unsigned int adomains); ++int oprofile_set_passive(int passive_domains[], unsigned int pdomains); + + #endif /* OPROF_H */ +diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c ./drivers/oprofile/oprofile_files.c +--- ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c 2006-09-12 19:02:10.000000000 +0100 ++++ ./drivers/oprofile/oprofile_files.c 2006-09-19 14:06:05.000000000 +0100 +@@ -5,15 +5,21 @@ + * @remark Read the file COPYING + * + * @author John Levon <levon@xxxxxxxxxxxxxxxxx> ++ * ++ * Modified by Aravind Menon for Xen ++ * These modifications are: ++ * Copyright (C) 2005 Hewlett-Packard Co. + */ + + #include <linux/fs.h> + #include <linux/oprofile.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> + + #include "event_buffer.h" + #include "oprofile_stats.h" + #include "oprof.h" +- ++ + unsigned long fs_buffer_size = 131072; + unsigned long fs_cpu_buffer_size = 8192; + unsigned long fs_buffer_watershed = 32768; /* FIXME: tune */ +@@ -117,11 +123,202 @@ static ssize_t dump_write(struct file * + static struct file_operations dump_fops = { + .write = dump_write, + }; +- ++ ++#define TMPBUFSIZE 512 ++ ++static unsigned int adomains = 0; ++static int active_domains[MAX_OPROF_DOMAINS + 1]; ++static DEFINE_MUTEX(adom_mutex); ++ ++static ssize_t adomain_write(struct file * file, char const __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char *tmpbuf; ++ char *startp, *endp; ++ int i; ++ unsigned long val; ++ ssize_t retval = count; ++ ++ if (*offset) ++ return -EINVAL; ++ if (count > TMPBUFSIZE - 1) ++ return -EINVAL; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmpbuf, buf, count)) { ++ kfree(tmpbuf); ++ return -EFAULT; ++ } ++ tmpbuf[count] = 0; ++ ++ mutex_lock(&adom_mutex); ++ ++ startp = tmpbuf; ++ /* Parse one more than MAX_OPROF_DOMAINS, for easy error checking */ ++ for (i = 0; i <= MAX_OPROF_DOMAINS; i++) { ++ val = simple_strtoul(startp, &endp, 0); ++ if (endp == startp) ++ break; ++ while (ispunct(*endp) || isspace(*endp)) ++ endp++; ++ active_domains[i] = val; ++ if (active_domains[i] != val) ++ /* Overflow, force error below */ ++ i = MAX_OPROF_DOMAINS + 1; ++ startp = endp; ++ } ++ /* Force error on trailing junk */ ++ adomains = *startp ? MAX_OPROF_DOMAINS + 1 : i; ++ ++ kfree(tmpbuf); ++ ++ if (adomains > MAX_OPROF_DOMAINS ++ || oprofile_set_active(active_domains, adomains)) { ++ adomains = 0; ++ retval = -EINVAL; ++ } ++ ++ mutex_unlock(&adom_mutex); ++ return retval; ++} ++ ++static ssize_t adomain_read(struct file * file, char __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char * tmpbuf; ++ size_t len; ++ int i; ++ ssize_t retval; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ mutex_lock(&adom_mutex); ++ ++ len = 0; ++ for (i = 0; i < adomains; i++) ++ len += snprintf(tmpbuf + len, ++ len < TMPBUFSIZE ? TMPBUFSIZE - len : 0, ++ "%u ", active_domains[i]); ++ WARN_ON(len > TMPBUFSIZE); ++ if (len != 0 && len <= TMPBUFSIZE) ++ tmpbuf[len-1] = '\n'; ++ ++ mutex_unlock(&adom_mutex); ++ ++ retval = simple_read_from_buffer(buf, count, offset, tmpbuf, len); ++ ++ kfree(tmpbuf); ++ return retval; ++} ++ ++ ++static struct file_operations active_domain_ops = { ++ .read = adomain_read, ++ .write = adomain_write, ++}; ++ ++static unsigned int pdomains = 0; ++static int passive_domains[MAX_OPROF_DOMAINS]; ++static DEFINE_MUTEX(pdom_mutex); ++ ++static ssize_t pdomain_write(struct file * file, char const __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char *tmpbuf; ++ char *startp, *endp; ++ int i; ++ unsigned long val; ++ ssize_t retval = count; ++ ++ if (*offset) ++ return -EINVAL; ++ if (count > TMPBUFSIZE - 1) ++ return -EINVAL; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmpbuf, buf, count)) { ++ kfree(tmpbuf); ++ return -EFAULT; ++ } ++ tmpbuf[count] = 0; ++ ++ mutex_lock(&pdom_mutex); ++ ++ startp = tmpbuf; ++ /* Parse one more than MAX_OPROF_DOMAINS, for easy error checking */ ++ for (i = 0; i <= MAX_OPROF_DOMAINS; i++) { ++ val = simple_strtoul(startp, &endp, 0); ++ if (endp == startp) ++ break; ++ while (ispunct(*endp) || isspace(*endp)) ++ endp++; ++ passive_domains[i] = val; ++ if (passive_domains[i] != val) ++ /* Overflow, force error below */ ++ i = MAX_OPROF_DOMAINS + 1; ++ startp = endp; ++ } ++ /* Force error on trailing junk */ ++ pdomains = *startp ? MAX_OPROF_DOMAINS + 1 : i; ++ ++ kfree(tmpbuf); ++ ++ if (pdomains > MAX_OPROF_DOMAINS ++ || oprofile_set_passive(passive_domains, pdomains)) { ++ pdomains = 0; ++ retval = -EINVAL; ++ } ++ ++ mutex_unlock(&pdom_mutex); ++ return retval; ++} ++ ++static ssize_t pdomain_read(struct file * file, char __user * buf, ++ size_t count, loff_t * offset) ++{ ++ char * tmpbuf; ++ size_t len; ++ int i; ++ ssize_t retval; ++ ++ if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ mutex_lock(&pdom_mutex); ++ ++ len = 0; ++ for (i = 0; i < pdomains; i++) ++ len += snprintf(tmpbuf + len, ++ len < TMPBUFSIZE ? TMPBUFSIZE - len : 0, ++ "%u ", passive_domains[i]); ++ WARN_ON(len > TMPBUFSIZE); ++ if (len != 0 && len <= TMPBUFSIZE) ++ tmpbuf[len-1] = '\n'; ++ ++ mutex_unlock(&pdom_mutex); ++ ++ retval = simple_read_from_buffer(buf, count, offset, tmpbuf, len); ++ ++ kfree(tmpbuf); ++ return retval; ++} ++ ++static struct file_operations passive_domain_ops = { ++ .read = pdomain_read, ++ .write = pdomain_write, ++}; ++ + void oprofile_create_files(struct super_block * sb, struct dentry * root) + { + oprofilefs_create_file(sb, root, "enable", &enable_fops); + oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666); ++ oprofilefs_create_file(sb, root, "active_domains", &active_domain_ops); ++ oprofilefs_create_file(sb, root, "passive_domains", &passive_domain_ops); + oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops); + oprofilefs_create_ulong(sb, root, "buffer_size", &fs_buffer_size); + oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed); +diff -pruN ../orig-linux-2.6.16.29/include/linux/oprofile.h ./include/linux/oprofile.h +--- ../orig-linux-2.6.16.29/include/linux/oprofile.h 2006-09-12 19:02:10.000000000 +0100 ++++ ./include/linux/oprofile.h 2006-09-19 14:06:05.000000000 +0100 +@@ -16,6 +16,8 @@ + #include <linux/types.h> + #include <linux/spinlock.h> + #include <asm/atomic.h> ++ ++#include <xen/interface/xenoprof.h> + + struct super_block; + struct dentry; +@@ -27,6 +29,11 @@ struct oprofile_operations { + /* create any necessary configuration files in the oprofile fs. + * Optional. */ + int (*create_files)(struct super_block * sb, struct dentry * root); ++ /* setup active domains with Xen */ ++ int (*set_active)(int *active_domains, unsigned int adomains); ++ /* setup passive domains with Xen */ ++ int (*set_passive)(int *passive_domains, unsigned int pdomains); ++ + /* Do any necessary interrupt setup. Optional. */ + int (*setup)(void); + /* Do any necessary interrupt shutdown. Optional. */ +@@ -68,6 +75,8 @@ void oprofile_add_pc(unsigned long pc, i + /* add a backtrace entry, to be called from the ->backtrace callback */ + void oprofile_add_trace(unsigned long eip); + ++/* add a domain switch entry */ ++int oprofile_add_domain_switch(int32_t domain_id); + + /** + * Create a file of the given name as a child of the given root, with diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/blktap-aio-16_03_06.patch --- a/patches/linux-2.6.16.13/blktap-aio-16_03_06.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,297 +0,0 @@ -diff -pruN ../pristine-linux-2.6.16-rc5/fs/aio.c ./fs/aio.c ---- ../pristine-linux-2.6.16-rc5/fs/aio.c 2006-03-14 14:10:10.827401387 +0000 -+++ ./fs/aio.c 2006-03-16 09:57:53.898316582 +0000 -@@ -34,6 +34,11 @@ - #include <asm/uaccess.h> - #include <asm/mmu_context.h> - -+#ifdef CONFIG_EPOLL -+#include <linux/poll.h> -+#include <linux/eventpoll.h> -+#endif -+ - #if DEBUG > 1 - #define dprintk printk - #else -@@ -1016,6 +1021,10 @@ put_rq: - if (waitqueue_active(&ctx->wait)) - wake_up(&ctx->wait); - -+#ifdef CONFIG_EPOLL -+ if (ctx->file && waitqueue_active(&ctx->poll_wait)) -+ wake_up(&ctx->poll_wait); -+#endif - if (ret) - put_ioctx(ctx); - -@@ -1025,6 +1034,8 @@ put_rq: - /* aio_read_evt - * Pull an event off of the ioctx's event ring. Returns the number of - * events fetched (0 or 1 ;-) -+ * If ent parameter is 0, just returns the number of events that would -+ * be fetched. - * FIXME: make this use cmpxchg. - * TODO: make the ringbuffer user mmap()able (requires FIXME). - */ -@@ -1047,13 +1058,18 @@ static int aio_read_evt(struct kioctx *i - - head = ring->head % info->nr; - if (head != ring->tail) { -- struct io_event *evp = aio_ring_event(info, head, KM_USER1); -- *ent = *evp; -- head = (head + 1) % info->nr; -- smp_mb(); /* finish reading the event before updatng the head */ -- ring->head = head; -- ret = 1; -- put_aio_ring_event(evp, KM_USER1); -+ if (ent) { /* event requested */ -+ struct io_event *evp = -+ aio_ring_event(info, head, KM_USER1); -+ *ent = *evp; -+ head = (head + 1) % info->nr; -+ /* finish reading the event before updatng the head */ -+ smp_mb(); -+ ring->head = head; -+ ret = 1; -+ put_aio_ring_event(evp, KM_USER1); -+ } else /* only need to know availability */ -+ ret = 1; - } - spin_unlock(&info->ring_lock); - -@@ -1236,9 +1252,78 @@ static void io_destroy(struct kioctx *io - - aio_cancel_all(ioctx); - wait_for_all_aios(ioctx); -+#ifdef CONFIG_EPOLL -+ /* forget the poll file, but it's up to the user to close it */ -+ if (ioctx->file) { -+ ioctx->file->private_data = 0; -+ ioctx->file = 0; -+ } -+#endif - put_ioctx(ioctx); /* once for the lookup */ - } - -+#ifdef CONFIG_EPOLL -+ -+static int aio_queue_fd_close(struct inode *inode, struct file *file) -+{ -+ struct kioctx *ioctx = file->private_data; -+ if (ioctx) { -+ file->private_data = 0; -+ spin_lock_irq(&ioctx->ctx_lock); -+ ioctx->file = 0; -+ spin_unlock_irq(&ioctx->ctx_lock); -+ } -+ return 0; -+} -+ -+static unsigned int aio_queue_fd_poll(struct file *file, poll_table *wait) -+{ unsigned int pollflags = 0; -+ struct kioctx *ioctx = file->private_data; -+ -+ if (ioctx) { -+ -+ spin_lock_irq(&ioctx->ctx_lock); -+ /* Insert inside our poll wait queue */ -+ poll_wait(file, &ioctx->poll_wait, wait); -+ -+ /* Check our condition */ -+ if (aio_read_evt(ioctx, 0)) -+ pollflags = POLLIN | POLLRDNORM; -+ spin_unlock_irq(&ioctx->ctx_lock); -+ } -+ -+ return pollflags; -+} -+ -+static struct file_operations aioq_fops = { -+ .release = aio_queue_fd_close, -+ .poll = aio_queue_fd_poll -+}; -+ -+/* make_aio_fd: -+ * Create a file descriptor that can be used to poll the event queue. -+ * Based and piggybacked on the excellent epoll code. -+ */ -+ -+static int make_aio_fd(struct kioctx *ioctx) -+{ -+ int error, fd; -+ struct inode *inode; -+ struct file *file; -+ -+ error = ep_getfd(&fd, &inode, &file, NULL, &aioq_fops); -+ if (error) -+ return error; -+ -+ /* associate the file with the IO context */ -+ file->private_data = ioctx; -+ ioctx->file = file; -+ init_waitqueue_head(&ioctx->poll_wait); -+ return fd; -+} -+#endif -+ -+ - /* sys_io_setup: - * Create an aio_context capable of receiving at least nr_events. - * ctxp must not point to an aio_context that already exists, and -@@ -1251,18 +1336,30 @@ static void io_destroy(struct kioctx *io - * resources are available. May fail with -EFAULT if an invalid - * pointer is passed for ctxp. Will fail with -ENOSYS if not - * implemented. -+ * -+ * To request a selectable fd, the user context has to be initialized -+ * to 1, instead of 0, and the return value is the fd. -+ * This keeps the system call compatible, since a non-zero value -+ * was not allowed so far. - */ - asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp) - { - struct kioctx *ioctx = NULL; - unsigned long ctx; - long ret; -+ int make_fd = 0; - - ret = get_user(ctx, ctxp); - if (unlikely(ret)) - goto out; - - ret = -EINVAL; -+#ifdef CONFIG_EPOLL -+ if (ctx == 1) { -+ make_fd = 1; -+ ctx = 0; -+ } -+#endif - if (unlikely(ctx || nr_events == 0)) { - pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n", - ctx, nr_events); -@@ -1273,8 +1370,12 @@ asmlinkage long sys_io_setup(unsigned nr - ret = PTR_ERR(ioctx); - if (!IS_ERR(ioctx)) { - ret = put_user(ioctx->user_id, ctxp); -- if (!ret) -- return 0; -+#ifdef CONFIG_EPOLL -+ if (make_fd && ret >= 0) -+ ret = make_aio_fd(ioctx); -+#endif -+ if (ret >= 0) -+ return ret; - - get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */ - io_destroy(ioctx); - -diff -pruN ../pristine-linux-2.6.16-rc5/fs/eventpoll.c ./fs/eventpoll.c ---- ../pristine-linux-2.6.16-rc5/fs/eventpoll.c 2006-01-03 03:21:10.000000000 +0000 -+++ ./fs/eventpoll.c 2006-03-16 10:04:35.469956167 +0000 -@@ -235,8 +235,6 @@ struct ep_pqueue { - - static void ep_poll_safewake_init(struct poll_safewake *psw); - static void ep_poll_safewake(struct poll_safewake *psw, wait_queue_head_t *wq); --static int ep_getfd(int *efd, struct inode **einode, struct file **efile, -- struct eventpoll *ep); - static int ep_alloc(struct eventpoll **pep); - static void ep_free(struct eventpoll *ep); - static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd); -@@ -266,7 +264,7 @@ static int ep_events_transfer(struct eve - static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, - int maxevents, long timeout); - static int eventpollfs_delete_dentry(struct dentry *dentry); --static struct inode *ep_eventpoll_inode(void); -+static struct inode *ep_eventpoll_inode(struct file_operations *fops); - static struct super_block *eventpollfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data); -@@ -525,7 +523,7 @@ asmlinkage long sys_epoll_create(int siz - * Creates all the items needed to setup an eventpoll file. That is, - * a file structure, and inode and a free file descriptor. - */ -- error = ep_getfd(&fd, &inode, &file, ep); -+ error = ep_getfd(&fd, &inode, &file, ep, &eventpoll_fops); - if (error) - goto eexit_2; - -@@ -710,8 +708,8 @@ eexit_1: - /* - * Creates the file descriptor to be used by the epoll interface. - */ --static int ep_getfd(int *efd, struct inode **einode, struct file **efile, -- struct eventpoll *ep) -+int ep_getfd(int *efd, struct inode **einode, struct file **efile, -+ struct eventpoll *ep, struct file_operations *fops) - { - struct qstr this; - char name[32]; -@@ -727,7 +725,7 @@ static int ep_getfd(int *efd, struct ino - goto eexit_1; - - /* Allocates an inode from the eventpoll file system */ -- inode = ep_eventpoll_inode(); -+ inode = ep_eventpoll_inode(fops); - error = PTR_ERR(inode); - if (IS_ERR(inode)) - goto eexit_2; -@@ -758,7 +756,7 @@ static int ep_getfd(int *efd, struct ino - - file->f_pos = 0; - file->f_flags = O_RDONLY; -- file->f_op = &eventpoll_fops; -+ file->f_op = fops; - file->f_mode = FMODE_READ; - file->f_version = 0; - file->private_data = ep; -@@ -1574,7 +1572,7 @@ static int eventpollfs_delete_dentry(str - } - - --static struct inode *ep_eventpoll_inode(void) -+static struct inode *ep_eventpoll_inode(struct file_operations *fops) - { - int error = -ENOMEM; - struct inode *inode = new_inode(eventpoll_mnt->mnt_sb); -@@ -1582,7 +1580,7 @@ static struct inode *ep_eventpoll_inode( - if (!inode) - goto eexit_1; - -- inode->i_fop = &eventpoll_fops; -+ inode->i_fop = fops; - - /* - * Mark the inode dirty from the very beginning, - -diff -pruN ../pristine-linux-2.6.16-rc5/include/linux/aio.h ./include/linux/aio.h ---- ../pristine-linux-2.6.16-rc5/include/linux/aio.h 2006-03-14 14:10:21.597916731 +0000 -+++ ./include/linux/aio.h 2006-03-16 10:05:39.848833028 +0000 -@@ -191,6 +191,11 @@ struct kioctx { - struct aio_ring_info ring_info; - - struct work_struct wq; -+#ifdef CONFIG_EPOLL -+ // poll integration -+ wait_queue_head_t poll_wait; -+ struct file *file; -+#endif - }; - - /* prototypes */ - -diff -pruN ../pristine-linux-2.6.16-rc5/include/linux/eventpoll.h ./include/linux/eventpoll.h ---- ../pristine-linux-2.6.16-rc5/include/linux/eventpoll.h 2006-01-03 03:21:10.000000000 +0000 -+++ ./include/linux/eventpoll.h 2006-03-16 10:08:51.577809317 +0000 -@@ -86,6 +86,12 @@ static inline void eventpoll_release(str - } - - -+/* -+ * called by aio code to create fd that can poll the aio event queueQ -+ */ -+struct eventpoll; -+int ep_getfd(int *efd, struct inode **einode, struct file **efile, -+ struct eventpoll *ep, struct file_operations *fops); - #else - - static inline void eventpoll_init_file(struct file *file) {} diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/device_bind.patch --- a/patches/linux-2.6.16.13/device_bind.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -diff -pruN ../pristine-linux-2.6.16.13/drivers/base/bus.c ./drivers/base/bus.c ---- ../pristine-linux-2.6.16.13/drivers/base/bus.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./drivers/base/bus.c 2006-05-04 17:41:30.000000000 +0100 -@@ -188,6 +188,11 @@ static ssize_t driver_bind(struct device - up(&dev->sem); - if (dev->parent) - up(&dev->parent->sem); -+ -+ if (err > 0) /* success */ -+ err = count; -+ else if (err == 0) /* driver didn't accept device */ -+ err = -ENODEV; - } - put_device(dev); - put_bus(bus); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/fix-hz-suspend.patch --- a/patches/linux-2.6.16.13/fix-hz-suspend.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -diff -pruN ../pristine-linux-2.6.16.13/kernel/timer.c ./kernel/timer.c ---- ../pristine-linux-2.6.16.13/kernel/timer.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./kernel/timer.c 2006-06-29 14:34:12.788957720 +0100 -@@ -555,6 +555,22 @@ found: - } - spin_unlock(&base->t_base.lock); - -+ /* -+ * It can happen that other CPUs service timer IRQs and increment -+ * jiffies, but we have not yet got a local timer tick to process -+ * the timer wheels. In that case, the expiry time can be before -+ * jiffies, but since the high-resolution timer here is relative to -+ * jiffies, the default expression when high-resolution timers are -+ * not active, -+ * -+ * time_before(MAX_JIFFY_OFFSET + jiffies, expires) -+ * -+ * would falsely evaluate to true. If that is the case, just -+ * return jiffies so that we can immediately fire the local timer -+ */ -+ if (time_before(expires, jiffies)) -+ return jiffies; -+ - if (time_before(hr_expires, expires)) - return hr_expires; - diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/fix-ide-cd-pio-mode.patch --- a/patches/linux-2.6.16.13/fix-ide-cd-pio-mode.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -diff -ru ../pristine-linux-2.6.16.13/drivers/ide/ide-lib.c ./drivers/ide/ide-lib.c ---- ../pristine-linux-2.6.16.13/drivers/ide/ide-lib.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./drivers/ide/ide-lib.c 2006-05-24 18:37:05.000000000 +0100 -@@ -410,10 +410,10 @@ - { - u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */ - -- if (!PCI_DMA_BUS_IS_PHYS) { -- addr = BLK_BOUNCE_ANY; -- } else if (on && drive->media == ide_disk) { -- if (HWIF(drive)->pci_dev) -+ if (on && drive->media == ide_disk) { -+ if (!PCI_DMA_BUS_IS_PHYS) -+ addr = BLK_BOUNCE_ANY; -+ else if (HWIF(drive)->pci_dev) - addr = HWIF(drive)->pci_dev->dma_mask; - } - diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/i386-mach-io-check-nmi.patch --- a/patches/linux-2.6.16.13/i386-mach-io-check-nmi.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -diff -pruN ../pristine-linux-2.6.16.13/arch/i386/kernel/traps.c ./arch/i386/kernel/traps.c ---- ../pristine-linux-2.6.16.13/arch/i386/kernel/traps.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./arch/i386/kernel/traps.c 2006-05-04 17:41:34.000000000 +0100 -@@ -567,18 +567,11 @@ static void mem_parity_error(unsigned ch - - static void io_check_error(unsigned char reason, struct pt_regs * regs) - { -- unsigned long i; -- - printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n"); - show_registers(regs); - - /* Re-enable the IOCK line, wait for a few seconds */ -- reason = (reason & 0xf) | 8; -- outb(reason, 0x61); -- i = 2000; -- while (--i) udelay(1000); -- reason &= ~8; -- outb(reason, 0x61); -+ clear_io_check_error(reason); - } - - static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) -diff -pruN ../pristine-linux-2.6.16.13/include/asm-i386/mach-default/mach_traps.h ./include/asm-i386/mach-default/mach_traps.h ---- ../pristine-linux-2.6.16.13/include/asm-i386/mach-default/mach_traps.h 2006-05-02 22:38:44.000000000 +0100 -+++ ./include/asm-i386/mach-default/mach_traps.h 2006-05-04 17:41:34.000000000 +0100 -@@ -15,6 +15,18 @@ static inline void clear_mem_error(unsig - outb(reason, 0x61); - } - -+static inline void clear_io_check_error(unsigned char reason) -+{ -+ unsigned long i; -+ -+ reason = (reason & 0xf) | 8; -+ outb(reason, 0x61); -+ i = 2000; -+ while (--i) udelay(1000); -+ reason &= ~8; -+ outb(reason, 0x61); -+} -+ - static inline unsigned char get_nmi_reason(void) - { - return inb(0x61); diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/ipv6-no-autoconf.patch --- a/patches/linux-2.6.16.13/ipv6-no-autoconf.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ - net/ipv6/addrconf.c | 2 ++ - 1 files changed, 2 insertions(+) - -Index: build/net/ipv6/addrconf.c -=================================================================== ---- build.orig/net/ipv6/addrconf.c -+++ build/net/ipv6/addrconf.c -@@ -2462,6 +2462,7 @@ static void addrconf_dad_start(struct in - spin_lock_bh(&ifp->lock); - - if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || -+ !(dev->flags&IFF_MULTICAST) || - !(ifp->flags&IFA_F_TENTATIVE)) { - ifp->flags &= ~IFA_F_TENTATIVE; - spin_unlock_bh(&ifp->lock); -@@ -2546,6 +2547,7 @@ static void addrconf_dad_completed(struc - if (ifp->idev->cnf.forwarding == 0 && - ifp->idev->cnf.rtr_solicits > 0 && - (dev->flags&IFF_LOOPBACK) == 0 && -+ (dev->flags & IFF_MULTICAST) && - (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { - struct in6_addr all_routers; - diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/net-csum.patch --- a/patches/linux-2.6.16.13/net-csum.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -diff -pruN ../pristine-linux-2.6.16.13/net/ipv4/netfilter/ip_nat_proto_tcp.c ./net/ipv4/netfilter/ip_nat_proto_tcp.c ---- ../pristine-linux-2.6.16.13/net/ipv4/netfilter/ip_nat_proto_tcp.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./net/ipv4/netfilter/ip_nat_proto_tcp.c 2006-05-16 13:28:19.000000000 +0100 -@@ -129,7 +129,12 @@ tcp_manip_pkt(struct sk_buff **pskb, - if (hdrsize < sizeof(*hdr)) - return 1; - -- hdr->check = ip_nat_cheat_check(~oldip, newip, -+#ifdef CONFIG_XEN -+ if ((*pskb)->proto_csum_blank) -+ hdr->check = ip_nat_cheat_check(oldip, ~newip, hdr->check); -+ else -+#endif -+ hdr->check = ip_nat_cheat_check(~oldip, newip, - ip_nat_cheat_check(oldport ^ 0xFFFF, - newport, - hdr->check)); -diff -pruN ../pristine-linux-2.6.16.13/net/ipv4/netfilter/ip_nat_proto_udp.c ./net/ipv4/netfilter/ip_nat_proto_udp.c ---- ../pristine-linux-2.6.16.13/net/ipv4/netfilter/ip_nat_proto_udp.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./net/ipv4/netfilter/ip_nat_proto_udp.c 2006-05-16 13:30:14.000000000 +0100 -@@ -113,11 +113,17 @@ udp_manip_pkt(struct sk_buff **pskb, - newport = tuple->dst.u.udp.port; - portptr = &hdr->dest; - } -- if (hdr->check) /* 0 is a special case meaning no checksum */ -- hdr->check = ip_nat_cheat_check(~oldip, newip, -+ if (hdr->check) { /* 0 is a special case meaning no checksum */ -+#ifdef CONFIG_XEN -+ if ((*pskb)->proto_csum_blank) -+ hdr->check = ip_nat_cheat_check(oldip, ~newip, hdr->check); -+ else -+#endif -+ hdr->check = ip_nat_cheat_check(~oldip, newip, - ip_nat_cheat_check(*portptr ^ 0xFFFF, - newport, - hdr->check)); -+ } - *portptr = newport; - return 1; - } -diff -pruN ../pristine-linux-2.6.16.13/net/ipv4/xfrm4_output.c ./net/ipv4/xfrm4_output.c ---- ../pristine-linux-2.6.16.13/net/ipv4/xfrm4_output.c 2006-05-02 22:38:44.000000000 +0100 -+++ ./net/ipv4/xfrm4_output.c 2006-05-04 17:41:37.000000000 +0100 -@@ -17,6 +17,8 @@ - #include <net/xfrm.h> - #include <net/icmp.h> - -+extern int skb_checksum_setup(struct sk_buff *skb); -+ - /* Add encapsulation header. - * - * In transport mode, the IP header will be moved forward to make space -@@ -103,6 +105,10 @@ static int xfrm4_output_one(struct sk_bu - struct xfrm_state *x = dst->xfrm; - int err; - -+ err = skb_checksum_setup(skb); -+ if (err) -+ goto error_nolock; -+ - if (skb->ip_summed == CHECKSUM_HW) { - err = skb_checksum_help(skb, 0); - if (err) diff -r c5d4d47bbeb8 -r 041be3f6b38e patches/linux-2.6.16.13/net-gso-0-base.patch --- a/patches/linux-2.6.16.13/net-gso-0-base.patch Tue Sep 19 14:26:22 2006 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2907 +0,0 @@ -diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt -index 3c0a5ba..847cedb 100644 ---- a/Documentation/networking/netdevices.txt -+++ b/Documentation/networking/netdevices.txt -@@ -42,9 +42,9 @@ dev->get_stats: - Context: nominally process, but don't sleep inside an rwlock - - dev->hard_start_xmit: -- Synchronization: dev->xmit_lock spinlock. -+ Synchronization: netif_tx_lock spinlock. - When the driver sets NETIF_F_LLTX in dev->features this will be -- called without holding xmit_lock. In this case the driver -+ called without holding netif_tx_lock. In this case the driver - has to lock by itself when needed. It is recommended to use a try lock - for this and return -1 when the spin lock fails. - The locking there should also properly protect against -@@ -62,12 +62,12 @@ dev->hard_start_xmit: - Only valid when NETIF_F_LLTX is set. - - dev->tx_timeout: -- Synchronization: dev->xmit_lock spinlock. -+ Synchronization: netif_tx_lock spinlock. - Context: BHs disabled - Notes: netif_queue_stopped() is guaranteed true - - dev->set_multicast_list: -- Synchronization: dev->xmit_lock spinlock. -+ Synchronization: netif_tx_lock spinlock. - Context: BHs disabled - - dev->poll: -diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c -index 4be9769..2e7cac7 100644 ---- a/drivers/block/aoe/aoenet.c -+++ b/drivers/block/aoe/aoenet.c -@@ -95,9 +95,8 @@ mac_addr(char addr[6]) - static struct sk_buff * - skb_check(struct sk_buff *skb) - { -- if (skb_is_nonlinear(skb)) - if ((skb = skb_share_check(skb, GFP_ATOMIC))) -- if (skb_linearize(skb, GFP_ATOMIC) < 0) { -+ if (skb_linearize(skb)) { - dev_kfree_skb(skb); - return NULL; - } -diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c -index a2408d7..c90e620 100644 ---- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c -+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c -@@ -821,7 +821,8 @@ void ipoib_mcast_restart_task(void *dev_ - - ipoib_mcast_stop_thread(dev, 0); - -- spin_lock_irqsave(&dev->xmit_lock, flags); -+ local_irq_save(flags); -+ netif_tx_lock(dev); - spin_lock(&priv->lock); - - /* -@@ -896,7 +897,8 @@ void ipoib_mcast_restart_task(void *dev_ - } - - spin_unlock(&priv->lock); -- spin_unlock_irqrestore(&dev->xmit_lock, flags); -+ netif_tx_unlock(dev); -+ local_irq_restore(flags); - - /* We have to cancel outside of the spinlock */ - list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { -diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c -index 6711eb6..8d2351f 100644 ---- a/drivers/media/dvb/dvb-core/dvb_net.c -+++ b/drivers/media/dvb/dvb-core/dvb_net.c -@@ -1052,7 +1052,7 @@ static void wq_set_multicast_list (void - - dvb_net_feed_stop(dev); - priv->rx_mode = RX_MODE_UNI; -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - - if (dev->flags & IFF_PROMISC) { - dprintk("%s: promiscuous mode\n", dev->name); -@@ -1077,7 +1077,7 @@ static void wq_set_multicast_list (void - } - } - -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - dvb_net_feed_start(dev); - } - -diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c -index dd41049..6615583 100644 ---- a/drivers/net/8139cp.c -+++ b/drivers/net/8139cp.c -@@ -794,7 +794,7 @@ #endif - entry = cp->tx_head; - eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - if (dev->features & NETIF_F_TSO) -- mss = skb_shinfo(skb)->tso_size; -+ mss = skb_shinfo(skb)->gso_size; - - if (skb_shinfo(skb)->nr_frags == 0) { - struct cp_desc *txd = &cp->tx_ring[entry]; -diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c -index a24200d..b5e39a1 100644 ---- a/drivers/net/bnx2.c -+++ b/drivers/net/bnx2.c -@@ -1593,7 +1593,7 @@ bnx2_tx_int(struct bnx2 *bp) - skb = tx_buf->skb; - #ifdef BCM_TSO - /* partial BD completions possible with TSO packets */ -- if (skb_shinfo(skb)->tso_size) { -+ if (skb_shinfo(skb)->gso_size) { - u16 last_idx, last_ring_idx; - - last_idx = sw_cons + -@@ -1948,7 +1948,7 @@ bnx2_poll(struct net_device *dev, int *b - return 1; - } - --/* Called with rtnl_lock from vlan functions and also dev->xmit_lock -+/* Called with rtnl_lock from vlan functions and also netif_tx_lock - * from set_multicast. - */ - static void -@@ -4403,7 +4403,7 @@ bnx2_vlan_rx_kill_vid(struct net_device - } - #endif - --/* Called with dev->xmit_lock. -+/* Called with netif_tx_lock. - * hard_start_xmit is pseudo-lockless - a lock is only required when - * the tx queue is full. This way, we get the benefit of lockless - * operations most of the time without the complexities to handle -@@ -4441,7 +4441,7 @@ bnx2_start_xmit(struct sk_buff *skb, str - (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); - } - #ifdef BCM_TSO -- if ((mss = skb_shinfo(skb)->tso_size) && -+ if ((mss = skb_shinfo(skb)->gso_size) && - (skb->len > (bp->dev->mtu + ETH_HLEN))) { - u32 tcp_opt_len, ip_tcp_len; - -diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c -index bcf9f17..e970921 100644 ---- a/drivers/net/bonding/bond_main.c -+++ b/drivers/net/bonding/bond_main.c -@@ -1145,8 +1145,7 @@ int bond_sethwaddr(struct net_device *bo - } - - #define BOND_INTERSECT_FEATURES \ -- (NETIF_F_SG|NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM|\ -- NETIF_F_TSO|NETIF_F_UFO) -+ (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_TSO | NETIF_F_UFO) - - /* - * Compute the common dev->feature set available to all slaves. Some -@@ -1164,9 +1163,7 @@ static int bond_compute_features(struct - features &= (slave->dev->features & BOND_INTERSECT_FEATURES); - - if ((features & NETIF_F_SG) && -- !(features & (NETIF_F_IP_CSUM | -- NETIF_F_NO_CSUM | -- NETIF_F_HW_CSUM))) -+ !(features & NETIF_F_ALL_CSUM)) - features &= ~NETIF_F_SG; - - /* -@@ -4147,7 +4144,7 @@ static int bond_init(struct net_device * - */ - bond_dev->features |= NETIF_F_VLAN_CHALLENGED; - -- /* don't acquire bond device's xmit_lock when -+ /* don't acquire bond device's netif_tx_lock when - * transmitting */ - bond_dev->features |= NETIF_F_LLTX; - -diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c -index 30ff8ea..7b7d360 100644 ---- a/drivers/net/chelsio/sge.c -+++ b/drivers/net/chelsio/sge.c -@@ -1419,7 +1419,7 @@ int t1_start_xmit(struct sk_buff *skb, s - struct cpl_tx_pkt *cpl; - - #ifdef NETIF_F_TSO -- if (skb_shinfo(skb)->tso_size) { -+ if (skb_shinfo(skb)->gso_size) { - int eth_type; - struct cpl_tx_pkt_lso *hdr; - -@@ -1434,7 +1434,7 @@ #ifdef NETIF_F_TSO - hdr->ip_hdr_words = skb->nh.iph->ihl; - hdr->tcp_hdr_words = skb->h.th->doff; - hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type, -- skb_shinfo(skb)->tso_size)); -+ skb_shinfo(skb)->gso_size)); - hdr->len = htonl(skb->len - sizeof(*hdr)); - cpl = (struct cpl_tx_pkt *)hdr; - sge->stats.tx_lso_pkts++; -diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c -index fa29402..681d284 100644 ---- a/drivers/net/e1000/e1000_main.c -+++ b/drivers/net/e1000/e1000_main.c -@@ -2526,7 +2526,7 @@ #ifdef NETIF_F_TSO - uint8_t ipcss, ipcso, tucss, tucso, hdr_len; - int err; - -- if (skb_shinfo(skb)->tso_size) { -+ if (skb_shinfo(skb)->gso_size) { - if (skb_header_cloned(skb)) { - err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); - if (err) -@@ -2534,7 +2534,7 @@ #ifdef NETIF_F_TSO - } - - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); -- mss = skb_shinfo(skb)->tso_size; -+ mss = skb_shinfo(skb)->gso_size; - if (skb->protocol == ntohs(ETH_P_IP)) { - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; -@@ -2651,7 +2651,7 @@ #ifdef NETIF_F_TSO - * tso gets written back prematurely before the data is fully - * DMAd to the controller */ - if (!skb->data_len && tx_ring->last_tx_tso && -- !skb_shinfo(skb)->tso_size) { -+ !skb_shinfo(skb)->gso_size) { - tx_ring->last_tx_tso = 0; - size -= 4; - } -@@ -2893,7 +2893,7 @@ #endif - } - - #ifdef NETIF_F_TSO -- mss = skb_shinfo(skb)->tso_size; -+ mss = skb_shinfo(skb)->gso_size; - /* The controller does a simple calculation to - * make sure there is enough room in the FIFO before - * initiating the DMA for each buffer. The calc is: -@@ -2935,7 +2935,7 @@ #endif - #ifdef NETIF_F_TSO - /* Controller Erratum workaround */ - if (!skb->data_len && tx_ring->last_tx_tso && -- !skb_shinfo(skb)->tso_size) -+ !skb_shinfo(skb)->gso_size) - count++; - #endif - -diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c -index 3682ec6..c35f16e 100644 ---- a/drivers/net/forcedeth.c -+++ b/drivers/net/forcedeth.c -@@ -482,9 +482,9 @@ #define LPA_1000HALF 0x0400 - * critical parts: - * - rx is (pseudo-) lockless: it relies on the single-threading provided - * by the arch code for interrupts. -- * - tx setup is lockless: it relies on dev->xmit_lock. Actual submission -+ * - tx setup is lockless: it relies on netif_tx_lock. Actual submission - * needs dev->priv->lock :-( -- * - set_multicast_list: preparation lockless, relies on dev->xmit_lock. -+ * - set_multicast_list: preparation lockless, relies on netif_tx_lock. - */ - - /* in dev: base, irq */ -@@ -1016,7 +1016,7 @@ static void drain_ring(struct net_device - - /* - * nv_start_xmit: dev->hard_start_xmit function -- * Called with dev->xmit_lock held. -+ * Called with netif_tx_lock held. - */ - static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) - { -@@ -1105,8 +1105,8 @@ static int nv_start_xmit(struct sk_buff - np->tx_skbuff[nr] = skb; - - #ifdef NETIF_F_TSO -- if (skb_shinfo(skb)->tso_size) -- tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->tso_size << NV_TX2_TSO_SHIFT); -+ if (skb_shinfo(skb)->gso_size) -+ tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); - else - #endif - tx_flags_extra = (skb->ip_summed == CHECKSUM_HW ? (NV_TX2_CHECKSUM_L3|NV_TX2_CHECKSUM_L4) : 0); -@@ -1203,7 +1203,7 @@ static void nv_tx_done(struct net_device - - /* - * nv_tx_timeout: dev->tx_timeout function -- * Called with dev->xmit_lock held. -+ * Called with netif_tx_lock held. - */ - static void nv_tx_timeout(struct net_device *dev) - { -@@ -1524,7 +1524,7 @@ static int nv_change_mtu(struct net_devi - * Changing the MTU is a rare event, it shouldn't matter. - */ - disable_irq(dev->irq); -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - spin_lock(&np->lock); - /* stop engines */ - nv_stop_rx(dev); -@@ -1559,7 +1559,7 @@ static int nv_change_mtu(struct net_devi - nv_start_rx(dev); - nv_start_tx(dev); - spin_unlock(&np->lock); -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - enable_irq(dev->irq); - } - return 0; -@@ -1594,7 +1594,7 @@ static int nv_set_mac_address(struct net - memcpy(dev->dev_addr, macaddr->sa_data, ETH_ALEN); - - if (netif_running(dev)) { -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - spin_lock_irq(&np->lock); - - /* stop rx engine */ -@@ -1606,7 +1606,7 @@ static int nv_set_mac_address(struct net - /* restart rx engine */ - nv_start_rx(dev); - spin_unlock_irq(&np->lock); -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - } else { - nv_copy_mac_to_hw(dev); - } -@@ -1615,7 +1615,7 @@ static int nv_set_mac_address(struct net - - /* - * nv_set_multicast: dev->set_multicast function -- * Called with dev->xmit_lock held. -+ * Called with netif_tx_lock held. - */ - static void nv_set_multicast(struct net_device *dev) - { -diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c -index 102c1f0..d12605f 100644 ---- a/drivers/net/hamradio/6pack.c -+++ b/drivers/net/hamradio/6pack.c -@@ -308,9 +308,9 @@ static int sp_set_mac_address(struct net - { - struct sockaddr_ax25 *sa = addr; - -- spin_lock_irq(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); -- spin_unlock_irq(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - - return 0; - } -@@ -767,9 +767,9 @@ static int sixpack_ioctl(struct tty_stru - break; - } - -- spin_lock_irq(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN); -- spin_unlock_irq(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - - err = 0; - break; -diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c -index dc5e9d5..5c66f5a 100644 ---- a/drivers/net/hamradio/mkiss.c -+++ b/drivers/net/hamradio/mkiss.c -@@ -357,9 +357,9 @@ static int ax_set_mac_address(struct net - { - struct sockaddr_ax25 *sa = addr; - -- spin_lock_irq(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); -- spin_unlock_irq(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - - return 0; - } -@@ -886,9 +886,9 @@ static int mkiss_ioctl(struct tty_struct - break; - } - -- spin_lock_irq(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - memcpy(dev->dev_addr, addr, AX25_ADDR_LEN); -- spin_unlock_irq(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - - err = 0; - break; -diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c -index 31fb2d7..2e222ef 100644 ---- a/drivers/net/ifb.c -+++ b/drivers/net/ifb.c -@@ -76,13 +76,13 @@ static void ri_tasklet(unsigned long dev - dp->st_task_enter++; - if ((skb = skb_peek(&dp->tq)) == NULL) { - dp->st_txq_refl_try++; -- if (spin_trylock(&_dev->xmit_lock)) { -+ if (netif_tx_trylock(_dev)) { - dp->st_rxq_enter++; - while ((skb = skb_dequeue(&dp->rq)) != NULL) { - skb_queue_tail(&dp->tq, skb); - dp->st_rx2tx_tran++; - } -- spin_unlock(&_dev->xmit_lock); -+ netif_tx_unlock(_dev); - } else { - /* reschedule */ - dp->st_rxq_notenter++; -@@ -110,7 +110,7 @@ static void ri_tasklet(unsigned long dev - } - } - -- if (spin_trylock(&_dev->xmit_lock)) { -+ if (netif_tx_trylock(_dev)) { - dp->st_rxq_check++; - if ((skb = skb_peek(&dp->rq)) == NULL) { - dp->tasklet_pending = 0; -@@ -118,10 +118,10 @@ static void ri_tasklet(unsigned long dev - netif_wake_queue(_dev); - } else { - dp->st_rxq_rsch++; -- spin_unlock(&_dev->xmit_lock); -+ netif_tx_unlock(_dev); - goto resched; - } -- spin_unlock(&_dev->xmit_lock); -+ netif_tx_unlock(_dev); - } else { - resched: - dp->tasklet_pending = 1; -diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c -index a9f49f0..339d4a7 100644 ---- a/drivers/net/irda/vlsi_ir.c -+++ b/drivers/net/irda/vlsi_ir.c -@@ -959,7 +959,7 @@ static int vlsi_hard_start_xmit(struct s - || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) - break; - udelay(100); -- /* must not sleep here - we are called under xmit_lock! */ -+ /* must not sleep here - called under netif_tx_lock! */ - } - } - -diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c -index f9f77e4..bdab369 100644 ---- a/drivers/net/ixgb/ixgb_main.c -+++ b/drivers/net/ixgb/ixgb_main.c -@@ -1163,7 +1163,7 @@ #ifdef NETIF_F_TSO - uint16_t ipcse, tucse, mss; - int err; - -- if(likely(skb_shinfo(skb)->tso_size)) { -+ if(likely(skb_shinfo(skb)->gso_size)) { - if (skb_header_cloned(skb)) { - err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); - if (err) -@@ -1171,7 +1171,7 @@ #ifdef NETIF_F_TSO - } - - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); -- mss = skb_shinfo(skb)->tso_size; -+ mss = skb_shinfo(skb)->gso_size; - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, -diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c -index 690a1aa..9bcaa80 100644 ---- a/drivers/net/loopback.c -+++ b/drivers/net/loopback.c -@@ -74,7 +74,7 @@ static void emulate_large_send_offload(s - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4)); - unsigned int doffset = (iph->ihl + th->doff) * 4; -- unsigned int mtu = skb_shinfo(skb)->tso_size + doffset; -+ unsigned int mtu = skb_shinfo(skb)->gso_size + doffset; - unsigned int offset = 0; - u32 seq = ntohl(th->seq); - u16 id = ntohs(iph->id); -@@ -139,7 +139,7 @@ #ifndef LOOPBACK_MUST_CHECKSUM - #endif - - #ifdef LOOPBACK_TSO -- if (skb_shinfo(skb)->tso_size) { -+ if (skb_shinfo(skb)->gso_size) { - BUG_ON(skb->protocol != htons(ETH_P_IP)); - BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); - -diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c -index c0998ef..0fac9d5 100644 ---- a/drivers/net/mv643xx_eth.c -+++ b/drivers/net/mv643xx_eth.c -@@ -1107,7 +1107,7 @@ static int mv643xx_eth_start_xmit(struct - - #ifdef MV643XX_CHECKSUM_OFFLOAD_TX - if (has_tiny_unaligned_frags(skb)) { -- if ((skb_linearize(skb, GFP_ATOMIC) != 0)) { -+ if (__skb_linearize(skb)) { - stats->tx_dropped++; - printk(KERN_DEBUG "%s: failed to linearize tiny " - "unaligned fragment\n", dev->name); -diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c -index 9d6d254..c9ed624 100644 ---- a/drivers/net/natsemi.c -+++ b/drivers/net/natsemi.c -@@ -323,12 +323,12 @@ performance critical codepaths: - The rx process only runs in the interrupt handler. Access from outside - the interrupt handler is only permitted after disable_irq(). - --The rx process usually runs under the dev->xmit_lock. If np->intr_tx_reap -+The rx process usually runs under the netif_tx_lock. If np->intr_tx_reap - is set, then access is permitted under spin_lock_irq(&np->lock). - - Thus configuration functions that want to access everything must call - disable_irq(dev->irq); -- spin_lock_bh(dev->xmit_lock); -+ netif_tx_lock_bh(dev); - spin_lock_irq(&np->lock); - - IV. Notes -diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c -index 8cc0d0b..e53b313 100644 ---- a/drivers/net/r8169.c -+++ b/drivers/net/r8169.c -@@ -2171,7 +2171,7 @@ static int rtl8169_xmit_frags(struct rtl - static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev) - { - if (dev->features & NETIF_F_TSO) { -- u32 mss = skb_shinfo(skb)->tso_size; -+ u32 mss = skb_shinfo(skb)->gso_size; - - if (mss) - return LargeSend | ((mss & MSSMask) << MSSShift); -diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c -index b7f00d6..439f45f 100644 ---- a/drivers/net/s2io.c -+++ b/drivers/net/s2io.c -@@ -3522,8 +3522,8 @@ #endif - txdp->Control_1 = 0; - txdp->Control_2 = 0; - #ifdef NETIF_F_TSO -- mss = skb_shinfo(skb)->tso_size; -- if (mss) { -+ mss = skb_shinfo(skb)->gso_size; -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) { - txdp->Control_1 |= TXD_TCP_LSO_EN; - txdp->Control_1 |= TXD_TCP_LSO_MSS(mss); - } -@@ -3543,10 +3543,10 @@ #endif - } - - frg_len = skb->len - skb->data_len; -- if (skb_shinfo(skb)->ufo_size) { -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) { - int ufo_size; - -- ufo_size = skb_shinfo(skb)->ufo_size; -+ ufo_size = skb_shinfo(skb)->gso_size; - ufo_size &= ~7; - txdp->Control_1 |= TXD_UFO_EN; - txdp->Control_1 |= TXD_UFO_MSS(ufo_size); -@@ -3572,7 +3572,7 @@ #endif - txdp->Host_Control = (unsigned long) skb; - txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); - -- if (skb_shinfo(skb)->ufo_size) -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) - txdp->Control_1 |= TXD_UFO_EN; - - frg_cnt = skb_shinfo(skb)->nr_frags; -@@ -3587,12 +3587,12 @@ #endif - (sp->pdev, frag->page, frag->page_offset, - frag->size, PCI_DMA_TODEVICE); - txdp->Control_1 = TXD_BUFFER0_SIZE(frag->size); -- if (skb_shinfo(skb)->ufo_size) -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) - txdp->Control_1 |= TXD_UFO_EN; - } - txdp->Control_1 |= TXD_GATHER_CODE_LAST; - -- if (skb_shinfo(skb)->ufo_size) -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) - frg_cnt++; /* as Txd0 was used for inband header */ - - tx_fifo = mac_control->tx_FIFO_start[queue]; -@@ -3606,7 +3606,7 @@ #ifdef NETIF_F_TSO - if (mss) - val64 |= TX_FIFO_SPECIAL_FUNC; - #endif -- if (skb_shinfo(skb)->ufo_size) -+ if (skb_shinfo(skb)->gso_type == SKB_GSO_UDPV4) - val64 |= TX_FIFO_SPECIAL_FUNC; - writeq(val64, &tx_fifo->List_Control); - -diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c -index 0618cd5..2a55eb3 100644 ---- a/drivers/net/sky2.c -+++ b/drivers/net/sky2.c -@@ -1125,7 +1125,7 @@ static unsigned tx_le_req(const struct s - count = sizeof(dma_addr_t) / sizeof(u32); - count += skb_shinfo(skb)->nr_frags * count; - -- if (skb_shinfo(skb)->tso_size) -+ if (skb_shinfo(skb)->gso_size) - ++count; - - if (skb->ip_summed == CHECKSUM_HW) -@@ -1197,7 +1197,7 @@ static int sky2_xmit_frame(struct sk_buf - } - - /* Check for TCP Segmentation Offload */ -- mss = skb_shinfo(skb)->tso_size; -+ mss = skb_shinfo(skb)->gso_size; - if (mss != 0) { - /* just drop the packet if non-linear expansion fails */ - if (skb_header_cloned(skb) && -diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c -index caf4102..fc9164a 100644 ---- a/drivers/net/tg3.c -+++ b/drivers/net/tg3.c -@@ -3664,7 +3664,7 @@ static int tg3_start_xmit(struct sk_buff - #if TG3_TSO_SUPPORT != 0 - mss = 0; - if (skb->len > (tp->dev->mtu + ETH_HLEN) && -- (mss = skb_shinfo(skb)->tso_size) != 0) { -+ (mss = skb_shinfo(skb)->gso_size) != 0) { - int tcp_opt_len, ip_tcp_len; - - if (skb_header_cloned(skb) && -diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c -index 5b1af39..11de5af 100644 ---- a/drivers/net/tulip/winbond-840.c -+++ b/drivers/net/tulip/winbond-840.c -@@ -1605,11 +1605,11 @@ #ifdef CONFIG_PM - * - get_stats: - * spin_lock_irq(np->lock), doesn't touch hw if not present - * - hard_start_xmit: -- * netif_stop_queue + spin_unlock_wait(&dev->xmit_lock); -+ * synchronize_irq + netif_tx_disable; - * - tx_timeout: -- * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); -+ * netif_device_detach + netif_tx_disable; - * - set_multicast_list -- * netif_device_detach + spin_unlock_wait(&dev->xmit_lock); -+ * netif_device_detach + netif_tx_disable; - * - interrupt handler - * doesn't touch hw if not present, synchronize_irq waits for - * running instances of the interrupt handler. -@@ -1635,11 +1635,10 @@ static int w840_suspend (struct pci_dev - netif_device_detach(dev); - update_csr6(dev, 0); - iowrite32(0, ioaddr + IntrEnable); -- netif_stop_queue(dev); - spin_unlock_irq(&np->lock); - -- spin_unlock_wait(&dev->xmit_lock); - synchronize_irq(dev->irq); -+ netif_tx_disable(dev); - - np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff; - -diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c -index 4c76cb7..30c48c9 100644 ---- a/drivers/net/typhoon.c -+++ b/drivers/net/typhoon.c -@@ -340,7 +340,7 @@ #define typhoon_synchronize_irq(x) synch - #endif - - #if defined(NETIF_F_TSO) --#define skb_tso_size(x) (skb_shinfo(x)->tso_size) -+#define skb_tso_size(x) (skb_shinfo(x)->gso_size) - #define TSO_NUM_DESCRIPTORS 2 - #define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT - #else -diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c -index ed1f837..2eb6b5f 100644 ---- a/drivers/net/via-velocity.c -+++ b/drivers/net/via-velocity.c -@@ -1899,6 +1899,13 @@ static int velocity_xmit(struct sk_buff - - int pktlen = skb->len; - -+#ifdef VELOCITY_ZERO_COPY_SUPPORT -+ if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { -+ kfree_skb(skb); -+ return 0; -+ } -+#endif -+ - spin_lock_irqsave(&vptr->lock, flags); - - index = vptr->td_curr[qnum]; -@@ -1914,8 +1921,6 @@ static int velocity_xmit(struct sk_buff - */ - if (pktlen < ETH_ZLEN) { - /* Cannot occur until ZC support */ -- if(skb_linearize(skb, GFP_ATOMIC)) -- return 0; - pktlen = ETH_ZLEN; - memcpy(tdinfo->buf, skb->data, skb->len); - memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len); -@@ -1933,7 +1938,6 @@ #ifdef VELOCITY_ZERO_COPY_SUPPORT - int nfrags = skb_shinfo(skb)->nr_frags; - tdinfo->skb = skb; - if (nfrags > 6) { -- skb_linearize(skb, GFP_ATOMIC); - memcpy(tdinfo->buf, skb->data, skb->len); - tdinfo->skb_dma[0] = tdinfo->buf_dma; - td_ptr->tdesc0.pktsize = -diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c -index 6fd0bf7..75237c1 100644 ---- a/drivers/net/wireless/orinoco.c -+++ b/drivers/net/wireless/orinoco.c -@@ -1835,7 +1835,9 @@ static int __orinoco_program_rids(struct - /* Set promiscuity / multicast*/ - priv->promiscuous = 0; - priv->mc_count = 0; -- __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ -+ -+ /* FIXME: what about netif_tx_lock */ -+ __orinoco_set_multicast_list(dev); - - return 0; - } -diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c -index 82cb4af..57cec40 100644 ---- a/drivers/s390/net/qeth_eddp.c -+++ b/drivers/s390/net/qeth_eddp.c -@@ -421,7 +421,7 @@ #endif /* CONFIG_QETH_VLAN */ - } - tcph = eddp->skb->h.th; - while (eddp->skb_offset < eddp->skb->len) { -- data_len = min((int)skb_shinfo(eddp->skb)->tso_size, -+ data_len = min((int)skb_shinfo(eddp->skb)->gso_size, - (int)(eddp->skb->len - eddp->skb_offset)); - /* prepare qdio hdr */ - if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){ -@@ -516,20 +516,20 @@ qeth_eddp_calc_num_pages(struct qeth_edd - - QETH_DBF_TEXT(trace, 5, "eddpcanp"); - /* can we put multiple skbs in one page? */ -- skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len); -+ skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->gso_size + hdr_len); - if (skbs_per_page > 1){ -- ctx->num_pages = (skb_shinfo(skb)->tso_segs + 1) / -+ ctx->num_pages = (skb_shinfo(skb)->gso_segs + 1) / - skbs_per_page + 1; - ctx->elements_per_skb = 1; - } else { - /* no -> how many elements per skb? */ -- ctx->elements_per_skb = (skb_shinfo(skb)->tso_size + hdr_len + -+ ctx->elements_per_skb = (skb_shinfo(skb)->gso_size + hdr_len + - PAGE_SIZE) >> PAGE_SHIFT; - ctx->num_pages = ctx->elements_per_skb * -- (skb_shinfo(skb)->tso_segs + 1); -+ (skb_shinfo(skb)->gso_segs + 1); - } - ctx->num_elements = ctx->elements_per_skb * -- (skb_shinfo(skb)->tso_segs + 1); -+ (skb_shinfo(skb)->gso_segs + 1); - } - - static inline struct qeth_eddp_context * -diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c -index dba7f7f..d9cc997 100644 ---- a/drivers/s390/net/qeth_main.c -+++ b/drivers/s390/net/qeth_main.c -@@ -4454,7 +4454,7 @@ qeth_send_packet(struct qeth_card *card, - queue = card->qdio.out_qs - [qeth_get_priority_queue(card, skb, ipv, cast_type)]; - -- if (skb_shinfo(skb)->tso_size) -+ if (skb_shinfo(skb)->gso_size) - large_send = card->options.large_send; - - /*are we able to do TSO ? If so ,prepare and send it from here */ -@@ -4501,7 +4501,7 @@ qeth_send_packet(struct qeth_card *card, - card->stats.tx_packets++; - card->stats.tx_bytes += skb->len; - #ifdef CONFIG_QETH_PERF_STATS -- if (skb_shinfo(skb)->tso_size && -+ if (skb_shinfo(skb)->gso_size && - !(large_send == QETH_LARGE_SEND_NO)) { - card->perf_stats.large_send_bytes += skb->len; - card->perf_stats.large_send_cnt++; -diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h -index 1286dde..89cbf34 100644 ---- a/drivers/s390/net/qeth_tso.h -+++ b/drivers/s390/net/qeth_tso.h -@@ -51,7 +51,7 @@ qeth_tso_fill_header(struct qeth_card *c - hdr->ext.hdr_version = 1; - hdr->ext.hdr_len = 28; - /*insert non-fix values */ -- hdr->ext.mss = skb_shinfo(skb)->tso_size; -+ hdr->ext.mss = skb_shinfo(skb)->gso_size; - hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); - hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - - sizeof(struct qeth_hdr_tso)); -diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h -index 93535f0..9269df7 100644 ---- a/include/linux/ethtool.h -+++ b/include/linux/ethtool.h -@@ -408,6 +408,8 @@ #define ETHTOOL_STSO 0x0000001f /* Set - #define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ - #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ - #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ -+#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ -+#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ - - /* compatibility with older code */ - #define SPARC_ETH_GSET ETHTOOL_GSET -diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h -index 7fda03d..47b0965 100644 ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -230,7 +230,8 @@ enum netdev_state_t - __LINK_STATE_SCHED, - __LINK_STATE_NOCARRIER, - __LINK_STATE_RX_SCHED, -- __LINK_STATE_LINKWATCH_PENDING -+ __LINK_STATE_LINKWATCH_PENDING, -+ __LINK_STATE_QDISC_RUNNING, - }; - - -@@ -306,9 +307,17 @@ #define NETIF_F_HW_VLAN_TX 128 /* Transm - #define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */ - #define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */ - #define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */ --#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */ -+#define NETIF_F_GSO 2048 /* Enable software GSO. */ - #define NETIF_F_LLTX 4096 /* LockLess TX */ --#define NETIF_F_UFO 8192 /* Can offload UDP Large Send*/ -+ -+ /* Segmentation offload features */ -+#define NETIF_F_GSO_SHIFT 16 -+#define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) -+#define NETIF_F_UFO (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT) -+#define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) -+ -+#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) -+#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) - - struct net_device *next_sched; - -@@ -394,6 +403,9 @@ #define NETIF_F_UFO 8192 - struct list_head qdisc_list; - unsigned long tx_queue_len; /* Max frames per queue allowed */ - -+ /* Partially transmitted GSO packet. */ -+ struct sk_buff *gso_skb; -+ - /* ingress path synchronizer */ - spinlock_t ingress_lock; - struct Qdisc *qdisc_ingress; -@@ -402,7 +414,7 @@ #define NETIF_F_UFO 8192 - * One part is mostly used on xmit path (device) - */ - /* hard_start_xmit synchronizer */ -- spinlock_t xmit_lock ____cacheline_aligned_in_smp; -+ spinlock_t _xmit_lock ____cacheline_aligned_in_smp; - /* cpu id of processor entered to hard_start_xmit or -1, - if nobody entered there. - */ -@@ -527,6 +539,8 @@ struct packet_type { - struct net_device *, - struct packet_type *, - struct net_device *); -+ struct sk_buff *(*gso_segment)(struct sk_buff *skb, -+ int features); - void *af_packet_priv; - struct list_head list; - }; -@@ -693,7 +707,8 @@ extern int dev_change_name(struct net_d - extern int dev_set_mtu(struct net_device *, int); - extern int dev_set_mac_address(struct net_device *, - struct sockaddr *); --extern void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); -+extern int dev_hard_start_xmit(struct sk_buff *skb, -+ struct net_device *dev); - - extern void dev_init(void); - -@@ -900,11 +915,43 @@ static inline void __netif_rx_complete(s - clear_bit(__LINK_STATE_RX_SCHED, &dev->state); - } - -+static inline void netif_tx_lock(struct net_device *dev) -+{ -+ spin_lock(&dev->_xmit_lock); -+ dev->xmit_lock_owner = smp_processor_id(); -+} -+ -+static inline void netif_tx_lock_bh(struct net_device *dev) -+{ -+ spin_lock_bh(&dev->_xmit_lock); -+ dev->xmit_lock_owner = smp_processor_id(); -+} -+ -+static inline int netif_tx_trylock(struct net_device *dev) -+{ -+ int err = spin_trylock(&dev->_xmit_lock); -+ if (!err) -+ dev->xmit_lock_owner = smp_processor_id(); -+ return err; -+} -+ -+static inline void netif_tx_unlock(struct net_device *dev) -+{ -+ dev->xmit_lock_owner = -1; -+ spin_unlock(&dev->_xmit_lock); -+} -+ -+static inline void netif_tx_unlock_bh(struct net_device *dev) -+{ -+ dev->xmit_lock_owner = -1; -+ spin_unlock_bh(&dev->_xmit_lock); -+} -+ - static inline void netif_tx_disable(struct net_device *dev) - { -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - netif_stop_queue(dev); -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - } - - /* These functions live elsewhere (drivers/net/net_init.c, but related) */ -@@ -932,6 +979,7 @@ extern int netdev_max_backlog; - extern int weight_p; - extern int netdev_set_master(struct net_device *dev, struct net_device *master); - extern int skb_checksum_help(struct sk_buff *skb, int inward); -+extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features); - #ifdef CONFIG_BUG - extern void netdev_rx_csum_fault(struct net_device *dev); - #else -@@ -951,6 +999,18 @@ #endif - - extern void linkwatch_run_queue(void); - -+static inline int skb_gso_ok(struct sk_buff *skb, int features) -+{ -+ int feature = skb_shinfo(skb)->gso_size ? -+ skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0; -+ return (features & feature) == feature; -+} -+ -+static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) -+{ -+ return !skb_gso_ok(skb, dev->features); -+} -+ - #endif /* __KERNEL__ */ - - #endif /* _LINUX_DEV_H */ -diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h -index ad7cc22..b19d45d 100644 ---- a/include/linux/skbuff.h -+++ b/include/linux/skbuff.h -@@ -134,9 +134,10 @@ struct skb_frag_struct { - struct skb_shared_info { - atomic_t dataref; - unsigned short nr_frags; -- unsigned short tso_size; -- unsigned short tso_segs; -- unsigned short ufo_size; -+ unsigned short gso_size; -+ /* Warning: this field is not always filled in (UFO)! */ -+ unsigned short gso_segs; -+ unsigned short gso_type; - unsigned int ip6_frag_id; - struct sk_buff *frag_list; - skb_frag_t frags[MAX_SKB_FRAGS]; -@@ -168,6 +169,14 @@ enum { - SKB_FCLONE_CLONE, - }; - -+enum { -+ SKB_GSO_TCPV4 = 1 << 0, -+ SKB_GSO_UDPV4 = 1 << 1, -+ -+ /* This indicates the skb is from an untrusted source. */ -+ SKB_GSO_DODGY = 1 << 2, -+}; -+ - /** - * struct sk_buff - socket buffer - * @next: Next buffer in list -@@ -1148,18 +1157,34 @@ static inline int skb_can_coalesce(struc - return 0; - } - -+static inline int __skb_linearize(struct sk_buff *skb) -+{ -+ return __pskb_pull_tail(skb, skb->data_len) ? 0 : -ENOMEM; -+} -+ - /** - * skb_linearize - convert paged skb to linear one - * @skb: buffer to linarize -- * @gfp: allocation mode - * - * If there is no free memory -ENOMEM is returned, otherwise zero - * is returned and the old skb data released. - */ --extern int __skb_linearize(struct sk_buff *skb, gfp_t gfp); --static inline int skb_linearize(struct sk_buff *skb, gfp_t gfp) -+static inline int skb_linearize(struct sk_buff *skb) -+{ -+ return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; -+} -+ -+/** -+ * skb_linearize_cow - make sure skb is linear and writable -+ * @skb: buffer to process -+ * -+ * If there is no free memory -ENOMEM is returned, otherwise zero -+ * is returned and the old skb data released. -+ */ -+static inline int skb_linearize_cow(struct sk_buff *skb) - { -- return __skb_linearize(skb, gfp); -+ return skb_is_nonlinear(skb) || skb_cloned(skb) ? -+ __skb_linearize(skb) : 0; - } - - /** -@@ -1254,6 +1279,7 @@ extern void skb_split(struct sk_b - struct sk_buff *skb1, const u32 len); - - extern void skb_release_data(struct sk_buff *skb); -+extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); - - static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, - int len, void *buffer) -diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h -index b94d1ad..75b5b93 100644 ---- a/include/net/pkt_sched.h -+++ b/include/net/pkt_sched.h -@@ -218,12 +218,13 @@ extern struct qdisc_rate_table *qdisc_ge - struct rtattr *tab); - extern void qdisc_put_rtab(struct qdisc_rate_table *tab); - --extern int qdisc_restart(struct net_device *dev); -+extern void __qdisc_run(struct net_device *dev); - - static inline void qdisc_run(struct net_device *dev) - { -- while (!netif_queue_stopped(dev) && qdisc_restart(dev) < 0) -- /* NOTHING */; -+ if (!netif_queue_stopped(dev) && -+ !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) -+ __qdisc_run(dev); - } - - extern int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, -diff --git a/include/net/protocol.h b/include/net/protocol.h -index 6dc5970..0d2dcdb 100644 ---- a/include/net/protocol.h -+++ b/include/net/protocol.h -@@ -37,6 +37,8 @@ #define MAX_INET_PROTOS 256 /* Must be - struct net_protocol { - int (*handler)(struct sk_buff *skb); - void (*err_handler)(struct sk_buff *skb, u32 info); -+ struct sk_buff *(*gso_segment)(struct sk_buff *skb, -+ int features); - int no_policy; - }; - -diff --git a/include/net/sock.h b/include/net/sock.h -index f63d0d5..a8e8d21 100644 ---- a/include/net/sock.h -+++ b/include/net/sock.h -@@ -1064,9 +1064,13 @@ static inline void sk_setup_caps(struct - { - __sk_dst_set(sk, dst); - sk->sk_route_caps = dst->dev->features; -+ if (sk->sk_route_caps & NETIF_F_GSO) -+ sk->sk_route_caps |= NETIF_F_TSO; - if (sk->sk_route_caps & NETIF_F_TSO) { - if (sock_flag(sk, SOCK_NO_LARGESEND) || dst->header_len) - sk->sk_route_caps &= ~NETIF_F_TSO; -+ else -+ sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; - } - } - -diff --git a/include/net/tcp.h b/include/net/tcp.h -index 77f21c6..70e1d5f 100644 ---- a/include/net/tcp.h -+++ b/include/net/tcp.h -@@ -552,13 +552,13 @@ #include <net/tcp_ecn.h> - */ - static inline int tcp_skb_pcount(const struct sk_buff *skb) - { -- return skb_shinfo(skb)->tso_segs; -+ return skb_shinfo(skb)->gso_segs; - } - - /* This is valid iff tcp_skb_pcount() > 1. */ - static inline int tcp_skb_mss(const struct sk_buff *skb) - { -- return skb_shinfo(skb)->tso_size; -+ return skb_shinfo(skb)->gso_size; - } - - static inline void tcp_dec_pcount_approx(__u32 *count, -@@ -1063,6 +1063,8 @@ extern struct request_sock_ops tcp_reque - - extern int tcp_v4_destroy_sock(struct sock *sk); - -+extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); -+ - #ifdef CONFIG_PROC_FS - extern int tcp4_proc_init(void); - extern void tcp4_proc_exit(void); -diff --git a/net/atm/clip.c b/net/atm/clip.c -index 1842a4e..6dc21a7 100644 ---- a/net/atm/clip.c -+++ b/net/atm/clip.c -@@ -101,7 +101,7 @@ static void unlink_clip_vcc(struct clip_ - printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc); - return; - } -- spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */ -+ netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ - entry->neigh->used = jiffies; - for (walk = &entry->vccs; *walk; walk = &(*walk)->next) - if (*walk == clip_vcc) { -@@ -125,7 +125,7 @@ static void unlink_clip_vcc(struct clip_ - printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc " - "0x%p)\n",entry,clip_vcc); - out: -- spin_unlock_bh(&entry->neigh->dev->xmit_lock); -+ netif_tx_unlock_bh(entry->neigh->dev); - } - - /* The neighbour entry n->lock is held. */ -diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c -index 0b33a7b..180e79b 100644 ---- a/net/bridge/br_device.c -+++ b/net/bridge/br_device.c -@@ -146,9 +146,9 @@ static int br_set_tx_csum(struct net_dev - struct net_bridge *br = netdev_priv(dev); - - if (data) -- br->feature_mask |= NETIF_F_IP_CSUM; -+ br->feature_mask |= NETIF_F_NO_CSUM; - else -- br->feature_mask &= ~NETIF_F_IP_CSUM; -+ br->feature_mask &= ~NETIF_F_ALL_CSUM; - - br_features_recompute(br); - return 0; -@@ -185,6 +185,6 @@ void br_dev_setup(struct net_device *dev - dev->set_mac_address = br_set_mac_address; - dev->priv_flags = IFF_EBRIDGE; - -- dev->features = NETIF_F_SG | NETIF_F_FRAGLIST -- | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM; -+ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | -+ NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST; - } -diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c -index 2d24fb4..00b1128 100644 ---- a/net/bridge/br_forward.c -+++ b/net/bridge/br_forward.c -@@ -32,7 +32,7 @@ static inline int should_deliver(const s - int br_dev_queue_push_xmit(struct sk_buff *skb) - { - /* drop mtu oversized packets except tso */ -- if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->tso_size) -+ if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->gso_size) - kfree_skb(skb); - else { - #ifdef CONFIG_BRIDGE_NETFILTER -diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c -index f36b35e..0617146 100644 ---- a/net/bridge/br_if.c -+++ b/net/bridge/br_if.c -@@ -385,17 +385,28 @@ void br_features_recompute(struct net_br - struct net_bridge_port *p; - unsigned long features, checksum; - -- features = br->feature_mask &~ NETIF_F_IP_CSUM; -- checksum = br->feature_mask & NETIF_F_IP_CSUM; -+ checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0; -+ features = br->feature_mask & ~NETIF_F_ALL_CSUM; - - list_for_each_entry(p, &br->port_list, list) { -- if (!(p->dev->features -- & (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM))) -+ unsigned long feature = p->dev->features; -+ -+ if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) -+ checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; -+ if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) -+ checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; -+ if (!(feature & NETIF_F_IP_CSUM)) - checksum = 0; -- features &= p->dev->features; -+ -+ if (feature & NETIF_F_GSO) -+ feature |= NETIF_F_TSO; -+ feature |= NETIF_F_GSO; -+ -+ features &= feature; - } - -- br->dev->features = features | checksum | NETIF_F_LLTX; -+ br->dev->features = features | checksum | NETIF_F_LLTX | -+ NETIF_F_GSO_ROBUST; - } - - /* called with RTNL */ -diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c -index 9e27373..588207f 100644 ---- a/net/bridge/br_netfilter.c -+++ b/net/bridge/br_netfilter.c -@@ -743,7 +743,7 @@ static int br_nf_dev_queue_xmit(struct s - { - if (skb->protocol == htons(ETH_P_IP) && - skb->len > skb->dev->mtu && -- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) -+ !skb_shinfo(skb)->gso_size) - return ip_fragment(skb, br_dev_queue_push_xmit); - else - return br_dev_queue_push_xmit(skb); -diff --git a/net/core/dev.c b/net/core/dev.c -index 12a214c..32e1056 100644 ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -115,6 +115,7 @@ #include <linux/wireless.h> /* Note : w - #include <net/iw_handler.h> - #endif /* CONFIG_NET_RADIO */ - #include <asm/current.h> -+#include <linux/err.h> - - /* - * The list of packet types we will receive (as opposed to discard) -@@ -1032,7 +1033,7 @@ static inline void net_timestamp(struct - * taps currently in use. - */ - --void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) -+static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) - { - struct packet_type *ptype; - -@@ -1106,6 +1107,45 @@ out: - return ret; - } - -+/** -+ * skb_gso_segment - Perform segmentation on skb. -+ * @skb: buffer to segment -+ * @features: features for the output path (see dev->features) -+ * -+ * This function segments the given skb and returns a list of segments. -+ * -+ * It may return NULL if the skb requires no segmentation. This is -+ * only possible when GSO is used for verifying header integrity. -+ */ -+struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) -+{ -+ struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); -+ struct packet_type *ptype; -+ int type = skb->protocol; -+ -+ BUG_ON(skb_shinfo(skb)->frag_list); -+ BUG_ON(skb->ip_summed != CHECKSUM_HW); -+ -+ skb->mac.raw = skb->data; -+ skb->mac_len = skb->nh.raw - skb->data; -+ __skb_pull(skb, skb->mac_len); -+ -+ rcu_read_lock(); -+ list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { -+ if (ptype->type == type && !ptype->dev && ptype->gso_segment) { -+ segs = ptype->gso_segment(skb, features); -+ break; -+ } -+ } -+ rcu_read_unlock(); -+ -+ __skb_push(skb, skb->data - skb->mac.raw); -+ -+ return segs; -+} -+ -+EXPORT_SYMBOL(skb_gso_segment); -+ - /* Take action when hardware reception checksum errors are detected. */ - #ifdef CONFIG_BUG - void netdev_rx_csum_fault(struct net_device *dev) -@@ -1142,75 +1182,108 @@ #else - #define illegal_highdma(dev, skb) (0) - #endif - --/* Keep head the same: replace data */ --int __skb_linearize(struct sk_buff *skb, gfp_t gfp_mask) --{ -- unsigned int size; -- u8 *data; -- long offset; -- struct skb_shared_info *ninfo; -- int headerlen = skb->data - skb->head; -- int expand = (skb->tail + skb->data_len) - skb->end; -- -- if (skb_shared(skb)) -- BUG(); -- -- if (expand <= 0) -- expand = 0; -- -- size = skb->end - skb->head + expand; -- size = SKB_DATA_ALIGN(size); -- data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); -- if (!data) -- return -ENOMEM; -- -- /* Copy entire thing */ -- if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len)) -- BUG(); -- -- /* Set up shinfo */ -- ninfo = (struct skb_shared_info*)(data + size); -- atomic_set(&ninfo->dataref, 1); -- ninfo->tso_size = skb_shinfo(skb)->tso_size; -- ninfo->tso_segs = skb_shinfo(skb)->tso_segs; -- ninfo->nr_frags = 0; -- ninfo->frag_list = NULL; -- -- /* Offset between the two in bytes */ -- offset = data - skb->head; -- -- /* Free old data. */ -- skb_release_data(skb); -- -- skb->head = data; -- skb->end = data + size; -- -- /* Set up new pointers */ -- skb->h.raw += offset; -- skb->nh.raw += offset; -- skb->mac.raw += offset; -- skb->tail += offset; -- skb->data += offset; -- -- /* We are no longer a clone, even if we were. */ -- skb->cloned = 0; -- -- skb->tail += skb->data_len; -- skb->data_len = 0; -+struct dev_gso_cb { -+ void (*destructor)(struct sk_buff *skb); -+}; -+ -+#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)->cb) -+ -+static void dev_gso_skb_destructor(struct sk_buff *skb) -+{ -+ struct dev_gso_cb *cb; -+ -+ do { -+ struct sk_buff *nskb = skb->next; -+ -+ skb->next = nskb->next; -+ nskb->next = NULL; -+ kfree_skb(nskb); -+ } while (skb->next); -+ -+ cb = DEV_GSO_CB(skb); -+ if (cb->destructor) -+ cb->destructor(skb); -+} -+ -+/** -+ * dev_gso_segment - Perform emulated hardware segmentation on skb. -+ * @skb: buffer to segment -+ * -+ * This function segments the given skb and stores the list of segments -+ * in skb->next. -+ */ -+static int dev_gso_segment(struct sk_buff *skb) -+{ -+ struct net_device *dev = skb->dev; -+ struct sk_buff *segs; -+ int features = dev->features & ~(illegal_highdma(dev, skb) ? -+ NETIF_F_SG : 0); -+ -+ segs = skb_gso_segment(skb, features); -+ -+ /* Verifying header integrity only. */ -+ if (!segs) -+ return 0; -+ -+ if (unlikely(IS_ERR(segs))) -+ return PTR_ERR(segs); -+ -+ skb->next = segs; -+ DEV_GSO_CB(skb)->destructor = skb->destructor; -+ skb->destructor = dev_gso_skb_destructor; -+ -+ return 0; -+} -+ -+int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ if (likely(!skb->next)) { -+ if (netdev_nit) -+ dev_queue_xmit_nit(skb, dev); -+ -+ if (netif_needs_gso(dev, skb)) { -+ if (unlikely(dev_gso_segment(skb))) -+ goto out_kfree_skb; -+ if (skb->next) -+ goto gso; -+ } -+ -+ return dev->hard_start_xmit(skb, dev); -+ } -+ -+gso: -+ do { -+ struct sk_buff *nskb = skb->next; -+ int rc; -+ -+ skb->next = nskb->next; -+ nskb->next = NULL; -+ rc = dev->hard_start_xmit(nskb, dev); -+ if (unlikely(rc)) { -+ nskb->next = skb->next; -+ skb->next = nskb; -+ return rc; -+ } -+ if (unlikely(netif_queue_stopped(dev) && skb->next)) -+ return NETDEV_TX_BUSY; -+ } while (skb->next); -+ -+ skb->destructor = DEV_GSO_CB(skb)->destructor; -+ -+out_kfree_skb: -+ kfree_skb(skb); - return 0; - } - - #define HARD_TX_LOCK(dev, cpu) { \ - if ((dev->features & NETIF_F_LLTX) == 0) { \ -- spin_lock(&dev->xmit_lock); \ -- dev->xmit_lock_owner = cpu; \ -+ netif_tx_lock(dev); \ - } \ - } - - #define HARD_TX_UNLOCK(dev) { \ - if ((dev->features & NETIF_F_LLTX) == 0) { \ -- dev->xmit_lock_owner = -1; \ -- spin_unlock(&dev->xmit_lock); \ -+ netif_tx_unlock(dev); \ - } \ - } - -@@ -1246,9 +1319,13 @@ int dev_queue_xmit(struct sk_buff *skb) - struct Qdisc *q; - int rc = -ENOMEM; - -+ /* GSO will handle the following emulations directly. */ -+ if (netif_needs_gso(dev, skb)) -+ goto gso; -+ - if (skb_shinfo(skb)->frag_list && - !(dev->features & NETIF_F_FRAGLIST) && -- __skb_linearize(skb, GFP_ATOMIC)) -+ __skb_linearize(skb)) - goto out_kfree_skb; - - /* Fragmented skb is linearized if device does not support SG, -@@ -1257,25 +1334,26 @@ int dev_queue_xmit(struct sk_buff *skb) - */ - if (skb_shinfo(skb)->nr_frags && - (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) && -- __skb_linearize(skb, GFP_ATOMIC)) -+ __skb_linearize(skb)) - goto out_kfree_skb; - - /* If packet is not checksummed and device does not support - * checksumming for this protocol, complete checksumming here. - */ - if (skb->ip_summed == CHECKSUM_HW && -- (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) && -+ (!(dev->features & NETIF_F_GEN_CSUM) && - (!(dev->features & NETIF_F_IP_CSUM) || - skb->protocol != htons(ETH_P_IP)))) - if (skb_checksum_help(skb, 0)) - goto out_kfree_skb; - -+gso: - spin_lock_prefetch(&dev->queue_lock); - - /* Disable soft irqs for various locks below. Also - * stops preemption for RCU. - */ -- local_bh_disable(); -+ rcu_read_lock_bh(); - - /* Updates of qdisc are serialized by queue_lock. - * The struct Qdisc which is pointed to by qdisc is now a -@@ -1309,8 +1387,8 @@ #endif - /* The device has no queue. Common case for software devices: - loopback, all the sorts of tunnels... - -- Really, it is unlikely that xmit_lock protection is necessary here. -- (f.e. loopback and IP tunnels are clean ignoring statistics -+ Really, it is unlikely that netif_tx_lock protection is necessary -+ here. (f.e. loopback and IP tunnels are clean ignoring statistics - counters.) - However, it is possible, that they rely on protection - made by us here. -@@ -1326,11 +1404,8 @@ #endif - HARD_TX_LOCK(dev, cpu); - - if (!netif_queue_stopped(dev)) { -- if (netdev_nit) -- dev_queue_xmit_nit(skb, dev); -- - rc = 0; -- if (!dev->hard_start_xmit(skb, dev)) { -+ if (!dev_hard_start_xmit(skb, dev)) { - HARD_TX_UNLOCK(dev); - goto out; - } -@@ -1349,13 +1424,13 @@ #endif - } - - rc = -ENETDOWN; -- local_bh_enable(); -+ rcu_read_unlock_bh(); - - out_kfree_skb: - kfree_skb(skb); - return rc; - out: -- local_bh_enable(); -+ rcu_read_unlock_bh(); - return rc; - } - -@@ -2670,7 +2745,7 @@ int register_netdevice(struct net_device - BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); - - spin_lock_init(&dev->queue_lock); -- spin_lock_init(&dev->xmit_lock); -+ spin_lock_init(&dev->_xmit_lock); - dev->xmit_lock_owner = -1; - #ifdef CONFIG_NET_CLS_ACT - spin_lock_init(&dev->ingress_lock); -@@ -2714,9 +2789,7 @@ #endif - - /* Fix illegal SG+CSUM combinations. */ - if ((dev->features & NETIF_F_SG) && -- !(dev->features & (NETIF_F_IP_CSUM | -- NETIF_F_NO_CSUM | -- NETIF_F_HW_CSUM))) { -+ !(dev->features & NETIF_F_ALL_CSUM)) { - printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", - dev->name); - dev->features &= ~NETIF_F_SG; -@@ -3268,7 +3341,6 @@ subsys_initcall(net_dev_init); - EXPORT_SYMBOL(__dev_get_by_index); - EXPORT_SYMBOL(__dev_get_by_name); - EXPORT_SYMBOL(__dev_remove_pack); --EXPORT_SYMBOL(__skb_linearize); - EXPORT_SYMBOL(dev_valid_name); - EXPORT_SYMBOL(dev_add_pack); - EXPORT_SYMBOL(dev_alloc_name); -diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c -index 05d6085..c57d887 100644 ---- a/net/core/dev_mcast.c -+++ b/net/core/dev_mcast.c -@@ -62,7 +62,7 @@ #include <net/arp.h> - * Device mc lists are changed by bh at least if IPv6 is enabled, - * so that it must be bh protected. - * -- * We block accesses to device mc filters with dev->xmit_lock. -+ * We block accesses to device mc filters with netif_tx_lock. - */ - - /* -@@ -93,9 +93,9 @@ static void __dev_mc_upload(struct net_d - - void dev_mc_upload(struct net_device *dev) - { -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - __dev_mc_upload(dev); -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - } - - /* -@@ -107,7 +107,7 @@ int dev_mc_delete(struct net_device *dev - int err = 0; - struct dev_mc_list *dmi, **dmip; - -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - - for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { - /* -@@ -139,13 +139,13 @@ int dev_mc_delete(struct net_device *dev - */ - __dev_mc_upload(dev); - -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - return 0; - } - } - err = -ENOENT; - done: -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - return err; - } - -@@ -160,7 +160,7 @@ int dev_mc_add(struct net_device *dev, v - - dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); - -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { - if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && - dmi->dmi_addrlen == alen) { -@@ -176,7 +176,7 @@ int dev_mc_add(struct net_device *dev, v - } - - if ((dmi = dmi1) == NULL) { -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - return -ENOMEM; - } - memcpy(dmi->dmi_addr, addr, alen); -@@ -189,11 +189,11 @@ int dev_mc_add(struct net_device *dev, v - - __dev_mc_upload(dev); - -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - return 0; - - done: -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - kfree(dmi1); - return err; - } -@@ -204,7 +204,7 @@ done: - - void dev_mc_discard(struct net_device *dev) - { -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - - while (dev->mc_list != NULL) { - struct dev_mc_list *tmp = dev->mc_list; -@@ -215,7 +215,7 @@ void dev_mc_discard(struct net_device *d - } - dev->mc_count = 0; - -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - } - - #ifdef CONFIG_PROC_FS -@@ -250,7 +250,7 @@ static int dev_mc_seq_show(struct seq_fi - struct dev_mc_list *m; - struct net_device *dev = v; - -- spin_lock_bh(&dev->xmit_lock); -+ netif_tx_lock_bh(dev); - for (m = dev->mc_list; m; m = m->next) { - int i; - -@@ -262,7 +262,7 @@ static int dev_mc_seq_show(struct seq_fi - - seq_putc(seq, '\n'); - } -- spin_unlock_bh(&dev->xmit_lock); -+ netif_tx_unlock_bh(dev); - return 0; - } - -diff --git a/net/core/ethtool.c b/net/core/ethtool.c -index e6f7610..27ce168 100644 ---- a/net/core/ethtool.c -+++ b/net/core/ethtool.c -@@ -30,7 +30,7 @@ u32 ethtool_op_get_link(struct net_devic - - u32 ethtool_op_get_tx_csum(struct net_device *dev) - { -- return (dev->features & (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM)) != 0; -+ return (dev->features & NETIF_F_ALL_CSUM) != 0; - } - - int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) -@@ -551,9 +551,7 @@ static int ethtool_set_sg(struct net_dev - return -EFAULT; - - if (edata.data && -- !(dev->features & (NETIF_F_IP_CSUM | -- NETIF_F_NO_CSUM | -- NETIF_F_HW_CSUM))) -+ !(dev->features & NETIF_F_ALL_CSUM)) - return -EINVAL; - - return __ethtool_set_sg(dev, edata.data); -@@ -591,7 +589,7 @@ static int ethtool_set_tso(struct net_de - - static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr) - { -- struct ethtool_value edata = { ETHTOOL_GTSO }; -+ struct ethtool_value edata = { ETHTOOL_GUFO }; - - if (!dev->ethtool_ops->get_ufo) - return -EOPNOTSUPP; -@@ -600,6 +598,7 @@ static int ethtool_get_ufo(struct net_de - return -EFAULT; - return 0; - } -+ - static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) - { - struct ethtool_value edata; -@@ -615,6 +614,29 @@ static int ethtool_set_ufo(struct net_de - return dev->ethtool_ops->set_ufo(dev, edata.data); - } - -+static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) -+{ -+ struct ethtool_value edata = { ETHTOOL_GGSO }; -+ -+ edata.data = dev->features & NETIF_F_GSO; -+ if (copy_to_user(useraddr, &edata, sizeof(edata))) -+ return -EFAULT; -+ return 0; -+} -+ -+static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) -+{ -+ struct ethtool_value edata; -+ -+ if (copy_from_user(&edata, useraddr, sizeof(edata))) -+ return -EFAULT; -+ if (edata.data) -+ dev->features |= NETIF_F_GSO; -+ else -+ dev->features &= ~NETIF_F_GSO; -+ return 0; -+} -+ - static int ethtool_self_test(struct net_device *dev, char __user *useraddr) - { - struct ethtool_test test; -@@ -906,6 +928,12 @@ int dev_ethtool(struct ifreq *ifr) - case ETHTOOL_SUFO: - rc = ethtool_set_ufo(dev, useraddr); - break; -+ case ETHTOOL_GGSO: -+ rc = ethtool_get_gso(dev, useraddr); -+ break; -+ case ETHTOOL_SGSO: -+ rc = ethtool_set_gso(dev, useraddr); -+ break; - default: - rc = -EOPNOTSUPP; - } -diff --git a/net/core/netpoll.c b/net/core/netpoll.c -index ea51f8d..ec28d3b 100644 ---- a/net/core/netpoll.c -+++ b/net/core/netpoll.c -@@ -273,24 +273,21 @@ static void netpoll_send_skb(struct netp - - do { - npinfo->tries--; -- spin_lock(&np->dev->xmit_lock); -- np->dev->xmit_lock_owner = smp_processor_id(); -+ netif_tx_lock(np->dev); - - /* - * network drivers do not expect to be called if the queue is - * stopped. - */ - if (netif_queue_stopped(np->dev)) { -- np->dev->xmit_lock_owner = -1; -- spin_unlock(&np->dev->xmit_lock); -+ netif_tx_unlock(np->dev); - netpoll_poll(np); - udelay(50); - continue; - } - - status = np->dev->hard_start_xmit(skb, np->dev); -- np->dev->xmit_lock_owner = -1; -- spin_unlock(&np->dev->xmit_lock); -+ netif_tx_unlock(np->dev); - - /* success */ - if(!status) { -diff --git a/net/core/pktgen.c b/net/core/pktgen.c -index da16f8f..2380347 100644 ---- a/net/core/pktgen.c -+++ b/net/core/pktgen.c -@@ -2582,7 +2582,7 @@ static __inline__ void pktgen_xmit(struc - } - } - -- spin_lock_bh(&odev->xmit_lock); -+ netif_tx_lock_bh(odev); - if (!netif_queue_stopped(odev)) { - - atomic_inc(&(pkt_dev->skb->users)); -@@ -2627,7 +2627,7 @@ retry_now: - pkt_dev->next_tx_ns = 0; - } - -- spin_unlock_bh(&odev->xmit_lock); -+ netif_tx_unlock_bh(odev); - - /* If pkt_dev->count is zero, then run forever */ - if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { -diff --git a/net/core/skbuff.c b/net/core/skbuff.c -index 2144952..46f56af 100644 ---- a/net/core/skbuff.c -+++ b/net/core/skbuff.c -@@ -164,9 +164,9 @@ struct sk_buff *__alloc_skb(unsigned int - shinfo = skb_shinfo(skb); - atomic_set(&shinfo->dataref, 1); - shinfo->nr_frags = 0; -- shinfo->tso_size = 0; -- shinfo->tso_segs = 0; -- shinfo->ufo_size = 0; -+ shinfo->gso_size = 0; -+ shinfo->gso_segs = 0; -+ shinfo->gso_type = 0; - shinfo->ip6_frag_id = 0; - shinfo->frag_list = NULL; - -@@ -230,8 +230,9 @@ struct sk_buff *alloc_skb_from_cache(kme - - atomic_set(&(skb_shinfo(skb)->dataref), 1); - skb_shinfo(skb)->nr_frags = 0; -- skb_shinfo(skb)->tso_size = 0; -- skb_shinfo(skb)->tso_segs = 0; -+ skb_shinfo(skb)->gso_size = 0; -+ skb_shinfo(skb)->gso_segs = 0; -+ skb_shinfo(skb)->gso_type = 0; - skb_shinfo(skb)->frag_list = NULL; - out: - return skb; -@@ -501,8 +502,9 @@ #endif - new->tc_index = old->tc_index; - #endif - atomic_set(&new->users, 1); -- skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size; -- skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs; -+ skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; -+ skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; -+ skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; - } - - /** -@@ -1777,6 +1779,133 @@ int skb_append_datato_frags(struct sock - return 0; - } - -+/** -+ * skb_segment - Perform protocol segmentation on skb. -+ * @skb: buffer to segment -+ * @features: features for the output path (see dev->features) -+ * -+ * This function performs segmentation on the given skb. It returns -+ * the segment at the given position. It returns NULL if there are -+ * no more segments to generate, or when an error is encountered. -+ */ -+struct sk_buff *skb_segment(struct sk_buff *skb, int features) -+{ -+ struct sk_buff *segs = NULL; -+ struct sk_buff *tail = NULL; -+ unsigned int mss = skb_shinfo(skb)->gso_size; -+ unsigned int doffset = skb->data - skb->mac.raw; -+ unsigned int offset = doffset; -+ unsigned int headroom; -+ unsigned int len; -+ int sg = features & NETIF_F_SG; -+ int nfrags = skb_shinfo(skb)->nr_frags; -+ int err = -ENOMEM; -+ int i = 0; -+ int pos; -+ -+ __skb_push(skb, doffset); -+ headroom = skb_headroom(skb); -+ pos = skb_headlen(skb); -+ -+ do { -+ struct sk_buff *nskb; -+ skb_frag_t *frag; -+ int hsize, nsize; -+ int k; -+ int size; -+ -+ len = skb->len - offset; -+ if (len > mss) -+ len = mss; -+ -+ hsize = skb_headlen(skb) - offset; -+ if (hsize < 0) -+ hsize = 0; -+ nsize = hsize + doffset; -+ if (nsize > len + doffset || !sg) -+ nsize = len + doffset; -+ -+ nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); -+ if (unlikely(!nskb)) -+ goto err; -+ -+ if (segs) -+ tail->next = nskb; -+ else -+ segs = nskb; -+ tail = nskb; -+ -+ nskb->dev = skb->dev; -+ nskb->priority = skb->priority; -+ nskb->protocol = skb->protocol; -+ nskb->dst = dst_clone(skb->dst); -+ memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); -+ nskb->pkt_type = skb->pkt_type; -+ nskb->mac_len = skb->mac_len; -+ -+ skb_reserve(nskb, headroom); -+ nskb->mac.raw = nskb->data; -+ nskb->nh.raw = nskb->data + skb->mac_len; -+ nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); -+ memcpy(skb_put(nskb, doffset), skb->data, doffset); -+ -+ if (!sg) { -+ nskb->csum = skb_copy_and_csum_bits(skb, offset, -+ skb_put(nskb, len), -+ len, 0); -+ continue; -+ } -+ -+ frag = skb_shinfo(nskb)->frags; -+ k = 0; -+ -+ nskb->ip_summed = CHECKSUM_HW; -+ nskb->csum = skb->csum; -+ memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); -+ -+ while (pos < offset + len) { -+ BUG_ON(i >= nfrags); -+ -+ *frag = skb_shinfo(skb)->frags[i]; -+ get_page(frag->page); -+ size = frag->size; -+ -+ if (pos < offset) { -+ frag->page_offset += offset - pos; -+ frag->size -= offset - pos; -+ } -+ -+ k++; -+ -+ if (pos + size <= offset + len) { -+ i++; -+ pos += size; -+ } else { -+ frag->size -= pos + size - (offset + len); -+ break; -+ } -+ -+ frag++; -+ } -+ -+ skb_shinfo(nskb)->nr_frags = k; -+ nskb->data_len = len - hsize; -+ nskb->len += nskb->data_len; -+ nskb->truesize += nskb->data_len; -+ } while ((offset += len) < skb->len); -+ -+ return segs; -+ -+err: -+ while ((skb = segs)) { -+ segs = skb->next; -+ kfree(skb); -+ } -+ return ERR_PTR(err); -+} -+ -+EXPORT_SYMBOL_GPL(skb_segment); -+ - void __init skb_init(void) - { - skbuff_head_cache = kmem_cache_create("skbuff_head_cache", -diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c -index 44bda85..2e3323a 100644 ---- a/net/decnet/dn_nsp_in.c -+++ b/net/decnet/dn_nsp_in.c -@@ -801,8 +801,7 @@ got_it: - * We linearize everything except data segments here. - */ - if (cb->nsp_flags & ~0x60) { -- if (unlikely(skb_is_nonlinear(skb)) && -- skb_linearize(skb, GFP_ATOMIC) != 0) -+ if (unlikely(skb_linearize(skb))) - goto free_out; - } - -diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c -index 3407f19..a0a25e0 100644 ---- a/net/decnet/dn_route.c -+++ b/net/decnet/dn_route.c -@@ -629,8 +629,7 @@ int dn_route_rcv(struct sk_buff *skb, st - padlen); - - if (flags & DN_RT_PKT_CNTL) { -- if (unlikely(skb_is_nonlinear(skb)) && -- skb_linearize(skb, GFP_ATOMIC) != 0) -+ if (unlikely(skb_linearize(skb))) - goto dump_it; - - switch(flags & DN_RT_CNTL_MSK) { -diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c -index 97c276f..5ba719e 100644 ---- a/net/ipv4/af_inet.c -+++ b/net/ipv4/af_inet.c -@@ -68,6 +68,7 @@ - */ - - #include <linux/config.h> -+#include <linux/err.h> - #include <linux/errno.h> - #include <linux/types.h> - #include <linux/socket.h> -@@ -1084,6 +1085,54 @@ int inet_sk_rebuild_header(struct sock * - - EXPORT_SYMBOL(inet_sk_rebuild_header); - -+static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) -+{ -+ struct sk_buff *segs = ERR_PTR(-EINVAL); -+ struct iphdr *iph; -+ struct net_protocol *ops; -+ int proto; -+ int ihl; -+ int id; -+ -+ if (!pskb_may_pull(skb, sizeof(*iph))) -+ goto out; -+ -+ iph = skb->nh.iph; -+ ihl = iph->ihl * 4; -+ if (ihl < sizeof(*iph)) -+ goto out; -+ -+ if (!pskb_may_pull(skb, ihl)) -+ goto out; -+ -+ skb->h.raw = __skb_pull(skb, ihl); -+ iph = skb->nh.iph; -+ id = ntohs(iph->id); -+ proto = iph->protocol & (MAX_INET_PROTOS - 1); -+ segs = ERR_PTR(-EPROTONOSUPPORT); -+ -+ rcu_read_lock(); -+ ops = rcu_dereference(inet_protos[proto]); -+ if (ops && ops->gso_segment) -+ segs = ops->gso_segment(skb, features); -+ rcu_read_unlock(); -+ -+ if (!segs || unlikely(IS_ERR(segs))) -+ goto out; -+ -+ skb = segs; -+ do { -+ iph = skb->nh.iph; -+ iph->id = htons(id++); -+ iph->tot_len = htons(skb->len - skb->mac_len); -+ iph->check = 0; -+ iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); -+ } while ((skb = skb->next)); -+ -+out: -+ return segs; -+} -+ - #ifdef CONFIG_IP_MULTICAST - static struct net_protocol igmp_protocol = { - .handler = igmp_rcv, -@@ -1093,6 +1142,7 @@ #endif - static struct net_protocol tcp_protocol = { - .handler = tcp_v4_rcv, - .err_handler = tcp_v4_err, -+ .gso_segment = tcp_tso_segment, - .no_policy = 1, - }; - -@@ -1138,6 +1188,7 @@ static int ipv4_proc_init(void); - static struct packet_type ip_packet_type = { - .type = __constant_htons(ETH_P_IP), - .func = ip_rcv, -+ .gso_segment = inet_gso_segment, - }; - - static int __init inet_init(void) -diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c -index 8dcba38..19c3c73 100644 ---- a/net/ipv4/ip_output.c -+++ b/net/ipv4/ip_output.c -@@ -210,8 +210,7 @@ #if defined(CONFIG_NETFILTER) && defined - return dst_output(skb); - } - #endif -- if (skb->len > dst_mtu(skb->dst) && -- !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) -+ if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) - return ip_fragment(skb, ip_finish_output2); - else - return ip_finish_output2(skb); -@@ -362,7 +361,7 @@ packet_routed: - } - - ip_select_ident_more(iph, &rt->u.dst, sk, -- (skb_shinfo(skb)->tso_segs ?: 1) - 1); -+ (skb_shinfo(skb)->gso_segs ?: 1) - 1); - - /* Add an IP checksum. */ - ip_send_check(iph); -@@ -743,7 +742,8 @@ static inline int ip_ufo_append_data(str - (length - transhdrlen)); - if (!err) { - /* specify the length of each IP datagram fragment*/ -- skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); -+ skb_shinfo(skb)->gso_size = mtu - fragheaderlen; -+ skb_shinfo(skb)->gso_type = SKB_GSO_UDPV4; - __skb_queue_tail(&sk->sk_write_queue, skb); - - return 0; -@@ -839,7 +839,7 @@ int ip_append_data(struct sock *sk, - */ - if (transhdrlen && - length + fragheaderlen <= mtu && _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |