]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
x86/uv: Work around UV2 BAU hangs
authorCliff Wickman <cpw@sgi.com>
Fri, 22 Jun 2012 13:14:59 +0000 (08:14 -0500)
committerIngo Molnar <mingo@kernel.org>
Mon, 25 Jun 2012 12:45:05 +0000 (14:45 +0200)
On SGI's UV2 the BAU (Broadcast Assist Unit) driver can hang
under a heavy load. To cure this:

- Disable the UV2 extended status mode (see UV2_EXT_SHFT), as
  this mode changes BAU behavior in more ways then just delivering
  an extra bit of status.  Revert status to just two meaningful bits,
  like UV1.

- Use no IPI-style resets on UV2.  Just give up the request for
  whatever the reason it failed and let it be accomplished with
  the legacy IPI method.

- Use no alternate sending descriptor (the former UV2 workaround
  bcp->using_desc and handle_uv2_busy() stuff).  Just disable the
  use of the BAU for a period of time in favor of the legacy IPI
  method when the h/w bug leaves a descriptor busy.

  -- new tunable: giveup_limit determines the threshold at which a hub is
     so plugged that it should do all requests with the legacy IPI method for a
     period of time
  -- generalize disable_for_congestion() (renamed disable_for_period()) for
     use whenever a hub should avoid using the BAU for a period of time

Also:

 - Fix find_another_by_swack(), which is part of the UV2 bug workaround

 - Correct and clarify the statistics (new stats s_overipilimit, s_giveuplimit,
   s_enters, s_ipifordisabled, s_plugged, s_congested)

Signed-off-by: Cliff Wickman <cpw@sgi.com>
Link: http://lkml.kernel.org/r/20120622131459.GC31884@sgi.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/include/asm/uv/uv_bau.h
arch/x86/platform/uv/tlb_uv.c

index 847c00b721b2f938e6306fa9eb2a88205cbf7b46..a06983cdc125d842c58ca13dfc6817282413ee54 100644 (file)
 #define IPI_RESET_LIMIT                        1
 /* after this # consecutive successes, bump up the throttle if it was lowered */
 #define COMPLETE_THRESHOLD             5
+/* after this # of giveups (fall back to kernel IPI's) disable the use of
+   the BAU for a period of time */
+#define GIVEUP_LIMIT                   100
 
 #define UV_LB_SUBNODEID                        0x10
 
 #define FLUSH_RETRY_TIMEOUT            2
 #define FLUSH_GIVEUP                   3
 #define FLUSH_COMPLETE                 4
-#define FLUSH_RETRY_BUSYBUG            5
 
 /*
  * tuning the action when the numalink network is extremely delayed
                                                   microseconds */
 #define CONGESTED_REPS                 10      /* long delays averaged over
                                                   this many broadcasts */
-#define CONGESTED_PERIOD               30      /* time for the bau to be
+#define DISABLED_PERIOD                        10      /* time for the bau to be
                                                   disabled, in seconds */
 /* see msg_type: */
 #define MSG_NOOP                       0
@@ -520,7 +522,12 @@ struct ptc_stats {
        unsigned long   s_uv2_wars;             /* uv2 workaround, perm. busy */
        unsigned long   s_uv2_wars_hw;          /* uv2 workaround, hiwater */
        unsigned long   s_uv2_war_waits;        /* uv2 workaround, long waits */
-       unsigned long   s_enters;               /* entries to the driver */
+       unsigned long   s_overipilimit;         /* over the ipi reset limit */
+       unsigned long   s_giveuplimit;          /* disables, over giveup limit*/
+       unsigned long   s_enters;               /* entries to the driver */
+       unsigned long   s_ipifordisabled;       /* fall back to IPI; disabled */
+       unsigned long   s_plugged;              /* plugged by h/w bug*/
+       unsigned long   s_congested;            /* giveup on long wait */
        /* destination statistics */
        unsigned long   d_alltlb;               /* times all tlb's on this
                                                   cpu were flushed */
@@ -588,8 +595,7 @@ struct bau_control {
        int                     ipi_attempts;
        int                     conseccompletes;
        short                   nobau;
-       int                     baudisabled;
-       int                     set_bau_off;
+       short                   baudisabled;
        short                   cpu;
        short                   osnode;
        short                   uvhub_cpu;
@@ -598,14 +604,16 @@ struct bau_control {
        short                   cpus_in_socket;
        short                   cpus_in_uvhub;
        short                   partition_base_pnode;
-       short                   using_desc; /* an index, like uvhub_cpu */
-       unsigned int            inuse_map;
+       short                   busy;       /* all were busy (war) */
        unsigned short          message_number;
        unsigned short          uvhub_quiesce;
        short                   socket_acknowledge_count[DEST_Q_SIZE];
        cycles_t                send_message;
+       cycles_t                period_end;
+       cycles_t                period_time;
        spinlock_t              uvhub_lock;
        spinlock_t              queue_lock;
+       spinlock_t              disable_lock;
        /* tunables */
        int                     max_concurr;
        int                     max_concurr_const;
@@ -616,9 +624,9 @@ struct bau_control {
        int                     complete_threshold;
        int                     cong_response_us;
        int                     cong_reps;
-       int                     cong_period;
-       unsigned long           clocks_per_100_usec;
-       cycles_t                period_time;
+       cycles_t                disabled_period;
+       int                     period_giveups;
+       int                     giveup_limit;
        long                    period_requests;
        struct hub_and_pnode    *thp;
 };
index 1492170cbb5a0928435401049af9729c5ee19de4..71b5d5a07d7bbd7c26a5f9ae99cf5f0e8cacacbf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     SGI UltraViolet TLB flush routines.
  *
- *     (c) 2008-2011 Cliff Wickman <cpw@sgi.com>, SGI.
+ *     (c) 2008-2012 Cliff Wickman <cpw@sgi.com>, SGI.
  *
  *     This code is released under the GNU General Public License version 2 or
  *     later.
@@ -39,8 +39,6 @@ static int timeout_base_ns[] = {
 static int timeout_us;
 static int nobau;
 static int nobau_perm;
-static int baudisabled;
-static spinlock_t disable_lock;
 static cycles_t congested_cycles;
 
 /* tunables: */
@@ -48,12 +46,13 @@ static int max_concurr              = MAX_BAU_CONCURRENT;
 static int max_concurr_const   = MAX_BAU_CONCURRENT;
 static int plugged_delay       = PLUGGED_DELAY;
 static int plugsb4reset                = PLUGSB4RESET;
+static int giveup_limit                = GIVEUP_LIMIT;
 static int timeoutsb4reset     = TIMEOUTSB4RESET;
 static int ipi_reset_limit     = IPI_RESET_LIMIT;
 static int complete_threshold  = COMPLETE_THRESHOLD;
 static int congested_respns_us = CONGESTED_RESPONSE_US;
 static int congested_reps      = CONGESTED_REPS;
-static int congested_period    = CONGESTED_PERIOD;
+static int disabled_period     = DISABLED_PERIOD;
 
 static struct tunables tunables[] = {
        {&max_concurr, MAX_BAU_CONCURRENT}, /* must be [0] */
@@ -64,7 +63,8 @@ static struct tunables tunables[] = {
        {&complete_threshold, COMPLETE_THRESHOLD},
        {&congested_respns_us, CONGESTED_RESPONSE_US},
        {&congested_reps, CONGESTED_REPS},
-       {&congested_period, CONGESTED_PERIOD}
+       {&disabled_period, DISABLED_PERIOD},
+       {&giveup_limit, GIVEUP_LIMIT}
 };
 
 static struct dentry *tunables_dir;
@@ -313,7 +313,7 @@ static void bau_process_message(struct msg_desc *mdp, struct bau_control *bcp,
                 * Both sockets dump their completed count total into
                 * the message's count.
                 */
-               smaster->socket_acknowledge_count[mdp->msg_slot] = 0;
+               *sp = 0;
                asp = (struct atomic_short *)&msg->acknowledge_count;
                msg_ack_count = atom_asr(socket_ack_count, asp);
 
@@ -526,16 +526,15 @@ static int uv1_wait_completion(struct bau_desc *bau_desc,
 }
 
 /*
- * UV2 has an extra bit of status in the ACTIVATION_STATUS_2 register.
+ * UV2 could have an extra bit of status in the ACTIVATION_STATUS_2 register.
+ * But not currently used.
  */
 static unsigned long uv2_read_status(unsigned long offset, int rshft, int desc)
 {
        unsigned long descriptor_status;
-       unsigned long descriptor_status2;
 
-       descriptor_status = ((read_lmmr(offset) >> rshft) & UV_ACT_STATUS_MASK);
-       descriptor_status2 = (read_mmr_uv2_status() >> desc) & 0x1UL;
-       descriptor_status = (descriptor_status << 1) | descriptor_status2;
+       descriptor_status =
+               ((read_lmmr(offset) >> rshft) & UV_ACT_STATUS_MASK) << 1;
        return descriptor_status;
 }
 
@@ -566,87 +565,11 @@ int normal_busy(struct bau_control *bcp)
  */
 int handle_uv2_busy(struct bau_control *bcp)
 {
-       int busy_one = bcp->using_desc;
-       int normal = bcp->uvhub_cpu;
-       int selected = -1;
-       int i;
-       unsigned long descriptor_status;
-       unsigned long status;
-       int mmr_offset;
-       struct bau_desc *bau_desc_old;
-       struct bau_desc *bau_desc_new;
-       struct bau_control *hmaster = bcp->uvhub_master;
        struct ptc_stats *stat = bcp->statp;
-       cycles_t ttm;
 
        stat->s_uv2_wars++;
-       spin_lock(&hmaster->uvhub_lock);
-       /* try for the original first */
-       if (busy_one != normal) {
-               if (!normal_busy(bcp))
-                       selected = normal;
-       }
-       if (selected < 0) {
-               /* can't use the normal, select an alternate */
-               mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_1;
-               descriptor_status = read_lmmr(mmr_offset);
-
-               /* scan available descriptors 32-63 */
-               for (i = 0; i < UV_CPUS_PER_AS; i++) {
-                       if ((hmaster->inuse_map & (1 << i)) == 0) {
-                               status = ((descriptor_status >>
-                                               (i * UV_ACT_STATUS_SIZE)) &
-                                               UV_ACT_STATUS_MASK) << 1;
-                               if (status != UV2H_DESC_BUSY) {
-                                       selected = i + UV_CPUS_PER_AS;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       if (busy_one != normal)
-               /* mark the busy alternate as not in-use */
-               hmaster->inuse_map &= ~(1 << (busy_one - UV_CPUS_PER_AS));
-
-       if (selected >= 0) {
-               /* switch to the selected descriptor */
-               if (selected != normal) {
-                       /* set the selected alternate as in-use */
-                       hmaster->inuse_map |=
-                                       (1 << (selected - UV_CPUS_PER_AS));
-                       if (selected > stat->s_uv2_wars_hw)
-                               stat->s_uv2_wars_hw = selected;
-               }
-               bau_desc_old = bcp->descriptor_base;
-               bau_desc_old += (ITEMS_PER_DESC * busy_one);
-               bcp->using_desc = selected;
-               bau_desc_new = bcp->descriptor_base;
-               bau_desc_new += (ITEMS_PER_DESC * selected);
-               *bau_desc_new = *bau_desc_old;
-       } else {
-               /*
-                * All are busy. Wait for the normal one for this cpu to
-                * free up.
-                */
-               stat->s_uv2_war_waits++;
-               spin_unlock(&hmaster->uvhub_lock);
-               ttm = get_cycles();
-               do {
-                       cpu_relax();
-               } while (normal_busy(bcp));
-               spin_lock(&hmaster->uvhub_lock);
-               /* switch to the original descriptor */
-               bcp->using_desc = normal;
-               bau_desc_old = bcp->descriptor_base;
-               bau_desc_old += (ITEMS_PER_DESC * bcp->using_desc);
-               bcp->using_desc = (ITEMS_PER_DESC * normal);
-               bau_desc_new = bcp->descriptor_base;
-               bau_desc_new += (ITEMS_PER_DESC * normal);
-               *bau_desc_new = *bau_desc_old; /* copy the entire descriptor */
-       }
-       spin_unlock(&hmaster->uvhub_lock);
-       return FLUSH_RETRY_BUSYBUG;
+       bcp->busy = 1;
+       return FLUSH_GIVEUP;
 }
 
 static int uv2_wait_completion(struct bau_desc *bau_desc,
@@ -655,7 +578,7 @@ static int uv2_wait_completion(struct bau_desc *bau_desc,
 {
        unsigned long descriptor_stat;
        cycles_t ttm;
-       int desc = bcp->using_desc;
+       int desc = bcp->uvhub_cpu;
        long busy_reps = 0;
        struct ptc_stats *stat = bcp->statp;
 
@@ -663,24 +586,38 @@ static int uv2_wait_completion(struct bau_desc *bau_desc,
 
        /* spin on the status MMR, waiting for it to go idle */
        while (descriptor_stat != UV2H_DESC_IDLE) {
-               /*
-                * Our software ack messages may be blocked because
-                * there are no swack resources available.  As long
-                * as none of them has timed out hardware will NACK
-                * our message and its state will stay IDLE.
-                */
-               if ((descriptor_stat == UV2H_DESC_SOURCE_TIMEOUT) ||
-                   (descriptor_stat == UV2H_DESC_DEST_PUT_ERR)) {
+               if ((descriptor_stat == UV2H_DESC_SOURCE_TIMEOUT)) {
+                       /*
+                        * A h/w bug on the destination side may
+                        * have prevented the message being marked
+                        * pending, thus it doesn't get replied to
+                        * and gets continually nacked until it times
+                        * out with a SOURCE_TIMEOUT.
+                        */
                        stat->s_stimeout++;
                        return FLUSH_GIVEUP;
-               } else if (descriptor_stat == UV2H_DESC_DEST_STRONG_NACK) {
-                       stat->s_strongnacks++;
-                       bcp->conseccompletes = 0;
-                       return FLUSH_GIVEUP;
                } else if (descriptor_stat == UV2H_DESC_DEST_TIMEOUT) {
+                       ttm = get_cycles();
+
+                       /*
+                        * Our retries may be blocked by all destination
+                        * swack resources being consumed, and a timeout
+                        * pending.  In that case hardware returns the
+                        * ERROR that looks like a destination timeout.
+                        * Without using the extended status we have to
+                        * deduce from the short time that this was a
+                        * strong nack.
+                        */
+                       if (cycles_2_us(ttm - bcp->send_message) < timeout_us) {
+                               bcp->conseccompletes = 0;
+                               stat->s_plugged++;
+                               /* FLUSH_RETRY_PLUGGED causes hang on boot */
+                               return FLUSH_GIVEUP;
+                       }
                        stat->s_dtimeout++;
                        bcp->conseccompletes = 0;
-                       return FLUSH_RETRY_TIMEOUT;
+                       /* FLUSH_RETRY_TIMEOUT causes hang on boot */
+                       return FLUSH_GIVEUP;
                } else {
                        busy_reps++;
                        if (busy_reps > 1000000) {
@@ -688,9 +625,8 @@ static int uv2_wait_completion(struct bau_desc *bau_desc,
                                busy_reps = 0;
                                ttm = get_cycles();
                                if ((ttm - bcp->send_message) >
-                                       (bcp->clocks_per_100_usec)) {
+                                               bcp->timeout_interval)
                                        return handle_uv2_busy(bcp);
-                               }
                        }
                        /*
                         * descriptor_stat is still BUSY
@@ -714,7 +650,7 @@ static int wait_completion(struct bau_desc *bau_desc,
 {
        int right_shift;
        unsigned long mmr_offset;
-       int desc = bcp->using_desc;
+       int desc = bcp->uvhub_cpu;
 
        if (desc < UV_CPUS_PER_AS) {
                mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_0;
@@ -793,33 +729,31 @@ static void destination_timeout(struct bau_desc *bau_desc,
 }
 
 /*
- * Completions are taking a very long time due to a congested numalink
- * network.
+ * Stop all cpus on a uvhub from using the BAU for a period of time.
+ * This is reversed by check_enable.
  */
-static void disable_for_congestion(struct bau_control *bcp,
-                                       struct ptc_stats *stat)
+static void disable_for_period(struct bau_control *bcp, struct ptc_stats *stat)
 {
-       /* let only one cpu do this disabling */
-       spin_lock(&disable_lock);
-
-       if (!baudisabled && bcp->period_requests &&
-           ((bcp->period_time / bcp->period_requests) > congested_cycles)) {
-               int tcpu;
-               struct bau_control *tbcp;
-               /* it becomes this cpu's job to turn on the use of the
-                  BAU again */
-               baudisabled = 1;
-               bcp->set_bau_off = 1;
-               bcp->set_bau_on_time = get_cycles();
-               bcp->set_bau_on_time += sec_2_cycles(bcp->cong_period);
+       int tcpu;
+       struct bau_control *tbcp;
+       struct bau_control *hmaster;
+       cycles_t tm1;
+
+       hmaster = bcp->uvhub_master;
+       spin_lock(&hmaster->disable_lock);
+       if (!bcp->baudisabled) {
                stat->s_bau_disabled++;
+               tm1 = get_cycles();
                for_each_present_cpu(tcpu) {
                        tbcp = &per_cpu(bau_control, tcpu);
-                       tbcp->baudisabled = 1;
+                       if (tbcp->uvhub_master == hmaster) {
+                               tbcp->baudisabled = 1;
+                               tbcp->set_bau_on_time =
+                                       tm1 + bcp->disabled_period;
+                       }
                }
        }
-
-       spin_unlock(&disable_lock);
+       spin_unlock(&hmaster->disable_lock);
 }
 
 static void count_max_concurr(int stat, struct bau_control *bcp,
@@ -850,16 +784,30 @@ static void record_send_stats(cycles_t time1, cycles_t time2,
                        bcp->period_requests++;
                        bcp->period_time += elapsed;
                        if ((elapsed > congested_cycles) &&
-                           (bcp->period_requests > bcp->cong_reps))
-                               disable_for_congestion(bcp, stat);
+                           (bcp->period_requests > bcp->cong_reps) &&
+                           ((bcp->period_time / bcp->period_requests) >
+                                                       congested_cycles)) {
+                               stat->s_congested++;
+                               disable_for_period(bcp, stat);
+                       }
                }
        } else
                stat->s_requestor--;
 
        if (completion_status == FLUSH_COMPLETE && try > 1)
                stat->s_retriesok++;
-       else if (completion_status == FLUSH_GIVEUP)
+       else if (completion_status == FLUSH_GIVEUP) {
                stat->s_giveup++;
+               if (get_cycles() > bcp->period_end)
+                       bcp->period_giveups = 0;
+               bcp->period_giveups++;
+               if (bcp->period_giveups == 1)
+                       bcp->period_end = get_cycles() + bcp->disabled_period;
+               if (bcp->period_giveups > bcp->giveup_limit) {
+                       disable_for_period(bcp, stat);
+                       stat->s_giveuplimit++;
+               }
+       }
 }
 
 /*
@@ -903,7 +851,8 @@ static void handle_cmplt(int completion_status, struct bau_desc *bau_desc,
  * Returns 1 if it gives up entirely and the original cpu mask is to be
  * returned to the kernel.
  */
-int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp)
+int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp,
+       struct bau_desc *bau_desc)
 {
        int seq_number = 0;
        int completion_stat = 0;
@@ -916,24 +865,23 @@ int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp)
        struct bau_control *hmaster = bcp->uvhub_master;
        struct uv1_bau_msg_header *uv1_hdr = NULL;
        struct uv2_bau_msg_header *uv2_hdr = NULL;
-       struct bau_desc *bau_desc;
 
-       if (bcp->uvhub_version == 1)
+       if (bcp->uvhub_version == 1) {
+               uv1 = 1;
                uv1_throttle(hmaster, stat);
+       }
 
        while (hmaster->uvhub_quiesce)
                cpu_relax();
 
        time1 = get_cycles();
+       if (uv1)
+               uv1_hdr = &bau_desc->header.uv1_hdr;
+       else
+               uv2_hdr = &bau_desc->header.uv2_hdr;
+
        do {
-               bau_desc = bcp->descriptor_base;
-               bau_desc += (ITEMS_PER_DESC * bcp->using_desc);
-               if (bcp->uvhub_version == 1) {
-                       uv1 = 1;
-                       uv1_hdr = &bau_desc->header.uv1_hdr;
-               } else
-                       uv2_hdr = &bau_desc->header.uv2_hdr;
-               if ((try == 0) || (completion_stat == FLUSH_RETRY_BUSYBUG)) {
+               if (try == 0) {
                        if (uv1)
                                uv1_hdr->msg_type = MSG_REGULAR;
                        else
@@ -951,25 +899,24 @@ int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp)
                        uv1_hdr->sequence = seq_number;
                else
                        uv2_hdr->sequence = seq_number;
-               index = (1UL << AS_PUSH_SHIFT) | bcp->using_desc;
+               index = (1UL << AS_PUSH_SHIFT) | bcp->uvhub_cpu;
                bcp->send_message = get_cycles();
 
                write_mmr_activation(index);
 
                try++;
                completion_stat = wait_completion(bau_desc, bcp, try);
-               /* UV2: wait_completion() may change the bcp->using_desc */
 
                handle_cmplt(completion_stat, bau_desc, bcp, hmaster, stat);
 
                if (bcp->ipi_attempts >= bcp->ipi_reset_limit) {
                        bcp->ipi_attempts = 0;
+                       stat->s_overipilimit++;
                        completion_stat = FLUSH_GIVEUP;
                        break;
                }
                cpu_relax();
        } while ((completion_stat == FLUSH_RETRY_PLUGGED) ||
-                (completion_stat == FLUSH_RETRY_BUSYBUG) ||
                 (completion_stat == FLUSH_RETRY_TIMEOUT));
 
        time2 = get_cycles();
@@ -990,28 +937,33 @@ int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp)
 }
 
 /*
- * The BAU is disabled. When the disabled time period has expired, the cpu
- * that disabled it must re-enable it.
- * Return 0 if it is re-enabled for all cpus.
+ * The BAU is disabled for this uvhub. When the disabled time period has
+ * expired re-enable it.
+ * Return 0 if it is re-enabled for all cpus on this uvhub.
  */
 static int check_enable(struct bau_control *bcp, struct ptc_stats *stat)
 {
        int tcpu;
        struct bau_control *tbcp;
+       struct bau_control *hmaster;
 
-       if (bcp->set_bau_off) {
-               if (get_cycles() >= bcp->set_bau_on_time) {
-                       stat->s_bau_reenabled++;
-                       baudisabled = 0;
-                       for_each_present_cpu(tcpu) {
-                               tbcp = &per_cpu(bau_control, tcpu);
+       hmaster = bcp->uvhub_master;
+       spin_lock(&hmaster->disable_lock);
+       if (bcp->baudisabled && (get_cycles() >= bcp->set_bau_on_time)) {
+               stat->s_bau_reenabled++;
+               for_each_present_cpu(tcpu) {
+                       tbcp = &per_cpu(bau_control, tcpu);
+                       if (tbcp->uvhub_master == hmaster) {
                                tbcp->baudisabled = 0;
                                tbcp->period_requests = 0;
                                tbcp->period_time = 0;
+                               tbcp->period_giveups = 0;
                        }
-                       return 0;
                }
+               spin_unlock(&hmaster->disable_lock);
+               return 0;
        }
+       spin_unlock(&hmaster->disable_lock);
        return -1;
 }
 
@@ -1113,6 +1065,8 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
        struct cpumask *flush_mask;
        struct ptc_stats *stat;
        struct bau_control *bcp;
+       unsigned long descriptor_status;
+       unsigned long status;
 
        bcp = &per_cpu(bau_control, cpu);
        stat = bcp->statp;
@@ -1121,10 +1075,22 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
        if (bcp->nobau)
                return cpumask;
 
+       if (bcp->busy) {
+               descriptor_status =
+                       read_lmmr(UVH_LB_BAU_SB_ACTIVATION_STATUS_0);
+               status = ((descriptor_status >> (bcp->uvhub_cpu *
+                       UV_ACT_STATUS_SIZE)) & UV_ACT_STATUS_MASK) << 1;
+               if (status == UV2H_DESC_BUSY)
+                       return cpumask;
+               bcp->busy = 0;
+       }
+
        /* bau was disabled due to slow response */
        if (bcp->baudisabled) {
-               if (check_enable(bcp, stat))
+               if (check_enable(bcp, stat)) {
+                       stat->s_ipifordisabled++;
                        return cpumask;
+               }
        }
 
        /*
@@ -1140,7 +1106,7 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
                stat->s_ntargself++;
 
        bau_desc = bcp->descriptor_base;
-       bau_desc += (ITEMS_PER_DESC * bcp->using_desc);
+       bau_desc += (ITEMS_PER_DESC * bcp->uvhub_cpu);
        bau_uvhubs_clear(&bau_desc->distribution, UV_DISTRIBUTION_SIZE);
        if (set_distrib_bits(flush_mask, bcp, bau_desc, &locals, &remotes))
                return NULL;
@@ -1153,25 +1119,27 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
         * uv_flush_send_and_wait returns 0 if all cpu's were messaged,
         * or 1 if it gave up and the original cpumask should be returned.
         */
-       if (!uv_flush_send_and_wait(flush_mask, bcp))
+       if (!uv_flush_send_and_wait(flush_mask, bcp, bau_desc))
                return NULL;
        else
                return cpumask;
 }
 
 /*
- * Search the message queue for any 'other' message with the same software
- * acknowledge resource bit vector.
+ * Search the message queue for any 'other' unprocessed message with the
+ * same software acknowledge resource bit vector as the 'msg' message.
  */
 struct bau_pq_entry *find_another_by_swack(struct bau_pq_entry *msg,
-                       struct bau_control *bcp, unsigned char swack_vec)
+                                          struct bau_control *bcp)
 {
        struct bau_pq_entry *msg_next = msg + 1;
+       unsigned char swack_vec = msg->swack_vec;
 
        if (msg_next > bcp->queue_last)
                msg_next = bcp->queue_first;
-       while ((msg_next->swack_vec != 0) && (msg_next != msg)) {
-               if (msg_next->swack_vec == swack_vec)
+       while (msg_next != msg) {
+               if ((msg_next->canceled == 0) && (msg_next->replied_to == 0) &&
+                               (msg_next->swack_vec == swack_vec))
                        return msg_next;
                msg_next++;
                if (msg_next > bcp->queue_last)
@@ -1200,32 +1168,30 @@ void process_uv2_message(struct msg_desc *mdp, struct bau_control *bcp)
                 * This message was assigned a swack resource, but no
                 * reserved acknowlegment is pending.
                 * The bug has prevented this message from setting the MMR.
-                * And no other message has used the same sw_ack resource.
-                * Do the requested shootdown but do not reply to the msg.
-                * (the 0 means make no acknowledge)
                 */
-               bau_process_message(mdp, bcp, 0);
-               return;
-       }
-
-       /*
-        * Some message has set the MMR 'pending' bit; it might have been
-        * another message.  Look for that message.
-        */
-       other_msg = find_another_by_swack(msg, bcp, msg->swack_vec);
-       if (other_msg) {
-               /* There is another.  Do not ack the current one. */
-               bau_process_message(mdp, bcp, 0);
                /*
-                * Let the natural processing of that message acknowledge
-                * it. Don't get the processing of sw_ack's out of order.
+                * Some message has set the MMR 'pending' bit; it might have
+                * been another message.  Look for that message.
                 */
-               return;
+               other_msg = find_another_by_swack(msg, bcp);
+               if (other_msg) {
+                       /*
+                        * There is another. Process this one but do not
+                        * ack it.
+                        */
+                       bau_process_message(mdp, bcp, 0);
+                       /*
+                        * Let the natural processing of that other message
+                        * acknowledge it. Don't get the processing of sw_ack's
+                        * out of order.
+                        */
+                       return;
+               }
        }
 
        /*
-        * There is no other message using this sw_ack, so it is safe to
-        * acknowledge it.
+        * Either the MMR shows this one pending a reply or there is no
+        * other message using this sw_ack, so it is safe to acknowledge it.
         */
        bau_process_message(mdp, bcp, 1);
 
@@ -1330,7 +1296,8 @@ static void __init enable_timeouts(void)
                 */
                mmr_image |= (1L << SOFTACK_MSHIFT);
                if (is_uv2_hub()) {
-                       mmr_image |= (1L << UV2_EXT_SHFT);
+                       /* hw bug workaround; do not use extended status */
+                       mmr_image &= ~(1L << UV2_EXT_SHFT);
                }
                write_mmr_misc_control(pnode, mmr_image);
        }
@@ -1379,24 +1346,26 @@ static int ptc_seq_show(struct seq_file *file, void *data)
        cpu = *(loff_t *)data;
        if (!cpu) {
                seq_printf(file,
-               "# cpu bauoff sent stime self locals remotes ncpus localhub ");
+                "# cpu bauoff sent stime self locals remotes ncpus localhub ");
                seq_printf(file,
                        "remotehub numuvhubs numuvhubs16 numuvhubs8 ");
                seq_printf(file,
-                   "numuvhubs4 numuvhubs2 numuvhubs1 dto snacks retries rok ");
+                       "numuvhubs4 numuvhubs2 numuvhubs1 dto snacks retries ");
+               seq_printf(file,
+                       "rok resetp resett giveup sto bz throt disable ");
                seq_printf(file,
-                       "resetp resett giveup sto bz throt enters swack recv rtime ");
+                       "enable wars warshw warwaits enters ipidis plugged ");
                seq_printf(file,
-                       "all one mult none retry canc nocan reset rcan ");
+                       "ipiover glim cong swack recv rtime all one mult ");
                seq_printf(file,
-                       "disable enable wars warshw warwaits\n");
+                       "none retry canc nocan reset rcan\n");
        }
        if (cpu < num_possible_cpus() && cpu_online(cpu)) {
                bcp = &per_cpu(bau_control, cpu);
                stat = bcp->statp;
                /* source side statistics */
                seq_printf(file,
-                          "cpu %d %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld ",
+                       "cpu %d %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld ",
                           cpu, bcp->nobau, stat->s_requestor,
                           cycles_2_us(stat->s_time),
                           stat->s_ntargself, stat->s_ntarglocals,
@@ -1407,25 +1376,28 @@ static int ptc_seq_show(struct seq_file *file, void *data)
                           stat->s_ntarguvhub8, stat->s_ntarguvhub4,
                           stat->s_ntarguvhub2, stat->s_ntarguvhub1,
                           stat->s_dtimeout, stat->s_strongnacks);
-               seq_printf(file, "%ld %ld %ld %ld %ld %ld %ld %ld %ld ",
+               seq_printf(file, "%ld %ld %ld %ld %ld %ld %ld %ld ",
                           stat->s_retry_messages, stat->s_retriesok,
                           stat->s_resets_plug, stat->s_resets_timeout,
                           stat->s_giveup, stat->s_stimeout,
-                          stat->s_busy, stat->s_throttles, stat->s_enters);
+                          stat->s_busy, stat->s_throttles);
+               seq_printf(file, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld ",
+                          stat->s_bau_disabled, stat->s_bau_reenabled,
+                          stat->s_uv2_wars, stat->s_uv2_wars_hw,
+                          stat->s_uv2_war_waits, stat->s_enters,
+                          stat->s_ipifordisabled, stat->s_plugged,
+                          stat->s_overipilimit, stat->s_giveuplimit,
+                          stat->s_congested);
 
                /* destination side statistics */
                seq_printf(file,
-                          "%lx %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld ",
+                       "%lx %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n",
                           read_gmmr_sw_ack(uv_cpu_to_pnode(cpu)),
                           stat->d_requestee, cycles_2_us(stat->d_time),
                           stat->d_alltlb, stat->d_onetlb, stat->d_multmsg,
                           stat->d_nomsg, stat->d_retries, stat->d_canceled,
                           stat->d_nocanceled, stat->d_resets,
                           stat->d_rcanceled);
-               seq_printf(file, "%ld %ld %ld %ld %ld\n",
-                       stat->s_bau_disabled, stat->s_bau_reenabled,
-                       stat->s_uv2_wars, stat->s_uv2_wars_hw,
-                       stat->s_uv2_war_waits);
        }
        return 0;
 }
@@ -1439,13 +1411,14 @@ static ssize_t tunables_read(struct file *file, char __user *userbuf,
        char *buf;
        int ret;
 
-       buf = kasprintf(GFP_KERNEL, "%s %s %s\n%d %d %d %d %d %d %d %d %d\n",
-               "max_concur plugged_delay plugsb4reset",
-               "timeoutsb4reset ipi_reset_limit complete_threshold",
-               "congested_response_us congested_reps congested_period",
+       buf = kasprintf(GFP_KERNEL, "%s %s %s\n%d %d %d %d %d %d %d %d %d %d\n",
+               "max_concur plugged_delay plugsb4reset timeoutsb4reset",
+               "ipi_reset_limit complete_threshold congested_response_us",
+               "congested_reps disabled_period giveup_limit",
                max_concurr, plugged_delay, plugsb4reset,
                timeoutsb4reset, ipi_reset_limit, complete_threshold,
-               congested_respns_us, congested_reps, congested_period);
+               congested_respns_us, congested_reps, disabled_period,
+               giveup_limit);
 
        if (!buf)
                return -ENOMEM;
@@ -1616,7 +1589,8 @@ static ssize_t tunables_write(struct file *file, const char __user *user,
                bcp->complete_threshold =       complete_threshold;
                bcp->cong_response_us =         congested_respns_us;
                bcp->cong_reps =                congested_reps;
-               bcp->cong_period =              congested_period;
+               bcp->disabled_period =          sec_2_cycles(disabled_period);
+               bcp->giveup_limit =             giveup_limit;
        }
        return count;
 }
@@ -1745,6 +1719,10 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode)
                         *   fairness chaining multilevel count replied_to
                         */
                } else {
+                       /*
+                        * BIOS uses legacy mode, but UV2 hardware always
+                        * uses native mode for selective broadcasts.
+                        */
                        uv2_hdr = &bd2->header.uv2_hdr;
                        uv2_hdr->swack_flag =   1;
                        uv2_hdr->base_dest_nasid =
@@ -1896,10 +1874,11 @@ static void __init init_per_cpu_tunables(void)
                bcp->complete_threshold         = complete_threshold;
                bcp->cong_response_us           = congested_respns_us;
                bcp->cong_reps                  = congested_reps;
-               bcp->cong_period                = congested_period;
-               bcp->clocks_per_100_usec =      usec_2_cycles(100);
+               bcp->disabled_period =          sec_2_cycles(disabled_period);
+               bcp->giveup_limit =             giveup_limit;
                spin_lock_init(&bcp->queue_lock);
                spin_lock_init(&bcp->uvhub_lock);
+               spin_lock_init(&bcp->disable_lock);
        }
 }
 
@@ -2020,7 +1999,6 @@ static int scan_sock(struct socket_desc *sdp, struct uvhub_desc *bdp,
                }
                bcp->uvhub_master = *hmasterp;
                bcp->uvhub_cpu = uv_cpu_hub_info(cpu)->blade_processor_id;
-               bcp->using_desc = bcp->uvhub_cpu;
                if (bcp->uvhub_cpu >= MAX_CPUS_PER_UVHUB) {
                        printk(KERN_EMERG "%d cpus per uvhub invalid\n",
                                bcp->uvhub_cpu);
@@ -2123,7 +2101,6 @@ static int __init uv_bau_init(void)
        }
 
        nuvhubs = uv_num_possible_blades();
-       spin_lock_init(&disable_lock);
        congested_cycles = usec_2_cycles(congested_respns_us);
 
        uv_base_pnode = 0x7fffffff;