diff -ur /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_file.c linux-2.6.15-rc5-git3/perfmon/perfmon_file.c --- /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_file.c 2005-12-15 15:10:36.000000000 -0800 +++ linux-2.6.15-rc5-git3/perfmon/perfmon_file.c 2005-12-15 15:06:54.000000000 -0800 @@ -247,7 +247,7 @@ loff_t *ppos) { struct pfm_context *ctx; - pfm_msg_t *msg; + pfm_msg_t *msg, msg_buf; ssize_t ret; unsigned long flags; DECLARE_WAITQUEUE(wait, current); @@ -274,6 +274,10 @@ if (size > sizeof(pfm_msg_t)) size = sizeof(pfm_msg_t); + /* + * we must masks interrupts to avoid a race condition + * with the PMU interrupt handler. + */ spin_lock_irqsave(&ctx->ctx_lock, flags); if(PFM_CTXQ_EMPTY(ctx) == 0) @@ -348,17 +352,27 @@ if (unlikely(msg == NULL)) goto retry; - DPRINT(("type=%d size=%zu\n", msg->type, size)); + /* + * we must make a local copy before we unlock + * to ensure that the message queue cannot fill + * (overwriting our message) up before + * we do copy_to_user() which cannot be done + * with interrupts masked. + */ + msg_buf = *msg; + DPRINT(("type=%d size=%zu\n", msg->type, size)); /* * message can be truncated when size < sizeof(pfm_msg_t) * The leftover is systematically lost */ - ret = -EFAULT; - if(copy_to_user(buf, msg, size) == 0) ret = size; abort_locked: spin_unlock_irqrestore(&ctx->ctx_lock, flags); + + ret = -EFAULT; + if(copy_to_user(buf, &msg_buf, size) == 0) + ret = size; abort: return ret; } diff -ur /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_fmt.c linux-2.6.15-rc5-git3/perfmon/perfmon_fmt.c --- /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_fmt.c 2005-12-15 15:10:36.000000000 -0800 +++ linux-2.6.15-rc5-git3/perfmon/perfmon_fmt.c 2005-12-15 15:04:38.000000000 -0800 @@ -67,7 +67,7 @@ if (fmt == NULL) return; spin_lock(&pfm_smpl_fmt_lock); - module_put(fmt->owner); + if (fmt->owner) module_put(fmt->owner); spin_unlock(&pfm_smpl_fmt_lock); } diff -ur /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_pmu.c linux-2.6.15-rc5-git3/perfmon/perfmon_pmu.c --- /tmp/linux-2.6.15-rc5-git3.orig/perfmon/perfmon_pmu.c 2005-12-15 15:10:36.000000000 -0800 +++ linux-2.6.15-rc5-git3/perfmon/perfmon_pmu.c 2005-12-15 15:06:34.000000000 -0800 @@ -286,7 +286,7 @@ if (pfm_pmu_conf == NULL) return; spin_lock(&pfm_pmu_conf_lock); - module_put(pfm_pmu_conf->owner); + if (pfm_pmu_conf->owner) module_put(pfm_pmu_conf->owner); spin_unlock(&pfm_pmu_conf_lock); }