tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: A simple cpufreq(9)



On Sat, Sep 24, 2011 at 07:53:47PM +0200, Joerg Sonnenberger wrote:
> I was listening possible decision making factors. Depending on your
> environment, you have all or none of them. The main point is that good
> decision making needs more than just "You can toogle this".

So here is a quick draft for the first iteration with the cpuctl(8). If there
are issues, speak now, otherwise I'll proceed with something based on this.

- Jukka.
/*      $NetBSD$ */

/*-
 * Copyright (c) 2011 Jukka Ruohonen <jruohonen%iki.fi@localhost>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: subr_cpufreq.c,v 1.15 2011/09/02 22:25:08 Exp $");

#include <sys/param.h>
#include <sys/cpu.h>
#include <sys/cpufreq.h>
#include <sys/kmem.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/time.h>
#include <sys/xcall.h>

static struct cpufreq_if *cpufreq_if = NULL;
static int                cpufreq_latency(void);

int
cpufreq_register(struct cpufreq_if *cif)
{
        size_t i, j;
        int rv;

        KASSERT(cif != NULL);
        KASSERT(cif->get_freq != NULL);
        KASSERT(cif->set_freq != NULL);
        KASSERT(cif->state_count > 0);
        KASSERT(cif->state_count < CPUFREQ_STATE_MAX);

        mutex_enter(&cpu_lock);

        if (cpufreq_if != NULL) {
                mutex_exit(&cpu_lock);
                return EALREADY;
        }

        mutex_exit(&cpu_lock);
        cpufreq_if = kmem_zalloc(sizeof(*cif), KM_SLEEP);

        if (cpufreq_if == NULL)
                return ENOMEM;

        mutex_enter(&cpu_lock);

        cpufreq_if->cookie = cif->cookie;
        cpufreq_if->get_freq = cif->get_freq;
        cpufreq_if->set_freq = cif->set_freq;

        for (i = j = 0; i < cif->state_count; i++) {

                if (cif->state[i].freq == 0)
                        continue;
                else {
                        j++;
                }

                cpufreq_if->state[i].freq = cif->state[i].freq;
                cpufreq_if->state[i].power = cif->state[i].power;
        }

        cpufreq_if->state_count = j;
        rv = cpufreq_latency();
        mutex_exit(&cpu_lock);

        return rv;
}

void
cpufreq_unregister(struct cpufreq_if *cif)
{

        mutex_enter(&cpu_lock);

        if (cpufreq_if == NULL) {
                mutex_exit(&cpu_lock);
                return;
        }

        mutex_exit(&cpu_lock);
        kmem_free(cpufreq_if, sizeof(*cif));
}

static int
cpufreq_latency(void)
{
        struct cpufreq_state *state;
        struct timespec nta, ntb;
        const size_t n = 10;
        uint64_t sample;
        size_t i, j;

        /*
         * Sample the transition latency for each state.
         */
        for (i = 0; i < cpufreq_if->state_count; i++) {

                state = &cpufreq_if->state[i];
                KASSERT(state->freq > 0 && state->freq < 9999);

                for (j = 0, sample = 0; j < n; j++) {

                        nta.tv_sec = nta.tv_nsec = 0;
                        ntb.tv_sec = ntb.tv_nsec = 0;

                        nanotime(&nta);

                        mutex_exit(&cpu_lock);
                        cpufreq_set(curcpu(), state[i].freq);
                        mutex_enter(&cpu_lock);

                        nanotime(&ntb);
                        timespecsub(&ntb, &nta, &ntb);

                        /*
                         * If the transition latency is measured
                         * in seconds, the backend is not suitable.
                         */
                        if (ntb.tv_sec != 0)
                                continue;

                        sample += ntb.tv_nsec;
                }

                if (sample == 0)
                        return EMSGSIZE;

                state->latency = sample / (uint64_t)n;
        }

        return 0;
}

void
cpufreq_get(struct cpu_info *ci, uint32_t freq)
{
        struct cpufreq_if *cif = cpufreq_if;
        uint64_t xc;

        mutex_enter(&cpu_lock);
        xc = xc_unicast(0, (*cif->get_freq), cif->cookie, &freq, ci);
        xc_wait(xc);
        mutex_exit(&cpu_lock);
}

void
cpufreq_set(struct cpu_info *ci, uint32_t freq)
{
        struct cpufreq_if *cif = cpufreq_if;
        uint64_t xc;

        mutex_enter(&cpu_lock);
        xc = xc_unicast(0, (*cif->set_freq), cif->cookie, &freq, ci);
        xc_wait(xc);
        mutex_exit(&cpu_lock);
}

MODULE(MODULE_CLASS_DRIVER, cpufreq, NULL);

static int
cpufreq_modcmd(modcmd_t cmd, void *aux)
{

        if (cmd != MODULE_CMD_INIT && cmd != MODULE_CMD_FINI)
                return ENOTTY;

        return 0;
}
/*      $NetBSD$ */

/*-
 * Copyright (c) 2011 Jukka Ruohonen <jruohonen%iki.fi@localhost>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SYS_CPUFREQ_H_
#define _SYS_CPUFREQ_H_

#ifndef _SYS_XCALL_H_
#include <sys/xcall.h>
#endif

#define CPUFREQ_STATE_MAX       255

struct cpufreq_state {
        uint32_t                 freq;          /* MHz  */
        uint32_t                 power;         /* wW   */
        uint64_t                 latency;       /* nsec */
};

struct cpufreq_if {
        struct cpufreq_state     state[CPUFREQ_STATE_MAX];
        uint16_t                 state_count;

#ifdef _KERNEL
        void                    *cookie;
        xcfunc_t                 get_freq;
        xcfunc_t                 set_freq;
#endif  /* _KERNEL */
};

int     cpufreq_register(struct cpufreq_if *);
void    cpufreq_unregister(struct cpufreq_if *);
void    cpufreq_get(struct cpu_info *, uint32_t);
void    cpufreq_set(struct cpu_info *, uint32_t);

#endif /* _SYS_CPUFREQ_H_ */


Home | Main Index | Thread Index | Old Index