Module: xenomai-forge
Branch: master
Commit: 3dbaac04c94363c841b537099463918a682f550e
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=3dbaac04c94363c841b537099463918a682f550e

Author: Philippe Gerum <r...@xenomai.org>
Date:   Sun Dec 22 17:06:43 2013 +0100

cobalt/posix: introduce sched_getconfig syscall

This service retrieves the current scheduling parameters for a
particular policy on a given CPU (converse to sched_setconfig).

---

 include/cobalt/kernel/sched-quota.h |    2 +
 include/cobalt/kernel/sched-tp.h    |    8 +-
 kernel/cobalt/posix/syscall.c       |    1 +
 kernel/cobalt/posix/thread.c        |  195 ++++++++++++++++++++++++++++++++---
 kernel/cobalt/posix/thread.h        |    7 +-
 kernel/cobalt/sched-quota.c         |    6 ++
 kernel/cobalt/sched-tp.c            |   23 +++++
 7 files changed, 227 insertions(+), 15 deletions(-)

diff --git a/include/cobalt/kernel/sched-quota.h 
b/include/cobalt/kernel/sched-quota.h
index c3b44ba..d5e4bdd 100644
--- a/include/cobalt/kernel/sched-quota.h
+++ b/include/cobalt/kernel/sched-quota.h
@@ -40,6 +40,8 @@ struct xnsched_quota_group {
        int nr_active;
        int nr_threads;
        int tgid;
+       int quota_percent;
+       int quota_peak_percent;
 };
 
 struct xnsched_quota {
diff --git a/include/cobalt/kernel/sched-tp.h b/include/cobalt/kernel/sched-tp.h
index 48da571..dad2f6d 100644
--- a/include/cobalt/kernel/sched-tp.h
+++ b/include/cobalt/kernel/sched-tp.h
@@ -36,6 +36,7 @@ struct xnsched_tp_window {
 struct xnsched_tp_schedule {
        int pwin_nr;
        xnticks_t tf_duration;
+       atomic_t refcount;
        struct xnsched_tp_window pwins[0];
 };
 
@@ -77,6 +78,11 @@ void xnsched_tp_stop_schedule(struct xnsched *sched);
 
 int xnsched_tp_get_partition(struct xnsched *sched);
 
-#endif /* !CONFIG_XENO_OPT_SCHED_TP */
+struct xnsched_tp_schedule *
+xnsched_tp_get_schedule(struct xnsched *sched);
+
+void xnsched_tp_put_schedule(struct xnsched_tp_schedule *gps);
+
+#endif /* CONFIG_XENO_OPT_SCHED_TP */
 
 #endif /* !_COBALT_KERNEL_SCHED_TP_H */
diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c
index e3a135d..374745f 100644
--- a/kernel/cobalt/posix/syscall.c
+++ b/kernel/cobalt/posix/syscall.c
@@ -169,6 +169,7 @@ static struct xnsyscall cobalt_syscalls[] = {
        SKINCALL_DEF(sc_cobalt_event_wait, cobalt_event_wait, primary),
        SKINCALL_DEF(sc_cobalt_event_sync, cobalt_event_sync, any),
        SKINCALL_DEF(sc_cobalt_sched_setconfig_np, cobalt_sched_setconfig_np, 
any),
+       SKINCALL_DEF(sc_cobalt_sched_getconfig_np, cobalt_sched_getconfig_np, 
any),
 };
 
 struct xnpersonality cobalt_personality = {
diff --git a/kernel/cobalt/posix/thread.c b/kernel/cobalt/posix/thread.c
index bd15952..89f9640 100644
--- a/kernel/cobalt/posix/thread.c
+++ b/kernel/cobalt/posix/thread.c
@@ -1288,6 +1288,11 @@ int set_tp_config(int cpu, union sched_config *config, 
size_t len)
        spl_t s;
        int n;
 
+       if (config->tp.nr_windows == 0) {
+               gps = NULL;
+               goto set_schedule;
+       }
+
        gps = xnmalloc(sizeof(*gps) + config->tp.nr_windows * sizeof(*w));
        if (gps == NULL)
                goto fail;
@@ -1316,17 +1321,18 @@ int set_tp_config(int cpu, union sched_config *config, 
size_t len)
                next_offset += duration;
        }
 
+       atomic_set(&gps->refcount, 1);
        gps->pwin_nr = n;
        gps->tf_duration = next_offset;
+set_schedule:
        sched = xnsched_struct(cpu);
-
        xnlock_get_irqsave(&nklock, s);
        ogps = xnsched_tp_set_schedule(sched, gps);
        xnsched_tp_start_schedule(sched);
        xnlock_put_irqrestore(&nklock, s);
 
        if (ogps)
-               xnfree(ogps);
+               xnsched_tp_put_schedule(ogps);
 
        return 0;
 
@@ -1336,10 +1342,68 @@ fail:
        return -EINVAL;
 }
 
+static inline
+ssize_t get_tp_config(int cpu, union sched_config __user *u_config,
+                     size_t len)
+{
+       struct xnsched_tp_window *pw, *w;
+       struct xnsched_tp_schedule *gps;
+       struct sched_tp_window *pp, *p;
+       union sched_config *config;
+       struct xnsched *sched;
+       ssize_t ret = 0, elen;
+       spl_t s;
+       int n;
+
+       xnlock_get_irqsave(&nklock, s);
+
+       sched = xnsched_struct(cpu);
+       gps = xnsched_tp_get_schedule(sched);
+       if (gps == NULL) {
+               xnlock_put_irqrestore(&nklock, s);
+               return 0;
+       }
+
+       xnlock_put_irqrestore(&nklock, s);
+
+       elen = sched_tp_confsz(gps->pwin_nr);
+       if (elen > len) {
+               ret = -ENOSPC;
+               goto out;
+       }
+
+       config = xnmalloc(elen);
+       if (config == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       config->tp.nr_windows = gps->pwin_nr;
+       for (n = 0, pp = p = config->tp.windows, pw = w = gps->pwins;
+            n < gps->pwin_nr; pp = p, p++, pw = w, w++, n++) {
+               ns2ts(&p->offset, w->w_offset);
+               ns2ts(&pp->duration, w->w_offset - pw->w_offset);
+               p->ptid = w->w_part;
+       }
+       ns2ts(&pp->duration, gps->tf_duration - pw->w_offset);
+       ret = __xn_safe_copy_to_user(u_config, config, elen);
+out:
+       xnsched_tp_put_schedule(gps);
+
+       return ret ?: elen;
+}
+
 #else /* !CONFIG_XENO_OPT_SCHED_TP */
 
-static inline
-int set_tp_config(int cpu, union sched_config *config, size_t len)
+static inline int
+set_tp_config(int cpu, union sched_config *config, size_t len)
+{
+       return -EINVAL;
+}
+
+static inline ssize_t
+get_tp_config(int cpu, union sched_config __user *u_config,
+             size_t len)
 {
        return -EINVAL;
 }
@@ -1349,14 +1413,17 @@ int set_tp_config(int cpu, union sched_config *config, 
size_t len)
 #ifdef CONFIG_XENO_OPT_SCHED_QUOTA
 
 static inline
-int set_quota_config(int cpu, union sched_config *config, size_t len)
+int set_quota_config(int cpu, const union sched_config *config, size_t len)
 {
-       struct __sched_config_quota *p = &config->quota;
+       const struct __sched_config_quota *p = &config->quota;
+       int ret = -ESRCH, quota_percent, quota_peak_percent;
        struct xnsched_quota_group *tg;
        struct xnsched *sched;
-       int ret = -ESRCH;
        spl_t s;
 
+       if (len < sizeof(*p))
+               return -EINVAL;
+
        if (p->op == sched_quota_add) {
                tg = xnmalloc(sizeof(*tg));
                if (tg == NULL)
@@ -1402,13 +1469,56 @@ int set_quota_config(int cpu, union sched_config 
*config, size_t len)
                return ret;
        }
 
+       if (p->op == sched_quota_get) {
+               xnlock_get_irqsave(&nklock, s);
+               sched = xnsched_struct(cpu);
+               tg = xnsched_quota_find_group(sched, p->get.tgid);
+               if (tg) {
+                       quota_percent = tg->quota_percent;
+                       quota_peak_percent = tg->quota_peak_percent;
+                       ret = 0;
+               }
+               xnlock_put_irqrestore(&nklock, s);
+               if (ret)
+                       return ret;
+               ret = __xn_safe_copy_to_user(p->get.quota_r, &quota_percent,
+                                            sizeof(quota_percent));
+               if (ret)
+                       return ret;
+               ret = __xn_safe_copy_to_user(p->get.quota_peak_r,
+                                            &quota_peak_percent,
+                                            sizeof(quota_peak_percent));
+               return ret;
+       }
+
        return -EINVAL;
 }
 
+static inline
+ssize_t get_quota_config(int cpu, union sched_config __user *u_config,
+                        size_t len)
+{
+       union sched_config buf;
+       
+       if (__xn_safe_copy_from_user(&buf, (const void __user *)u_config, len))
+               return -EFAULT;
+
+       buf.quota.op = sched_quota_get;
+
+       return set_quota_config(cpu, &buf, len);
+}
+
 #else /* !CONFIG_XENO_OPT_SCHED_QUOTA */
 
 static inline
-int set_quota_config(int cpu, union sched_config *config, size_t len)
+int set_quota_config(int cpu, const union sched_config *config, size_t len)
+{
+       return -EINVAL;
+}
+
+static inline
+ssize_t get_quota_config(int cpu, union sched_config __user *u_config,
+                        size_t len)
 {
        return -EINVAL;
 }
@@ -1425,7 +1535,7 @@ int set_quota_config(int cpu, union sched_config *config, 
size_t len)
  * @param policy scheduling policy to which the configuration data
  * applies. Currently, SCHED_TP and SCHED_QUOTA are valid.
  *
- * @param p a pointer to the configuration data to load for @a
+ * @param u_config a pointer to the configuration data to load on @a
  * cpu, applicable to @a policy.
  *
  * @par Settings applicable to SCHED_TP
@@ -1473,14 +1583,22 @@ int set_quota_config(int cpu, union sched_config 
*config, size_t len)
  *      percentage of the quota interval (config.quota.set.quota), and
  *      the peak percentage allowed (config.quota.set.quota_peak).
  *
+ *    - sched_quota_get for retrieving the scheduling parameters of a
+ *      thread group defined on @a cpu. The group identifier should be
+ *      passed in config.quota.get.tgid. The allotted percentage of
+ *      the quota interval (config.quota.get.quota_r), and the peak
+ *      percentage (config.quota.get.quota_peak_r) will be written to
+ *      the given output variables. The result of this operation is
+ *      identical to calling sched_getconfig_np().
+ *
  * @param len overall length of the configuration data (in bytes).
  *
  * @return 0 on success;
  * @return an error number if:
  *
  * - EINVAL, @a cpu is invalid, or @a policy is unsupported by the
- * current kernel configuration, @a len is zero, or @a p contains
- * invalid parameters.
+ * current kernel configuration, @a len is invalid, or @a u_config
+ * contains invalid parameters.
  *
  * - ENOMEM, lack of memory to perform the operation.
  *
@@ -1491,7 +1609,8 @@ int set_quota_config(int cpu, union sched_config *config, 
size_t len)
  *   identifier required to perform the operation is not valid.
  */
 int cobalt_sched_setconfig_np(int cpu, int policy,
-                             union sched_config __user *u_config, size_t len)
+                             const union sched_config __user *u_config,
+                             size_t len)
 {
        union sched_config *buf;
        int ret;
@@ -1506,7 +1625,7 @@ int cobalt_sched_setconfig_np(int cpu, int policy,
        if (buf == NULL)
                return -ENOMEM;
 
-       if (__xn_safe_copy_from_user(buf, (void __user *)u_config, len)) {
+       if (__xn_safe_copy_from_user(buf, (const void __user *)u_config, len)) {
                ret = -EFAULT;
                goto out;
        }
@@ -1527,4 +1646,54 @@ out:
        return ret;
 }
 
+/**
+ * Retrieve CPU-specific scheduler settings for a given policy.  A
+ * configuration is strictly local to the target @a cpu, and may
+ * differ from other processors.
+ *
+ * @param cpu processor to retrieve the configuration of.
+ *
+ * @param policy scheduling policy to which the configuration data
+ * applies. Currently, SCHED_TP and SCHED_QUOTA are valid.
+ *
+ * @param u_config a pointer to a memory area where the configuration
+ * data will be copied back. This area must be at least @a len bytes
+ * long.
+ *
+ * @param len overall length of the configuration data (in bytes).
+ *
+ * @return the number of bytes copied to @a u_config on success;
+ * @return a negative error number if:
+ *
+ * - EINVAL, @a cpu is invalid, or @a policy is unsupported by the
+ * current kernel configuration, or @a len cannot hold the retrieved
+ * configuration data.
+ *
+ * - ESRCH, with @a policy equal to SCHED_QUOTA, if the group
+ *   identifier required to perform the operation is not valid.
+ *
+ * - ENOMEM, lack of memory to perform the operation.
+ *
+ * - ENOSPC, @a len is too short.
+ */
+ssize_t cobalt_sched_getconfig_np(int cpu, int policy,
+                                 union sched_config __user *u_config,
+                                 size_t len)
+{
+       ssize_t ret;
+
+       switch (policy) {
+       case SCHED_TP:
+               ret = get_tp_config(cpu, u_config, len);
+               break;
+       case SCHED_QUOTA:
+               ret = get_quota_config(cpu, u_config, len);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 /*@}*/
diff --git a/kernel/cobalt/posix/thread.h b/kernel/cobalt/posix/thread.h
index 7ec9f2e..19aaf2f 100644
--- a/kernel/cobalt/posix/thread.h
+++ b/kernel/cobalt/posix/thread.h
@@ -176,9 +176,14 @@ int cobalt_sched_max_prio(int policy);
 
 int cobalt_sched_setconfig_np(int cpu,
                              int policy,
-                             union sched_config __user *u_config,
+                             const union sched_config __user *u_config,
                              size_t len);
 
+ssize_t cobalt_sched_getconfig_np(int cpu,
+                                 int policy,
+                                 union sched_config __user *u_config,
+                                 size_t len);
+
 struct xnpersonality *cobalt_thread_map(struct xnthread *curr);
 
 struct xnpersonality *cobalt_thread_exit(struct xnthread *curr);
diff --git a/kernel/cobalt/sched-quota.c b/kernel/cobalt/sched-quota.c
index 9e45196..efe5c2b 100644
--- a/kernel/cobalt/sched-quota.c
+++ b/kernel/cobalt/sched-quota.c
@@ -488,6 +488,8 @@ int xnsched_quota_create_group(struct xnsched_quota_group 
*tg,
        tg->sched = sched;
        tg->run_budget_ns = qs->period_ns;
        tg->run_credit_ns = 0;
+       tg->quota_percent = 100;
+       tg->quota_peak_percent = 100;
        tg->quota_ns = qs->period_ns;
        tg->quota_peak_ns = qs->period_ns;
        tg->nr_active = 0;
@@ -531,6 +533,8 @@ void xnsched_quota_set_limit(struct xnsched_quota_group *tg,
        atomic_only();
 
        if (quota_percent < 0) { /* Quota off. */
+               tg->quota_percent = 100;
+               tg->quota_peak_percent = 100;
                tg->quota_ns = qs->period_ns;
                tg->quota_peak_ns = qs->period_ns;
                tg->run_budget_ns = qs->period_ns;
@@ -544,6 +548,8 @@ void xnsched_quota_set_limit(struct xnsched_quota_group *tg,
        if (quota_peak_percent < quota_percent)
                quota_peak_percent = quota_percent;
 
+       tg->quota_percent = quota_percent;
+       tg->quota_peak_percent = quota_peak_percent;
        tg->quota_ns = xnarch_div64(qs->period_ns * quota_percent, 100);
        tg->quota_peak_ns = xnarch_div64(qs->period_ns * quota_peak_percent, 
100);
        tg->run_budget_ns = tg->quota_ns;
diff --git a/kernel/cobalt/sched-tp.c b/kernel/cobalt/sched-tp.c
index ce834df..6c024d4 100644
--- a/kernel/cobalt/sched-tp.c
+++ b/kernel/cobalt/sched-tp.c
@@ -19,6 +19,7 @@
  * 02111-1307, USA.
  */
 #include <cobalt/kernel/sched.h>
+#include <cobalt/kernel/heap.h>
 
 static void tp_schedule_next(struct xnsched_tp *tp)
 {
@@ -271,6 +272,28 @@ done:
 }
 EXPORT_SYMBOL_GPL(xnsched_tp_set_schedule);
 
+struct xnsched_tp_schedule *
+xnsched_tp_get_schedule(struct xnsched *sched)
+{
+       struct xnsched_tp_schedule *gps;
+
+       gps = sched->tp.gps;
+       if (gps == NULL)
+               return NULL;
+
+       atomic_inc(&gps->refcount);
+
+       return gps;
+}
+EXPORT_SYMBOL_GPL(xnsched_tp_get_schedule);
+
+void xnsched_tp_put_schedule(struct xnsched_tp_schedule *gps)
+{
+       if (atomic_dec_and_test(&gps->refcount))
+               xnfree(gps);
+}
+EXPORT_SYMBOL_GPL(xnsched_tp_put_schedule);
+
 int xnsched_tp_get_partition(struct xnsched *sched)
 {
        struct xnsched_tp *tp = &sched->tp;


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to