Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1cf24bdb authored by Jack Steiner's avatar Jack Steiner Committed by Tony Luck
Browse files

[IA64] - Reduce overhead of FP exception logging messages



Improve the scalability of the fpswa code that rate-limits
logging of messages.

There are 2 distinctly different problems in this code.

1) If prctl is used to disable logging, last_time is never
   updated. The result is that fpu_swa_count is zeroed out on
   EVERY fp fault. This causes a very very hot cache line.
   The fix reduces the wallclock time of a 1024p FP exception test
   from 28734 sec to 19 sec!!!

2) On VERY large systems, excessive messages are logged because
   multiple cpus can each reset or increment fpu_swa_count at
   about the same time. The result is that hundreds of messages
   are logged each second. The fixes reduces the logging rate
   to ~1 per second.

Signed-off-by: default avatarJack Steiner <steiner@sgi.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent 8b9c1068
Loading
Loading
Loading
Loading
+40 −10
Original line number Diff line number Diff line
@@ -307,6 +307,15 @@ fp_emulate (int fp_fault, void *bundle, long *ipsr, long *fpsr, long *isr, long
	return ret.status;
}

struct fpu_swa_msg {
	unsigned long count;
	unsigned long time;
};
static DEFINE_PER_CPU(struct fpu_swa_msg, cpulast);
DECLARE_PER_CPU(struct fpu_swa_msg, cpulast);
static struct fpu_swa_msg last __cacheline_aligned;


/*
 * Handle floating-point assist faults and traps.
 */
@@ -316,8 +325,6 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
	long exception, bundle[2];
	unsigned long fault_ip;
	struct siginfo siginfo;
	static int fpu_swa_count = 0;
	static unsigned long last_time;

	fault_ip = regs->cr_iip;
	if (!fp_fault && (ia64_psr(regs)->ri == 0))
@@ -325,15 +332,38 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
	if (copy_from_user(bundle, (void __user *) fault_ip, sizeof(bundle)))
		return -1;

	if (jiffies - last_time > 5*HZ)
		fpu_swa_count = 0;
	if ((fpu_swa_count < 4) && !(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT)) {
		last_time = jiffies;
		++fpu_swa_count;
	if (!(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT))  {
		unsigned long count, current_jiffies = jiffies;
		struct fpu_swa_msg *cp = &__get_cpu_var(cpulast);

		if (unlikely(current_jiffies > cp->time))
			cp->count = 0;
		if (unlikely(cp->count < 5)) {
			cp->count++;
			cp->time = current_jiffies + 5 * HZ;

			/* minimize races by grabbing a copy of count BEFORE checking last.time. */
			count = last.count;
			barrier();

			/*
			 * Lower 4 bits are used as a count. Upper bits are a sequence
			 * number that is updated when count is reset. The cmpxchg will
			 * fail is seqno has changed. This minimizes mutiple cpus
			 * reseting the count.
			 */
			if (current_jiffies > last.time)
				(void) cmpxchg_acq(&last.count, count, 16 + (count & ~15));

			/* used fetchadd to atomically update the count */
			if ((last.count & 15) < 5 && (ia64_fetchadd(1, &last.count, acq) & 15) < 5) {
				last.time = current_jiffies + 5 * HZ;
				printk(KERN_WARNING
		       			"%s(%d): floating-point assist fault at ip %016lx, isr %016lx\n",
		       			current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri, isr);
			}
		}
	}

	exception = fp_emulate(fp_fault, bundle, &regs->cr_ipsr, &regs->ar_fpsr, &isr, &regs->pr,
			       &regs->cr_ifs, regs);