Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / arch / tile / kernel / hardwall.c
1 /*
2  * Copyright 2010 Tilera Corporation. All Rights Reserved.
3  *
4  *   This program is free software; you can redistribute it and/or
5  *   modify it under the terms of the GNU General Public License
6  *   as published by the Free Software Foundation, version 2.
7  *
8  *   This program is distributed in the hope that it will be useful, but
9  *   WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11  *   NON INFRINGEMENT.  See the GNU General Public License for
12  *   more details.
13  */
14
15 #include <linux/fs.h>
16 #include <linux/proc_fs.h>
17 #include <linux/seq_file.h>
18 #include <linux/rwsem.h>
19 #include <linux/kprobes.h>
20 #include <linux/sched.h>
21 #include <linux/hardirq.h>
22 #include <linux/uaccess.h>
23 #include <linux/smp.h>
24 #include <linux/cdev.h>
25 #include <linux/compat.h>
26 #include <asm/hardwall.h>
27 #include <asm/traps.h>
28 #include <asm/siginfo.h>
29 #include <asm/irq_regs.h>
30
31 #include <arch/interrupts.h>
32 #include <arch/spr_def.h>
33
34
35 /*
36  * This data structure tracks the rectangle data, etc., associated
37  * one-to-one with a "struct file *" from opening HARDWALL_FILE.
38  * Note that the file's private data points back to this structure.
39  */
40 struct hardwall_info {
41         struct list_head list;             /* "rectangles" list */
42         struct list_head task_head;        /* head of tasks in this hardwall */
43         struct cpumask cpumask;            /* cpus in the rectangle */
44         int ulhc_x;                        /* upper left hand corner x coord */
45         int ulhc_y;                        /* upper left hand corner y coord */
46         int width;                         /* rectangle width */
47         int height;                        /* rectangle height */
48         int id;                            /* integer id for this hardwall */
49         int teardown_in_progress;          /* are we tearing this one down? */
50 };
51
52 /* Currently allocated hardwall rectangles */
53 static LIST_HEAD(rectangles);
54
55 /* /proc/tile/hardwall */
56 static struct proc_dir_entry *hardwall_proc_dir;
57
58 /* Functions to manage files in /proc/tile/hardwall. */
59 static void hardwall_add_proc(struct hardwall_info *rect);
60 static void hardwall_remove_proc(struct hardwall_info *rect);
61
62 /*
63  * Guard changes to the hardwall data structures.
64  * This could be finer grained (e.g. one lock for the list of hardwall
65  * rectangles, then separate embedded locks for each one's list of tasks),
66  * but there are subtle correctness issues when trying to start with
67  * a task's "hardwall" pointer and lock the correct rectangle's embedded
68  * lock in the presence of a simultaneous deactivation, so it seems
69  * easier to have a single lock, given that none of these data
70  * structures are touched very frequently during normal operation.
71  */
72 static DEFINE_SPINLOCK(hardwall_lock);
73
74 /* Allow disabling UDN access. */
75 static int udn_disabled;
76 static int __init noudn(char *str)
77 {
78         pr_info("User-space UDN access is disabled\n");
79         udn_disabled = 1;
80         return 0;
81 }
82 early_param("noudn", noudn);
83
84
85 /*
86  * Low-level primitives
87  */
88
89 /* Set a CPU bit if the CPU is online. */
90 #define cpu_online_set(cpu, dst) do { \
91         if (cpu_online(cpu))          \
92                 cpumask_set_cpu(cpu, dst);    \
93 } while (0)
94
95
96 /* Does the given rectangle contain the given x,y coordinate? */
97 static int contains(struct hardwall_info *r, int x, int y)
98 {
99         return (x >= r->ulhc_x && x < r->ulhc_x + r->width) &&
100                 (y >= r->ulhc_y && y < r->ulhc_y + r->height);
101 }
102
103 /* Compute the rectangle parameters and validate the cpumask. */
104 static int setup_rectangle(struct hardwall_info *r, struct cpumask *mask)
105 {
106         int x, y, cpu, ulhc, lrhc;
107
108         /* The first cpu is the ULHC, the last the LRHC. */
109         ulhc = find_first_bit(cpumask_bits(mask), nr_cpumask_bits);
110         lrhc = find_last_bit(cpumask_bits(mask), nr_cpumask_bits);
111
112         /* Compute the rectangle attributes from the cpus. */
113         r->ulhc_x = cpu_x(ulhc);
114         r->ulhc_y = cpu_y(ulhc);
115         r->width = cpu_x(lrhc) - r->ulhc_x + 1;
116         r->height = cpu_y(lrhc) - r->ulhc_y + 1;
117         cpumask_copy(&r->cpumask, mask);
118         r->id = ulhc;   /* The ulhc cpu id can be the hardwall id. */
119
120         /* Width and height must be positive */
121         if (r->width <= 0 || r->height <= 0)
122                 return -EINVAL;
123
124         /* Confirm that the cpumask is exactly the rectangle. */
125         for (y = 0, cpu = 0; y < smp_height; ++y)
126                 for (x = 0; x < smp_width; ++x, ++cpu)
127                         if (cpumask_test_cpu(cpu, mask) != contains(r, x, y))
128                                 return -EINVAL;
129
130         /*
131          * Note that offline cpus can't be drained when this UDN
132          * rectangle eventually closes.  We used to detect this
133          * situation and print a warning, but it annoyed users and
134          * they ignored it anyway, so now we just return without a
135          * warning.
136          */
137         return 0;
138 }
139
140 /* Do the two given rectangles overlap on any cpu? */
141 static int overlaps(struct hardwall_info *a, struct hardwall_info *b)
142 {
143         return a->ulhc_x + a->width > b->ulhc_x &&    /* A not to the left */
144                 b->ulhc_x + b->width > a->ulhc_x &&   /* B not to the left */
145                 a->ulhc_y + a->height > b->ulhc_y &&  /* A not above */
146                 b->ulhc_y + b->height > a->ulhc_y;    /* B not above */
147 }
148
149
150 /*
151  * Hardware management of hardwall setup, teardown, trapping,
152  * and enabling/disabling PL0 access to the networks.
153  */
154
155 /* Bit field values to mask together for writes to SPR_XDN_DIRECTION_PROTECT */
156 enum direction_protect {
157         N_PROTECT = (1 << 0),
158         E_PROTECT = (1 << 1),
159         S_PROTECT = (1 << 2),
160         W_PROTECT = (1 << 3)
161 };
162
163 static void enable_firewall_interrupts(void)
164 {
165         arch_local_irq_unmask_now(INT_UDN_FIREWALL);
166 }
167
168 static void disable_firewall_interrupts(void)
169 {
170         arch_local_irq_mask_now(INT_UDN_FIREWALL);
171 }
172
173 /* Set up hardwall on this cpu based on the passed hardwall_info. */
174 static void hardwall_setup_ipi_func(void *info)
175 {
176         struct hardwall_info *r = info;
177         int cpu = smp_processor_id();
178         int x = cpu % smp_width;
179         int y = cpu / smp_width;
180         int bits = 0;
181         if (x == r->ulhc_x)
182                 bits |= W_PROTECT;
183         if (x == r->ulhc_x + r->width - 1)
184                 bits |= E_PROTECT;
185         if (y == r->ulhc_y)
186                 bits |= N_PROTECT;
187         if (y == r->ulhc_y + r->height - 1)
188                 bits |= S_PROTECT;
189         BUG_ON(bits == 0);
190         __insn_mtspr(SPR_UDN_DIRECTION_PROTECT, bits);
191         enable_firewall_interrupts();
192
193 }
194
195 /* Set up all cpus on edge of rectangle to enable/disable hardwall SPRs. */
196 static void hardwall_setup(struct hardwall_info *r)
197 {
198         int x, y, cpu, delta;
199         struct cpumask rect_cpus;
200
201         cpumask_clear(&rect_cpus);
202
203         /* First include the top and bottom edges */
204         cpu = r->ulhc_y * smp_width + r->ulhc_x;
205         delta = (r->height - 1) * smp_width;
206         for (x = 0; x < r->width; ++x, ++cpu) {
207                 cpu_online_set(cpu, &rect_cpus);
208                 cpu_online_set(cpu + delta, &rect_cpus);
209         }
210
211         /* Then the left and right edges */
212         cpu -= r->width;
213         delta = r->width - 1;
214         for (y = 0; y < r->height; ++y, cpu += smp_width) {
215                 cpu_online_set(cpu, &rect_cpus);
216                 cpu_online_set(cpu + delta, &rect_cpus);
217         }
218
219         /* Then tell all the cpus to set up their protection SPR */
220         on_each_cpu_mask(&rect_cpus, hardwall_setup_ipi_func, r, 1);
221 }
222
223 void __kprobes do_hardwall_trap(struct pt_regs* regs, int fault_num)
224 {
225         struct hardwall_info *rect;
226         struct task_struct *p;
227         struct siginfo info;
228         int x, y;
229         int cpu = smp_processor_id();
230         int found_processes;
231         unsigned long flags;
232
233         struct pt_regs *old_regs = set_irq_regs(regs);
234         irq_enter();
235
236         /* This tile trapped a network access; find the rectangle. */
237         x = cpu % smp_width;
238         y = cpu / smp_width;
239         spin_lock_irqsave(&hardwall_lock, flags);
240         list_for_each_entry(rect, &rectangles, list) {
241                 if (contains(rect, x, y))
242                         break;
243         }
244
245         /*
246          * It shouldn't be possible not to find this cpu on the
247          * rectangle list, since only cpus in rectangles get hardwalled.
248          * The hardwall is only removed after the UDN is drained.
249          */
250         BUG_ON(&rect->list == &rectangles);
251
252         /*
253          * If we already started teardown on this hardwall, don't worry;
254          * the abort signal has been sent and we are just waiting for things
255          * to quiesce.
256          */
257         if (rect->teardown_in_progress) {
258                 pr_notice("cpu %d: detected hardwall violation %#lx"
259                        " while teardown already in progress\n",
260                        cpu, (long) __insn_mfspr(SPR_UDN_DIRECTION_PROTECT));
261                 goto done;
262         }
263
264         /*
265          * Kill off any process that is activated in this rectangle.
266          * We bypass security to deliver the signal, since it must be
267          * one of the activated processes that generated the UDN
268          * message that caused this trap, and all the activated
269          * processes shared a single open file so are pretty tightly
270          * bound together from a security point of view to begin with.
271          */
272         rect->teardown_in_progress = 1;
273         wmb(); /* Ensure visibility of rectangle before notifying processes. */
274         pr_notice("cpu %d: detected hardwall violation %#lx...\n",
275                cpu, (long) __insn_mfspr(SPR_UDN_DIRECTION_PROTECT));
276         info.si_signo = SIGILL;
277         info.si_errno = 0;
278         info.si_code = ILL_HARDWALL;
279         found_processes = 0;
280         list_for_each_entry(p, &rect->task_head, thread.hardwall_list) {
281                 BUG_ON(p->thread.hardwall != rect);
282                 if (!(p->flags & PF_EXITING)) {
283                         found_processes = 1;
284                         pr_notice("hardwall: killing %d\n", p->pid);
285                         do_send_sig_info(info.si_signo, &info, p, false);
286                 }
287         }
288         if (!found_processes)
289                 pr_notice("hardwall: no associated processes!\n");
290
291  done:
292         spin_unlock_irqrestore(&hardwall_lock, flags);
293
294         /*
295          * We have to disable firewall interrupts now, or else when we
296          * return from this handler, we will simply re-interrupt back to
297          * it.  However, we can't clear the protection bits, since we
298          * haven't yet drained the network, and that would allow packets
299          * to cross out of the hardwall region.
300          */
301         disable_firewall_interrupts();
302
303         irq_exit();
304         set_irq_regs(old_regs);
305 }
306
307 /* Allow access from user space to the UDN. */
308 void grant_network_mpls(void)
309 {
310         __insn_mtspr(SPR_MPL_UDN_ACCESS_SET_0, 1);
311         __insn_mtspr(SPR_MPL_UDN_AVAIL_SET_0, 1);
312         __insn_mtspr(SPR_MPL_UDN_COMPLETE_SET_0, 1);
313         __insn_mtspr(SPR_MPL_UDN_TIMER_SET_0, 1);
314 #if !CHIP_HAS_REV1_XDN()
315         __insn_mtspr(SPR_MPL_UDN_REFILL_SET_0, 1);
316         __insn_mtspr(SPR_MPL_UDN_CA_SET_0, 1);
317 #endif
318 }
319
320 /* Deny access from user space to the UDN. */
321 void restrict_network_mpls(void)
322 {
323         __insn_mtspr(SPR_MPL_UDN_ACCESS_SET_1, 1);
324         __insn_mtspr(SPR_MPL_UDN_AVAIL_SET_1, 1);
325         __insn_mtspr(SPR_MPL_UDN_COMPLETE_SET_1, 1);
326         __insn_mtspr(SPR_MPL_UDN_TIMER_SET_1, 1);
327 #if !CHIP_HAS_REV1_XDN()
328         __insn_mtspr(SPR_MPL_UDN_REFILL_SET_1, 1);
329         __insn_mtspr(SPR_MPL_UDN_CA_SET_1, 1);
330 #endif
331 }
332
333
334 /*
335  * Code to create, activate, deactivate, and destroy hardwall rectangles.
336  */
337
338 /* Create a hardwall for the given rectangle */
339 static struct hardwall_info *hardwall_create(
340         size_t size, const unsigned char __user *bits)
341 {
342         struct hardwall_info *iter, *rect;
343         struct cpumask mask;
344         unsigned long flags;
345         int rc;
346
347         /* Reject crazy sizes out of hand, a la sys_mbind(). */
348         if (size > PAGE_SIZE)
349                 return ERR_PTR(-EINVAL);
350
351         /* Copy whatever fits into a cpumask. */
352         if (copy_from_user(&mask, bits, min(sizeof(struct cpumask), size)))
353                 return ERR_PTR(-EFAULT);
354
355         /*
356          * If the size was short, clear the rest of the mask;
357          * otherwise validate that the rest of the user mask was zero
358          * (we don't try hard to be efficient when validating huge masks).
359          */
360         if (size < sizeof(struct cpumask)) {
361                 memset((char *)&mask + size, 0, sizeof(struct cpumask) - size);
362         } else if (size > sizeof(struct cpumask)) {
363                 size_t i;
364                 for (i = sizeof(struct cpumask); i < size; ++i) {
365                         char c;
366                         if (get_user(c, &bits[i]))
367                                 return ERR_PTR(-EFAULT);
368                         if (c)
369                                 return ERR_PTR(-EINVAL);
370                 }
371         }
372
373         /* Allocate a new rectangle optimistically. */
374         rect = kmalloc(sizeof(struct hardwall_info),
375                         GFP_KERNEL | __GFP_ZERO);
376         if (rect == NULL)
377                 return ERR_PTR(-ENOMEM);
378         INIT_LIST_HEAD(&rect->task_head);
379
380         /* Compute the rectangle size and validate that it's plausible. */
381         rc = setup_rectangle(rect, &mask);
382         if (rc != 0) {
383                 kfree(rect);
384                 return ERR_PTR(rc);
385         }
386
387         /* Confirm it doesn't overlap and add it to the list. */
388         spin_lock_irqsave(&hardwall_lock, flags);
389         list_for_each_entry(iter, &rectangles, list) {
390                 if (overlaps(iter, rect)) {
391                         spin_unlock_irqrestore(&hardwall_lock, flags);
392                         kfree(rect);
393                         return ERR_PTR(-EBUSY);
394                 }
395         }
396         list_add_tail(&rect->list, &rectangles);
397         spin_unlock_irqrestore(&hardwall_lock, flags);
398
399         /* Set up appropriate hardwalling on all affected cpus. */
400         hardwall_setup(rect);
401
402         /* Create a /proc/tile/hardwall entry. */
403         hardwall_add_proc(rect);
404
405         return rect;
406 }
407
408 /* Activate a given hardwall on this cpu for this process. */
409 static int hardwall_activate(struct hardwall_info *rect)
410 {
411         int cpu, x, y;
412         unsigned long flags;
413         struct task_struct *p = current;
414         struct thread_struct *ts = &p->thread;
415
416         /* Require a rectangle. */
417         if (rect == NULL)
418                 return -ENODATA;
419
420         /* Not allowed to activate a rectangle that is being torn down. */
421         if (rect->teardown_in_progress)
422                 return -EINVAL;
423
424         /*
425          * Get our affinity; if we're not bound to this tile uniquely,
426          * we can't access the network registers.
427          */
428         if (cpumask_weight(&p->cpus_allowed) != 1)
429                 return -EPERM;
430
431         /* Make sure we are bound to a cpu in this rectangle. */
432         cpu = smp_processor_id();
433         BUG_ON(cpumask_first(&p->cpus_allowed) != cpu);
434         x = cpu_x(cpu);
435         y = cpu_y(cpu);
436         if (!contains(rect, x, y))
437                 return -EINVAL;
438
439         /* If we are already bound to this hardwall, it's a no-op. */
440         if (ts->hardwall) {
441                 BUG_ON(ts->hardwall != rect);
442                 return 0;
443         }
444
445         /* Success!  This process gets to use the user networks on this cpu. */
446         ts->hardwall = rect;
447         spin_lock_irqsave(&hardwall_lock, flags);
448         list_add(&ts->hardwall_list, &rect->task_head);
449         spin_unlock_irqrestore(&hardwall_lock, flags);
450         grant_network_mpls();
451         printk(KERN_DEBUG "Pid %d (%s) activated for hardwall: cpu %d\n",
452                p->pid, p->comm, cpu);
453         return 0;
454 }
455
456 /*
457  * Deactivate a task's hardwall.  Must hold hardwall_lock.
458  * This method may be called from free_task(), so we don't want to
459  * rely on too many fields of struct task_struct still being valid.
460  * We assume the cpus_allowed, pid, and comm fields are still valid.
461  */
462 static void _hardwall_deactivate(struct task_struct *task)
463 {
464         struct thread_struct *ts = &task->thread;
465
466         if (cpumask_weight(&task->cpus_allowed) != 1) {
467                 pr_err("pid %d (%s) releasing networks with"
468                        " an affinity mask containing %d cpus!\n",
469                        task->pid, task->comm,
470                        cpumask_weight(&task->cpus_allowed));
471                 BUG();
472         }
473
474         BUG_ON(ts->hardwall == NULL);
475         ts->hardwall = NULL;
476         list_del(&ts->hardwall_list);
477         if (task == current)
478                 restrict_network_mpls();
479 }
480
481 /* Deactivate a task's hardwall. */
482 int hardwall_deactivate(struct task_struct *task)
483 {
484         unsigned long flags;
485         int activated;
486
487         spin_lock_irqsave(&hardwall_lock, flags);
488         activated = (task->thread.hardwall != NULL);
489         if (activated)
490                 _hardwall_deactivate(task);
491         spin_unlock_irqrestore(&hardwall_lock, flags);
492
493         if (!activated)
494                 return -EINVAL;
495
496         printk(KERN_DEBUG "Pid %d (%s) deactivated for hardwall: cpu %d\n",
497                task->pid, task->comm, smp_processor_id());
498         return 0;
499 }
500
501 /* Stop a UDN switch before draining the network. */
502 static void stop_udn_switch(void *ignored)
503 {
504 #if !CHIP_HAS_REV1_XDN()
505         /* Freeze the switch and the demux. */
506         __insn_mtspr(SPR_UDN_SP_FREEZE,
507                      SPR_UDN_SP_FREEZE__SP_FRZ_MASK |
508                      SPR_UDN_SP_FREEZE__DEMUX_FRZ_MASK |
509                      SPR_UDN_SP_FREEZE__NON_DEST_EXT_MASK);
510 #endif
511 }
512
513 /* Drain all the state from a stopped switch. */
514 static void drain_udn_switch(void *ignored)
515 {
516 #if !CHIP_HAS_REV1_XDN()
517         int i;
518         int from_tile_words, ca_count;
519
520         /* Empty out the 5 switch point fifos. */
521         for (i = 0; i < 5; i++) {
522                 int words, j;
523                 __insn_mtspr(SPR_UDN_SP_FIFO_SEL, i);
524                 words = __insn_mfspr(SPR_UDN_SP_STATE) & 0xF;
525                 for (j = 0; j < words; j++)
526                         (void) __insn_mfspr(SPR_UDN_SP_FIFO_DATA);
527                 BUG_ON((__insn_mfspr(SPR_UDN_SP_STATE) & 0xF) != 0);
528         }
529
530         /* Dump out the 3 word fifo at top. */
531         from_tile_words = (__insn_mfspr(SPR_UDN_DEMUX_STATUS) >> 10) & 0x3;
532         for (i = 0; i < from_tile_words; i++)
533                 (void) __insn_mfspr(SPR_UDN_DEMUX_WRITE_FIFO);
534
535         /* Empty out demuxes. */
536         while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 0))
537                 (void) __tile_udn0_receive();
538         while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 1))
539                 (void) __tile_udn1_receive();
540         while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 2))
541                 (void) __tile_udn2_receive();
542         while (__insn_mfspr(SPR_UDN_DATA_AVAIL) & (1 << 3))
543                 (void) __tile_udn3_receive();
544         BUG_ON((__insn_mfspr(SPR_UDN_DATA_AVAIL) & 0xF) != 0);
545
546         /* Empty out catch all. */
547         ca_count = __insn_mfspr(SPR_UDN_DEMUX_CA_COUNT);
548         for (i = 0; i < ca_count; i++)
549                 (void) __insn_mfspr(SPR_UDN_CA_DATA);
550         BUG_ON(__insn_mfspr(SPR_UDN_DEMUX_CA_COUNT) != 0);
551
552         /* Clear demux logic. */
553         __insn_mtspr(SPR_UDN_DEMUX_CTL, 1);
554
555         /*
556          * Write switch state; experimentation indicates that 0xc3000
557          * is an idle switch point.
558          */
559         for (i = 0; i < 5; i++) {
560                 __insn_mtspr(SPR_UDN_SP_FIFO_SEL, i);
561                 __insn_mtspr(SPR_UDN_SP_STATE, 0xc3000);
562         }
563 #endif
564 }
565
566 /* Reset random UDN state registers at boot up and during hardwall teardown. */
567 void reset_network_state(void)
568 {
569 #if !CHIP_HAS_REV1_XDN()
570         /* Reset UDN coordinates to their standard value */
571         unsigned int cpu = smp_processor_id();
572         unsigned int x = cpu % smp_width;
573         unsigned int y = cpu / smp_width;
574 #endif
575
576         if (udn_disabled)
577                 return;
578
579 #if !CHIP_HAS_REV1_XDN()
580         __insn_mtspr(SPR_UDN_TILE_COORD, (x << 18) | (y << 7));
581
582         /* Set demux tags to predefined values and enable them. */
583         __insn_mtspr(SPR_UDN_TAG_VALID, 0xf);
584         __insn_mtspr(SPR_UDN_TAG_0, (1 << 0));
585         __insn_mtspr(SPR_UDN_TAG_1, (1 << 1));
586         __insn_mtspr(SPR_UDN_TAG_2, (1 << 2));
587         __insn_mtspr(SPR_UDN_TAG_3, (1 << 3));
588 #endif
589
590         /* Clear out other random registers so we have a clean slate. */
591         __insn_mtspr(SPR_UDN_AVAIL_EN, 0);
592         __insn_mtspr(SPR_UDN_DEADLOCK_TIMEOUT, 0);
593 #if !CHIP_HAS_REV1_XDN()
594         __insn_mtspr(SPR_UDN_REFILL_EN, 0);
595         __insn_mtspr(SPR_UDN_DEMUX_QUEUE_SEL, 0);
596         __insn_mtspr(SPR_UDN_SP_FIFO_SEL, 0);
597 #endif
598
599         /* Start the switch and demux. */
600 #if !CHIP_HAS_REV1_XDN()
601         __insn_mtspr(SPR_UDN_SP_FREEZE, 0);
602 #endif
603 }
604
605 /* Restart a UDN switch after draining. */
606 static void restart_udn_switch(void *ignored)
607 {
608         reset_network_state();
609
610         /* Disable firewall interrupts. */
611         __insn_mtspr(SPR_UDN_DIRECTION_PROTECT, 0);
612         disable_firewall_interrupts();
613 }
614
615 /* Build a struct cpumask containing all valid tiles in bounding rectangle. */
616 static void fill_mask(struct hardwall_info *r, struct cpumask *result)
617 {
618         int x, y, cpu;
619
620         cpumask_clear(result);
621
622         cpu = r->ulhc_y * smp_width + r->ulhc_x;
623         for (y = 0; y < r->height; ++y, cpu += smp_width - r->width) {
624                 for (x = 0; x < r->width; ++x, ++cpu)
625                         cpu_online_set(cpu, result);
626         }
627 }
628
629 /* Last reference to a hardwall is gone, so clear the network. */
630 static void hardwall_destroy(struct hardwall_info *rect)
631 {
632         struct task_struct *task;
633         unsigned long flags;
634         struct cpumask mask;
635
636         /* Make sure this file actually represents a rectangle. */
637         if (rect == NULL)
638                 return;
639
640         /*
641          * Deactivate any remaining tasks.  It's possible to race with
642          * some other thread that is exiting and hasn't yet called
643          * deactivate (when freeing its thread_info), so we carefully
644          * deactivate any remaining tasks before freeing the
645          * hardwall_info object itself.
646          */
647         spin_lock_irqsave(&hardwall_lock, flags);
648         list_for_each_entry(task, &rect->task_head, thread.hardwall_list)
649                 _hardwall_deactivate(task);
650         spin_unlock_irqrestore(&hardwall_lock, flags);
651
652         /* Drain the UDN. */
653         printk(KERN_DEBUG "Clearing hardwall rectangle %dx%d %d,%d\n",
654                rect->width, rect->height, rect->ulhc_x, rect->ulhc_y);
655         fill_mask(rect, &mask);
656         on_each_cpu_mask(&mask, stop_udn_switch, NULL, 1);
657         on_each_cpu_mask(&mask, drain_udn_switch, NULL, 1);
658
659         /* Restart switch and disable firewall. */
660         on_each_cpu_mask(&mask, restart_udn_switch, NULL, 1);
661
662         /* Remove the /proc/tile/hardwall entry. */
663         hardwall_remove_proc(rect);
664
665         /* Now free the rectangle from the list. */
666         spin_lock_irqsave(&hardwall_lock, flags);
667         BUG_ON(!list_empty(&rect->task_head));
668         list_del(&rect->list);
669         spin_unlock_irqrestore(&hardwall_lock, flags);
670         kfree(rect);
671 }
672
673
674 static int hardwall_proc_show(struct seq_file *sf, void *v)
675 {
676         struct hardwall_info *rect = sf->private;
677         char buf[256];
678
679         int rc = cpulist_scnprintf(buf, sizeof(buf), &rect->cpumask);
680         buf[rc++] = '\n';
681         seq_write(sf, buf, rc);
682         return 0;
683 }
684
685 static int hardwall_proc_open(struct inode *inode,
686                               struct file *file)
687 {
688         return single_open(file, hardwall_proc_show, PDE(inode)->data);
689 }
690
691 static const struct file_operations hardwall_proc_fops = {
692         .open           = hardwall_proc_open,
693         .read           = seq_read,
694         .llseek         = seq_lseek,
695         .release        = single_release,
696 };
697
698 static void hardwall_add_proc(struct hardwall_info *rect)
699 {
700         char buf[64];
701         snprintf(buf, sizeof(buf), "%d", rect->id);
702         proc_create_data(buf, 0444, hardwall_proc_dir,
703                          &hardwall_proc_fops, rect);
704 }
705
706 static void hardwall_remove_proc(struct hardwall_info *rect)
707 {
708         char buf[64];
709         snprintf(buf, sizeof(buf), "%d", rect->id);
710         remove_proc_entry(buf, hardwall_proc_dir);
711 }
712
713 int proc_pid_hardwall(struct task_struct *task, char *buffer)
714 {
715         struct hardwall_info *rect = task->thread.hardwall;
716         return rect ? sprintf(buffer, "%d\n", rect->id) : 0;
717 }
718
719 void proc_tile_hardwall_init(struct proc_dir_entry *root)
720 {
721         if (!udn_disabled)
722                 hardwall_proc_dir = proc_mkdir("hardwall", root);
723 }
724
725
726 /*
727  * Character device support via ioctl/close.
728  */
729
730 static long hardwall_ioctl(struct file *file, unsigned int a, unsigned long b)
731 {
732         struct hardwall_info *rect = file->private_data;
733
734         if (_IOC_TYPE(a) != HARDWALL_IOCTL_BASE)
735                 return -EINVAL;
736
737         switch (_IOC_NR(a)) {
738         case _HARDWALL_CREATE:
739                 if (udn_disabled)
740                         return -ENOSYS;
741                 if (rect != NULL)
742                         return -EALREADY;
743                 rect = hardwall_create(_IOC_SIZE(a),
744                                         (const unsigned char __user *)b);
745                 if (IS_ERR(rect))
746                         return PTR_ERR(rect);
747                 file->private_data = rect;
748                 return 0;
749
750         case _HARDWALL_ACTIVATE:
751                 return hardwall_activate(rect);
752
753         case _HARDWALL_DEACTIVATE:
754                 if (current->thread.hardwall != rect)
755                         return -EINVAL;
756                 return hardwall_deactivate(current);
757
758         case _HARDWALL_GET_ID:
759                 return rect ? rect->id : -EINVAL;
760
761         default:
762                 return -EINVAL;
763         }
764 }
765
766 #ifdef CONFIG_COMPAT
767 static long hardwall_compat_ioctl(struct file *file,
768                                   unsigned int a, unsigned long b)
769 {
770         /* Sign-extend the argument so it can be used as a pointer. */
771         return hardwall_ioctl(file, a, (unsigned long)compat_ptr(b));
772 }
773 #endif
774
775 /* The user process closed the file; revoke access to user networks. */
776 static int hardwall_flush(struct file *file, fl_owner_t owner)
777 {
778         struct hardwall_info *rect = file->private_data;
779         struct task_struct *task, *tmp;
780         unsigned long flags;
781
782         if (rect) {
783                 /*
784                  * NOTE: if multiple threads are activated on this hardwall
785                  * file, the other threads will continue having access to the
786                  * UDN until they are context-switched out and back in again.
787                  *
788                  * NOTE: A NULL files pointer means the task is being torn
789                  * down, so in that case we also deactivate it.
790                  */
791                 spin_lock_irqsave(&hardwall_lock, flags);
792                 list_for_each_entry_safe(task, tmp, &rect->task_head,
793                                          thread.hardwall_list) {
794                         if (task->files == owner || task->files == NULL)
795                                 _hardwall_deactivate(task);
796                 }
797                 spin_unlock_irqrestore(&hardwall_lock, flags);
798         }
799
800         return 0;
801 }
802
803 /* This hardwall is gone, so destroy it. */
804 static int hardwall_release(struct inode *inode, struct file *file)
805 {
806         hardwall_destroy(file->private_data);
807         return 0;
808 }
809
810 static const struct file_operations dev_hardwall_fops = {
811         .open           = nonseekable_open,
812         .unlocked_ioctl = hardwall_ioctl,
813 #ifdef CONFIG_COMPAT
814         .compat_ioctl   = hardwall_compat_ioctl,
815 #endif
816         .flush          = hardwall_flush,
817         .release        = hardwall_release,
818 };
819
820 static struct cdev hardwall_dev;
821
822 static int __init dev_hardwall_init(void)
823 {
824         int rc;
825         dev_t dev;
826
827         rc = alloc_chrdev_region(&dev, 0, 1, "hardwall");
828         if (rc < 0)
829                 return rc;
830         cdev_init(&hardwall_dev, &dev_hardwall_fops);
831         rc = cdev_add(&hardwall_dev, dev, 1);
832         if (rc < 0)
833                 return rc;
834
835         return 0;
836 }
837 late_initcall(dev_hardwall_init);