コード例 #1
0
/*
 * Called from lwp_exit() and thread_exit()
 */
void
kcpc_passivate(void)
{
	kcpc_ctx_t *ctx = curthread->t_cpc_ctx;
	kcpc_set_t *set = curthread->t_cpc_set;

	if (set == NULL)
		return;

	/*
	 * We're cleaning up after this thread; ensure there are no dangling
	 * CPC pointers left behind. The context and set will be freed by
	 * freectx() in the case of an LWP-bound set, and by kcpc_unbind() in
	 * the case of a CPU-bound set.
	 */
	curthread->t_cpc_ctx = NULL;

	if (ctx == NULL) {
		/*
		 * This thread has a set but no context; it must be a CPU-bound
		 * set. The hardware will be stopped via kcpc_unbind() when the
		 * process exits and closes its file descriptors with
		 * kcpc_close(). Our only job here is to clean up this thread's
		 * state; the set will be freed with the unbind().
		 */
		(void) kcpc_unbind(set);
		/*
		 * Unbinding a set belonging to the current thread should clear
		 * its set pointer.
		 */
		ASSERT(curthread->t_cpc_set == NULL);
		return;
	}

	curthread->t_cpc_set = NULL;

	/*
	 * This thread/LWP is exiting but context switches will continue to
	 * happen for a bit as the exit proceeds.  Kernel preemption must be
	 * disabled here to prevent a race between checking or setting the
	 * INVALID_STOPPED flag here and kcpc_restore() setting the flag during
	 * a context switch.
	 */

	kpreempt_disable();
	if ((ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0) {
		pcbe_ops->pcbe_allstop();
		atomic_or_uint(&ctx->kc_flags,
		    KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED);
	}
	kpreempt_enable();
}
コード例 #2
0
ファイル: cpc.c プロジェクト: apprisi/illumos-gate
/*
 * System call to access CPU performance counters.
 */
static int
cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3)
{
	kthread_t	*t;
	int		error;
	int		size;
	const char	*str;
	int		code;

	/*
	 * This CPC syscall should only be loaded if it found a PCBE to use.
	 */
	ASSERT(pcbe_ops != NULL);

	if (curproc->p_agenttp == curthread) {
		/*
		 * Only if /proc is invoking this system call from
		 * the agent thread do we allow the caller to examine
		 * the contexts of other lwps in the process.  And
		 * because we know we're the agent, we know we don't
		 * have to grab p_lock because no-one else can change
		 * the state of the process.
		 */
		if ((t = idtot(curproc, lwpid)) == NULL || t == curthread)
			return (set_errno(ESRCH));
		ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL);
	} else
		t = curthread;

	if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE))
		return (set_errno(EINVAL));

	switch (cmd) {
	case CPC_BIND:
		/*
		 * udata1 = pointer to packed nvlist buffer
		 * udata2 = size of packed nvlist buffer
		 * udata3 = User addr to return error subcode in.
		 */

		rw_enter(&kcpc_cpuctx_lock, RW_READER);
		if (kcpc_cpuctx || dtrace_cpc_in_use) {
			rw_exit(&kcpc_cpuctx_lock);
			return (set_errno(EAGAIN));
		}

		if (kcpc_hw_lwp_hook() != 0) {
			rw_exit(&kcpc_cpuctx_lock);
			return (set_errno(EACCES));
		}

		/*
		 * An LWP may only have one set bound to it at a time; if there
		 * is a set bound to this LWP already, we unbind it here.
		 */
		if (t->t_cpc_set != NULL)
			(void) kcpc_unbind(t->t_cpc_set);
		ASSERT(t->t_cpc_set == NULL);

		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
		    (size_t)udata2)) != 0) {
			rw_exit(&kcpc_cpuctx_lock);
			return (set_errno(error));
		}

		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
			rw_exit(&kcpc_cpuctx_lock);
			kcpc_free_set(t->t_cpc_set);
			t->t_cpc_set = NULL;
			if (copyout(&error, udata3, sizeof (error)) == -1)
				return (set_errno(EFAULT));
			return (set_errno(EINVAL));
		}

		if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) {
			rw_exit(&kcpc_cpuctx_lock);
			kcpc_free_set(t->t_cpc_set);
			t->t_cpc_set = NULL;
			/*
			 * EINVAL and EACCES are the only errors with more
			 * specific subcodes.
			 */
			if ((error == EINVAL || error == EACCES) &&
			    copyout(&code, udata3, sizeof (code)) == -1)
				return (set_errno(EFAULT));
			return (set_errno(error));
		}

		rw_exit(&kcpc_cpuctx_lock);
		return (0);
	case CPC_SAMPLE:
		/*
		 * udata1 = pointer to user's buffer
		 * udata2 = pointer to user's hrtime
		 * udata3 = pointer to user's tick
		 */
		/*
		 * We only allow thread-bound sets to be sampled via the
		 * syscall, so if this set has a CPU-bound context, return an
		 * error.
		 */
		if (t->t_cpc_set->ks_ctx->kc_cpuid != -1)
			return (set_errno(EINVAL));
		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
		    udata3)) != 0)
			return (set_errno(error));

		return (0);
	case CPC_PRESET:
	case CPC_RESTART:
		/*
		 * These are valid only if this lwp has a bound set.
		 */
		if (t->t_cpc_set == NULL)
			return (set_errno(EINVAL));
		if (cmd == CPC_PRESET) {
			/*
			 * The preset is shipped up to us from userland in two
			 * parts. This lets us handle 64-bit values from 32-bit
			 * and 64-bit applications in the same manner.
			 *
			 * udata1 = index of request to preset
			 * udata2 = new 64-bit preset (most sig. 32 bits)
			 * udata3 = new 64-bit preset (least sig. 32 bits)
			 */
			if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1,
			    ((uint64_t)(uintptr_t)udata2 << 32ULL) |
			    (uint64_t)(uintptr_t)udata3)) != 0)
				return (set_errno(error));
		} else {
			/*
			 * udata[1-3] = unused
			 */
			if ((error = kcpc_restart(t->t_cpc_set)) != 0)
				return (set_errno(error));
		}
		return (0);
	case CPC_ENABLE:
	case CPC_DISABLE:
		udata1 = 0;
		/*FALLTHROUGH*/
	case CPC_USR_EVENTS:
	case CPC_SYS_EVENTS:
		if (t != curthread || t->t_cpc_set == NULL)
			return (set_errno(EINVAL));
		/*
		 * Provided for backwards compatibility with CPCv1.
		 *
		 * Stop the counters and record the current counts. Use the
		 * counts as the preset to rebind a new set with the requests
		 * reconfigured as requested.
		 *
		 * udata1: 1 == enable; 0 == disable
		 * udata{2,3}: unused
		 */
		rw_enter(&kcpc_cpuctx_lock, RW_READER);
		if ((error = kcpc_enable(t,
		    cmd, (int)(uintptr_t)udata1)) != 0) {
			rw_exit(&kcpc_cpuctx_lock);
			return (set_errno(error));
		}
		rw_exit(&kcpc_cpuctx_lock);
		return (0);
	case CPC_NPIC:
		return (cpc_ncounters);
	case CPC_CAPS:
		return (pcbe_ops->pcbe_caps);
	case CPC_EVLIST_SIZE:
	case CPC_LIST_EVENTS:
		/*
		 * udata1 = pointer to user's int or buffer
		 * udata2 = picnum
		 * udata3 = unused
		 */
		if ((uintptr_t)udata2 >= cpc_ncounters)
			return (set_errno(EINVAL));

		size = strlen(
		    pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1;

		if (cmd == CPC_EVLIST_SIZE) {
			if (suword32(udata1, size) == -1)
				return (set_errno(EFAULT));
		} else {
			if (copyout(
			    pcbe_ops->pcbe_list_events((uintptr_t)udata2),
			    udata1, size) == -1)
				return (set_errno(EFAULT));
		}
		return (0);
	case CPC_ATTRLIST_SIZE:
	case CPC_LIST_ATTRS:
		/*
		 * udata1 = pointer to user's int or buffer
		 * udata2 = unused
		 * udata3 = unused
		 *
		 * attrlist size is length of PCBE-supported attributes, plus
		 * room for "picnum\0" plus an optional ',' separator char.
		 */
		str = pcbe_ops->pcbe_list_attrs();
		size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1;
		if (str[0] != '\0')
			/*
			 * A ',' separator character is necessary.
			 */
			size += 1;

		if (cmd == CPC_ATTRLIST_SIZE) {
			if (suword32(udata1, size) == -1)
				return (set_errno(EFAULT));
		} else {
			/*
			 * Copyout the PCBE attributes, and then append the
			 * generic attribute list (with separator if necessary).
			 */
			if (copyout(str, udata1, strlen(str)) == -1)
				return (set_errno(EFAULT));
			if (str[0] != '\0') {
				if (copyout(SEPARATOR ATTRLIST,
				    ((char *)udata1) + strlen(str),
				    strlen(SEPARATOR ATTRLIST) + 1)
				    == -1)
					return (set_errno(EFAULT));
			} else
				if (copyout(ATTRLIST,
				    (char *)udata1 + strlen(str),
				    strlen(ATTRLIST) + 1) == -1)
					return (set_errno(EFAULT));
		}
		return (0);
	case CPC_IMPL_NAME:
	case CPC_CPUREF:
		/*
		 * udata1 = pointer to user's buffer
		 * udata2 = unused
		 * udata3 = unused
		 */
		if (cmd == CPC_IMPL_NAME) {
			str = pcbe_ops->pcbe_impl_name();
			ASSERT(strlen(str) < CPC_MAX_IMPL_NAME);
		} else {
			str = pcbe_ops->pcbe_cpuref();
			ASSERT(strlen(str) < CPC_MAX_CPUREF);
		}

		if (copyout(str, udata1, strlen(str) + 1) != 0)
			return (set_errno(EFAULT));
		return (0);
	case CPC_INVALIDATE:
		kcpc_invalidate(t);
		return (0);
	case CPC_RELE:
		if ((error = kcpc_unbind(t->t_cpc_set)) != 0)
			return (set_errno(error));
		return (0);
	default:
		return (set_errno(EINVAL));
	}
}
コード例 #3
0
ファイル: cpc.c プロジェクト: apprisi/illumos-gate
/*ARGSUSED*/
static int
kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp)
{
	kthread_t	*t = curthread;
	processorid_t	cpuid;
	void		*udata1 = NULL;
	void		*udata2 = NULL;
	void		*udata3 = NULL;
	int		error;
	int		code;

	STRUCT_DECL(__cpc_args, args);

	STRUCT_INIT(args, flags);

	if (curthread->t_bind_cpu != getminor(dev))
		return (EAGAIN);  /* someone unbound it? */

	cpuid = getminor(dev);

	if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) {
		if (copyin((void *)data, STRUCT_BUF(args),
		    STRUCT_SIZE(args)) == -1)
			return (EFAULT);

		udata1 = STRUCT_FGETP(args, udata1);
		udata2 = STRUCT_FGETP(args, udata2);
		udata3 = STRUCT_FGETP(args, udata3);
	}

	switch (cmd) {
	case CPCIO_BIND:
		/*
		 * udata1 = pointer to packed nvlist buffer
		 * udata2 = size of packed nvlist buffer
		 * udata3 = User addr to return error subcode in.
		 */
		if (t->t_cpc_set != NULL) {
			(void) kcpc_unbind(t->t_cpc_set);
			ASSERT(t->t_cpc_set == NULL);
		}

		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
		    (size_t)udata2)) != 0) {
			return (error);
		}

		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
			kcpc_free_set(t->t_cpc_set);
			t->t_cpc_set = NULL;
			if (copyout(&error, udata3, sizeof (error)) == -1)
				return (EFAULT);
			return (EINVAL);
		}

		if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) {
			kcpc_free_set(t->t_cpc_set);
			t->t_cpc_set = NULL;
			/*
			 * Subcodes are only returned for EINVAL and EACCESS.
			 */
			if ((error == EINVAL || error == EACCES) &&
			    copyout(&code, udata3, sizeof (code)) == -1)
				return (EFAULT);
			return (error);
		}

		return (0);
	case CPCIO_SAMPLE:
		/*
		 * udata1 = pointer to user's buffer
		 * udata2 = pointer to user's hrtime
		 * udata3 = pointer to user's tick
		 */
		/*
		 * Only CPU-bound sets may be sampled via the ioctl(). If this
		 * set has no CPU-bound context, return an error.
		 */
		if (t->t_cpc_set == NULL)
			return (EINVAL);
		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
		    udata3)) != 0)
			return (error);
		return (0);
	case CPCIO_RELE:
		if (t->t_cpc_set == NULL)
			return (EINVAL);
		return (kcpc_unbind(t->t_cpc_set));
	default:
		return (EINVAL);
	}
}
コード例 #4
0
/*
 * Caller must hold kcpc_cpuctx_lock.
 */
int
kcpc_enable(kthread_t *t, int cmd, int enable)
{
	kcpc_ctx_t	*ctx = t->t_cpc_ctx;
	kcpc_set_t	*set = t->t_cpc_set;
	kcpc_set_t	*newset;
	int		i;
	int		flag;
	int		err;

	ASSERT(RW_READ_HELD(&kcpc_cpuctx_lock));

	if (ctx == NULL) {
		/*
		 * This thread has a set but no context; it must be a
		 * CPU-bound set.
		 */
		ASSERT(t->t_cpc_set != NULL);
		ASSERT(t->t_cpc_set->ks_ctx->kc_cpuid != -1);
		return (EINVAL);
	} else if (ctx->kc_flags & KCPC_CTX_INVALID)
		return (EAGAIN);

	if (cmd == CPC_ENABLE) {
		if ((ctx->kc_flags & KCPC_CTX_FREEZE) == 0)
			return (EINVAL);
		kpreempt_disable();
		atomic_and_uint(&ctx->kc_flags, ~KCPC_CTX_FREEZE);
		kcpc_restore(ctx);
		kpreempt_enable();
	} else if (cmd == CPC_DISABLE) {
		if (ctx->kc_flags & KCPC_CTX_FREEZE)
			return (EINVAL);
		kpreempt_disable();
		kcpc_save(ctx);
		atomic_or_uint(&ctx->kc_flags, KCPC_CTX_FREEZE);
		kpreempt_enable();
	} else if (cmd == CPC_USR_EVENTS || cmd == CPC_SYS_EVENTS) {
		/*
		 * Strategy for usr/sys: stop counters and update set's presets
		 * with current counter values, unbind, update requests with
		 * new config, then re-bind.
		 */
		flag = (cmd == CPC_USR_EVENTS) ?
		    CPC_COUNT_USER: CPC_COUNT_SYSTEM;

		kpreempt_disable();
		atomic_or_uint(&ctx->kc_flags,
		    KCPC_CTX_INVALID | KCPC_CTX_INVALID_STOPPED);
		pcbe_ops->pcbe_allstop();
		kpreempt_enable();
		for (i = 0; i < set->ks_nreqs; i++) {
			set->ks_req[i].kr_preset = *(set->ks_req[i].kr_data);
			if (enable)
				set->ks_req[i].kr_flags |= flag;
			else
				set->ks_req[i].kr_flags &= ~flag;
		}
		newset = kcpc_dup_set(set);
		if (kcpc_unbind(set) != 0)
			return (EINVAL);
		t->t_cpc_set = newset;
		if (kcpc_bind_thread(newset, t, &err) != 0) {
			t->t_cpc_set = NULL;
			kcpc_free_set(newset);
			return (EINVAL);
		}
	} else
		return (EINVAL);

	return (0);
}