--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 11:59:00 2007
+From: David Miller <davem@davemloft.net>
+Date: Thu, 29 Mar 2007 11:57:36 -0700 (PDT)
+Subject: DCCP: Fix exploitable hole in DCCP socket options
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.115736.39158322.davem@davemloft.net>
+
+
+From: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+
+[DCCP] getsockopt: Fix DCCP_SOCKOPT_[SEND,RECV]_CSCOV
+
+We were only checking if there was enough space to put the int, but
+left len as specified by the (malicious) user, sigh, fix it by setting
+len to sizeof(val) and transfering just one int worth of data, the one
+asked for.
+
+Also check for negative len values.
+
+Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/dccp/proto.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/net/dccp/proto.c
++++ b/net/dccp/proto.c
+@@ -575,7 +575,7 @@ static int do_dccp_getsockopt(struct soc
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+- if (len < sizeof(int))
++ if (len < (int)sizeof(int))
+ return -EINVAL;
+
+ dp = dccp_sk(sk);
+@@ -589,9 +589,11 @@ static int do_dccp_getsockopt(struct soc
+ (__be32 __user *)optval, optlen);
+ case DCCP_SOCKOPT_SEND_CSCOV:
+ val = dp->dccps_pcslen;
++ len = sizeof(val);
+ break;
+ case DCCP_SOCKOPT_RECV_CSCOV:
+ val = dp->dccps_pcrlen;
++ len = sizeof(val);
+ break;
+ case 128 ... 191:
+ return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 12:34:12 2007
+From: Al Viro <viro@ftp.linux.org.uk>
+Date: Thu, 29 Mar 2007 12:32:48 -0700 (PDT)
+Subject: Fix decnet endianness
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.123248.62343482.davem@davemloft.net>
+
+
+From: Al Viro <viro@ftp.linux.org.uk>
+
+[PATCH] FRA_{DST,SRC} are le16 for decnet
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/decnet/dn_rules.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/net/decnet/dn_rules.c
++++ b/net/decnet/dn_rules.c
+@@ -151,10 +151,10 @@ static int dn_fib_rule_configure(struct
+ }
+
+ if (tb[FRA_SRC])
+- r->src = nla_get_u16(tb[FRA_SRC]);
++ r->src = nla_get_le16(tb[FRA_SRC]);
+
+ if (tb[FRA_DST])
+- r->dst = nla_get_u16(tb[FRA_DST]);
++ r->dst = nla_get_le16(tb[FRA_DST]);
+
+ r->src_len = frh->src_len;
+ r->srcmask = dnet_make_mask(r->src_len);
+@@ -176,10 +176,10 @@ static int dn_fib_rule_compare(struct fi
+ if (frh->dst_len && (r->dst_len != frh->dst_len))
+ return 0;
+
+- if (tb[FRA_SRC] && (r->src != nla_get_u16(tb[FRA_SRC])))
++ if (tb[FRA_SRC] && (r->src != nla_get_le16(tb[FRA_SRC])))
+ return 0;
+
+- if (tb[FRA_DST] && (r->dst != nla_get_u16(tb[FRA_DST])))
++ if (tb[FRA_DST] && (r->dst != nla_get_le16(tb[FRA_DST])))
+ return 0;
+
+ return 1;
+@@ -214,9 +214,9 @@ static int dn_fib_rule_fill(struct fib_r
+ frh->tos = 0;
+
+ if (r->dst_len)
+- NLA_PUT_U16(skb, FRA_DST, r->dst);
++ NLA_PUT_LE16(skb, FRA_DST, r->dst);
+ if (r->src_len)
+- NLA_PUT_U16(skb, FRA_SRC, r->src);
++ NLA_PUT_LE16(skb, FRA_SRC, r->src);
+
+ return 0;
+
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Mar 21 13:09:37 2007
+Message-ID: <460190A8.8020507@redhat.com>
+Date: Wed, 21 Mar 2007 16:08:08 -0400
+From: Albert Lee <albertcc@tw.ibm.com>
+To: linux-stable <stable@kernel.org>
+Subject: ide: clear bmdma status in ide_intr() for ICHx controllers (revised #4)
+
+From: Albert Lee <albertcc@tw.ibm.com>
+
+ide: clear bmdma status in ide_intr() for ICHx controllers (revised #4)
+
+patch 1/2 (revised):
+- Fix drive->waiting_for_dma to work with CDB-intr devices.
+- Do the dma status clearing in ide_intr() and add a new
+ hwif->ide_dma_clear_irq for Intel ICHx controllers.
+
+Revised per Alan, Sergei and Bart's advice.
+
+Patch against 2.6.20-rc6. Tested ok on my ICH4 and pdc20275 adapters.
+Please review/apply, thanks.
+
+Signed-off-by: Albert Lee <albertcc@tw.ibm.com>
+Cc: Sergei Shtylyov <sshtylyov@ru.mvista.com>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Cc: Adam Hawks <awhawks@us.ibm.com>
+Cc: Chuck Ebbert <cebbert@redhat.com>
+Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/ide/ide-cd.c | 8 ++++++
+ drivers/ide/ide-io.c | 11 ++++++++
+ drivers/ide/ide.c | 1
+ drivers/ide/pci/piix.c | 63 +++++++++++++++++++++++++++++++++++++------------
+ include/linux/ide.h | 1
+ 5 files changed, 69 insertions(+), 15 deletions(-)
+
+--- a/drivers/ide/ide-cd.c
++++ b/drivers/ide/ide-cd.c
+@@ -930,6 +930,10 @@ static ide_startstop_t cdrom_start_packe
+ HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
+
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
++ /* waiting for CDB interrupt, not DMA yet. */
++ if (info->dma)
++ drive->waiting_for_dma = 0;
++
+ /* packet command */
+ ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry);
+ return ide_started;
+@@ -972,6 +976,10 @@ static ide_startstop_t cdrom_transfer_pa
+ /* Check for errors. */
+ if (cdrom_decode_status(drive, DRQ_STAT, NULL))
+ return ide_stopped;
++
++ /* Ok, next interrupt will be DMA interrupt. */
++ if (info->dma)
++ drive->waiting_for_dma = 1;
+ } else {
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (ide_wait_stat(&startstop, drive, DRQ_STAT,
+--- a/drivers/ide/ide-io.c
++++ b/drivers/ide/ide-io.c
+@@ -1646,6 +1646,17 @@ irqreturn_t ide_intr (int irq, void *dev
+ del_timer(&hwgroup->timer);
+ spin_unlock(&ide_lock);
+
++ /* Some controllers might set DMA INTR no matter DMA or PIO;
++ * bmdma status might need to be cleared even for
++ * PIO interrupts to prevent spurious/lost irq.
++ */
++ if (hwif->ide_dma_clear_irq && !(drive->waiting_for_dma))
++ /* ide_dma_end() needs bmdma status for error checking.
++ * So, skip clearing bmdma status here and leave it
++ * to ide_dma_end() if this is dma interrupt.
++ */
++ hwif->ide_dma_clear_irq(drive);
++
+ if (drive->unmask)
+ local_irq_enable_in_hardirq();
+ /* service this interrupt, may set handler for next interrupt */
+--- a/drivers/ide/ide.c
++++ b/drivers/ide/ide.c
+@@ -503,6 +503,7 @@ static void ide_hwif_restore(ide_hwif_t
+ hwif->ide_dma_on = tmp_hwif->ide_dma_on;
+ hwif->ide_dma_off_quietly = tmp_hwif->ide_dma_off_quietly;
+ hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq;
++ hwif->ide_dma_clear_irq = tmp_hwif->ide_dma_clear_irq;
+ hwif->ide_dma_host_on = tmp_hwif->ide_dma_host_on;
+ hwif->ide_dma_host_off = tmp_hwif->ide_dma_host_off;
+ hwif->ide_dma_lostirq = tmp_hwif->ide_dma_lostirq;
+--- a/drivers/ide/pci/piix.c
++++ b/drivers/ide/pci/piix.c
+@@ -411,17 +411,14 @@ fast_ata_pio:
+ }
+
+ /**
+- * init_chipset_piix - set up the PIIX chipset
+- * @dev: PCI device to set up
+- * @name: Name of the device
++ * piix_is_ichx - check if ICHx
++ * @dev: PCI device to check
+ *
+- * Initialize the PCI device as required. For the PIIX this turns
+- * out to be nice and simple
++ * returns 1 if ICHx, 0 otherwise.
+ */
+-
+-static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name)
++static int piix_is_ichx(struct pci_dev *dev)
+ {
+- switch(dev->device) {
++ switch (dev->device) {
+ case PCI_DEVICE_ID_INTEL_82801EB_1:
+ case PCI_DEVICE_ID_INTEL_82801AA_1:
+ case PCI_DEVICE_ID_INTEL_82801AB_1:
+@@ -439,19 +436,51 @@ static unsigned int __devinit init_chips
+ case PCI_DEVICE_ID_INTEL_ICH7_21:
+ case PCI_DEVICE_ID_INTEL_ESB2_18:
+ case PCI_DEVICE_ID_INTEL_ICH8_6:
+- {
+- unsigned int extra = 0;
+- pci_read_config_dword(dev, 0x54, &extra);
+- pci_write_config_dword(dev, 0x54, extra|0x400);
+- }
+- default:
+- break;
++ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
++ * init_chipset_piix - set up the PIIX chipset
++ * @dev: PCI device to set up
++ * @name: Name of the device
++ *
++ * Initialize the PCI device as required. For the PIIX this turns
++ * out to be nice and simple
++ */
++
++static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name)
++{
++ if (piix_is_ichx(dev)) {
++ unsigned int extra = 0;
++ pci_read_config_dword(dev, 0x54, &extra);
++ pci_write_config_dword(dev, 0x54, extra|0x400);
++ }
++
++ return 0;
++}
++
++/**
++ * piix_dma_clear_irq - clear BMDMA status
++ * @drive: IDE drive to clear
++ *
++ * Called from ide_intr() for PIO interrupts
++ * to clear BMDMA status as needed by ICHx
++ */
++static void piix_dma_clear_irq(ide_drive_t *drive)
++{
++ ide_hwif_t *hwif = HWIF(drive);
++ u8 dma_stat;
++
++ /* clear the INTR & ERROR bits */
++ dma_stat = hwif->INB(hwif->dma_status);
++ /* Should we force the bit as well ? */
++ hwif->OUTB(dma_stat, hwif->dma_status);
++}
++
++/**
+ * init_hwif_piix - fill in the hwif for the PIIX
+ * @hwif: IDE interface
+ *
+@@ -487,6 +516,10 @@ static void __devinit init_hwif_piix(ide
+ if (!hwif->dma_base)
+ return;
+
++ /* ICHx need to clear the bmdma status for all interrupts */
++ if (piix_is_ichx(hwif->pci_dev))
++ hwif->ide_dma_clear_irq = &piix_dma_clear_irq;
++
+ hwif->atapi_dma = 1;
+ hwif->ultra_mask = 0x3f;
+ hwif->mwdma_mask = 0x06;
+--- a/include/linux/ide.h
++++ b/include/linux/ide.h
+@@ -727,6 +727,7 @@ typedef struct hwif_s {
+ int (*ide_dma_on)(ide_drive_t *drive);
+ int (*ide_dma_off_quietly)(ide_drive_t *drive);
+ int (*ide_dma_test_irq)(ide_drive_t *drive);
++ void (*ide_dma_clear_irq)(ide_drive_t *drive);
+ int (*ide_dma_host_on)(ide_drive_t *drive);
+ int (*ide_dma_host_off)(ide_drive_t *drive);
+ int (*ide_dma_lostirq)(ide_drive_t *drive);
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Mar 21 13:10:10 2007
+From: Albert Lee <albertcc@tw.ibm.com>
+Date: Wed, 21 Mar 2007 16:08:49 -0400
+Subject: ide: remove clearing bmdma status from cdrom_decode_status() (rev #4)
+To: linux-stable <stable@kernel.org>
+Message-ID: <460190D1.2080305@redhat.com>
+
+From: Albert Lee <albertcc@tw.ibm.com>
+
+ide: remove clearing bmdma status from cdrom_decode_status() (rev #4)
+
+patch 2/2:
+ Remove clearing bmdma status from cdrom_decode_status() since ATA devices
+ might need it as well.
+
+ (http://lkml.org/lkml/2006/12/4/201 and http://lkml.org/lkml/2006/11/15/94)
+
+Signed-off-by: Albert Lee <albertcc@tw.ibm.com>
+Cc: Sergei Shtylyov <sshtylyov@ru.mvista.com>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Cc: "Adam W. Hawks" <awhawks@us.ibm.com>
+Cc: Chuck Ebbert <cebbert@redhat.com>
+Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/ide/ide-cd.c | 7 -------
+ drivers/ide/pci/piix.c | 4 ----
+ include/linux/ide.h | 1 -
+ 3 files changed, 12 deletions(-)
+
+--- a/drivers/ide/ide-cd.c
++++ b/drivers/ide/ide-cd.c
+@@ -687,15 +687,8 @@ static void ide_dump_status_no_sense(ide
+ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
+ {
+ struct request *rq = HWGROUP(drive)->rq;
+- ide_hwif_t *hwif = HWIF(drive);
+ int stat, err, sense_key;
+
+- /* We may have bogus DMA interrupts in PIO state here */
+- if (HWIF(drive)->dma_status && hwif->atapi_irq_bogon) {
+- stat = hwif->INB(hwif->dma_status);
+- /* Should we force the bit as well ? */
+- hwif->OUTB(stat, hwif->dma_status);
+- }
+ /* Check for errors. */
+ stat = HWIF(drive)->INB(IDE_STATUS_REG);
+ if (stat_ret)
+--- a/drivers/ide/pci/piix.c
++++ b/drivers/ide/pci/piix.c
+@@ -502,10 +502,6 @@ static void __devinit init_hwif_piix(ide
+ /* This is a painful system best to let it self tune for now */
+ return;
+ }
+- /* ESB2 appears to generate spurious DMA interrupts in PIO mode
+- when in native mode */
+- if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_ESB2_18)
+- hwif->atapi_irq_bogon = 1;
+
+ hwif->autodma = 0;
+ hwif->tuneproc = &piix_tune_drive;
+--- a/include/linux/ide.h
++++ b/include/linux/ide.h
+@@ -797,7 +797,6 @@ typedef struct hwif_s {
+ unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
+ unsigned no_io_32bit : 1; /* 1 = can not do 32-bit IO ops */
+ unsigned err_stops_fifo : 1; /* 1=data FIFO is cleared by an error */
+- unsigned atapi_irq_bogon : 1; /* Generates spurious DMA interrupts in PIO mode */
+
+ struct device gendev;
+ struct completion gendev_rel_comp; /* To deal with device release() */
--- /dev/null
+From stable-bounces@linux.kernel.org Sun Mar 25 12:25:57 2007
+From: Stefan Richter <stefanr@s5r6.in-berlin.de>
+Date: Sun, 25 Mar 2007 21:24:43 +0200 (CEST)
+Subject: ieee1394: dv1394: fix CardBus card ejection
+To: stable@kernel.org
+Cc: linux-kernel@vger.kernel.org
+Message-ID: <tkrat.7af9edf7b7c51abd@s5r6.in-berlin.de>
+Content-Disposition: INLINE
+
+From: Stefan Richter <stefanr@s5r6.in-berlin.de>
+
+Fix NULL pointer dereference on hot ejection of a FireWire card while
+dv1394 was loaded. http://bugzilla.kernel.org/show_bug.cgi?id=7121
+I did not test card ejection with open /dev/dv1394 files yet.
+
+Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/ieee1394/dv1394.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+--- a/drivers/ieee1394/dv1394.c
++++ b/drivers/ieee1394/dv1394.c
+@@ -2267,11 +2267,7 @@ static void dv1394_remove_host (struct h
+ {
+ struct video_card *video;
+ unsigned long flags;
+- int id = host->id;
+-
+- /* We only work with the OHCI-1394 driver */
+- if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+- return;
++ int id = host->id, found_ohci_card = 0;
+
+ /* find the corresponding video_cards */
+ do {
+@@ -2284,6 +2280,7 @@ static void dv1394_remove_host (struct h
+ if ((tmp_vid->id >> 2) == id) {
+ list_del(&tmp_vid->list);
+ video = tmp_vid;
++ found_ohci_card = 1;
+ break;
+ }
+ }
+@@ -2293,8 +2290,9 @@ static void dv1394_remove_host (struct h
+ dv1394_un_init(video);
+ } while (video != NULL);
+
+- class_device_destroy(hpsb_protocol_class,
+- MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)));
++ if (found_ohci_card)
++ class_device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
++ IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id << 2)));
+ }
+
+ static void dv1394_add_host (struct hpsb_host *host)
--- /dev/null
+From stable-bounces@linux.kernel.org Mon Mar 26 18:58:25 2007
+From: David Miller <davem@davemloft.net>
+Date: Mon, 26 Mar 2007 18:56:59 -0700 (PDT)
+Subject: IPV6: Fix ipv6 round-robin locking.
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070326.185659.08322864.davem@davemloft.net>
+
+From: David Miller <davem@davemloft.net>
+
+[IPV6]: Fix routing round-robin locking.
+
+As per RFC2461, section 6.3.6, item #2, when no routers on the
+matching list are known to be reachable or probably reachable we
+do round robin on those available routes so that we make sure
+to probe as many of them as possible to detect when one becomes
+reachable faster.
+
+Each routing table has a rwlock protecting the tree and the linked
+list of routes at each leaf. The round robin code executes during
+lookup and thus with the rwlock taken as a reader. A small local
+spinlock tries to provide protection but this does not work at all
+for two reasons:
+
+1) The round-robin list manipulation, as coded, goes like this (with
+ read lock held):
+
+ walk routes finding head and tail
+
+ spin_lock();
+ rotate list using head and tail
+ spin_unlock();
+
+ While one thread is rotating the list, another thread can
+ end up with stale values of head and tail and then proceed
+ to corrupt the list when it gets the lock. This ends up causing
+ the OOPS in fib6_add() later onthat many people have been hitting.
+
+2) All the other code paths that run with the rwlock held as
+ a reader do not expect the list to change on them, they
+ expect it to remain completely fixed while they hold the
+ lock in that way.
+
+So, simply stated, it is impossible to implement this correctly using
+a manipulation of the list without violating the rwlock locking
+semantics.
+
+Reimplement using a per-fib6_node round-robin pointer. This way we
+don't need to manipulate the list at all, and since the round-robin
+pointer can only ever point to real existing entries we don't need
+to perform any locking on the changing of the round-robin pointer
+itself. We only need to reset the round-robin pointer to NULL when
+the entry it is pointing to is removed.
+
+The idea is from Thomas Graf and it is very similar to how this
+was implemented before the advanced router selection code when in.
+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+
+---
+ include/net/ip6_fib.h | 1
+ net/ipv6/ip6_fib.c | 8 ++++
+ net/ipv6/route.c | 97 ++++++++++++++++++++++++++++++--------------------
+ 3 files changed, 68 insertions(+), 38 deletions(-)
+
+--- a/include/net/ip6_fib.h
++++ b/include/net/ip6_fib.h
+@@ -58,6 +58,7 @@ struct fib6_node
+ __u16 fn_bit; /* bit key */
+ __u16 fn_flags;
+ __u32 fn_sernum;
++ struct rt6_info *rr_ptr;
+ };
+
+ #ifndef CONFIG_IPV6_SUBTREES
+--- a/net/ipv6/ip6_fib.c
++++ b/net/ipv6/ip6_fib.c
+@@ -659,6 +659,10 @@ static int fib6_add_rt2node(struct fib6_
+ ins = &iter->u.next;
+ }
+
++ /* Reset round-robin state, if necessary */
++ if (ins == &fn->leaf)
++ fn->rr_ptr = NULL;
++
+ /*
+ * insert node
+ */
+@@ -1110,6 +1114,10 @@ static void fib6_del_route(struct fib6_n
+ rt6_stats.fib_rt_entries--;
+ rt6_stats.fib_discarded_routes++;
+
++ /* Reset round-robin state, if necessary */
++ if (fn->rr_ptr == rt)
++ fn->rr_ptr = NULL;
++
+ /* Adjust walkers */
+ read_lock(&fib6_walker_lock);
+ FOR_WALKERS(w) {
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -354,55 +354,76 @@ static int rt6_score_route(struct rt6_in
+ return m;
+ }
+
+-static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
+- int strict)
++static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
++ int *mpri, struct rt6_info *match)
+ {
+- struct rt6_info *match = NULL, *last = NULL;
+- struct rt6_info *rt, *rt0 = *head;
+- u32 metric;
++ int m;
++
++ if (rt6_check_expired(rt))
++ goto out;
++
++ m = rt6_score_route(rt, oif, strict);
++ if (m < 0)
++ goto out;
++
++ if (m > *mpri) {
++ if (strict & RT6_LOOKUP_F_REACHABLE)
++ rt6_probe(match);
++ *mpri = m;
++ match = rt;
++ } else if (strict & RT6_LOOKUP_F_REACHABLE) {
++ rt6_probe(rt);
++ }
++
++out:
++ return match;
++}
++
++static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
++ struct rt6_info *rr_head,
++ u32 metric, int oif, int strict)
++{
++ struct rt6_info *rt, *match;
+ int mpri = -1;
+
+- RT6_TRACE("%s(head=%p(*head=%p), oif=%d)\n",
+- __FUNCTION__, head, head ? *head : NULL, oif);
++ match = NULL;
++ for (rt = rr_head; rt && rt->rt6i_metric == metric;
++ rt = rt->u.next)
++ match = find_match(rt, oif, strict, &mpri, match);
++ for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
++ rt = rt->u.next)
++ match = find_match(rt, oif, strict, &mpri, match);
+
+- for (rt = rt0, metric = rt0->rt6i_metric;
+- rt && rt->rt6i_metric == metric && (!last || rt != rt0);
+- rt = rt->u.next) {
+- int m;
++ return match;
++}
+
+- if (rt6_check_expired(rt))
+- continue;
++static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
++{
++ struct rt6_info *match, *rt0;
+
+- last = rt;
++ RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
++ __FUNCTION__, fn->leaf, oif);
+
+- m = rt6_score_route(rt, oif, strict);
+- if (m < 0)
+- continue;
++ rt0 = fn->rr_ptr;
++ if (!rt0)
++ fn->rr_ptr = rt0 = fn->leaf;
+
+- if (m > mpri) {
+- if (strict & RT6_LOOKUP_F_REACHABLE)
+- rt6_probe(match);
+- match = rt;
+- mpri = m;
+- } else if (strict & RT6_LOOKUP_F_REACHABLE) {
+- rt6_probe(rt);
+- }
+- }
++ match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
+
+ if (!match &&
+- (strict & RT6_LOOKUP_F_REACHABLE) &&
+- last && last != rt0) {
++ (strict & RT6_LOOKUP_F_REACHABLE)) {
++ struct rt6_info *next = rt0->u.next;
++
+ /* no entries matched; do round-robin */
+- static DEFINE_SPINLOCK(lock);
+- spin_lock(&lock);
+- *head = rt0->u.next;
+- rt0->u.next = last->u.next;
+- last->u.next = rt0;
+- spin_unlock(&lock);
++ if (!next || next->rt6i_metric != rt0->rt6i_metric)
++ next = fn->leaf;
++
++ if (next != rt0)
++ fn->rr_ptr = next;
+ }
+
+- RT6_TRACE("%s() => %p, score=%d\n",
+- __FUNCTION__, match, mpri);
++ RT6_TRACE("%s() => %p\n",
++ __FUNCTION__, match);
+
+ return (match ? match : &ip6_null_entry);
+ }
+@@ -648,7 +669,7 @@ restart_2:
+ fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
+
+ restart:
+- rt = rt6_select(&fn->leaf, fl->iif, strict | reachable);
++ rt = rt6_select(fn, fl->iif, strict | reachable);
+ BACKTRACK(&fl->fl6_src);
+ if (rt == &ip6_null_entry ||
+ rt->rt6i_flags & RTF_CACHE)
+@@ -743,7 +764,7 @@ restart_2:
+ fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
+
+ restart:
+- rt = rt6_select(&fn->leaf, fl->oif, strict | reachable);
++ rt = rt6_select(fn, fl->oif, strict | reachable);
+ BACKTRACK(&fl->fl6_src);
+ if (rt == &ip6_null_entry ||
+ rt->rt6i_flags & RTF_CACHE)
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 12:35:35 2007
+From: Thomas Graf <tgraf@suug.ch>
+Date: Thu, 29 Mar 2007 12:34:13 -0700 (PDT)
+Subject: NET: Fix FIB rules compatability
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.123413.31642189.davem@davemloft.net>
+
+
+From: Thomas Graf <tgraf@suug.ch>
+
+[NET]: Fix fib_rules compatibility breakage
+
+Based upon a patch from Patrick McHardy.
+
+The fib_rules netlink attribute policy introduced in 2.6.19 broke
+userspace compatibilty. When specifying a rule with "from all"
+or "to all", iproute adds a zero byte long netlink attribute,
+but the policy requires all addresses to have a size equal to
+sizeof(struct in_addr)/sizeof(struct in6_addr), resulting in a
+validation error.
+
+Check attribute length of FRA_SRC/FRA_DST in the generic framework
+by letting the family specific rules implementation provide the
+length of an address. Report an error if address length is non
+zero but no address attribute is provided. Fix actual bug by
+checking address length for non-zero instead of relying on
+availability of attribute.
+
+Signed-off-by: Thomas Graf <tgraf@suug.ch>
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/net/fib_rules.h | 1 +
+ net/core/fib_rules.c | 30 ++++++++++++++++++++++++++++++
+ net/decnet/dn_rules.c | 13 ++++++-------
+ net/ipv4/fib_rules.c | 14 ++++++--------
+ net/ipv6/fib6_rules.c | 14 +++++---------
+ 5 files changed, 48 insertions(+), 24 deletions(-)
+
+--- a/include/net/fib_rules.h
++++ b/include/net/fib_rules.h
+@@ -34,6 +34,7 @@ struct fib_rules_ops
+ int family;
+ struct list_head list;
+ int rule_size;
++ int addr_size;
+
+ int (*action)(struct fib_rule *,
+ struct flowi *, int,
+--- a/net/core/fib_rules.c
++++ b/net/core/fib_rules.c
+@@ -152,6 +152,28 @@ out:
+
+ EXPORT_SYMBOL_GPL(fib_rules_lookup);
+
++static int validate_rulemsg(struct fib_rule_hdr *frh, struct nlattr **tb,
++ struct fib_rules_ops *ops)
++{
++ int err = -EINVAL;
++
++ if (frh->src_len)
++ if (tb[FRA_SRC] == NULL ||
++ frh->src_len > (ops->addr_size * 8) ||
++ nla_len(tb[FRA_SRC]) != ops->addr_size)
++ goto errout;
++
++ if (frh->dst_len)
++ if (tb[FRA_DST] == NULL ||
++ frh->dst_len > (ops->addr_size * 8) ||
++ nla_len(tb[FRA_DST]) != ops->addr_size)
++ goto errout;
++
++ err = 0;
++errout:
++ return err;
++}
++
+ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+ {
+ struct fib_rule_hdr *frh = nlmsg_data(nlh);
+@@ -173,6 +195,10 @@ int fib_nl_newrule(struct sk_buff *skb,
+ if (err < 0)
+ goto errout;
+
++ err = validate_rulemsg(frh, tb, ops);
++ if (err < 0)
++ goto errout;
++
+ rule = kzalloc(ops->rule_size, GFP_KERNEL);
+ if (rule == NULL) {
+ err = -ENOMEM;
+@@ -260,6 +286,10 @@ int fib_nl_delrule(struct sk_buff *skb,
+ if (err < 0)
+ goto errout;
+
++ err = validate_rulemsg(frh, tb, ops);
++ if (err < 0)
++ goto errout;
++
+ list_for_each_entry(rule, ops->rules_list, list) {
+ if (frh->action && (frh->action != rule->action))
+ continue;
+--- a/net/decnet/dn_rules.c
++++ b/net/decnet/dn_rules.c
+@@ -109,8 +109,6 @@ errout:
+
+ static struct nla_policy dn_fib_rule_policy[FRA_MAX+1] __read_mostly = {
+ FRA_GENERIC_POLICY,
+- [FRA_SRC] = { .type = NLA_U16 },
+- [FRA_DST] = { .type = NLA_U16 },
+ };
+
+ static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
+@@ -133,7 +131,7 @@ static int dn_fib_rule_configure(struct
+ int err = -EINVAL;
+ struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
+
+- if (frh->src_len > 16 || frh->dst_len > 16 || frh->tos)
++ if (frh->tos)
+ goto errout;
+
+ if (rule->table == RT_TABLE_UNSPEC) {
+@@ -150,10 +148,10 @@ static int dn_fib_rule_configure(struct
+ }
+ }
+
+- if (tb[FRA_SRC])
++ if (frh->src_len)
+ r->src = nla_get_le16(tb[FRA_SRC]);
+
+- if (tb[FRA_DST])
++ if (frh->dst_len)
+ r->dst = nla_get_le16(tb[FRA_DST]);
+
+ r->src_len = frh->src_len;
+@@ -176,10 +174,10 @@ static int dn_fib_rule_compare(struct fi
+ if (frh->dst_len && (r->dst_len != frh->dst_len))
+ return 0;
+
+- if (tb[FRA_SRC] && (r->src != nla_get_le16(tb[FRA_SRC])))
++ if (frh->src_len && (r->src != nla_get_le16(tb[FRA_SRC])))
+ return 0;
+
+- if (tb[FRA_DST] && (r->dst != nla_get_le16(tb[FRA_DST])))
++ if (frh->dst_len && (r->dst != nla_get_le16(tb[FRA_DST])))
+ return 0;
+
+ return 1;
+@@ -249,6 +247,7 @@ int dn_fib_dump_rules(struct sk_buff *sk
+ static struct fib_rules_ops dn_fib_rules_ops = {
+ .family = AF_DECnet,
+ .rule_size = sizeof(struct dn_fib_rule),
++ .addr_size = sizeof(u16),
+ .action = dn_fib_rule_action,
+ .match = dn_fib_rule_match,
+ .configure = dn_fib_rule_configure,
+--- a/net/ipv4/fib_rules.c
++++ b/net/ipv4/fib_rules.c
+@@ -171,8 +171,6 @@ static struct fib_table *fib_empty_table
+
+ static struct nla_policy fib4_rule_policy[FRA_MAX+1] __read_mostly = {
+ FRA_GENERIC_POLICY,
+- [FRA_SRC] = { .type = NLA_U32 },
+- [FRA_DST] = { .type = NLA_U32 },
+ [FRA_FLOW] = { .type = NLA_U32 },
+ };
+
+@@ -183,8 +181,7 @@ static int fib4_rule_configure(struct fi
+ int err = -EINVAL;
+ struct fib4_rule *rule4 = (struct fib4_rule *) rule;
+
+- if (frh->src_len > 32 || frh->dst_len > 32 ||
+- (frh->tos & ~IPTOS_TOS_MASK))
++ if (frh->tos & ~IPTOS_TOS_MASK)
+ goto errout;
+
+ if (rule->table == RT_TABLE_UNSPEC) {
+@@ -201,10 +198,10 @@ static int fib4_rule_configure(struct fi
+ }
+ }
+
+- if (tb[FRA_SRC])
++ if (frh->src_len)
+ rule4->src = nla_get_be32(tb[FRA_SRC]);
+
+- if (tb[FRA_DST])
++ if (frh->dst_len)
+ rule4->dst = nla_get_be32(tb[FRA_DST]);
+
+ #ifdef CONFIG_NET_CLS_ROUTE
+@@ -242,10 +239,10 @@ static int fib4_rule_compare(struct fib_
+ return 0;
+ #endif
+
+- if (tb[FRA_SRC] && (rule4->src != nla_get_be32(tb[FRA_SRC])))
++ if (frh->src_len && (rule4->src != nla_get_be32(tb[FRA_SRC])))
+ return 0;
+
+- if (tb[FRA_DST] && (rule4->dst != nla_get_be32(tb[FRA_DST])))
++ if (frh->dst_len && (rule4->dst != nla_get_be32(tb[FRA_DST])))
+ return 0;
+
+ return 1;
+@@ -309,6 +306,7 @@ static size_t fib4_rule_nlmsg_payload(st
+ static struct fib_rules_ops fib4_rules_ops = {
+ .family = AF_INET,
+ .rule_size = sizeof(struct fib4_rule),
++ .addr_size = sizeof(u32),
+ .action = fib4_rule_action,
+ .match = fib4_rule_match,
+ .configure = fib4_rule_configure,
+--- a/net/ipv6/fib6_rules.c
++++ b/net/ipv6/fib6_rules.c
+@@ -131,8 +131,6 @@ static int fib6_rule_match(struct fib_ru
+
+ static struct nla_policy fib6_rule_policy[FRA_MAX+1] __read_mostly = {
+ FRA_GENERIC_POLICY,
+- [FRA_SRC] = { .len = sizeof(struct in6_addr) },
+- [FRA_DST] = { .len = sizeof(struct in6_addr) },
+ };
+
+ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
+@@ -142,9 +140,6 @@ static int fib6_rule_configure(struct fi
+ int err = -EINVAL;
+ struct fib6_rule *rule6 = (struct fib6_rule *) rule;
+
+- if (frh->src_len > 128 || frh->dst_len > 128)
+- goto errout;
+-
+ if (rule->action == FR_ACT_TO_TBL) {
+ if (rule->table == RT6_TABLE_UNSPEC)
+ goto errout;
+@@ -155,11 +150,11 @@ static int fib6_rule_configure(struct fi
+ }
+ }
+
+- if (tb[FRA_SRC])
++ if (frh->src_len)
+ nla_memcpy(&rule6->src.addr, tb[FRA_SRC],
+ sizeof(struct in6_addr));
+
+- if (tb[FRA_DST])
++ if (frh->dst_len)
+ nla_memcpy(&rule6->dst.addr, tb[FRA_DST],
+ sizeof(struct in6_addr));
+
+@@ -186,11 +181,11 @@ static int fib6_rule_compare(struct fib_
+ if (frh->tos && (rule6->tclass != frh->tos))
+ return 0;
+
+- if (tb[FRA_SRC] &&
++ if (frh->src_len &&
+ nla_memcmp(tb[FRA_SRC], &rule6->src.addr, sizeof(struct in6_addr)))
+ return 0;
+
+- if (tb[FRA_DST] &&
++ if (frh->dst_len &&
+ nla_memcmp(tb[FRA_DST], &rule6->dst.addr, sizeof(struct in6_addr)))
+ return 0;
+
+@@ -240,6 +235,7 @@ static size_t fib6_rule_nlmsg_payload(st
+ static struct fib_rules_ops fib6_rules_ops = {
+ .family = AF_INET6,
+ .rule_size = sizeof(struct fib6_rule),
++ .addr_size = sizeof(struct in6_addr),
+ .action = fib6_rule_action,
+ .match = fib6_rule_match,
+ .configure = fib6_rule_configure,
--- /dev/null
+From stable-bounces@linux.kernel.org Mon Mar 26 18:15:09 2007
+From: Patrick McHardy <kaber@trash.net>
+Date: Mon, 26 Mar 2007 18:13:51 -0700 (PDT)
+Subject: NET: Fix packet classidier NULL pointer OOPS
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070326.181351.72710253.davem@davemloft.net>
+
+From: Patrick McHardy <kaber@trash.net>
+
+[NET_SCHED]: cls_basic: fix NULL pointer dereference
+
+cls_basic doesn't allocate tp->root before it is linked into the
+active classifier list, resulting in a NULL pointer dereference
+when packets hit the classifier before its ->change function is
+called.
+
+Reported by Chris Madden <chris@reflexsecurity.com>
+
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/sched/cls_basic.c | 16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+--- a/net/sched/cls_basic.c
++++ b/net/sched/cls_basic.c
+@@ -82,6 +82,13 @@ static void basic_put(struct tcf_proto *
+
+ static int basic_init(struct tcf_proto *tp)
+ {
++ struct basic_head *head;
++
++ head = kzalloc(sizeof(*head), GFP_KERNEL);
++ if (head == NULL)
++ return -ENOBUFS;
++ INIT_LIST_HEAD(&head->flist);
++ tp->root = head;
+ return 0;
+ }
+
+@@ -177,15 +184,6 @@ static int basic_change(struct tcf_proto
+ }
+
+ err = -ENOBUFS;
+- if (head == NULL) {
+- head = kzalloc(sizeof(*head), GFP_KERNEL);
+- if (head == NULL)
+- goto errout;
+-
+- INIT_LIST_HEAD(&head->flist);
+- tp->root = head;
+- }
+-
+ f = kzalloc(sizeof(*f), GFP_KERNEL);
+ if (f == NULL)
+ goto errout;
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 12:24:08 2007
+From: Alexey Dobriyan <adobriyan@sw.ru>
+Date: Thu, 29 Mar 2007 12:22:40 -0700 (PDT)
+Subject: NET: Fix sock_attach_fd() failure in sys_accept()
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.122240.08322946.davem@davemloft.net>
+
+
+From: Alexey Dobriyan <adobriyan@sw.ru>
+
+[NET]: Correct accept(2) recovery after sock_attach_fd()
+
+* d_alloc() in sock_attach_fd() fails leaving ->f_dentry of new file NULL
+* bail out to out_fd label, doing fput()/__fput() on new file
+* but __fput() assumes valid ->f_dentry and dereferences it
+
+Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/socket.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -1368,7 +1368,7 @@ asmlinkage long sys_accept(int fd, struc
+
+ err = sock_attach_fd(newsock, newfile);
+ if (err < 0)
+- goto out_fd;
++ goto out_fd_simple;
+
+ err = security_socket_accept(sock, newsock);
+ if (err)
+@@ -1401,6 +1401,11 @@ out_put:
+ fput_light(sock->file, fput_needed);
+ out:
+ return err;
++out_fd_simple:
++ sock_release(newsock);
++ put_filp(newfile);
++ put_unused_fd(newfd);
++ goto out_put;
+ out_fd:
+ fput(newfile);
+ put_unused_fd(newfd);
--- /dev/null
+From stable-bounces@linux.kernel.org Mon Mar 26 18:16:55 2007
+From: Patrick McHardy <kaber@trash.net>
+Date: Mon, 26 Mar 2007 18:15:37 -0700 (PDT)
+Subject: NET_SCHED: Fix ingress qdisc locking.
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070326.181537.59655672.davem@davemloft.net>
+
+From: Patrick McHardy <kaber@trash.net>
+
+[NET_SCHED]: Fix ingress locking
+
+Ingress queueing uses a seperate lock for serializing enqueue operations,
+but fails to properly protect itself against concurrent changes to the
+qdisc tree. Use queue_lock for now since the real fix it quite intrusive.
+
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+
+---
+ net/core/dev.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -1750,10 +1750,10 @@ static int ing_filter(struct sk_buff *sk
+
+ skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);
+
+- spin_lock(&dev->ingress_lock);
++ spin_lock(&dev->queue_lock);
+ if ((q = dev->qdisc_ingress) != NULL)
+ result = q->enqueue(skb, q);
+- spin_unlock(&dev->ingress_lock);
++ spin_unlock(&dev->queue_lock);
+
+ }
+
--- /dev/null
+From stable-bounces@linux.kernel.org Mon Mar 26 19:08:55 2007
+From: G. Liakhovetski <gl@dsa-ac.de>
+Date: Mon, 26 Mar 2007 19:07:40 -0700 (PDT)
+Subject: PPP: Fix PPP skb leak
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070326.190740.21927260.davem@davemloft.net>
+
+From: G. Liakhovetski <gl@dsa-ac.de>
+
+[PPP]: Don't leak an sk_buff on interface destruction.
+
+Signed-off-by: G. Liakhovetski <gl@dsa-ac.de>
+Acked-by: Paul Mackerras <paulus@samba.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/ppp_generic.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/ppp_generic.c
++++ b/drivers/net/ppp_generic.c
+@@ -2544,6 +2544,9 @@ static void ppp_destroy_interface(struct
+ ppp->active_filter = NULL;
+ #endif /* CONFIG_PPP_FILTER */
+
++ if (ppp->xmit_pending)
++ kfree_skb(ppp->xmit_pending);
++
+ kfree(ppp);
+ }
+
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 22 09:47:58 2007
+Date: Thu, 22 Mar 2007 12:39:04 -0400
+Cc: Robert Hancock <hancockr@shaw.ca>
+Subject: sata_nv: delay on switching between NCQ and non-NCQ commands
+To: linux-stable <stable@kernel.org>
+Cc: Robert Hancock <hancockr@shaw.ca>
+Message-ID: <4602B128.6090000@redhat.com>
+
+
+From: Robert Hancock <hancockr@shaw.ca>
+
+sata_nv: delay on switching between NCQ and non-NCQ commands
+
+This patch appears to solve some problems with commands timing out in
+cases where an NCQ command is immediately followed by a non-NCQ command
+(or possibly vice versa). This is a rather ugly solution, but until we
+know more about why this is needed, this is about all we can do.
+
+[backport to 2.6.20 by Chuck Ebbert <cebbert@redhat.com>]
+
+Signed-off-by: Robert Hancock <hancockr@shaw.ca>
+Cc: Chuck Ebbert <cebbert@redhat.com>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/ata/sata_nv.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/ata/sata_nv.c
++++ b/drivers/ata/sata_nv.c
+@@ -214,6 +214,7 @@ struct nv_adma_port_priv {
+ struct nv_adma_prd *aprd;
+ dma_addr_t aprd_dma;
+ u8 flags;
++ int last_issue_ncq;
+ };
+
+ #define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))
+@@ -1151,6 +1152,7 @@ static unsigned int nv_adma_qc_issue(str
+ {
+ struct nv_adma_port_priv *pp = qc->ap->private_data;
+ void __iomem *mmio = nv_adma_ctl_block(qc->ap);
++ int curr_ncq = (qc->tf.protocol == ATA_PROT_NCQ);
+
+ VPRINTK("ENTER\n");
+
+@@ -1166,6 +1168,14 @@ static unsigned int nv_adma_qc_issue(str
+ /* write append register, command tag in lower 8 bits
+ and (number of cpbs to append -1) in top 8 bits */
+ wmb();
++
++ if(curr_ncq != pp->last_issue_ncq) {
++ /* Seems to need some delay before switching between NCQ and non-NCQ
++ commands, else we get command timeouts and such. */
++ udelay(20);
++ pp->last_issue_ncq = curr_ncq;
++ }
++
+ writew(qc->tag, mmio + NV_ADMA_APPEND);
+
+ DPRINTK("Issued tag %u\n",qc->tag);
--- /dev/null
+ide-clear-bmdma-status-in-ide_intr-for-ichx-controllers.patch
+ide-remove-clearing-bmdma-status-from-cdrom_decode_status.patch
+sata_nv-delay-on-switching-between-ncq-and-non-ncq-commands.patch
+uml-fix-epoll.patch
+uml-host-vdso-fix.patch
+uml-fix-static-linking.patch
+uml-use-correct-register-file-size-everywhere.patch
+uml-fix-unreasonably-long-udelay.patch
+ieee1394-dv1394-fix-cardbus-card-ejection.patch
+net-fix-packet-classidier-null-pointer-oops.patch
+net_sched-fix-ingress-qdisc-locking.patch
+ipv6-fix-ipv6-round-robin-locking.patch
+ppp-fix-ppp-skb-leak.patch
+dccp-fix-exploitable-hole-in-dccp-socket-options.patch
+video-fix-ffb-dac-revision-probing.patch
+net-fix-sock_attach_fd-failure-in-sys_accept.patch
+sparc-fix-sparc-builds-with-gcc-4.2.x.patch
+fix-decnet-endianness.patch
+net-fix-fib-rules-compatability.patch
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 12:27:04 2007
+From: Mikael Pettersson <mikpe@it.uu.se>
+Date: Thu, 29 Mar 2007 12:25:38 -0700 (PDT)
+Subject: SPARC: Fix sparc builds with gcc-4.2.x
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.122538.21928111.davem@davemloft.net>
+
+From: Mikael Pettersson <mikpe@it.uu.se>
+
+[SPARC]: sparc64 gcc-4.2.0 20070317 -Werror failure
+
+Compiling 2.6.21-rc5 with gcc-4.2.0 20070317 (prerelease)
+for sparc64 fails as follows:
+
+ gcc -Wp,-MD,arch/sparc64/kernel/.time.o.d -nostdinc -isystem /home/mikpe/pkgs/linux-sparc64/gcc-4.2.0/lib/gcc/sparc64-unknown-linux-gnu/4.2.0/include -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Os -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare -Wa,--undeclared-regs -fomit-frame-pointer -fno-stack-protector -Wdeclaration-after-statement -Wno-pointer-sign -Werror -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(time)" -D"KBUILD_MODNAME=KBUILD_STR(time)" -c -o arch/sparc64/kernel/time.o arch/sparc64/kernel/time.c
+cc1: warnings being treated as errors
+arch/sparc64/kernel/time.c: In function 'kick_start_clock':
+arch/sparc64/kernel/time.c:559: warning: overflow in implicit constant conversion
+make[1]: *** [arch/sparc64/kernel/time.o] Error 1
+make: *** [arch/sparc64/kernel] Error 2
+
+gcc gets unhappy when the MSTK_SET macro's u8 __val variable
+is updated with &= ~0xff (MSTK_YEAR_MASK). Making the constant
+unsigned fixes the problem.
+
+[ I fixed up the sparc32 side as well -DaveM ]
+
+Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/asm-sparc/mostek.h | 2 +-
+ include/asm-sparc64/mostek.h | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/include/asm-sparc/mostek.h
++++ b/include/asm-sparc/mostek.h
+@@ -87,7 +87,7 @@ extern void __iomem *mstk48t02_regs;
+ #define MSTK_DOW_MASK 0x07
+ #define MSTK_DOM_MASK 0x3f
+ #define MSTK_MONTH_MASK 0x1f
+-#define MSTK_YEAR_MASK 0xff
++#define MSTK_YEAR_MASK 0xffU
+
+ /* Binary coded decimal conversion macros. */
+ #define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04))
+--- a/include/asm-sparc64/mostek.h
++++ b/include/asm-sparc64/mostek.h
+@@ -89,7 +89,7 @@ extern void __iomem *mstk48t02_regs;
+ #define MSTK_DOW_MASK 0x07
+ #define MSTK_DOM_MASK 0x3f
+ #define MSTK_MONTH_MASK 0x1f
+-#define MSTK_YEAR_MASK 0xff
++#define MSTK_YEAR_MASK 0xffU
+
+ /* Binary coded decimal conversion macros. */
+ #define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04))
--- /dev/null
+From stable-bounces@linux.kernel.org Fri Mar 23 11:22:17 2007
+From: Jeff Dike <jdike@addtoit.com>
+Date: Fri, 23 Mar 2007 14:18:17 -0400
+Subject: UML - fix epoll
+To: Andrew Morton <akpm@osdl.org>, stable@kernel.org
+Cc: uml-devel <user-mode-linux-devel@lists.sourceforge.net>
+Message-ID: <20070323181817.GA8694@c2.user-mode-linux.org>
+Content-Disposition: inline
+
+From: Jeff Dike <jdike@addtoit.com>
+
+UML/x86_64 needs the same packing of struct epoll_event as x86_64.
+
+Signed-off-by: Jeff Dike <jdike@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/linux/eventpoll.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/include/linux/eventpoll.h
++++ b/include/linux/eventpoll.h
+@@ -31,12 +31,19 @@
+ /*
+ * On x86-64 make the 64bit structure have the same alignment as the
+ * 32bit structure. This makes 32bit emulation easier.
++ *
++ * UML/x86_64 needs the same packing as x86_64 - UML + UML_X86 +
++ * 64_BIT adds up to UML/x86_64.
+ */
+ #ifdef __x86_64__
+ #define EPOLL_PACKED __attribute__((packed))
+ #else
++#if defined(CONFIG_UML) && defined(CONFIG_UML_X86) && defined(CONFIG_64BIT)
++#define EPOLL_PACKED __attribute__((packed))
++#else
+ #define EPOLL_PACKED
+ #endif
++#endif
+
+ struct epoll_event {
+ __u32 events;
--- /dev/null
+From stable-bounces@linux.kernel.org Sun Mar 25 09:58:58 2007
+From: Jeff Dike <jdike@addtoit.com>
+Date: Sun, 25 Mar 2007 12:54:32 -0400
+Subject: UML - Fix static linking
+To: Andrew Morton <akpm@osdl.org>, stable@kernel.org
+Cc: <JC.LAFOUCRIERE@CEA.FR>, uml-devel <user-mode-linux-devel@lists.sourceforge.net>
+Message-ID: <20070325165432.GA6232@c2.user-mode-linux.org>
+Content-Disposition: inline
+
+From: Jeff Dike <jdike@addtoit.com>
+
+During a static link, ld has started putting a .note section in the
+.uml.setup.init section. This has the result that the UML setups
+begin with 32 bytes of garbage and UML crashes immediately on boot.
+
+This patch creates a specific .note section for ld to drop this stuff
+into.
+
+Signed-off-by: Jeff Dike <jdike@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/asm-um/common.lds.S | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/asm-um/common.lds.S
++++ b/include/asm-um/common.lds.S
+@@ -15,6 +15,7 @@
+ PROVIDE (_unprotected_end = .);
+
+ . = ALIGN(4096);
++ .note : { *(note.*) }
+ __start___ex_table = .;
+ __ex_table : { *(__ex_table) }
+ __stop___ex_table = .;
--- /dev/null
+From stable-bounces@linux.kernel.org Wed Mar 28 16:28:40 2007
+From: "Paolo 'Blaisorblade' Giarrusso" <blaisorblade@yahoo.it>
+Date: Thu, 29 Mar 2007 01:26:11 +0200
+Subject: uml: fix unreasonably long udelay
+To: Andrew Morton <akpm@linux-foundation.org>, stable@kernel.org
+Cc: Jeff Dike <jdike@addtoit.com>
+Message-ID: <20070328232558.28719.96929.stgit@americanbeauty.home.lan>
+
+From: "Paolo 'Blaisorblade' Giarrusso" <blaisorblade@yahoo.it>
+
+Currently we have a confused udelay implementation.
+
+* __const_udelay does not accept usecs but xloops in i386 and x86_64
+* our implementation requires usecs as arg
+* it gets a xloops count when called by asm/arch/delay.h
+
+Bugs related to this (extremely long shutdown times) where reported by some
+x86_64 users, especially using Device Mapper.
+
+To hit this bug, a compile-time constant time parameter must be passed - that's
+why UML seems to work most times.
+Fix this with a simple udelay implementation.
+
+Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/um/sys-i386/delay.c | 11 -----------
+ arch/um/sys-x86_64/delay.c | 11 -----------
+ include/asm-um/delay.h | 17 ++++++++++++++---
+ 3 files changed, 14 insertions(+), 25 deletions(-)
+
+--- a/arch/um/sys-i386/delay.c
++++ b/arch/um/sys-i386/delay.c
+@@ -27,14 +27,3 @@ void __udelay(unsigned long usecs)
+ }
+
+ EXPORT_SYMBOL(__udelay);
+-
+-void __const_udelay(unsigned long usecs)
+-{
+- int i, n;
+-
+- n = (loops_per_jiffy * HZ * usecs) / MILLION;
+- for(i=0;i<n;i++)
+- cpu_relax();
+-}
+-
+-EXPORT_SYMBOL(__const_udelay);
+--- a/arch/um/sys-x86_64/delay.c
++++ b/arch/um/sys-x86_64/delay.c
+@@ -28,14 +28,3 @@ void __udelay(unsigned long usecs)
+ }
+
+ EXPORT_SYMBOL(__udelay);
+-
+-void __const_udelay(unsigned long usecs)
+-{
+- unsigned long i, n;
+-
+- n = (loops_per_jiffy * HZ * usecs) / MILLION;
+- for(i=0;i<n;i++)
+- cpu_relax();
+-}
+-
+-EXPORT_SYMBOL(__const_udelay);
+--- a/include/asm-um/delay.h
++++ b/include/asm-um/delay.h
+@@ -1,9 +1,20 @@
+ #ifndef __UM_DELAY_H
+ #define __UM_DELAY_H
+
+-#include "asm/arch/delay.h"
+-#include "asm/archparam.h"
+-
+ #define MILLION 1000000
+
++/* Undefined on purpose */
++extern void __bad_udelay(void);
++
++extern void __udelay(unsigned long usecs);
++extern void __delay(unsigned long loops);
++
++#define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
++ __bad_udelay() : __udelay(n))
++
++/* It appears that ndelay is not used at all for UML, and has never been
++ * implemented. */
++extern void __unimplemented_ndelay(void);
++#define ndelay(n) __unimplemented_ndelay()
++
+ #endif
--- /dev/null
+From stable-bounces@linux.kernel.org Fri Mar 23 12:41:21 2007
+From: Jeff Dike <jdike@addtoit.com>
+Date: Fri, 23 Mar 2007 15:37:30 -0400
+Subject: UML - host VDSO fix
+To: stable@kernel.org
+Cc: uml-devel <user-mode-linux-devel@lists.sourceforge.net>
+Message-ID: <20070323193730.GA9519@c2.user-mode-linux.org>
+Content-Disposition: inline
+
+From: Jeff Dike <jdike@addtoit.com>
+
+This fixes a problem seen by a number of people running UML on newer host
+kernels. init would hang with an infinite segfault loop.
+
+It turns out that the host kernel was providing a AT_SYSINFO_EHDR of
+0xffffe000, which faked UML into believing that the host VDSO page could be
+reused. However, AT_SYSINFO pointed into the middle of the address space, and
+was unmapped as a result. Because UML was providing AT_SYSINFO_EHDR and
+AT_SYSINFO to its own processes, these would branch to nowhere when trying to
+use the VDSO.
+
+The fix is to also check the location of AT_SYSINFO when deciding whether to
+use the host's VDSO.
+
+Signed-off-by: Jeff Dike <jdike@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/um/os-Linux/elf_aux.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/um/os-Linux/elf_aux.c
++++ b/arch/um/os-Linux/elf_aux.c
+@@ -40,6 +40,9 @@ __init void scan_elf_aux( char **envp)
+ switch ( auxv->a_type ) {
+ case AT_SYSINFO:
+ __kernel_vsyscall = auxv->a_un.a_val;
++ /* See if the page is under TASK_SIZE */
++ if (__kernel_vsyscall < (unsigned long) envp)
++ __kernel_vsyscall = 0;
+ break;
+ case AT_SYSINFO_EHDR:
+ vsyscall_ehdr = auxv->a_un.a_val;
--- /dev/null
+From stable-bounces@linux.kernel.org Sun Mar 25 10:06:00 2007
+From: Jeff Dike <jdike@addtoit.com>
+Date: Sun, 25 Mar 2007 13:01:44 -0400
+Subject: UML - use correct register file size everywhere
+To: Andrew Morton <akpm@osdl.org>, stable@kernel.org
+Cc: Davide Brini <brini@geomin.unibo.it>, uml-devel <user-mode-linux-devel@lists.sourceforge.net>
+Message-ID: <20070325170144.GB6232@c2.user-mode-linux.org>
+Content-Disposition: inline
+
+From: Jeff Dike <jdike@addtoit.com>
+
+This patch uses MAX_REG_NR consistently to refer to the register file
+size. FRAME_SIZE isn't sufficient because on x86_64, it is smaller
+than the ptrace register file size. MAX_REG_NR was introduced as a
+consistent way to get the number of registers, but wasn't used
+everywhere it should be.
+
+When this causes a problem, it makes PTRACE_SETREGS fail on x86_64
+because of a corrupted segment register value in the known-good
+register file. The patch also adds a register dump at that point in
+case there are any future problems here.
+
+Signed-off-by: Jeff Dike <jdike@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/um/include/sysdep-x86_64/ptrace.h | 4 ----
+ arch/um/os-Linux/skas/mem.c | 10 +++++++---
+ arch/um/os-Linux/skas/process.c | 6 +++---
+ arch/um/os-Linux/sys-i386/registers.c | 5 +++--
+ arch/um/os-Linux/sys-x86_64/registers.c | 4 ++--
+ 5 files changed, 15 insertions(+), 14 deletions(-)
+
+--- a/arch/um/include/sysdep-x86_64/ptrace.h
++++ b/arch/um/include/sysdep-x86_64/ptrace.h
+@@ -104,10 +104,6 @@ union uml_pt_regs {
+ #endif
+ #ifdef UML_CONFIG_MODE_SKAS
+ struct skas_regs {
+- /* x86_64 ptrace uses sizeof(user_regs_struct) as its register
+- * file size, while i386 uses FRAME_SIZE. Therefore, we need
+- * to use UM_FRAME_SIZE here instead of HOST_FRAME_SIZE.
+- */
+ unsigned long regs[MAX_REG_NR];
+ unsigned long fp[HOST_FP_SIZE];
+ struct faultinfo faultinfo;
+--- a/arch/um/os-Linux/skas/mem.c
++++ b/arch/um/os-Linux/skas/mem.c
+@@ -48,7 +48,7 @@ int multi_op_count = 0;
+ static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
+ {
+ unsigned long regs[MAX_REG_NR];
+- int n;
++ int n, i;
+ long ret, offset;
+ unsigned long * data;
+ unsigned long * syscall;
+@@ -66,9 +66,13 @@ static inline long do_syscall_stub(struc
+ (unsigned long) &__syscall_stub_start);
+
+ n = ptrace_setregs(pid, regs);
+- if(n < 0)
++ if(n < 0){
++ printk("Registers - \n");
++ for(i = 0; i < MAX_REG_NR; i++)
++ printk("\t%d\t0x%lx\n", i, regs[i]);
+ panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
+- n);
++ -n);
++ }
+
+ wait_stub_done(pid, 0, "do_syscall_stub");
+
+--- a/arch/um/os-Linux/skas/process.c
++++ b/arch/um/os-Linux/skas/process.c
+@@ -67,7 +67,7 @@ void wait_stub_done(int pid, int sig, ch
+
+ if((n < 0) || !WIFSTOPPED(status) ||
+ (WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGTRAP)){
+- unsigned long regs[HOST_FRAME_SIZE];
++ unsigned long regs[MAX_REG_NR];
+
+ if(ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
+ printk("Failed to get registers from stub, "
+@@ -76,7 +76,7 @@ void wait_stub_done(int pid, int sig, ch
+ int i;
+
+ printk("Stub registers -\n");
+- for(i = 0; i < HOST_FRAME_SIZE; i++)
++ for(i = 0; i < ARRAY_SIZE(regs); i++)
+ printk("\t%d - %lx\n", i, regs[i]);
+ }
+ panic("%s : failed to wait for SIGUSR1/SIGTRAP, "
+@@ -328,7 +328,7 @@ void userspace(union uml_pt_regs *regs)
+ int copy_context_skas0(unsigned long new_stack, int pid)
+ {
+ int err;
+- unsigned long regs[HOST_FRAME_SIZE];
++ unsigned long regs[MAX_REG_NR];
+ unsigned long fp_regs[HOST_FP_SIZE];
+ unsigned long current_stack = current_stub_stack();
+ struct stub_data *data = (struct stub_data *) current_stack;
+--- a/arch/um/os-Linux/sys-i386/registers.c
++++ b/arch/um/os-Linux/sys-i386/registers.c
+@@ -15,7 +15,7 @@
+
+ /* These are set once at boot time and not changed thereafter */
+
+-static unsigned long exec_regs[HOST_FRAME_SIZE];
++static unsigned long exec_regs[MAX_REG_NR];
+ static unsigned long exec_fp_regs[HOST_FP_SIZE];
+ static unsigned long exec_fpx_regs[HOST_XFP_SIZE];
+ static int have_fpx_regs = 1;
+@@ -101,6 +101,7 @@ void init_registers(int pid)
+ {
+ int err;
+
++ memset(exec_regs, 0, sizeof(exec_regs));
+ err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
+ if(err)
+ panic("check_ptrace : PTRACE_GETREGS failed, errno = %d",
+@@ -124,7 +125,7 @@ void init_registers(int pid)
+
+ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
+ {
+- memcpy(regs, exec_regs, HOST_FRAME_SIZE * sizeof(unsigned long));
++ memcpy(regs, exec_regs, sizeof(exec_regs));
+ if(fp_regs != NULL)
+ memcpy(fp_regs, exec_fp_regs,
+ HOST_FP_SIZE * sizeof(unsigned long));
+--- a/arch/um/os-Linux/sys-x86_64/registers.c
++++ b/arch/um/os-Linux/sys-x86_64/registers.c
+@@ -14,7 +14,7 @@
+
+ /* These are set once at boot time and not changed thereafter */
+
+-static unsigned long exec_regs[HOST_FRAME_SIZE];
++static unsigned long exec_regs[MAX_REG_NR];
+ static unsigned long exec_fp_regs[HOST_FP_SIZE];
+
+ void init_thread_registers(union uml_pt_regs *to)
+@@ -72,7 +72,7 @@ void init_registers(int pid)
+
+ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
+ {
+- memcpy(regs, exec_regs, HOST_FRAME_SIZE * sizeof(unsigned long));
++ memcpy(regs, exec_regs, sizeof(exec_regs));
+ if(fp_regs != NULL)
+ memcpy(fp_regs, exec_fp_regs,
+ HOST_FP_SIZE * sizeof(unsigned long));
--- /dev/null
+From stable-bounces@linux.kernel.org Thu Mar 29 12:17:46 2007
+From: David Miller <davem@davemloft.net>
+Date: Thu, 29 Mar 2007 12:16:27 -0700 (PDT)
+Subject: VIDEO: Fix FFB DAC revision probing
+To: stable@kernel.org
+Cc: bunk@stusta.de
+Message-ID: <20070329.121627.59655905.davem@davemloft.net>
+
+From: David Miller <davem@davemloft.net>
+
+[VIDEO] ffb: Fix two DAC handling bugs.
+
+The determination of whether the DAC has inverted cursor logic is
+broken, import the version checks the X.org driver uses to fix this.
+
+Next, when we change the timing generator, borrow code from X.org that
+does 10 NOP reads of the timing generator register afterwards to make
+sure the video-enable transition occurs cleanly.
+
+Finally, use macros for the DAC registers and fields in order to
+provide documentation for the next person who reads this code.
+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/video/ffb.c | 84 +++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 60 insertions(+), 24 deletions(-)
+
+--- a/drivers/video/ffb.c
++++ b/drivers/video/ffb.c
+@@ -336,14 +336,30 @@ struct ffb_dac {
+ u32 value2;
+ };
+
++#define FFB_DAC_UCTRL 0x1001 /* User Control */
++#define FFB_DAC_UCTRL_MANREV 0x00000f00 /* 4-bit Manufacturing Revision */
++#define FFB_DAC_UCTRL_MANREV_SHIFT 8
++#define FFB_DAC_TGEN 0x6000 /* Timing Generator */
++#define FFB_DAC_TGEN_VIDE 0x00000001 /* Video Enable */
++#define FFB_DAC_DID 0x8000 /* Device Identification */
++#define FFB_DAC_DID_PNUM 0x0ffff000 /* Device Part Number */
++#define FFB_DAC_DID_PNUM_SHIFT 12
++#define FFB_DAC_DID_REV 0xf0000000 /* Device Revision */
++#define FFB_DAC_DID_REV_SHIFT 28
++
++#define FFB_DAC_CUR_CTRL 0x100
++#define FFB_DAC_CUR_CTRL_P0 0x00000001
++#define FFB_DAC_CUR_CTRL_P1 0x00000002
++
+ struct ffb_par {
+ spinlock_t lock;
+ struct ffb_fbc __iomem *fbc;
+ struct ffb_dac __iomem *dac;
+
+ u32 flags;
+-#define FFB_FLAG_AFB 0x00000001
+-#define FFB_FLAG_BLANKED 0x00000002
++#define FFB_FLAG_AFB 0x00000001 /* AFB m3 or m6 */
++#define FFB_FLAG_BLANKED 0x00000002 /* screen is blanked */
++#define FFB_FLAG_INVCURSOR 0x00000004 /* DAC has inverted cursor logic */
+
+ u32 fg_cache __attribute__((aligned (8)));
+ u32 bg_cache;
+@@ -354,7 +370,6 @@ struct ffb_par {
+ unsigned long physbase;
+ unsigned long fbsize;
+
+- int dac_rev;
+ int board_type;
+ };
+
+@@ -426,11 +441,12 @@ static void ffb_switch_from_graph(struct
+ FFBWait(par);
+
+ /* Disable cursor. */
+- upa_writel(0x100, &dac->type2);
+- if (par->dac_rev <= 2)
++ upa_writel(FFB_DAC_CUR_CTRL, &dac->type2);
++ if (par->flags & FFB_FLAG_INVCURSOR)
+ upa_writel(0, &dac->value2);
+ else
+- upa_writel(3, &dac->value2);
++ upa_writel((FFB_DAC_CUR_CTRL_P0 |
++ FFB_DAC_CUR_CTRL_P1), &dac->value2);
+
+ spin_unlock_irqrestore(&par->lock, flags);
+ }
+@@ -664,18 +680,18 @@ ffb_blank(int blank, struct fb_info *inf
+ struct ffb_par *par = (struct ffb_par *) info->par;
+ struct ffb_dac __iomem *dac = par->dac;
+ unsigned long flags;
+- u32 tmp;
++ u32 val;
++ int i;
+
+ spin_lock_irqsave(&par->lock, flags);
+
+ FFBWait(par);
+
++ upa_writel(FFB_DAC_TGEN, &dac->type);
++ val = upa_readl(&dac->value);
+ switch (blank) {
+ case FB_BLANK_UNBLANK: /* Unblanking */
+- upa_writel(0x6000, &dac->type);
+- tmp = (upa_readl(&dac->value) | 0x1);
+- upa_writel(0x6000, &dac->type);
+- upa_writel(tmp, &dac->value);
++ val |= FFB_DAC_TGEN_VIDE;
+ par->flags &= ~FFB_FLAG_BLANKED;
+ break;
+
+@@ -683,13 +699,16 @@ ffb_blank(int blank, struct fb_info *inf
+ case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
+ case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
+ case FB_BLANK_POWERDOWN: /* Poweroff */
+- upa_writel(0x6000, &dac->type);
+- tmp = (upa_readl(&dac->value) & ~0x1);
+- upa_writel(0x6000, &dac->type);
+- upa_writel(tmp, &dac->value);
++ val &= ~FFB_DAC_TGEN_VIDE;
+ par->flags |= FFB_FLAG_BLANKED;
+ break;
+ }
++ upa_writel(FFB_DAC_TGEN, &dac->type);
++ upa_writel(val, &dac->value);
++ for (i = 0; i < 10; i++) {
++ upa_writel(FFB_DAC_TGEN, &dac->type);
++ upa_readl(&dac->value);
++ }
+
+ spin_unlock_irqrestore(&par->lock, flags);
+
+@@ -894,6 +913,7 @@ static int ffb_init_one(struct of_device
+ struct ffb_dac __iomem *dac;
+ struct all_info *all;
+ int err;
++ u32 dac_pnum, dac_rev, dac_mrev;
+
+ all = kzalloc(sizeof(*all), GFP_KERNEL);
+ if (!all)
+@@ -948,17 +968,31 @@ static int ffb_init_one(struct of_device
+ if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0)
+ upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr);
+
+- ffb_switch_from_graph(&all->par);
+-
+ dac = all->par.dac;
+- upa_writel(0x8000, &dac->type);
+- all->par.dac_rev = upa_readl(&dac->value) >> 0x1c;
++ upa_writel(FFB_DAC_DID, &dac->type);
++ dac_pnum = upa_readl(&dac->value);
++ dac_rev = (dac_pnum & FFB_DAC_DID_REV) >> FFB_DAC_DID_REV_SHIFT;
++ dac_pnum = (dac_pnum & FFB_DAC_DID_PNUM) >> FFB_DAC_DID_PNUM_SHIFT;
++
++ upa_writel(FFB_DAC_UCTRL, &dac->type);
++ dac_mrev = upa_readl(&dac->value);
++ dac_mrev = (dac_mrev & FFB_DAC_UCTRL_MANREV) >>
++ FFB_DAC_UCTRL_MANREV_SHIFT;
+
+ /* Elite3D has different DAC revision numbering, and no DAC revisions
+- * have the reversed meaning of cursor enable.
++ * have the reversed meaning of cursor enable. Otherwise, Pacifica 1
++ * ramdacs with manufacturing revision less than 3 have inverted
++ * cursor logic. We identify Pacifica 1 as not Pacifica 2, the
++ * latter having a part number value of 0x236e.
+ */
+- if (all->par.flags & FFB_FLAG_AFB)
+- all->par.dac_rev = 10;
++ if ((all->par.flags & FFB_FLAG_AFB) || dac_pnum == 0x236e) {
++ all->par.flags &= ~FFB_FLAG_INVCURSOR;
++ } else {
++ if (dac_mrev < 3)
++ all->par.flags |= FFB_FLAG_INVCURSOR;
++ }
++
++ ffb_switch_from_graph(&all->par);
+
+ /* Unblank it just to be sure. When there are multiple
+ * FFB/AFB cards in the system, or it is not the OBP
+@@ -993,10 +1027,12 @@ static int ffb_init_one(struct of_device
+
+ dev_set_drvdata(&op->dev, all);
+
+- printk("%s: %s at %016lx, type %d, DAC revision %d\n",
++ printk("%s: %s at %016lx, type %d, "
++ "DAC pnum[%x] rev[%d] manuf_rev[%d]\n",
+ dp->full_name,
+ ((all->par.flags & FFB_FLAG_AFB) ? "AFB" : "FFB"),
+- all->par.physbase, all->par.board_type, all->par.dac_rev);
++ all->par.physbase, all->par.board_type,
++ dac_pnum, dac_rev, dac_mrev);
+
+ return 0;
+ }