diff options
-rw-r--r-- | litmus/sched_mc_ce.c | 302 |
1 files changed, 293 insertions, 9 deletions
diff --git a/litmus/sched_mc_ce.c b/litmus/sched_mc_ce.c index b44012822532..8ed960c8729c 100644 --- a/litmus/sched_mc_ce.c +++ b/litmus/sched_mc_ce.c | |||
@@ -6,27 +6,31 @@ | |||
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <asm/atomic.h> | 8 | #include <asm/atomic.h> |
9 | #include <asm/uaccess.h> | ||
9 | 10 | ||
10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
11 | #include <linux/percpu.h> | 12 | #include <linux/percpu.h> |
12 | #include <linux/hrtimer.h> | 13 | #include <linux/hrtimer.h> |
13 | #include <linux/pid.h> | 14 | #include <linux/pid.h> |
14 | #include <linux/sched.h> | 15 | #include <linux/sched.h> |
16 | #include <linux/proc_fs.h> | ||
15 | 17 | ||
16 | #include <litmus/litmus.h> | 18 | #include <litmus/litmus.h> |
17 | #include <litmus/sched_plugin.h> | 19 | #include <litmus/sched_plugin.h> |
18 | #include <litmus/rt_domain.h> | 20 | #include <litmus/rt_domain.h> |
19 | #include <litmus/rt_param.h> | 21 | #include <litmus/rt_param.h> |
20 | #include <litmus/sched_mc.h> | 22 | #include <litmus/sched_mc.h> |
23 | #include <litmus/litmus_proc.h> | ||
21 | 24 | ||
22 | static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp; | 25 | static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp; |
23 | 26 | ||
24 | #define tsk_mc_data(t) (tsk_rt(t)->mc_data) | 27 | #define tsk_mc_data(t) (tsk_rt(t)->mc_data) |
25 | #define tsk_mc_crit(t) (tsk_mc_data(t)->mc_task.crit) | 28 | #define tsk_mc_crit(t) (tsk_mc_data(t)->mc_task.crit) |
26 | #define is_active_plugin() (litmus == mc_ce_plugin) | 29 | #define is_active_plugin() (litmus == &mc_ce_plugin) |
27 | 30 | ||
28 | static atomic_t start_time_set = ATOMIC_INIT(0); | 31 | static atomic_t start_time_set = ATOMIC_INIT(0); |
29 | static atomic64_t start_time = ATOMIC64_INIT(0); | 32 | static atomic64_t start_time = ATOMIC64_INIT(0); |
33 | static struct proc_dir_entry *mc_ce_dir = NULL, *ce_file = NULL; | ||
30 | 34 | ||
31 | /* | 35 | /* |
32 | * Cache the budget along with the struct PID for a task so that we don't need | 36 | * Cache the budget along with the struct PID for a task so that we don't need |
@@ -35,7 +39,9 @@ static atomic64_t start_time = ATOMIC64_INIT(0); | |||
35 | */ | 39 | */ |
36 | struct ce_dom_pid_entry { | 40 | struct ce_dom_pid_entry { |
37 | struct pid *pid; | 41 | struct pid *pid; |
42 | /* execution cost (sometimes called budget) */ | ||
38 | lt_t exec_cost; | 43 | lt_t exec_cost; |
44 | /* accumulated (summed) exec costs, including this one */ | ||
39 | lt_t acc_time; | 45 | lt_t acc_time; |
40 | }; | 46 | }; |
41 | 47 | ||
@@ -470,12 +476,38 @@ static struct sched_plugin mc_ce_plugin __cacheline_aligned_in_smp = { | |||
470 | .deactivate_plugin = mc_ce_deactivate_plugin, | 476 | .deactivate_plugin = mc_ce_deactivate_plugin, |
471 | }; | 477 | }; |
472 | 478 | ||
479 | static void clear_pid_entries(void) | ||
480 | { | ||
481 | int cpu, entry; | ||
482 | domain_t *dom; | ||
483 | struct ce_dom_data *ce_data; | ||
484 | |||
485 | for_each_online_cpu(cpu) { | ||
486 | dom = &per_cpu(mc_ce_doms, cpu); | ||
487 | ce_data = dom->data; | ||
488 | ce_data->num_pid_entries = 0; | ||
489 | ce_data->cycle_time = 0; | ||
490 | for (entry = 0; entry < CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS; | ||
491 | ++entry) { | ||
492 | if (NULL != ce_data->pid_entries[entry].pid) { | ||
493 | put_pid(ce_data->pid_entries[entry].pid); | ||
494 | ce_data->pid_entries[entry].pid = NULL; | ||
495 | } | ||
496 | ce_data->pid_entries[entry].exec_cost = 0; | ||
497 | ce_data->pid_entries[entry].acc_time = 0; | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | |||
502 | static int setup_proc(void); | ||
503 | |||
473 | static int __init init_sched_mc_ce(void) | 504 | static int __init init_sched_mc_ce(void) |
474 | { | 505 | { |
475 | struct ce_dom_data *ce_data; | 506 | struct ce_dom_data *ce_data; |
476 | domain_t *dom; | 507 | domain_t *dom; |
477 | int cpu, i; | 508 | int cpu, err; |
478 | 509 | ||
510 | clear_pid_entries(); | ||
479 | for_each_online_cpu(cpu) { | 511 | for_each_online_cpu(cpu) { |
480 | dom = &per_cpu(mc_ce_doms, cpu); | 512 | dom = &per_cpu(mc_ce_doms, cpu); |
481 | pd_domain_init(dom, NULL, NULL, NULL, NULL); | 513 | pd_domain_init(dom, NULL, NULL, NULL, NULL); |
@@ -484,17 +516,269 @@ static int __init init_sched_mc_ce(void) | |||
484 | hrtimer_init(&ce_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | 516 | hrtimer_init(&ce_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); |
485 | hrtimer_start_on_info_init(&ce_data->timer_info); | 517 | hrtimer_start_on_info_init(&ce_data->timer_info); |
486 | ce_data->cpu = cpu; | 518 | ce_data->cpu = cpu; |
487 | ce_data->num_pid_entries = 0; | ||
488 | ce_data->cycle_time = 0; | ||
489 | ce_data->timer.function = timer_callback; | 519 | ce_data->timer.function = timer_callback; |
520 | } | ||
521 | err = setup_proc(); | ||
522 | if (!err) | ||
523 | err = register_sched_plugin(&mc_ce_plugin); | ||
524 | return err; | ||
525 | } | ||
526 | |||
527 | #define BUF_SIZE PAGE_SIZE | ||
528 | static int write_into_proc(char *proc_buf, const int proc_size, char *fmt, ...) | ||
529 | { | ||
530 | static char buf[BUF_SIZE]; | ||
531 | int n; | ||
532 | va_list args; | ||
533 | |||
534 | va_start(args, fmt); | ||
535 | n = vscnprintf(buf, BUF_SIZE, fmt, args); | ||
536 | va_end(args); | ||
537 | if (BUF_SIZE <= n || proc_size <= n - 1) { | ||
538 | /* too big for formatting buffer or proc (less null byte) */ | ||
539 | n = -EINVAL; | ||
540 | goto out; | ||
541 | } | ||
542 | memcpy(proc_buf, buf, n - 1); | ||
543 | out: | ||
544 | return n; | ||
545 | } | ||
546 | #undef BUF_SIZE | ||
547 | |||
548 | /* | ||
549 | * Writes a PID entry to the procfs. | ||
550 | */ | ||
551 | #define PID_SPACE 15 | ||
552 | #define TASK_INFO_BUF (PID_SPACE + TASK_COMM_LEN) | ||
553 | static int write_pid_entry(char *page, const int count, const int cpu, | ||
554 | const int task, struct ce_dom_pid_entry *pid_entry) | ||
555 | { | ||
556 | static char task_info[TASK_INFO_BUF]; | ||
557 | struct task_struct *ts; | ||
558 | int n = 0, err, ti_n; | ||
559 | char *ti_b; | ||
560 | |||
561 | if (pid_entry->pid) { | ||
562 | rcu_read_lock(); | ||
563 | ts = pid_task(pid_entry->pid, PIDTYPE_PID); | ||
564 | rcu_read_unlock(); | ||
565 | |||
566 | /* get some information about the task */ | ||
567 | if (ts) { | ||
568 | ti_b = task_info; | ||
569 | ti_n = snprintf(ti_b, PID_SPACE, "%d", ts->pid); | ||
570 | if (PID_SPACE <= ti_n) | ||
571 | ti_n = PID_SPACE - 1; | ||
572 | ti_b += ti_n; | ||
573 | *ti_b = ' '; /* nuke the null byte */ | ||
574 | ti_b++; | ||
575 | get_task_comm(ti_b, ts); | ||
576 | task_info[TASK_INFO_BUF - 1] = '\0'; | ||
577 | } else { | ||
578 | const char *msg = "pid_task() failed :("; | ||
579 | strncpy(task_info, msg, sizeof(msg)); | ||
580 | } | ||
581 | |||
582 | } else | ||
583 | strncpy(task_info, "no", 3); | ||
584 | |||
585 | err = write_into_proc(page, count - n, "# task: %s\n", task_info); | ||
586 | if (err < 0) { | ||
587 | n = -ENOSPC; | ||
588 | goto out; | ||
589 | } | ||
590 | n += err; | ||
591 | err = write_into_proc(page, count - n, "%d, %d, %llu\n", | ||
592 | cpu, task, pid_entry->exec_cost); | ||
593 | if (err < 0) { | ||
594 | n = -ENOSPC; | ||
595 | goto out; | ||
596 | } | ||
597 | n =+ err; | ||
598 | out: | ||
599 | return n; | ||
600 | } | ||
601 | #undef PID_SPACE | ||
602 | #undef TASK_INFO_BUF | ||
490 | 603 | ||
491 | for (i = 0; i < CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS; ++i) { | 604 | /* |
492 | ce_data->pid_entries[i].pid = NULL; | 605 | * Called when the user-land reads from proc. |
493 | ce_data->pid_entries[i].exec_cost = 0; | 606 | */ |
494 | ce_data->pid_entries[i].acc_time = 0; | 607 | static int proc_read_ce_file(char *page, char **start, off_t off, int count, |
608 | int *eof, void *data) | ||
609 | { | ||
610 | int n = 0, err, cpu, t; | ||
611 | struct ce_dom_data *ce_data; | ||
612 | domain_t *dom; | ||
613 | |||
614 | if (off > 0) { | ||
615 | *eof = 1; | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | for_each_online_cpu(cpu) { | ||
620 | dom = &per_cpu(mc_ce_doms, cpu); | ||
621 | ce_data = dom->data; | ||
622 | for (t = 0; t < ce_data->num_pid_entries; ++t) { | ||
623 | err = write_pid_entry(page, count - n, | ||
624 | cpu, t, &ce_data->pid_entries[t]); | ||
625 | if (err < 0) { | ||
626 | n = -ENOSPC; | ||
627 | goto out; | ||
628 | } | ||
629 | n += err; | ||
495 | } | 630 | } |
496 | } | 631 | } |
497 | return register_sched_plugin(&mc_ce_plugin); | 632 | *eof = 1; |
633 | out: | ||
634 | return n; | ||
635 | } | ||
636 | |||
637 | /* | ||
638 | * Skip a commented line. | ||
639 | */ | ||
640 | static int skip_comment(const char *buf, const unsigned long max) | ||
641 | { | ||
642 | unsigned long i = 0; | ||
643 | const char *c = buf; | ||
644 | if (0 == max || !c || *c != '#') | ||
645 | return 0; | ||
646 | ++c; ++i; | ||
647 | for (; i < max; ++i) { | ||
648 | if (*c == '\n') { | ||
649 | ++c; ++i; | ||
650 | break; | ||
651 | } | ||
652 | ++c; | ||
653 | } | ||
654 | return i; | ||
655 | } | ||
656 | |||
657 | /* a budget of 1 millisecond is probably reasonable */ | ||
658 | #define BUDGET_THRESHOLD 1000000ULL | ||
659 | static int setup_pid_entry(const int cpu, const int task, const lt_t budget) | ||
660 | { | ||
661 | domain_t *dom = &per_cpu(mc_ce_doms, cpu); | ||
662 | struct ce_dom_data *ce_data = dom->data; | ||
663 | struct ce_dom_pid_entry *new_entry; | ||
664 | int err = 0; | ||
665 | |||
666 | /* check the inputs */ | ||
667 | if (cpu < 0 || cpu >= NR_CPUS || task < 1 || task > PID_MAX_DEFAULT || | ||
668 | budget < 1) { | ||
669 | printk(KERN_INFO "litmus: bad cpu or task ID sent to " | ||
670 | "MC-CE proc\n"); | ||
671 | err = -EINVAL; | ||
672 | goto out; | ||
673 | } | ||
674 | /* check for small budgets */ | ||
675 | if (BUDGET_THRESHOLD > budget) { | ||
676 | printk(KERN_CRIT "litmus: you gave a small budget for an " | ||
677 | "MC-CE task; that might be an issue.\n"); | ||
678 | } | ||
679 | /* check that we have space for a new entry */ | ||
680 | if (CONFIG_PLUGIN_MC_LEVEL_A_MAX_TASKS <= ce_data->num_pid_entries) { | ||
681 | printk(KERN_INFO "litmus: too many MC-CE tasks for cpu " | ||
682 | "%d\n", cpu); | ||
683 | err = -EINVAL; | ||
684 | goto out; | ||
685 | } | ||
686 | /* add the new entry */ | ||
687 | new_entry = &ce_data->pid_entries[ce_data->num_pid_entries]; | ||
688 | BUG_ON(NULL != new_entry->pid); | ||
689 | new_entry->exec_cost = budget; | ||
690 | new_entry->acc_time = ce_data->cycle_time + budget; | ||
691 | /* update the domain entry */ | ||
692 | ce_data->cycle_time += budget; | ||
693 | ce_data->num_pid_entries++; | ||
694 | out: | ||
695 | return err; | ||
696 | } | ||
697 | #undef BUDGET_THRESHOLD | ||
698 | |||
699 | /* | ||
700 | * Called when the user-land writes to proc. | ||
701 | * | ||
702 | * Error checking is quite minimal. Format is: | ||
703 | * <cpu>, <process ID>, <budget> | ||
704 | */ | ||
705 | #define PROCFS_MAX_SIZE PAGE_SIZE | ||
706 | static int proc_write_ce_file(struct file *file, const char __user *buffer, | ||
707 | unsigned long count, void *data) | ||
708 | { | ||
709 | static char kbuf[PROCFS_MAX_SIZE]; | ||
710 | char *c = kbuf, *c_skipped; | ||
711 | int cpu, task, cnt = 0, chars_read, converted, err; | ||
712 | lt_t budget; | ||
713 | |||
714 | if (is_active_plugin()) { | ||
715 | printk(KERN_INFO "litmus: can't edit MC-CE proc when plugin " | ||
716 | "active\n"); | ||
717 | cnt = -EINVAL; | ||
718 | goto out; | ||
719 | } | ||
720 | |||
721 | if (count > PROCFS_MAX_SIZE) { | ||
722 | cnt = -EINVAL; | ||
723 | goto out; | ||
724 | } | ||
725 | |||
726 | if (copy_from_user(kbuf, buffer, count)) { | ||
727 | cnt = -EFAULT; | ||
728 | goto out; | ||
729 | } | ||
730 | clear_pid_entries(); | ||
731 | while (cnt < count) { | ||
732 | c_skipped = skip_spaces(c); | ||
733 | if (c_skipped != c) { | ||
734 | chars_read = c_skipped - c; | ||
735 | cnt += chars_read; | ||
736 | c += chars_read; | ||
737 | continue; | ||
738 | } | ||
739 | if (*c == '#') { | ||
740 | chars_read = skip_comment(c, count - cnt); | ||
741 | cnt += chars_read; | ||
742 | c += chars_read; | ||
743 | continue; | ||
744 | } | ||
745 | chars_read = sscanf(c, "%d, %d, %llu%n", &cpu, &task, &budget, | ||
746 | &converted); | ||
747 | if (3 != converted) { | ||
748 | cnt = -EINVAL; | ||
749 | goto out; | ||
750 | } | ||
751 | cnt += chars_read; | ||
752 | c += chars_read; | ||
753 | err = setup_pid_entry(cpu, task, budget); | ||
754 | if (err) { | ||
755 | cnt = -EINVAL; | ||
756 | goto out; | ||
757 | } | ||
758 | } | ||
759 | out: | ||
760 | return cnt; | ||
761 | } | ||
762 | #undef PROCFS_MAX_SIZE | ||
763 | |||
764 | static int setup_proc(void) | ||
765 | { | ||
766 | int err; | ||
767 | err = make_plugin_proc_dir(&mc_ce_plugin, &mc_ce_dir); | ||
768 | if (err) { | ||
769 | printk(KERN_ERR "could not create MC-CE procfs dir.\n"); | ||
770 | goto out; | ||
771 | } | ||
772 | ce_file = create_proc_entry("ce_file", 0644, mc_ce_dir); | ||
773 | if (!ce_file) { | ||
774 | printk(KERN_ERR "could not create MC-CE procfs file.\n"); | ||
775 | err = -EIO; | ||
776 | goto out; | ||
777 | } | ||
778 | ce_file->read_proc = proc_read_ce_file; | ||
779 | ce_file->write_proc = proc_write_ce_file; | ||
780 | out: | ||
781 | return err; | ||
498 | } | 782 | } |
499 | 783 | ||
500 | module_init(init_sched_mc_ce); | 784 | module_init(init_sched_mc_ce); |