123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- /*
- * Copyright 2010 Tilera Corporation. All Rights Reserved.
- *
- * 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, version 2.
- *
- * 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, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- */
- #include <linux/fs.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/rwsem.h>
- #include <linux/kprobes.h>
- #include <linux/sched.h>
- #include <linux/hardirq.h>
- #include <linux/uaccess.h>
- #include <linux/smp.h>
- #include <linux/cdev.h>
- #include <linux/compat.h>
- #include <asm/hardwall.h>
- #include <asm/traps.h>
- #include <asm/siginfo.h>
- #include <asm/irq_regs.h>
- #include <arch/interrupts.h>
- #include <arch/spr_def.h>
- /*
- * This data structure tracks the rectangle data, etc., associated
- * one-to-one with a "struct file *" from opening HARDWALL_FILE.
- * Note that the file's private data points back to this structure.
- */
- struct hardwall_info {
- struct list_head list; /* "rectangles" list */
- struct list_head task_head; /* head of tasks in this hardwall */
- struct cpumask cpumask; /* cpus in the rectangle */
- int ulhc_x; /* upper left hand corner x coord */
- int ulhc_y; /* upper left hand corner y coord */
- int width; /* rectangle width */
- int height; /* rectangle height */
- int id; /* integer id for this hardwall */
- int teardown_in_progress; /* are we tearing this one down? */
- };
- /* Currently allocated hardwall rectangles */
- static LIST_HEAD(rectangles);
- /* /proc/tile/hardwall */
- static struct proc_dir_entry *hardwall_proc_dir;
- /* Functions to manage files in /proc/tile/hardwall. */
- static void hardwall_add_proc(struct hardwall_info *rect);
- static void hardwall_remove_proc(struct hardwall_info *rect);
- /*
- * Guard changes to the hardwall data structures.
- * This could be finer grained (e.g. one lock for the list of hardwall
- * rectangles, then separate embedded locks for each one's list of tasks),
- * but there are subtle correctness issues when trying to start with
- * a task's "hardwall" pointer and lock the correct rectangle's embedded
- * lock in the presence of a simultaneous deactivation, so it seems
- * easier to have a single lock, given that none of these data
- * structures are touched very frequently during normal operation.
- */
- static DEFINE_SPINLOCK(hardwall_lock);
- /* Allow disabling UDN access. */
- static int udn_disabled;
- static int __init noudn(char *str)
- {
- pr_info("User-space UDN access is disabled\n");
- udn_disabled = 1;
- return 0;
- }
- early_param("noudn", noudn);
- /*
- * Low-level primitives
- */
- /* Set a CPU bit if the CPU is online. */
- #define cpu_online_set(cpu, dst) do { \
- if (cpu_online(cpu)) \
- cpumask_set_cpu(cpu, dst); \
- } while (0)
- /* Does the given rectangle contain the given x,y coordinate? */
- static int contains(struct hardwall_info *r, int x, int y)
- {
- return (x >= r->ulhc_x && x < r->ulhc_x + r->width) &&
- (y >= r->ulhc_y && y < r->ulhc_y + r->height);
- }
- /* Compute the rectangle parameters and validate the cpumask. */
- static int setup_rectangle(struct hardwall_info *r, struct cpumask *mask)
- {
- int x, y, cpu, ulhc, lrhc;
- /* The first cpu is the ULHC, the last the LRHC. */
- ulhc = find_first_bit(cpumask_bits(mask), nr_cpumask_bits);
- lrhc = find_last_bit(cpumask_bits(mask), nr_cpumask_bits);
- /* Compute the rectangle attributes from the cpus. */
- r->ulhc_x = cpu_x(ulhc);
- r->ulhc_y = cpu_y(ulhc);
- r->width = cpu_x(lrhc) - r->ulhc_x + 1;
- r->height = cpu_y(lrhc) - r->ulhc_y + 1;
- cpumask_copy(&r->cpumask, mask);
- r->id = ulhc; /* The ulhc cpu id can be the hardwall id. */
- /* Width and height must be positive */
- if (r->width <= 0 || r->height <= 0)
- return -EINVAL;
- /* Confirm that the cpumask is exactly the rectangle. */
- for (y = 0, cpu = 0; y < smp_height; ++y)
- for (x = 0; x < smp_width; ++x, ++cpu)
- if (cpumask_test_cpu(cpu, mask) != contains(r, x, y))
- return -EINVAL;
- /*
- * Note that offline cpus can't be drained when this UDN
- * rectangle eventually closes. We used to detect this
- * situation and print a warning, but it annoyed users and
- * they ignored it anyway, so now we just return without a
- * warning.
- */
- return 0;
- }
- /* Do the two given rectangles overlap on any cpu? */
- static int overlaps(struct hardwall_info *a, struct hardwall_info *b)
- {
- return a->ulhc_x + a->width > b->ulhc_x && /* A not to the left */
- b->ulhc_x + b->width > a->ulhc_x && /* B not to the left */
- a->ulhc_y + a->height > b->ulhc_y && /* A not above */
- b->ulhc_y + b->height > a->ulhc_y; /* B not above */
- }
- /*
- * Hardware management of hardwall setup, teardown, trapping,
- * and enabling/disabling PL0 access to the networks.
- */
- /* Bit field values to mask together for writes to SPR_XDN_DIRECTION_PROTECT */
- enum direction_protect {
- N_PROTECT = (1 << 0),
- E_PROTECT = (1 << 1),
- S_PROTECT = (1 << 2),
- W_PROTECT = (1 << 3)
- };
- static void enable_firewall_interrupts(void)
- {
- arch_local_irq_unmask_now(INT_UDN_FIREWALL);
- }
- static void disable_firewall_interrupts(void)
- {
- arch_local_irq_mask_now(INT_UDN_FIREWALL);
- }
- /* Set up hardwall on this cpu based on the passed hardwall_info. */
- static void hardwall_setup_ipi_func(void *info)
- {
- struct hardwall_info *r = info;
- int cpu = smp_processor_id();
- int x = cpu % smp_width;
- int y = cpu / smp_width;
- int bits = 0;
- if (x == r->ulhc_x)
- bits |= W_PROTECT;
- if (x == r->ulhc_x + r->width - 1)
- bits |= E_PROTECT;
- if (y == r->ulhc_y)
- bits |= N_PROTECT;
- if (y == r->ulhc_y + r->height - 1)
- bits |= S_PROTECT;
- BUG_ON(bits == 0);
- __insn_mtspr(SPR_UDN_DIRECTION_PROTECT, bits);
- enable_firewall_interrupts();
- }
- /* Set up all cpus on edge of rectangle to enable/disable hardwall SPRs. */
- static void hardwall_setup(struct hardwall_info *r)
- {
- int x, y, cpu, delta;
- struct cpumask rect_cpus;
- cpumask_clear(&rect_cpus);
- /* First include the top and bottom edges */
- cpu = r->ulhc_y * smp_width + r->ulhc_x;
- delta = (r->height - 1) * smp_width;
- for (x = 0; x < r->width; ++x, ++cpu) {
- cpu_online_set(cpu, &rect_cpus);
- cpu_online_set(cpu + delta, &rect_cpus);
- }
- /* Then the left and right edges */
- cpu -= r->width;
- delta = r->width - 1;
- for (y = 0; y < r->height; ++y, cpu += smp_width) {
- cpu_online_set(cpu, &rect_cpus);
- cpu_online_set(cpu + delta, &rect_cpus);
- }
- /* Then tell all the cpus to set up their protection SPR */
- on_each_cpu_mask(&rect_cpus, hardwall_setup_ipi_func, r, 1);
- }
- void __kprobes do_hardwall_trap(struct pt_regs* regs, int fault_num)
- {
- struct hardwall_info *rect;
- struct task_struct *p;
- struct siginfo info;
- int x, y;
- int cpu = smp_processor_id();
- int found_processes;
- unsigned long flags;
- struct pt_regs *old_regs = set_irq_regs(regs);
- irq_enter();
- /* This tile trapped a network access; find the rectangle. */
- x = cpu % smp_width;
- y = cpu / smp_width;
- spin_lock_irqsave(&hardwall_lock, flags);
- list_for_each_entry(rect, &rectangles, list) {
- if (contains(rect, x, y))
- break;
- }
- /*
- * It shouldn't be possible not to find this cpu on the
- * rectangle list, since only cpus in rectangles get hardwalled.
- * The hardwall is only removed after the UDN is drained.
- */
- BUG_ON(&rect->list == &rectangles);
- /*
- * If we already started teardown on this hardwall, don't worry;
- * the abort signal has been sent and we are just waiting for things
- * to quiesce.
- */
- if (rect->teardown_in_progress) {
- pr_notice("cpu %d: detected hardwall violation %#lx"
- " while teardown already in progress\n",
- cpu, (long) __insn_mfspr(SPR_UDN_DIRECTION_PROTECT));
- goto done;
- }
- /*
- * Kill off any process that is activated in this rectangle.
- * We bypass security to deliver the signal, since it must be
- * one of the activated processes that generated the UDN
- * message that caused this trap, and all the activated
- * processes shared a single open file so are pretty tightly
- * bound together from a security point of view to begin with.
- */
- rect->teardown_in_progress = 1;
- wmb(); /* Ensure visibility of rectangle before notifying processes. */
- pr_notice("cpu %d: detected hardwall violation %#lx...\n",
- cpu, (long) __insn_mfspr(SPR_UDN_DIRECTION_PROTECT));
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_HARDWALL;
- found_processes = 0;
- list_for_each_entry(p, &rect->task_head, thread.hardwall_list) {
- BUG_ON(p->thread.hardwall != rect);
- if (!(p->flags & PF_EXITING)) {
- found_processes = 1;
- pr_notice("hardwall: killing %d\n", p->pid);
- do_send_sig_info(info.si_signo, &info, p, false);
- }
- }
- if (!found_processes)
- pr_notice("hardwall: no associated processes!\n");
- done:
- spin_unlock_irqrestore(&hardwall_lock, flags);
- /*
- * We have to disable firewall interrupts now, or else when we
- * return from this handler, we will simply re-interrupt back to
- * it. However, we can't clear the protection bits, since we
- * haven't yet drained the network, and that would allow packets
- * to cross out of the hardwall region.
- */
- disable_firewall_interrupts();
- irq_exit();
- set_irq_regs(old_regs);
- }
- /* Allow access from user space to the UDN. */
- void grant_network_mpls(void)
- {
- __insn_mtspr(SPR_MPL_UDN_ACCESS_SET_0, 1);
- __insn_mtspr(SPR_MPL_UDN_AVAIL_SET_0, 1);
- __insn_mtspr(SPR_MPL_UDN_COMPLETE_SET_0, 1);
- __insn_mtspr(SPR_MPL_UDN_TIMER_SET_0, 1);
- #if !CHIP_HAS_REV1_XDN()
- __insn_mtspr(SPR_MPL_UDN_REFILL_SET_0, 1);
- __insn_mtspr(SPR_MPL_UDN_CA_SET_0, 1);
- #endif
- }
- /* Deny access from user space to the UDN. */
- void restrict_network_mpls(void)
- {
- __insn_mtspr(SPR_MPL_UDN_ACCESS_SET_1, 1);
- __insn_mtspr(SPR_MPL_UDN_AVAIL_SET_1, 1);
- __insn_mtspr(SPR_MPL_UDN_COMPLETE_SET_1, 1);
- __insn_mtspr(SPR_MPL_UDN_TIMER_SET_1, 1);
- #if !CHIP_HAS_REV1_XDN()
- __insn_mtspr(SPR_MPL_UDN_REFILL_SET_1, 1);
- __insn_mtspr(SPR_MPL_UDN_CA_SET_1, 1);
- #endif
- }
- /*
- * Code to create, activate, deactivate, and destroy hardwall rectangles.
- */
- /* Create a hardwall for the given rectangle */
- static struct hardwall_info *hardwall_create(
- size_t size, const unsigned char __user *bits)
- {
- struct hardwall_info *iter, *rect;
- struct cpumask mask;
- unsigned long flags;
- int rc;
- /* Reject crazy sizes out of hand, a la sys_mbind(). */
- if (size > PAGE_SIZE)
- return ERR_PTR(-EINVAL);
- /* Copy whatever fits into a cpumask. */
- if (copy_from_user(&mask, bits, min(sizeof(struct cpumask), size)))
- return ERR_PTR(-EFAULT);
- /*
- * If the size was short, clear the rest of the mask;
- * otherwise validate that the rest of the user mask was zero
- * (we don't try hard to be efficient when validating huge masks).
- */
- if (size < sizeof(struct cpumask)) {
- memset((char *)&mask + size, 0, sizeof(struct cpumask) - size);
- } else if (size > sizeof(struct cpumask)) {
- size_t i;
- for (i = sizeof(struct cpumask); i < size; ++i) {
- char c;
- if (get_user(c, &bits[i]))
- return ERR_PTR(-EFAULT);
- if (c)
- return ERR_PTR(-EINVAL);
- }
- }
- /* Allocate a new rectangle optimistically. */
- rect = kmalloc(sizeof(struct hardwall_info),
- GFP_KERNEL | __GFP_ZERO);
- if (rect == NULL)
- return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&rect->task_head);
- /* Compute the rectangle size and validate that it's plausible. */
- rc = setup_rectangle(rect, &mask);
- if (rc != 0) {
- kfree(rect);
- return ERR_PTR(rc);
- }
- /* Confirm it doesn't overlap and add it to the list. */
- spin_lock_irqsave(&hardwall_lock, flags);
- list_for_each_entry(iter, &rectangles, list) {
- if (overlaps(iter, rect)) {
- spin_unlock_irqrestore(&hardwall_lock, flags);
- kfree(rect);
- return ERR_PTR(-EBUSY);
- }
- }
- list_add_tail(&rect->list, &rectangles);
- spin_unlock_irqrestore(&hardwall_lock, flags);
- /* Set up appropriate hardwalling on all affected cpus. */
- hardwall_setup(rect);
- /* Create a /proc/tile/hardwall entry. */
- hardwall_add_proc(rect);
- return rect;
- }
- /* Activate a given hardwall on this cpu for this process. */
- static int hardwall_activate(struct hardwall_info *rect)
- {
- int cpu, x, y;
- unsigned long flags;
- struct task_struct *p = current;
- struct thread_struct *ts = &p->thread;
- /* Require a rectangle. */
- if (rect == NULL)
- return -ENODATA;
- /* Not allowed to activate a rectangle that is being torn down. */
- if (rect->teardown_in_progress)
- return -EINVAL;
- /*
- * Get our affinity; if we're not bound to this tile uniquely,
- * we can't access the network registers.
- */
- if (cpumask_weight(&p->cpus_allowed) != 1)
- return -EPERM;
- /* Make sure we are bound to a cpu in this rectangle. */
- cpu = smp_processor_id();
- BUG_ON(cpumask_first(&p->cpus_allowed) != cpu);
- x = cpu_x(cpu);
- y = cpu_y(cpu);
- if (!contains(rect, x, y))
- return -EINVAL;
- /* If we are already bound to this hardwall, it's a no-op. */
- if (ts->hardwall) {
- BUG_ON(ts->hardwall != rect);
- return 0;
- }
- /* Success! This process gets to use the user networks on this cpu. */
- ts->hardwall = rect;
- spin_lock_irqsave(&hardwall_lock, flags);
- list_add(&ts->hardwall_list, &rect->task_head);
- spin_unlock_irqrestore(&hardwall_lock, flags);
- grant_network_mpls();
- printk(KERN_DEBUG "Pid %d (%s) activated for hardwall: cpu %d\n",
- p->pid, p->comm, cpu);
- return 0;
- }
- /*
- * Deactivate a task's hardwall. Must hold hardwall_lock.
- * This method may be called from free_task(), so we don't want to
- * rely on too many fields of struct task_struct still being valid.
- * We assume the cpus_allowed, pid, and comm fields are still valid.
- */
- static void _hardwall_deactivate(struct task_struct *task)
- {
- struct thread_struct *ts = &task->thread;
- if (cpumask_weight(&task->cpus_allowed) != 1) {
- pr_err("pid %d (%s) releasing networks with"
- " an affinity mask containing %d cpus!\n",
- task->pid, task->comm,
- cpumask_weight(&task->cpus_allowed));
- BUG();
- }
- BUG_ON(ts->hardwall == NULL);
- ts->hardwall = NULL;
- list_del(&ts->hardwall_list);
- if (task == current)
- restrict_network_mpls();
- }
- /* Deactivate a task's hardwall. */
- int hardwall_deactivate(struct task_struct *task)
- {
- unsigned long flags;
- int activated;
- spin_lock_irqsave(&hardwall_lock, flags);
- activated = (task->thread.hardwall != NULL);
- if (activated)
- _hardwall_deactivate(task);
- spin_unlock_irqrestore(&hardwall_lock, flags);
- if (!activated)
- return -EINVAL;
- printk(KERN_DEBUG "Pid %d (%s) deactivated for hardwall: cpu %d\n",
- task->pid, task->comm, smp_processor_id());
- return 0;
- }
- /* Stop a UDN switch before draining the network. */
- static void stop_udn_switch(void *ignored)
- {
- #if !CHIP_HAS_REV1_XDN()
- /* Freeze the switch and the demux. */
- __insn_mtspr(SPR_UDN_SP_FREEZE,
- SPR_UDN_SP_FREEZE__SP_FRZ_MASK |
- SPR_UDN_SP_FREEZE__DEMUX_FRZ_MASK |
- SPR_UDN_SP_FREEZE__NON_DEST_EXT_MASK);
- #endif
- }
- /* Drain all the state from a stopped switch. */
- static void drain_udn_switch(void *ignored)
- {
- #if !CHIP_HAS_REV1_XDN()
- int i;
- int from_tile_words, ca_count;
- /* Empty out the 5 switch point fifos. */
- for (i = 0; i < 5; i++) {
- int words, j;
- __insn_mtspr(SPR_UDN_SP_FIFO_SEL, i);
- words = __insn_mfspr(SPR_UDN_SP_STATE) & 0xF;
- for (j = 0; j < words; j++)
- (void) __insn_mfspr(SPR_UDN_SP_FIFO_DATA);
- BUG_ON((__insn_mfspr(SPR_UDN_SP_STATE) & 0xF) != 0);
- }
- /* Dump out the 3 word fifo at top. */
- from_tile_words = (__insn_mfspr(SPR_UDN_DEMUX_STATUS) >> 10) & 0x3;
- for (i = 0; i < from_tile_words; i++)
- (void) __insn_mfspr(SPR_UDN_DEMUX_WRITE_FIFO);
- /* Empty out demuxes. */
- while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 0))
- (void) __tile_udn0_receive();
- while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 1))
- (void) __tile_udn1_receive();
- while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 2))
- (void) __tile_udn2_receive();
- while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 3))
- (void) __tile_udn3_receive();
- BUG_ON((__insn_mfspr(SPR_UDN_DATA_AVAIL) & 0xF) != 0);
- /* Empty out catch all. */
- ca_count = __insn_mfspr(SPR_UDN_DEMUX_CA_COUNT);
- for (i = 0; i < ca_count; i++)
- (void) __insn_mfspr(SPR_UDN_CA_DATA);
- BUG_ON(__insn_mfspr(SPR_UDN_DEMUX_CA_COUNT) != 0);
- /* Clear demux logic. */
- __insn_mtspr(SPR_UDN_DEMUX_CTL, 1);
- /*
- * Write switch state; experimentation indicates that 0xc3000
- * is an idle switch point.
- */
- for (i = 0; i < 5; i++) {
- __insn_mtspr(SPR_UDN_SP_FIFO_SEL, i);
- __insn_mtspr(SPR_UDN_SP_STATE, 0xc3000);
- }
- #endif
- }
- /* Reset random UDN state registers at boot up and during hardwall teardown. */
- void reset_network_state(void)
- {
- #if !CHIP_HAS_REV1_XDN()
- /* Reset UDN coordinates to their standard value */
- unsigned int cpu = smp_processor_id();
- unsigned int x = cpu % smp_width;
- unsigned int y = cpu / smp_width;
- #endif
- if (udn_disabled)
- return;
- #if !CHIP_HAS_REV1_XDN()
- __insn_mtspr(SPR_UDN_TILE_COORD, (x << 18) | (y << 7));
- /* Set demux tags to predefined values and enable them. */
- __insn_mtspr(SPR_UDN_TAG_VALID, 0xf);
- __insn_mtspr(SPR_UDN_TAG_0, (1 << 0));
- __insn_mtspr(SPR_UDN_TAG_1, (1 << 1));
- __insn_mtspr(SPR_UDN_TAG_2, (1 << 2));
- __insn_mtspr(SPR_UDN_TAG_3, (1 << 3));
- #endif
- /* Clear out other random registers so we have a clean slate. */
- __insn_mtspr(SPR_UDN_AVAIL_EN, 0);
- __insn_mtspr(SPR_UDN_DEADLOCK_TIMEOUT, 0);
- #if !CHIP_HAS_REV1_XDN()
- __insn_mtspr(SPR_UDN_REFILL_EN, 0);
- __insn_mtspr(SPR_UDN_DEMUX_QUEUE_SEL, 0);
- __insn_mtspr(SPR_UDN_SP_FIFO_SEL, 0);
- #endif
- /* Start the switch and demux. */
- #if !CHIP_HAS_REV1_XDN()
- __insn_mtspr(SPR_UDN_SP_FREEZE, 0);
- #endif
- }
- /* Restart a UDN switch after draining. */
- static void restart_udn_switch(void *ignored)
- {
- reset_network_state();
- /* Disable firewall interrupts. */
- __insn_mtspr(SPR_UDN_DIRECTION_PROTECT, 0);
- disable_firewall_interrupts();
- }
- /* Build a struct cpumask containing all valid tiles in bounding rectangle. */
- static void fill_mask(struct hardwall_info *r, struct cpumask *result)
- {
- int x, y, cpu;
- cpumask_clear(result);
- cpu = r->ulhc_y * smp_width + r->ulhc_x;
- for (y = 0; y < r->height; ++y, cpu += smp_width - r->width) {
- for (x = 0; x < r->width; ++x, ++cpu)
- cpu_online_set(cpu, result);
- }
- }
- /* Last reference to a hardwall is gone, so clear the network. */
- static void hardwall_destroy(struct hardwall_info *rect)
- {
- struct task_struct *task;
- unsigned long flags;
- struct cpumask mask;
- /* Make sure this file actually represents a rectangle. */
- if (rect == NULL)
- return;
- /*
- * Deactivate any remaining tasks. It's possible to race with
- * some other thread that is exiting and hasn't yet called
- * deactivate (when freeing its thread_info), so we carefully
- * deactivate any remaining tasks before freeing the
- * hardwall_info object itself.
- */
- spin_lock_irqsave(&hardwall_lock, flags);
- list_for_each_entry(task, &rect->task_head, thread.hardwall_list)
- _hardwall_deactivate(task);
- spin_unlock_irqrestore(&hardwall_lock, flags);
- /* Drain the UDN. */
- printk(KERN_DEBUG "Clearing hardwall rectangle %dx%d %d,%d\n",
- rect->width, rect->height, rect->ulhc_x, rect->ulhc_y);
- fill_mask(rect, &mask);
- on_each_cpu_mask(&mask, stop_udn_switch, NULL, 1);
- on_each_cpu_mask(&mask, drain_udn_switch, NULL, 1);
- /* Restart switch and disable firewall. */
- on_each_cpu_mask(&mask, restart_udn_switch, NULL, 1);
- /* Remove the /proc/tile/hardwall entry. */
- hardwall_remove_proc(rect);
- /* Now free the rectangle from the list. */
- spin_lock_irqsave(&hardwall_lock, flags);
- BUG_ON(!list_empty(&rect->task_head));
- list_del(&rect->list);
- spin_unlock_irqrestore(&hardwall_lock, flags);
- kfree(rect);
- }
- static int hardwall_proc_show(struct seq_file *sf, void *v)
- {
- struct hardwall_info *rect = sf->private;
- char buf[256];
- int rc = cpulist_scnprintf(buf, sizeof(buf), &rect->cpumask);
- buf[rc++] = '\n';
- seq_write(sf, buf, rc);
- return 0;
- }
- static int hardwall_proc_open(struct inode *inode,
- struct file *file)
- {
- return single_open(file, hardwall_proc_show, PDE(inode)->data);
- }
- static const struct file_operations hardwall_proc_fops = {
- .open = hardwall_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static void hardwall_add_proc(struct hardwall_info *rect)
- {
- char buf[64];
- snprintf(buf, sizeof(buf), "%d", rect->id);
- proc_create_data(buf, 0444, hardwall_proc_dir,
- &hardwall_proc_fops, rect);
- }
- static void hardwall_remove_proc(struct hardwall_info *rect)
- {
- char buf[64];
- snprintf(buf, sizeof(buf), "%d", rect->id);
- remove_proc_entry(buf, hardwall_proc_dir);
- }
- int proc_pid_hardwall(struct task_struct *task, char *buffer)
- {
- struct hardwall_info *rect = task->thread.hardwall;
- return rect ? sprintf(buffer, "%d\n", rect->id) : 0;
- }
- void proc_tile_hardwall_init(struct proc_dir_entry *root)
- {
- if (!udn_disabled)
- hardwall_proc_dir = proc_mkdir("hardwall", root);
- }
- /*
- * Character device support via ioctl/close.
- */
- static long hardwall_ioctl(struct file *file, unsigned int a, unsigned long b)
- {
- struct hardwall_info *rect = file->private_data;
- if (_IOC_TYPE(a) != HARDWALL_IOCTL_BASE)
- return -EINVAL;
- switch (_IOC_NR(a)) {
- case _HARDWALL_CREATE:
- if (udn_disabled)
- return -ENOSYS;
- if (rect != NULL)
- return -EALREADY;
- rect = hardwall_create(_IOC_SIZE(a),
- (const unsigned char __user *)b);
- if (IS_ERR(rect))
- return PTR_ERR(rect);
- file->private_data = rect;
- return 0;
- case _HARDWALL_ACTIVATE:
- return hardwall_activate(rect);
- case _HARDWALL_DEACTIVATE:
- if (current->thread.hardwall != rect)
- return -EINVAL;
- return hardwall_deactivate(current);
- case _HARDWALL_GET_ID:
- return rect ? rect->id : -EINVAL;
- default:
- return -EINVAL;
- }
- }
- #ifdef CONFIG_COMPAT
- static long hardwall_compat_ioctl(struct file *file,
- unsigned int a, unsigned long b)
- {
- /* Sign-extend the argument so it can be used as a pointer. */
- return hardwall_ioctl(file, a, (unsigned long)compat_ptr(b));
- }
- #endif
- /* The user process closed the file; revoke access to user networks. */
- static int hardwall_flush(struct file *file, fl_owner_t owner)
- {
- struct hardwall_info *rect = file->private_data;
- struct task_struct *task, *tmp;
- unsigned long flags;
- if (rect) {
- /*
- * NOTE: if multiple threads are activated on this hardwall
- * file, the other threads will continue having access to the
- * UDN until they are context-switched out and back in again.
- *
- * NOTE: A NULL files pointer means the task is being torn
- * down, so in that case we also deactivate it.
- */
- spin_lock_irqsave(&hardwall_lock, flags);
- list_for_each_entry_safe(task, tmp, &rect->task_head,
- thread.hardwall_list) {
- if (task->files == owner || task->files == NULL)
- _hardwall_deactivate(task);
- }
- spin_unlock_irqrestore(&hardwall_lock, flags);
- }
- return 0;
- }
- /* This hardwall is gone, so destroy it. */
- static int hardwall_release(struct inode *inode, struct file *file)
- {
- hardwall_destroy(file->private_data);
- return 0;
- }
- static const struct file_operations dev_hardwall_fops = {
- .open = nonseekable_open,
- .unlocked_ioctl = hardwall_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = hardwall_compat_ioctl,
- #endif
- .flush = hardwall_flush,
- .release = hardwall_release,
- };
- static struct cdev hardwall_dev;
- static int __init dev_hardwall_init(void)
- {
- int rc;
- dev_t dev;
- rc = alloc_chrdev_region(&dev, 0, 1, "hardwall");
- if (rc < 0)
- return rc;
- cdev_init(&hardwall_dev, &dev_hardwall_fops);
- rc = cdev_add(&hardwall_dev, dev, 1);
- if (rc < 0)
- return rc;
- return 0;
- }
- late_initcall(dev_hardwall_init);
|