--- /dev/null
+From foo@baz Tue Nov 18 09:07:46 PST 2014
+From: Steffen Klassert <steffen.klassert@secunet.com>
+Date: Mon, 3 Nov 2014 09:19:30 +0100
+Subject: gre6: Move the setting of dev->iflink into the ndo_init functions.
+
+From: Steffen Klassert <steffen.klassert@secunet.com>
+
+[ Upstream commit f03eb128e3f4276f46442d14f3b8f864f3775821 ]
+
+Otherwise it gets overwritten by register_netdev().
+
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ipv6/ip6_gre.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/net/ipv6/ip6_gre.c
++++ b/net/ipv6/ip6_gre.c
+@@ -962,8 +962,6 @@ static void ip6gre_tnl_link_config(struc
+ else
+ dev->flags &= ~IFF_POINTOPOINT;
+
+- dev->iflink = p->link;
+-
+ /* Precalculate GRE options length */
+ if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
+ if (t->parms.o_flags&GRE_CSUM)
+@@ -1267,6 +1265,8 @@ static int ip6gre_tunnel_init(struct net
+ if (!dev->tstats)
+ return -ENOMEM;
+
++ dev->iflink = tunnel->parms.link;
++
+ return 0;
+ }
+
+@@ -1282,7 +1282,6 @@ static void ip6gre_fb_tunnel_init(struct
+ dev_hold(dev);
+ }
+
+-
+ static struct inet6_protocol ip6gre_protocol __read_mostly = {
+ .handler = ip6gre_rcv,
+ .err_handler = ip6gre_err,
+@@ -1458,6 +1457,8 @@ static int ip6gre_tap_init(struct net_de
+ if (!dev->tstats)
+ return -ENOMEM;
+
++ dev->iflink = tunnel->parms.link;
++
+ return 0;
+ }
+
--- /dev/null
+From foo@baz Tue Nov 18 09:07:46 PST 2014
+From: Steffen Klassert <steffen.klassert@secunet.com>
+Date: Mon, 3 Nov 2014 09:19:27 +0100
+Subject: ip6_tunnel: Use ip6_tnl_dev_init as the ndo_init function.
+
+From: Steffen Klassert <steffen.klassert@secunet.com>
+
+[ Upstream commit 6c6151daaf2d8dc2046d9926539feed5f66bf74e ]
+
+ip6_tnl_dev_init() sets the dev->iflink via a call to
+ip6_tnl_link_config(). After that, register_netdevice()
+sets dev->iflink = -1. So we loose the iflink configuration
+for ipv6 tunnels. Fix this by using ip6_tnl_dev_init() as the
+ndo_init function. Then ip6_tnl_dev_init() is called after
+dev->iflink is set to -1 from register_netdevice().
+
+Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/ipv6/ip6_tunnel.c | 10 +---------
+ 1 file changed, 1 insertion(+), 9 deletions(-)
+
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -265,9 +265,6 @@ static int ip6_tnl_create2(struct net_de
+ int err;
+
+ t = netdev_priv(dev);
+- err = ip6_tnl_dev_init(dev);
+- if (err < 0)
+- goto out;
+
+ err = register_netdevice(dev);
+ if (err < 0)
+@@ -1433,6 +1430,7 @@ ip6_tnl_change_mtu(struct net_device *de
+
+
+ static const struct net_device_ops ip6_tnl_netdev_ops = {
++ .ndo_init = ip6_tnl_dev_init,
+ .ndo_uninit = ip6_tnl_dev_uninit,
+ .ndo_start_xmit = ip6_tnl_xmit,
+ .ndo_do_ioctl = ip6_tnl_ioctl,
+@@ -1514,16 +1512,10 @@ static int __net_init ip6_fb_tnl_dev_ini
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+- int err = ip6_tnl_dev_init_gen(dev);
+-
+- if (err)
+- return err;
+
+ t->parms.proto = IPPROTO_IPV6;
+ dev_hold(dev);
+
+- ip6_tnl_link_config(t);
+-
+ rcu_assign_pointer(ip6n->tnls_wc[0], t);
+ return 0;
+ }
--- /dev/null
+From foo@baz Tue Nov 18 09:07:46 PST 2014
+From: Daniel Borkmann <dborkman@redhat.com>
+Date: Mon, 10 Nov 2014 18:00:09 +0100
+Subject: net: sctp: fix memory leak in auth key management
+
+From: Daniel Borkmann <dborkman@redhat.com>
+
+[ Upstream commit 4184b2a79a7612a9272ce20d639934584a1f3786 ]
+
+A very minimal and simple user space application allocating an SCTP
+socket, setting SCTP_AUTH_KEY setsockopt(2) on it and then closing
+the socket again will leak the memory containing the authentication
+key from user space:
+
+unreferenced object 0xffff8800837047c0 (size 16):
+ comm "a.out", pid 2789, jiffies 4296954322 (age 192.258s)
+ hex dump (first 16 bytes):
+ 01 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 ................
+ backtrace:
+ [<ffffffff816d7e8e>] kmemleak_alloc+0x4e/0xb0
+ [<ffffffff811c88d8>] __kmalloc+0xe8/0x270
+ [<ffffffffa0870c23>] sctp_auth_create_key+0x23/0x50 [sctp]
+ [<ffffffffa08718b1>] sctp_auth_set_key+0xa1/0x140 [sctp]
+ [<ffffffffa086b383>] sctp_setsockopt+0xd03/0x1180 [sctp]
+ [<ffffffff815bfd94>] sock_common_setsockopt+0x14/0x20
+ [<ffffffff815beb61>] SyS_setsockopt+0x71/0xd0
+ [<ffffffff816e58a9>] system_call_fastpath+0x12/0x17
+ [<ffffffffffffffff>] 0xffffffffffffffff
+
+This is bad because of two things, we can bring down a machine from
+user space when auth_enable=1, but also we would leave security sensitive
+keying material in memory without clearing it after use. The issue is
+that sctp_auth_create_key() already sets the refcount to 1, but after
+allocation sctp_auth_set_key() does an additional refcount on it, and
+thus leaving it around when we free the socket.
+
+Fixes: 65b07e5d0d0 ("[SCTP]: API updates to suport SCTP-AUTH extensions.")
+Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
+Cc: Vlad Yasevich <vyasevich@gmail.com>
+Acked-by: Neil Horman <nhorman@tuxdriver.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/sctp/auth.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/net/sctp/auth.c
++++ b/net/sctp/auth.c
+@@ -874,8 +874,6 @@ int sctp_auth_set_key(struct sctp_endpoi
+ list_add(&cur_key->key_list, sh_keys);
+
+ cur_key->key = key;
+- sctp_auth_key_hold(key);
+-
+ return 0;
+ nomem:
+ if (!replace)
--- /dev/null
+From foo@baz Tue Nov 18 09:07:46 PST 2014
+From: Daniel Borkmann <dborkman@redhat.com>
+Date: Mon, 10 Nov 2014 17:54:26 +0100
+Subject: net: sctp: fix NULL pointer dereference in af->from_addr_param on malformed packet
+
+From: Daniel Borkmann <dborkman@redhat.com>
+
+[ Upstream commit e40607cbe270a9e8360907cb1e62ddf0736e4864 ]
+
+An SCTP server doing ASCONF will panic on malformed INIT ping-of-death
+in the form of:
+
+ ------------ INIT[PARAM: SET_PRIMARY_IP] ------------>
+
+While the INIT chunk parameter verification dissects through many things
+in order to detect malformed input, it misses to actually check parameters
+inside of parameters. E.g. RFC5061, section 4.2.4 proposes a 'set primary
+IP address' parameter in ASCONF, which has as a subparameter an address
+parameter.
+
+So an attacker may send a parameter type other than SCTP_PARAM_IPV4_ADDRESS
+or SCTP_PARAM_IPV6_ADDRESS, param_type2af() will subsequently return 0
+and thus sctp_get_af_specific() returns NULL, too, which we then happily
+dereference unconditionally through af->from_addr_param().
+
+The trace for the log:
+
+BUG: unable to handle kernel NULL pointer dereference at 0000000000000078
+IP: [<ffffffffa01e9c62>] sctp_process_init+0x492/0x990 [sctp]
+PGD 0
+Oops: 0000 [#1] SMP
+[...]
+Pid: 0, comm: swapper Not tainted 2.6.32-504.el6.x86_64 #1 Bochs Bochs
+RIP: 0010:[<ffffffffa01e9c62>] [<ffffffffa01e9c62>] sctp_process_init+0x492/0x990 [sctp]
+[...]
+Call Trace:
+ <IRQ>
+ [<ffffffffa01f2add>] ? sctp_bind_addr_copy+0x5d/0xe0 [sctp]
+ [<ffffffffa01e1fcb>] sctp_sf_do_5_1B_init+0x21b/0x340 [sctp]
+ [<ffffffffa01e3751>] sctp_do_sm+0x71/0x1210 [sctp]
+ [<ffffffffa01e5c09>] ? sctp_endpoint_lookup_assoc+0xc9/0xf0 [sctp]
+ [<ffffffffa01e61f6>] sctp_endpoint_bh_rcv+0x116/0x230 [sctp]
+ [<ffffffffa01ee986>] sctp_inq_push+0x56/0x80 [sctp]
+ [<ffffffffa01fcc42>] sctp_rcv+0x982/0xa10 [sctp]
+ [<ffffffffa01d5123>] ? ipt_local_in_hook+0x23/0x28 [iptable_filter]
+ [<ffffffff8148bdc9>] ? nf_iterate+0x69/0xb0
+ [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0
+ [<ffffffff8148bf86>] ? nf_hook_slow+0x76/0x120
+ [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0
+[...]
+
+A minimal way to address this is to check for NULL as we do on all
+other such occasions where we know sctp_get_af_specific() could
+possibly return with NULL.
+
+Fixes: d6de3097592b ("[SCTP]: Add the handling of "Set Primary IP Address" parameter to INIT")
+Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
+Cc: Vlad Yasevich <vyasevich@gmail.com>
+Acked-by: Neil Horman <nhorman@tuxdriver.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/sctp/sm_make_chunk.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/sctp/sm_make_chunk.c
++++ b/net/sctp/sm_make_chunk.c
+@@ -2596,6 +2596,9 @@ do_addr_param:
+ addr_param = param.v + sizeof(sctp_addip_param_t);
+
+ af = sctp_get_af_specific(param_type2af(param.p->type));
++ if (af == NULL)
++ break;
++
+ af->from_addr_param(&addr, addr_param,
+ htons(asoc->peer.port), 0);
+
--- /dev/null
+ip6_tunnel-use-ip6_tnl_dev_init-as-the-ndo_init-function.patch
+gre6-move-the-setting-of-dev-iflink-into-the-ndo_init-functions.patch
+net-sctp-fix-null-pointer-dereference-in-af-from_addr_param-on-malformed-packet.patch
+net-sctp-fix-memory-leak-in-auth-key-management.patch
+sunvdc-add-cdrom-and-v1.1-protocol-support.patch
+sunvdc-compute-vdisk-geometry-from-capacity.patch
+sunvdc-limit-each-sg-segment-to-a-page.patch
+vio-fix-reuse-of-vio_dring-slot.patch
+sunvdc-don-t-call-vd_op_get_vtoc.patch
+sparc64-fix-crashes-in-schizo_pcierr_intr_other.patch
+sparc64-do-irq_-enter-exit-around-generic_smp_call_function.patch
+sparc32-implement-xchg-and-atomic_xchg-using-atomic_hash-locks.patch
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: Andreas Larsson <andreas@gaisler.com>
+Date: Wed, 5 Nov 2014 15:52:08 +0100
+Subject: sparc32: Implement xchg and atomic_xchg using ATOMIC_HASH locks
+
+From: Andreas Larsson <andreas@gaisler.com>
+
+[ Upstream commit 1a17fdc4f4ed06b63fac1937470378a5441a663a ]
+
+Atomicity between xchg and cmpxchg cannot be guaranteed when xchg is
+implemented with a swap and cmpxchg is implemented with locks.
+Without this, e.g. mcs_spin_lock and mcs_spin_unlock are broken.
+
+Signed-off-by: Andreas Larsson <andreas@gaisler.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/sparc/include/asm/atomic_32.h | 2 +-
+ arch/sparc/include/asm/cmpxchg_32.h | 12 ++----------
+ arch/sparc/lib/atomic32.c | 27 +++++++++++++++++++++++++++
+ 3 files changed, 30 insertions(+), 11 deletions(-)
+
+--- a/arch/sparc/include/asm/atomic_32.h
++++ b/arch/sparc/include/asm/atomic_32.h
+@@ -21,7 +21,7 @@
+
+ extern int __atomic_add_return(int, atomic_t *);
+ extern int atomic_cmpxchg(atomic_t *, int, int);
+-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
++extern int atomic_xchg(atomic_t *, int);
+ extern int __atomic_add_unless(atomic_t *, int, int);
+ extern void atomic_set(atomic_t *, int);
+
+--- a/arch/sparc/include/asm/cmpxchg_32.h
++++ b/arch/sparc/include/asm/cmpxchg_32.h
+@@ -11,22 +11,14 @@
+ #ifndef __ARCH_SPARC_CMPXCHG__
+ #define __ARCH_SPARC_CMPXCHG__
+
+-static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
+-{
+- __asm__ __volatile__("swap [%2], %0"
+- : "=&r" (val)
+- : "0" (val), "r" (m)
+- : "memory");
+- return val;
+-}
+-
++extern unsigned long __xchg_u32(volatile u32 *m, u32 new);
+ extern void __xchg_called_with_bad_pointer(void);
+
+ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
+ {
+ switch (size) {
+ case 4:
+- return xchg_u32(ptr, x);
++ return __xchg_u32(ptr, x);
+ }
+ __xchg_called_with_bad_pointer();
+ return x;
+--- a/arch/sparc/lib/atomic32.c
++++ b/arch/sparc/lib/atomic32.c
+@@ -40,6 +40,19 @@ int __atomic_add_return(int i, atomic_t
+ }
+ EXPORT_SYMBOL(__atomic_add_return);
+
++int atomic_xchg(atomic_t *v, int new)
++{
++ int ret;
++ unsigned long flags;
++
++ spin_lock_irqsave(ATOMIC_HASH(v), flags);
++ ret = v->counter;
++ v->counter = new;
++ spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
++ return ret;
++}
++EXPORT_SYMBOL(atomic_xchg);
++
+ int atomic_cmpxchg(atomic_t *v, int old, int new)
+ {
+ int ret;
+@@ -132,3 +145,17 @@ unsigned long __cmpxchg_u32(volatile u32
+ return (unsigned long)prev;
+ }
+ EXPORT_SYMBOL(__cmpxchg_u32);
++
++unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
++{
++ unsigned long flags;
++ u32 prev;
++
++ spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
++ prev = *ptr;
++ *ptr = new;
++ spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
++
++ return (unsigned long)prev;
++}
++EXPORT_SYMBOL(__xchg_u32);
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: "David S. Miller" <davem@davemloft.net>
+Date: Fri, 7 Nov 2014 09:50:48 -0800
+Subject: sparc64: Do irq_{enter,exit}() around generic_smp_call_function*().
+
+From: "David S. Miller" <davem@davemloft.net>
+
+[ Upstream commit ab5c780913bca0a5763ca05dd5c2cb5cb08ccb26 ]
+
+Otherwise rcu_irq_{enter,exit}() do not happen and we get dumps like:
+
+====================
+[ 188.275021] ===============================
+[ 188.309351] [ INFO: suspicious RCU usage. ]
+[ 188.343737] 3.18.0-rc3-00068-g20f3963-dirty #54 Not tainted
+[ 188.394786] -------------------------------
+[ 188.429170] include/linux/rcupdate.h:883 rcu_read_lock() used
+illegally while idle!
+[ 188.505235]
+other info that might help us debug this:
+
+[ 188.554230]
+RCU used illegally from idle CPU!
+rcu_scheduler_active = 1, debug_locks = 0
+[ 188.637587] RCU used illegally from extended quiescent state!
+[ 188.690684] 3 locks held by swapper/7/0:
+[ 188.721932] #0: (&x->wait#11){......}, at: [<0000000000495de8>] complete+0x8/0x60
+[ 188.797994] #1: (&p->pi_lock){-.-.-.}, at: [<000000000048510c>] try_to_wake_up+0xc/0x400
+[ 188.881343] #2: (rcu_read_lock){......}, at: [<000000000048a910>] select_task_rq_fair+0x90/0xb40
+[ 188.973043]stack backtrace:
+[ 188.993879] CPU: 7 PID: 0 Comm: swapper/7 Not tainted 3.18.0-rc3-00068-g20f3963-dirty #54
+[ 189.076187] Call Trace:
+[ 189.089719] [0000000000499360] lockdep_rcu_suspicious+0xe0/0x100
+[ 189.147035] [000000000048a99c] select_task_rq_fair+0x11c/0xb40
+[ 189.202253] [00000000004852d8] try_to_wake_up+0x1d8/0x400
+[ 189.252258] [000000000048554c] default_wake_function+0xc/0x20
+[ 189.306435] [0000000000495554] __wake_up_common+0x34/0x80
+[ 189.356448] [00000000004955b4] __wake_up_locked+0x14/0x40
+[ 189.406456] [0000000000495e08] complete+0x28/0x60
+[ 189.448142] [0000000000636e28] blk_end_sync_rq+0x8/0x20
+[ 189.496057] [0000000000639898] __blk_mq_end_request+0x18/0x60
+[ 189.550249] [00000000006ee014] scsi_end_request+0x94/0x180
+[ 189.601286] [00000000006ee334] scsi_io_completion+0x1d4/0x600
+[ 189.655463] [00000000006e51c4] scsi_finish_command+0xc4/0xe0
+[ 189.708598] [00000000006ed958] scsi_softirq_done+0x118/0x140
+[ 189.761735] [00000000006398ec] __blk_mq_complete_request_remote+0xc/0x20
+[ 189.827383] [00000000004c75d0] generic_smp_call_function_single_interrupt+0x150/0x1c0
+[ 189.906581] [000000000043e514] smp_call_function_single_client+0x14/0x40
+====================
+
+Based almost entirely upon a patch by Paul E. McKenney.
+
+Reported-by: Meelis Roos <mroos@linux.ee>
+Tested-by: Meelis Roos <mroos@linux.ee>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/sparc/kernel/smp_64.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/sparc/kernel/smp_64.c
++++ b/arch/sparc/kernel/smp_64.c
+@@ -821,13 +821,17 @@ void arch_send_call_function_single_ipi(
+ void __irq_entry smp_call_function_client(int irq, struct pt_regs *regs)
+ {
+ clear_softint(1 << irq);
++ irq_enter();
+ generic_smp_call_function_interrupt();
++ irq_exit();
+ }
+
+ void __irq_entry smp_call_function_single_client(int irq, struct pt_regs *regs)
+ {
+ clear_softint(1 << irq);
++ irq_enter();
+ generic_smp_call_function_single_interrupt();
++ irq_exit();
+ }
+
+ static void tsb_sync(void *info)
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: "David S. Miller" <davem@davemloft.net>
+Date: Sat, 1 Nov 2014 00:33:58 -0400
+Subject: sparc64: Fix crashes in schizo_pcierr_intr_other().
+
+From: "David S. Miller" <davem@davemloft.net>
+
+[ Upstream commit 7da89a2a3776442a57e918ca0b8678d1b16a7072 ]
+
+Meelis Roos reports crashes during bootup on a V480 that look like
+this:
+
+====================
+[ 61.300577] PCI: Scanning PBM /pci@9,600000
+[ 61.304867] schizo f009b070: PCI host bridge to bus 0003:00
+[ 61.310385] pci_bus 0003:00: root bus resource [io 0x7ffe9000000-0x7ffe9ffffff] (bus address [0x0000-0xffffff])
+[ 61.320515] pci_bus 0003:00: root bus resource [mem 0x7fb00000000-0x7fbffffffff] (bus address [0x00000000-0xffffffff])
+[ 61.331173] pci_bus 0003:00: root bus resource [bus 00]
+[ 61.385344] Unable to handle kernel NULL pointer dereference
+[ 61.390970] tsk->{mm,active_mm}->context = 0000000000000000
+[ 61.396515] tsk->{mm,active_mm}->pgd = fff000b000002000
+[ 61.401716] \|/ ____ \|/
+[ 61.401716] "@'/ .. \`@"
+[ 61.401716] /_| \__/ |_\
+[ 61.401716] \__U_/
+[ 61.416362] swapper/0(0): Oops [#1]
+[ 61.419837] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.18.0-rc1-00422-g2cc9188-dirty #24
+[ 61.427975] task: fff000b0fd8e9c40 ti: fff000b0fd928000 task.ti: fff000b0fd928000
+[ 61.435426] TSTATE: 0000004480e01602 TPC: 00000000004455e4 TNPC: 00000000004455e8 Y: 00000000 Not tainted
+[ 61.445230] TPC: <schizo_pcierr_intr+0x104/0x560>
+[ 61.449897] g0: 0000000000000000 g1: 0000000000000000 g2: 0000000000a10f78 g3: 000000000000000a
+[ 61.458563] g4: fff000b0fd8e9c40 g5: fff000b0fdd82000 g6: fff000b0fd928000 g7: 000000000000000a
+[ 61.467229] o0: 000000000000003d o1: 0000000000000000 o2: 0000000000000006 o3: fff000b0ffa5fc7e
+[ 61.475894] o4: 0000000000060000 o5: c000000000000000 sp: fff000b0ffa5f3c1 ret_pc: 00000000004455cc
+[ 61.484909] RPC: <schizo_pcierr_intr+0xec/0x560>
+[ 61.489500] l0: fff000b0fd8e9c40 l1: 0000000000a20800 l2: 0000000000000000 l3: 000000000119a430
+[ 61.498164] l4: 0000000001742400 l5: 00000000011cfbe0 l6: 00000000011319c0 l7: fff000b0fd8ea348
+[ 61.506830] i0: 0000000000000000 i1: fff000b0fdb34000 i2: 0000000320000000 i3: 0000000000000000
+[ 61.515497] i4: 00060002010b003f i5: 0000040004e02000 i6: fff000b0ffa5f481 i7: 00000000004a9920
+[ 61.524175] I7: <handle_irq_event_percpu+0x40/0x140>
+[ 61.529099] Call Trace:
+[ 61.531531] [00000000004a9920] handle_irq_event_percpu+0x40/0x140
+[ 61.537681] [00000000004a9a58] handle_irq_event+0x38/0x80
+[ 61.543145] [00000000004ac77c] handle_fasteoi_irq+0xbc/0x200
+[ 61.548860] [00000000004a9084] generic_handle_irq+0x24/0x40
+[ 61.554500] [000000000042be0c] handler_irq+0xac/0x100
+====================
+
+The problem is that pbm->pci_bus->self is NULL.
+
+This code is trying to go through the standard PCI config space
+interfaces to read the PCI controller's PCI_STATUS register.
+
+This doesn't work, because we more often than not do not enumerate
+the PCI controller as a bonafide PCI device during the OF device
+node scan. Therefore bus->self remains NULL.
+
+Existing common code for PSYCHO and PSYCHO-like PCI controllers
+handles this properly, by doing the config space access directly.
+
+Do the same here, pbm->pci_ops->{read,write}().
+
+Reported-by: Meelis Roos <mroos@linux.ee>
+Tested-by: Meelis Roos <mroos@linux.ee>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/sparc/kernel/pci_schizo.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/sparc/kernel/pci_schizo.c
++++ b/arch/sparc/kernel/pci_schizo.c
+@@ -581,7 +581,7 @@ static irqreturn_t schizo_pcierr_intr_ot
+ {
+ unsigned long csr_reg, csr, csr_error_bits;
+ irqreturn_t ret = IRQ_NONE;
+- u16 stat;
++ u32 stat;
+
+ csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
+ csr = upa_readq(csr_reg);
+@@ -617,7 +617,7 @@ static irqreturn_t schizo_pcierr_intr_ot
+ pbm->name);
+ ret = IRQ_HANDLED;
+ }
+- pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
++ pbm->pci_ops->read(pbm->pci_bus, 0, PCI_STATUS, 2, &stat);
+ if (stat & (PCI_STATUS_PARITY |
+ PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT |
+@@ -625,7 +625,7 @@ static irqreturn_t schizo_pcierr_intr_ot
+ PCI_STATUS_SIG_SYSTEM_ERROR)) {
+ printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
+ pbm->name, stat);
+- pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
++ pbm->pci_ops->write(pbm->pci_bus, 0, PCI_STATUS, 2, 0xffff);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
--- /dev/null
+From foo@baz Tue Nov 18 09:08:46 PST 2014
+From: Allen Pais <allen.pais@oracle.com>
+Date: Fri, 19 Sep 2014 09:42:14 -0400
+Subject: sunvdc: add cdrom and v1.1 protocol support
+
+From: Allen Pais <allen.pais@oracle.com>
+
+[ Upstream commit 9bce21828d54a95143f1b74619705c2dd8e88b92 ]
+
+Interpret the media type from v1.1 protocol to support CDROM/DVD.
+
+For v1.0 protocol, a disk's size continues to be calculated from the
+geometry returned by the vdisk server. The geometry returned by the server
+can be less than the actual number of sectors available in the backing
+image/device due to the rounding in the division used to compute the
+geometry in the vdisk server.
+
+In v1.1 protocol a disk's actual size in sectors is returned during the
+handshake. Use this size when v1.1 protocol is negotiated. Since this size
+will always be larger than the former geometry computed size, disks created
+under v1.0 will be forwards compatible to v1.1, but not vice versa.
+
+Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/sparc/include/asm/vio.h | 12 +++-
+ drivers/block/sunvdc.c | 109 ++++++++++++++++++++++++++++++++++++-------
+ 2 files changed, 101 insertions(+), 20 deletions(-)
+
+--- a/arch/sparc/include/asm/vio.h
++++ b/arch/sparc/include/asm/vio.h
+@@ -118,12 +118,18 @@ struct vio_disk_attr_info {
+ u8 vdisk_type;
+ #define VD_DISK_TYPE_SLICE 0x01 /* Slice in block device */
+ #define VD_DISK_TYPE_DISK 0x02 /* Entire block device */
+- u16 resv1;
++ u8 vdisk_mtype; /* v1.1 */
++#define VD_MEDIA_TYPE_FIXED 0x01 /* Fixed device */
++#define VD_MEDIA_TYPE_CD 0x02 /* CD Device */
++#define VD_MEDIA_TYPE_DVD 0x03 /* DVD Device */
++ u8 resv1;
+ u32 vdisk_block_size;
+ u64 operations;
+- u64 vdisk_size;
++ u64 vdisk_size; /* v1.1 */
+ u64 max_xfer_size;
+- u64 resv2[2];
++ u32 phys_block_size; /* v1.2 */
++ u32 resv2;
++ u64 resv3[1];
+ };
+
+ struct vio_disk_desc {
+--- a/drivers/block/sunvdc.c
++++ b/drivers/block/sunvdc.c
+@@ -9,6 +9,7 @@
+ #include <linux/blkdev.h>
+ #include <linux/hdreg.h>
+ #include <linux/genhd.h>
++#include <linux/cdrom.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/completion.h>
+@@ -22,8 +23,8 @@
+
+ #define DRV_MODULE_NAME "sunvdc"
+ #define PFX DRV_MODULE_NAME ": "
+-#define DRV_MODULE_VERSION "1.0"
+-#define DRV_MODULE_RELDATE "June 25, 2007"
++#define DRV_MODULE_VERSION "1.1"
++#define DRV_MODULE_RELDATE "February 13, 2013"
+
+ static char version[] =
+ DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+@@ -65,6 +66,7 @@ struct vdc_port {
+ u64 operations;
+ u32 vdisk_size;
+ u8 vdisk_type;
++ u8 vdisk_mtype;
+
+ char disk_name[32];
+
+@@ -79,9 +81,16 @@ static inline struct vdc_port *to_vdc_po
+
+ /* Ordered from largest major to lowest */
+ static struct vio_version vdc_versions[] = {
++ { .major = 1, .minor = 1 },
+ { .major = 1, .minor = 0 },
+ };
+
++static inline int vdc_version_supported(struct vdc_port *port,
++ u16 major, u16 minor)
++{
++ return port->vio.ver.major == major && port->vio.ver.minor >= minor;
++}
++
+ #define VDCBLK_NAME "vdisk"
+ static int vdc_major;
+ #define PARTITION_SHIFT 3
+@@ -103,9 +112,41 @@ static int vdc_getgeo(struct block_devic
+ return 0;
+ }
+
++/* Add ioctl/CDROM_GET_CAPABILITY to support cdrom_id in udev
++ * when vdisk_mtype is VD_MEDIA_TYPE_CD or VD_MEDIA_TYPE_DVD.
++ * Needed to be able to install inside an ldom from an iso image.
++ */
++static int vdc_ioctl(struct block_device *bdev, fmode_t mode,
++ unsigned command, unsigned long argument)
++{
++ int i;
++ struct gendisk *disk;
++
++ switch (command) {
++ case CDROMMULTISESSION:
++ pr_debug(PFX "Multisession CDs not supported\n");
++ for (i = 0; i < sizeof(struct cdrom_multisession); i++)
++ if (put_user(0, (char __user *)(argument + i)))
++ return -EFAULT;
++ return 0;
++
++ case CDROM_GET_CAPABILITY:
++ disk = bdev->bd_disk;
++
++ if (bdev->bd_disk && (disk->flags & GENHD_FL_CD))
++ return 0;
++ return -EINVAL;
++
++ default:
++ pr_debug(PFX "ioctl %08x not supported\n", command);
++ return -EINVAL;
++ }
++}
++
+ static const struct block_device_operations vdc_fops = {
+ .owner = THIS_MODULE,
+ .getgeo = vdc_getgeo,
++ .ioctl = vdc_ioctl,
+ };
+
+ static void vdc_finish(struct vio_driver_state *vio, int err, int waiting_for)
+@@ -165,9 +206,9 @@ static int vdc_handle_attr(struct vio_dr
+ struct vio_disk_attr_info *pkt = arg;
+
+ viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] "
+- "xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
++ "mtype[0x%x] xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n",
+ pkt->tag.stype, pkt->operations,
+- pkt->vdisk_size, pkt->vdisk_type,
++ pkt->vdisk_size, pkt->vdisk_type, pkt->vdisk_mtype,
+ pkt->xfer_mode, pkt->vdisk_block_size,
+ pkt->max_xfer_size);
+
+@@ -192,8 +233,11 @@ static int vdc_handle_attr(struct vio_dr
+ }
+
+ port->operations = pkt->operations;
+- port->vdisk_size = pkt->vdisk_size;
+ port->vdisk_type = pkt->vdisk_type;
++ if (vdc_version_supported(port, 1, 1)) {
++ port->vdisk_size = pkt->vdisk_size;
++ port->vdisk_mtype = pkt->vdisk_mtype;
++ }
+ if (pkt->max_xfer_size < port->max_xfer_size)
+ port->max_xfer_size = pkt->max_xfer_size;
+ port->vdisk_block_size = pkt->vdisk_block_size;
+@@ -663,18 +707,25 @@ static int probe_disk(struct vdc_port *p
+ return err;
+ }
+
+- err = generic_request(port, VD_OP_GET_DISKGEOM,
+- &port->geom, sizeof(port->geom));
+- if (err < 0) {
+- printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
+- "error %d\n", err);
+- return err;
++ if (vdc_version_supported(port, 1, 1)) {
++ /* vdisk_size should be set during the handshake, if it wasn't
++ * then the underlying disk is reserved by another system
++ */
++ if (port->vdisk_size == -1)
++ return -ENODEV;
++ } else {
++ err = generic_request(port, VD_OP_GET_DISKGEOM,
++ &port->geom, sizeof(port->geom));
++ if (err < 0) {
++ printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
++ "error %d\n", err);
++ return err;
++ }
++ port->vdisk_size = ((u64)port->geom.num_cyl *
++ (u64)port->geom.num_hd *
++ (u64)port->geom.num_sec);
+ }
+
+- port->vdisk_size = ((u64)port->geom.num_cyl *
+- (u64)port->geom.num_hd *
+- (u64)port->geom.num_sec);
+-
+ q = blk_init_queue(do_vdc_request, &port->vio.lock);
+ if (!q) {
+ printk(KERN_ERR PFX "%s: Could not allocate queue.\n",
+@@ -704,9 +755,32 @@ static int probe_disk(struct vdc_port *p
+
+ set_capacity(g, port->vdisk_size);
+
+- printk(KERN_INFO PFX "%s: %u sectors (%u MB)\n",
++ if (vdc_version_supported(port, 1, 1)) {
++ switch (port->vdisk_mtype) {
++ case VD_MEDIA_TYPE_CD:
++ pr_info(PFX "Virtual CDROM %s\n", port->disk_name);
++ g->flags |= GENHD_FL_CD;
++ g->flags |= GENHD_FL_REMOVABLE;
++ set_disk_ro(g, 1);
++ break;
++
++ case VD_MEDIA_TYPE_DVD:
++ pr_info(PFX "Virtual DVD %s\n", port->disk_name);
++ g->flags |= GENHD_FL_CD;
++ g->flags |= GENHD_FL_REMOVABLE;
++ set_disk_ro(g, 1);
++ break;
++
++ case VD_MEDIA_TYPE_FIXED:
++ pr_info(PFX "Virtual Hard disk %s\n", port->disk_name);
++ break;
++ }
++ }
++
++ pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n",
+ g->disk_name,
+- port->vdisk_size, (port->vdisk_size >> (20 - 9)));
++ port->vdisk_size, (port->vdisk_size >> (20 - 9)),
++ port->vio.ver.major, port->vio.ver.minor);
+
+ add_disk(g);
+
+@@ -765,6 +839,7 @@ static int vdc_port_probe(struct vio_dev
+ else
+ snprintf(port->disk_name, sizeof(port->disk_name),
+ VDCBLK_NAME "%c", 'a' + ((int)vdev->dev_no % 26));
++ port->vdisk_size = -1;
+
+ err = vio_driver_init(&port->vio, vdev, VDEV_DISK,
+ vdc_versions, ARRAY_SIZE(vdc_versions),
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: Allen Pais <allen.pais@oracle.com>
+Date: Fri, 19 Sep 2014 09:42:26 -0400
+Subject: sunvdc: compute vdisk geometry from capacity
+
+From: Allen Pais <allen.pais@oracle.com>
+
+[ Upstream commit de5b73f08468b4fc5e2f6d1505f650262622f78b ]
+
+The LDom diskserver doesn't return reliable geometry data. In addition,
+the types for all fields in the vio_disk_geom are u16, which were being
+truncated in the cast into the u8's of the Linux struct hd_geometry.
+
+Modify vdc_getgeo() to compute the geometry from the disk's capacity in a
+manner consistent with xen-blkfront::blkif_getgeo().
+
+Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/block/sunvdc.c | 23 ++++++++++++++---------
+ 1 file changed, 14 insertions(+), 9 deletions(-)
+
+--- a/drivers/block/sunvdc.c
++++ b/drivers/block/sunvdc.c
+@@ -70,7 +70,6 @@ struct vdc_port {
+
+ char disk_name[32];
+
+- struct vio_disk_geom geom;
+ struct vio_disk_vtoc label;
+ };
+
+@@ -103,11 +102,15 @@ static inline u32 vdc_tx_dring_avail(str
+ static int vdc_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+ {
+ struct gendisk *disk = bdev->bd_disk;
+- struct vdc_port *port = disk->private_data;
++ sector_t nsect = get_capacity(disk);
++ sector_t cylinders = nsect;
+
+- geo->heads = (u8) port->geom.num_hd;
+- geo->sectors = (u8) port->geom.num_sec;
+- geo->cylinders = port->geom.num_cyl;
++ geo->heads = 0xff;
++ geo->sectors = 0x3f;
++ sector_div(cylinders, geo->heads * geo->sectors);
++ geo->cylinders = cylinders;
++ if ((sector_t)(geo->cylinders + 1) * geo->heads * geo->sectors < nsect)
++ geo->cylinders = 0xffff;
+
+ return 0;
+ }
+@@ -714,16 +717,18 @@ static int probe_disk(struct vdc_port *p
+ if (port->vdisk_size == -1)
+ return -ENODEV;
+ } else {
++ struct vio_disk_geom geom;
++
+ err = generic_request(port, VD_OP_GET_DISKGEOM,
+- &port->geom, sizeof(port->geom));
++ &geom, sizeof(geom));
+ if (err < 0) {
+ printk(KERN_ERR PFX "VD_OP_GET_DISKGEOM returns "
+ "error %d\n", err);
+ return err;
+ }
+- port->vdisk_size = ((u64)port->geom.num_cyl *
+- (u64)port->geom.num_hd *
+- (u64)port->geom.num_sec);
++ port->vdisk_size = ((u64)geom.num_cyl *
++ (u64)geom.num_hd *
++ (u64)geom.num_sec);
+ }
+
+ q = blk_init_queue(do_vdc_request, &port->vio.lock);
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: Dwight Engen <dwight.engen@oracle.com>
+Date: Thu, 30 Oct 2014 15:55:35 -0400
+Subject: sunvdc: don't call VD_OP_GET_VTOC
+
+From: Dwight Engen <dwight.engen@oracle.com>
+
+[ Upstream commit 85b0c6e62c48bb9179fd5b3e954f362fb346cbd5 ]
+
+The VD_OP_GET_VTOC operation will succeed only if the vdisk backend has a
+VTOC label, otherwise it will fail. In particular, it will return error
+48 (ENOTSUP) if the disk has an EFI label. VTOC disk labels are already
+handled by directly reading the disk in block/partitions/sun.c (enabled by
+CONFIG_SUN_PARTITION which defaults to y on SPARC). Since port->label is
+unused in the driver, remove the call and the field.
+
+Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/block/sunvdc.c | 9 ---------
+ 1 file changed, 9 deletions(-)
+
+--- a/drivers/block/sunvdc.c
++++ b/drivers/block/sunvdc.c
+@@ -69,8 +69,6 @@ struct vdc_port {
+ u8 vdisk_mtype;
+
+ char disk_name[32];
+-
+- struct vio_disk_vtoc label;
+ };
+
+ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio)
+@@ -710,13 +708,6 @@ static int probe_disk(struct vdc_port *p
+ if (comp.err)
+ return comp.err;
+
+- err = generic_request(port, VD_OP_GET_VTOC,
+- &port->label, sizeof(port->label));
+- if (err < 0) {
+- printk(KERN_ERR PFX "VD_OP_GET_VTOC returns error %d\n", err);
+- return err;
+- }
+-
+ if (vdc_version_supported(port, 1, 1)) {
+ /* vdisk_size should be set during the handshake, if it wasn't
+ * then the underlying disk is reserved by another system
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: Dwight Engen <dwight.engen@oracle.com>
+Date: Fri, 19 Sep 2014 09:42:53 -0400
+Subject: sunvdc: limit each sg segment to a page
+
+From: Dwight Engen <dwight.engen@oracle.com>
+
+[ Upstream commit 5eed69ffd248c9f68f56c710caf07db134aef28b ]
+
+ldc_map_sg() could fail its check that the number of pages referred to
+by the sg scatterlist was <= the number of cookies.
+
+This fixes the issue by doing a similar thing to the xen-blkfront driver,
+ensuring that the scatterlist will only ever contain a segment count <=
+port->ring_cookies, and each segment will be page aligned, and <= page
+size. This ensures that the scatterlist is always mappable.
+
+Orabug: 19347817
+OraBZ: 15945
+
+Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/block/sunvdc.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/block/sunvdc.c
++++ b/drivers/block/sunvdc.c
+@@ -747,6 +747,10 @@ static int probe_disk(struct vdc_port *p
+
+ port->disk = g;
+
++ /* Each segment in a request is up to an aligned page in size. */
++ blk_queue_segment_boundary(q, PAGE_SIZE - 1);
++ blk_queue_max_segment_size(q, PAGE_SIZE);
++
+ blk_queue_max_segments(q, port->ring_cookies);
+ blk_queue_max_hw_sectors(q, port->max_xfer_size);
+ g->major = vdc_major;
--- /dev/null
+From foo@baz Tue Nov 18 09:08:47 PST 2014
+From: Dwight Engen <dwight.engen@oracle.com>
+Date: Fri, 19 Sep 2014 09:43:02 -0400
+Subject: vio: fix reuse of vio_dring slot
+
+From: Dwight Engen <dwight.engen@oracle.com>
+
+[ Upstream commit d0aedcd4f14a22e23b313f42b7e6e6ebfc0fbc31 ]
+
+vio_dring_avail() will allow use of every dring entry, but when the last
+entry is allocated then dr->prod == dr->cons which is indistinguishable from
+the ring empty condition. This causes the next allocation to reuse an entry.
+When this happens in sunvdc, the server side vds driver begins nack'ing the
+messages and ends up resetting the ldc channel. This problem does not effect
+sunvnet since it checks for < 2.
+
+The fix here is to just never allocate the very last dring slot so that full
+and empty are not the same condition. The request start path was changed to
+check for the ring being full a bit earlier, and to stop the blk_queue if
+there is no space left. The blk_queue will be restarted once the ring is
+only half full again. The number of ring entries was increased to 512 which
+matches the sunvnet and Solaris vdc drivers, and greatly reduces the
+frequency of hitting the ring full condition and the associated blk_queue
+stop/starting. The checks in sunvent were adjusted to account for
+vio_dring_avail() returning 1 less.
+
+Orabug: 19441666
+OraBZ: 14983
+
+Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/sparc/include/asm/vio.h | 2 -
+ drivers/block/sunvdc.c | 39 +++++++++++++++++++++----------------
+ drivers/net/ethernet/sun/sunvnet.c | 4 +--
+ 3 files changed, 26 insertions(+), 19 deletions(-)
+
+--- a/arch/sparc/include/asm/vio.h
++++ b/arch/sparc/include/asm/vio.h
+@@ -265,7 +265,7 @@ static inline u32 vio_dring_avail(struct
+ unsigned int ring_size)
+ {
+ return (dr->pending -
+- ((dr->prod - dr->cons) & (ring_size - 1)));
++ ((dr->prod - dr->cons) & (ring_size - 1)) - 1);
+ }
+
+ #define VIO_MAX_TYPE_LEN 32
+--- a/drivers/block/sunvdc.c
++++ b/drivers/block/sunvdc.c
+@@ -33,7 +33,7 @@ MODULE_DESCRIPTION("Sun LDOM virtual dis
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(DRV_MODULE_VERSION);
+
+-#define VDC_TX_RING_SIZE 256
++#define VDC_TX_RING_SIZE 512
+
+ #define WAITING_FOR_LINK_UP 0x01
+ #define WAITING_FOR_TX_SPACE 0x02
+@@ -283,7 +283,9 @@ static void vdc_end_one(struct vdc_port
+
+ __blk_end_request(req, (desc->status ? -EIO : 0), desc->size);
+
+- if (blk_queue_stopped(port->disk->queue))
++ /* restart blk queue when ring is half emptied */
++ if (blk_queue_stopped(port->disk->queue) &&
++ vdc_tx_dring_avail(dr) * 100 / VDC_TX_RING_SIZE >= 50)
+ blk_start_queue(port->disk->queue);
+ }
+
+@@ -435,12 +437,6 @@ static int __send_request(struct request
+ for (i = 0; i < nsg; i++)
+ len += sg[i].length;
+
+- if (unlikely(vdc_tx_dring_avail(dr) < 1)) {
+- blk_stop_queue(port->disk->queue);
+- err = -ENOMEM;
+- goto out;
+- }
+-
+ desc = vio_dring_cur(dr);
+
+ err = ldc_map_sg(port->vio.lp, sg, nsg,
+@@ -480,21 +476,32 @@ static int __send_request(struct request
+ port->req_id++;
+ dr->prod = (dr->prod + 1) & (VDC_TX_RING_SIZE - 1);
+ }
+-out:
+
+ return err;
+ }
+
+-static void do_vdc_request(struct request_queue *q)
++static void do_vdc_request(struct request_queue *rq)
+ {
+- while (1) {
+- struct request *req = blk_fetch_request(q);
++ struct request *req;
+
+- if (!req)
++ while ((req = blk_peek_request(rq)) != NULL) {
++ struct vdc_port *port;
++ struct vio_dring_state *dr;
++
++ port = req->rq_disk->private_data;
++ dr = &port->vio.drings[VIO_DRIVER_TX_RING];
++ if (unlikely(vdc_tx_dring_avail(dr) < 1))
++ goto wait;
++
++ blk_start_request(req);
++
++ if (__send_request(req) < 0) {
++ blk_requeue_request(rq, req);
++wait:
++ /* Avoid pointless unplugs. */
++ blk_stop_queue(rq);
+ break;
+-
+- if (__send_request(req) < 0)
+- __blk_end_request_all(req, -EIO);
++ }
+ }
+ }
+
+--- a/drivers/net/ethernet/sun/sunvnet.c
++++ b/drivers/net/ethernet/sun/sunvnet.c
+@@ -656,7 +656,7 @@ static int vnet_start_xmit(struct sk_buf
+ spin_lock_irqsave(&port->vio.lock, flags);
+
+ dr = &port->vio.drings[VIO_DRIVER_TX_RING];
+- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
++ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
+ if (!netif_queue_stopped(dev)) {
+ netif_stop_queue(dev);
+
+@@ -704,7 +704,7 @@ static int vnet_start_xmit(struct sk_buf
+ dev->stats.tx_bytes += skb->len;
+
+ dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
+- if (unlikely(vnet_tx_dring_avail(dr) < 2)) {
++ if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
+ netif_stop_queue(dev);
+ if (vnet_tx_dring_avail(dr) > VNET_TX_WAKEUP_THRESH(dr))
+ netif_wake_queue(dev);