diff options
author | Richard Hughes <richard@hughsie.com> | 2008-11-13 16:58:08 +0000 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2008-11-13 16:58:08 +0000 |
commit | a48ce21f83c16d964450763f26592cf6e570650e (patch) | |
tree | bbbfd04653c1eb1c433aa6b7912943ec0f602a5d /src | |
parent | b71c61335a2c5af679feab0d681826506b81180c (diff) |
feature: implement the .QoS interface -- still incomplete
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/dkp-marshal.list | 2 | ||||
-rw-r--r-- | src/dkp-qos.c | 525 | ||||
-rw-r--r-- | src/dkp-qos.h | 78 |
4 files changed, 606 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d8f57a2..56028fc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,7 @@ devkit_power_daemon_SOURCES = \ dkp-csr.h dkp-csr.c \ dkp-wup.h dkp-wup.c \ dkp-hid.h dkp-hid.c \ + dkp-qos.h dkp-qos.c \ dkp-history.h dkp-history.c \ sysfs-utils.h sysfs-utils.c \ main.c \ diff --git a/src/dkp-marshal.list b/src/dkp-marshal.list index 98218d7..deb5c1d 100644 --- a/src/dkp-marshal.list +++ b/src/dkp-marshal.list @@ -1,2 +1,4 @@ VOID:BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE VOID:STRING,BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE +VOID:STRING,INT + diff --git a/src/dkp-qos.c b/src/dkp-qos.c new file mode 100644 index 0000000..9dae996 --- /dev/null +++ b/src/dkp-qos.c @@ -0,0 +1,525 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <glib/gi18n.h> +#include <string.h> + +#include "egg-debug.h" +#include "egg-string.h" + +#include "dkp-qos.h" +#include "dkp-marshal.h" +#include "dkp-daemon.h" +#include "dkp-polkit.h" + +static void dkp_qos_class_init (DkpQosClass *klass); +static void dkp_qos_init (DkpQos *qos); +static void dkp_qos_finalize (GObject *object); + +#define DKP_QOS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DKP_TYPE_QOS, DkpQosPrivate)) + +typedef enum { + DKP_QOS_TYPE_NETWORK, + DKP_QOS_TYPE_CPU_DMA, + DKP_QOS_TYPE_UNKNOWN +} DkpQosType; + +typedef struct +{ + gint value; + guint uid; + guint pid; + gchar *sender; + guint32 cookie; + gboolean persistent; + DkpQosType type; +} DkpQosRequestObj; + +struct DkpQosPrivate +{ +// gint last_network; +// gint last_cpu_dma; +// GPtrArray *data_network; +// GPtrArray *data_cpu_dma; + GPtrArray *data; + gint last[DKP_QOS_TYPE_UNKNOWN]; + DkpPolkit *polkit; + DBusGConnection *connection; + DBusGProxy *proxy; +}; + +enum { + LATENCY_CHANGED, + REQUESTS_CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (DkpQos, dkp_qos, G_TYPE_OBJECT) + +/** + * dkp_qos_type_to_text: + **/ +static const gchar * +dkp_qos_type_to_text (DkpQosType type) +{ + if (type == DKP_QOS_TYPE_NETWORK) + return "network"; + if (type == DKP_QOS_TYPE_CPU_DMA) + return "cpu_dma"; + return NULL; +} + +/** + * dkp_qos_type_from_text: + **/ +static DkpQosType +dkp_qos_type_from_text (const gchar *type) +{ + if (egg_strequal (type, "network")) + return DKP_QOS_TYPE_NETWORK; + if (egg_strequal (type, "cpu_dma")) + return DKP_QOS_TYPE_CPU_DMA; + return DKP_QOS_TYPE_UNKNOWN; +} + +/** + * dkp_qos_find_from_cookie: + **/ +static DkpQosRequestObj * +dkp_qos_find_from_cookie (DkpQos *qos, guint32 cookie) +{ + guint i; + GPtrArray *data; + DkpQosRequestObj *obj; + + /* search list */ + data = qos->priv->data; + for (i=0; i<data->len; i++) { + obj = g_ptr_array_index (data, i); + if (obj->cookie == cookie) + return obj; + } + + /* nothing found */ + return NULL; +} + +/** + * dkp_qos_generate_cookie: + * + * Return value: a random cookie not already allocated + **/ +static guint32 +dkp_qos_generate_cookie (DkpQos *qos) +{ + guint32 cookie; + + /* iterate until we have a unique cookie */ + do { + cookie = (guint32) g_random_int_range (1, G_MAXINT32); + } while (dkp_qos_find_from_cookie (qos, cookie) != NULL); + + return cookie; +} + +/** + * dkp_qos_get_lowest: + **/ +static gint +dkp_qos_get_lowest (DkpQos *qos, DkpQosType type) +{ + guint i; + gint lowest = G_MAXINT; + GPtrArray *data; + DkpQosRequestObj *obj; + + /* find lowest */ + data = qos->priv->data; + for (i=0; i<data->len; i++) { + obj = g_ptr_array_index (data, i); + if (obj->type == type && obj->value > 0 && obj->value < lowest) + lowest = obj->value; + } + + /* no requests */ + if (lowest == G_MAXINT) + lowest = -1; + + return lowest; +} + +/** + * dkp_qos_latency_perhaps_changed: + **/ +static gboolean +dkp_qos_latency_perhaps_changed (DkpQos *qos, DkpQosType type) +{ + gint lowest; + gint *last; + + /* re-find the lowest value */ + lowest = dkp_qos_get_lowest (qos, type); + + /* find the last value */ + last = &qos->priv->last[type]; + + /* same value? */ + if (*last == lowest) + return FALSE; + + /* emit signal */ + g_signal_emit (qos, signals [LATENCY_CHANGED], 0, dkp_qos_type_to_text (type), lowest); + *last = lowest; + return TRUE; +} + +/** + * dkp_qos_request_latency: + * + * Return value: a new random cookie + **/ +void +dkp_qos_request_latency (DkpQos *qos, const gchar *type_text, gint value, gboolean persistent, DBusGMethodInvocation *context) +{ + DkpQosRequestObj *obj; + gchar *sender = NULL; + const gchar *auth; + GError *error; + guint uid; + gint pid; + PolKitCaller *caller = NULL; + polkit_bool_t retval; + DkpQosType type; + + /* get correct data */ + type = dkp_qos_type_from_text (type_text); + if (type == DKP_QOS_TYPE_UNKNOWN) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "type invalid: %s", type_text); + dbus_g_method_return_error (context, error); + goto out; + } + + /* as we are async, we can get the sender */ + sender = dbus_g_method_get_sender (context); + if (sender == NULL) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "no DBUS sender"); + dbus_g_method_return_error (context, error); + goto out; + } + + /* get the caller */ + caller = dkp_polkit_get_caller (qos->priv->polkit, context); + if (caller == NULL) + goto out; + + /* check auth */ + if (persistent) + auth = "org.freedesktop.devicekit.power.latency.request-latency-persistent"; + else + auth = "org.freedesktop.devicekit.power.latency.request-latency"; + if (!dkp_polkit_check_auth (qos->priv->polkit, caller, auth, context)) + goto out; + + /* get uid */ + retval = polkit_caller_get_uid (caller, &uid); + if (!retval) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "cannot get UID"); + dbus_g_method_return_error (context, error); + goto out; + } + + /* get pid */ + retval = polkit_caller_get_pid (caller, &pid); + if (!retval) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "cannot get PID"); + dbus_g_method_return_error (context, error); + goto out; + } + + /* seems okay, add to list */ + obj = g_new (DkpQosRequestObj, 1); + obj->cookie = dkp_qos_generate_cookie (qos); + obj->sender = g_strdup (sender); + obj->value = value; + obj->uid = uid; + obj->uid = pid; + obj->persistent = persistent; + obj->type = type; + g_ptr_array_add (qos->priv->data, obj); + + egg_debug ("Recieved Qos from '%s' (%i:%i)' saving as #%i", + obj->sender, obj->value, obj->persistent, obj->cookie); + + /* TODO: if persistent add to datadase */ + + /* only emit event on the first one */ + dkp_qos_latency_perhaps_changed (qos, type); + dbus_g_method_return (context, obj->cookie); +out: + if (caller != NULL) + polkit_caller_unref (caller); + g_free (sender); +} + +/** + * dkp_qos_free_data_obj: + **/ +static void +dkp_qos_free_data_obj (DkpQosRequestObj *obj) +{ + g_free (obj->sender); + g_free (obj); +} + +/** + * dkp_qos_cancel_request: + * + * Removes a cookie and associated data from the DkpQosRequestObj struct. + **/ +void +dkp_qos_cancel_request (DkpQos *qos, guint cookie, DBusGMethodInvocation *context) +{ + DkpQosRequestObj *obj; + GError *error; + gchar *sender = NULL; + PolKitCaller *caller = NULL; + + /* find the correct cookie */ + obj = dkp_qos_find_from_cookie (qos, cookie); + if (obj == NULL) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, + "Cannot find request for #%i", cookie); + dbus_g_method_return_error (context, error); + goto out; + } + + /* get the sender? */ + sender = dbus_g_method_get_sender (context); + if (sender == NULL) { + error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "no DBUS sender"); + dbus_g_method_return_error (context, error); + goto out; + } + + /* are we not the sender? */ + if (!egg_strequal (sender, obj->sender)) { + caller = dkp_polkit_get_caller (qos->priv->polkit, context); + if (caller == NULL) + goto out; + if (!dkp_polkit_check_auth (qos->priv->polkit, caller, "org.freedesktop.devicekit.power.latency.cancel-request", context)) + goto out; + } + + egg_debug ("Clear #%i", cookie); + + /* remove object from list */ + g_ptr_array_remove (qos->priv->data, obj); + dkp_qos_latency_perhaps_changed (qos, obj->type); + + /* TODO: if persistent remove from datadase */ + + dkp_qos_free_data_obj (obj); + + g_signal_emit (qos, signals [REQUESTS_CHANGED], 0); +out: + if (caller != NULL) + polkit_caller_unref (caller); + g_free (sender); +} + +/** + * dkp_qos_get_latency: + * + * Gets the current latency + **/ +gboolean +dkp_qos_get_latency (DkpQos *qos, const gchar *type_text, gint *value, GError **error) +{ + DkpQosType type; + + /* get correct data */ + type = dkp_qos_type_from_text (type_text); + if (type == DKP_QOS_TYPE_UNKNOWN) { + *error = g_error_new (DKP_DAEMON_ERROR, DKP_DAEMON_ERROR_GENERAL, "type invalid: %s", type_text); + return FALSE; + } + + /* get the lowest value for this type */ + *value = dkp_qos_get_lowest (qos, type); + return TRUE; +} + +/** + * dkp_qos_set_minimum_latency: + **/ +void +dkp_qos_set_minimum_latency (DkpQos *qos, const gchar *type, gint value, DBusGMethodInvocation *context) +{ + egg_warning ("Not implimented"); + return; +} + +/** + * dkp_qos_get_latency_requests: + **/ +gboolean +dkp_qos_get_latency_requests (DkpQos *qos, GPtrArray **requests, GError **error) +{ + egg_warning ("Not implimented"); + return FALSE; +} + + +/** + * dkp_qos_remove_dbus: + **/ +static void +dkp_qos_remove_dbus (DkpQos *qos, const gchar *sender) +{ + guint i; + GPtrArray *data; + DkpQosRequestObj *obj; + + /* remove *any* senders that match the sender */ + data = qos->priv->data; + for (i=0; i<data->len; i++) { + obj = g_ptr_array_index (data, i); + if (strcmp (obj->sender, sender) == 0) { + egg_debug ("Auto-revoked idle qos on %s", sender); + g_ptr_array_remove (qos->priv->data, obj); + dkp_qos_latency_perhaps_changed (qos, obj->type); + dkp_qos_free_data_obj (obj); + } + } +} + +/** + * dkp_qos_name_owner_changed_cb: + **/ +static void +dkp_qos_name_owner_changed_cb (DBusGProxy *proxy, const gchar *name, const gchar *prev, const gchar *new, DkpQos *qos) +{ + if (strlen (new) == 0) + dkp_qos_remove_dbus (qos, name); +} + +/** + * dkp_qos_class_init: + **/ +static void +dkp_qos_class_init (DkpQosClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = dkp_qos_finalize; + + signals [LATENCY_CHANGED] = + g_signal_new ("latency-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (DkpQosClass, latency_changed), + NULL, NULL, dkp_marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); + signals [REQUESTS_CHANGED] = + g_signal_new ("requests-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (DkpQosClass, requests_changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_type_class_add_private (klass, sizeof (DkpQosPrivate)); +} + +/** + * dkp_qos_init: + **/ +static void +dkp_qos_init (DkpQos *qos) +{ + guint i; + GError *error = NULL; + + qos->priv = DKP_QOS_GET_PRIVATE (qos); + qos->priv->polkit = dkp_polkit_new (); + qos->priv->data = g_ptr_array_new (); + + /* TODO: need to load persistent values */ + + /* setup lowest */ + for (i=0; i<DKP_QOS_TYPE_UNKNOWN; i++) + qos->priv->last[i] = dkp_qos_get_lowest (qos, i); + + qos->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (error != NULL) { + egg_warning ("Cannot connect to bus: %s", error->message); + g_error_free (error); + return; + } + + /* register on the bus */ + dbus_g_connection_register_g_object (qos->priv->connection, "/org/freedesktop/DeviceKit/Power", G_OBJECT (qos)); + + /* watch NOC */ + qos->priv->proxy = dbus_g_proxy_new_for_name_owner (qos->priv->connection, DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, NULL); + dbus_g_proxy_add_signal (qos->priv->proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (qos->priv->proxy, "NameOwnerChanged", + G_CALLBACK (dkp_qos_name_owner_changed_cb), qos, NULL); +} + +/** + * dkp_qos_finalize: + **/ +static void +dkp_qos_finalize (GObject *object) +{ + DkpQos *qos; + + g_return_if_fail (object != NULL); + g_return_if_fail (DKP_IS_QOS (object)); + + qos = DKP_QOS (object); + qos->priv = DKP_QOS_GET_PRIVATE (qos); + + g_ptr_array_foreach (qos->priv->data, (GFunc) dkp_qos_free_data_obj, NULL); + g_ptr_array_free (qos->priv->data, TRUE); + g_object_unref (qos->priv->proxy); + g_object_unref (qos->priv->polkit); + + G_OBJECT_CLASS (dkp_qos_parent_class)->finalize (object); +} + +/** + * dkp_qos_new: + **/ +DkpQos * +dkp_qos_new (void) +{ + DkpQos *qos; + qos = g_object_new (DKP_TYPE_QOS, NULL); + return DKP_QOS (qos); +} + diff --git a/src/dkp-qos.h b/src/dkp-qos.h new file mode 100644 index 0000000..ae002a9 --- /dev/null +++ b/src/dkp-qos.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DKP_QOS_H +#define __DKP_QOS_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +#define DKP_TYPE_QOS (dkp_qos_get_type ()) +#define DKP_QOS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DKP_TYPE_QOS, DkpQos)) +#define DKP_QOS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), DKP_TYPE_QOS, DkpQosClass)) +#define DKP_IS_QOS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DKP_TYPE_QOS)) +#define DKP_IS_QOS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DKP_TYPE_QOS)) +#define DKP_QOS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DKP_TYPE_QOS, DkpQosClass)) + +typedef struct DkpQosPrivate DkpQosPrivate; + +typedef struct +{ + GObject parent; + DkpQosPrivate *priv; +} DkpQos; + +typedef struct +{ + GObjectClass parent_class; + void (* latency_changed) (DkpQos *qos, + const gchar *type, + gint value); + void (* requests_changed) (DkpQos *qos); +} DkpQosClass; + +DkpQos *dkp_qos_new (void); +GType dkp_qos_get_type (void); +void dkp_qos_request_latency (DkpQos *qos, + const gchar *type, + gint value, + gboolean persistent, + DBusGMethodInvocation *context); +void dkp_qos_cancel_request (DkpQos *qos, + guint32 cookie, + DBusGMethodInvocation *context); +void dkp_qos_set_minimum_latency (DkpQos *qos, + const gchar *type, + gint value, + DBusGMethodInvocation *context); +gboolean dkp_qos_get_latency (DkpQos *qos, + const gchar *type, + gint *value, + GError **error); +gboolean dkp_qos_get_latency_requests (DkpQos *qos, + GPtrArray **requests, + GError **error); + +G_END_DECLS + +#endif /* __DKP_QOS_H */ |