]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/misc/sgi-xp/xpc_partition.c
sgi-xp: eliminate '>>>' in comments
[linux-2.6-omap-h63xx.git] / drivers / misc / sgi-xp / xpc_partition.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (c) 2004-2008 Silicon Graphics, Inc.  All Rights Reserved.
7  */
8
9 /*
10  * Cross Partition Communication (XPC) partition support.
11  *
12  *      This is the part of XPC that detects the presence/absence of
13  *      other partitions. It provides a heartbeat and monitors the
14  *      heartbeats of other partitions.
15  *
16  */
17
18 #include <linux/kernel.h>
19 #include <linux/sysctl.h>
20 #include <linux/cache.h>
21 #include <linux/mmzone.h>
22 #include <linux/nodemask.h>
23 #include <asm/sn/intr.h>
24 #include <asm/sn/sn_sal.h>
25 #include <asm/sn/nodepda.h>
26 #include <asm/sn/addrs.h>
27 #include "xpc.h"
28
29 /* XPC is exiting flag */
30 int xpc_exiting;
31
32 /* this partition's reserved page pointers */
33 struct xpc_rsvd_page *xpc_rsvd_page;
34 static u64 *xpc_part_nasids;
35 u64 *xpc_mach_nasids;
36
37 static int xpc_sizeof_nasid_mask;       /* actual size in bytes of nasid mask */
38 int xpc_nasid_mask_words;       /* actual size in words of nasid mask */
39
40 struct xpc_partition *xpc_partitions;
41
42 /*
43  * Guarantee that the kmalloc'd memory is cacheline aligned.
44  */
45 void *
46 xpc_kmalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
47 {
48         /* see if kmalloc will give us cachline aligned memory by default */
49         *base = kmalloc(size, flags);
50         if (*base == NULL)
51                 return NULL;
52
53         if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
54                 return *base;
55
56         kfree(*base);
57
58         /* nope, we'll have to do it ourselves */
59         *base = kmalloc(size + L1_CACHE_BYTES, flags);
60         if (*base == NULL)
61                 return NULL;
62
63         return (void *)L1_CACHE_ALIGN((u64)*base);
64 }
65
66 /*
67  * Given a nasid, get the physical address of the  partition's reserved page
68  * for that nasid. This function returns 0 on any error.
69  */
70 static u64
71 xpc_get_rsvd_page_pa(int nasid)
72 {
73         enum xp_retval ret;
74         s64 status;
75         u64 cookie = 0;
76         u64 rp_pa = nasid;      /* seed with nasid */
77         u64 len = 0;
78         u64 buf = buf;
79         u64 buf_len = 0;
80         void *buf_base = NULL;
81
82         while (1) {
83
84                 status = sn_partition_reserved_page_pa(buf, &cookie, &rp_pa,
85                                                        &len);
86
87                 dev_dbg(xpc_part, "SAL returned with status=%li, cookie="
88                         "0x%016lx, address=0x%016lx, len=0x%016lx\n",
89                         status, cookie, rp_pa, len);
90
91                 if (status != SALRET_MORE_PASSES)
92                         break;
93
94                 /* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
95                 if (L1_CACHE_ALIGN(len) > buf_len) {
96                         kfree(buf_base);
97                         buf_len = L1_CACHE_ALIGN(len);
98                         buf = (u64)xpc_kmalloc_cacheline_aligned(buf_len,
99                                                                  GFP_KERNEL,
100                                                                  &buf_base);
101                         if (buf_base == NULL) {
102                                 dev_err(xpc_part, "unable to kmalloc "
103                                         "len=0x%016lx\n", buf_len);
104                                 status = SALRET_ERROR;
105                                 break;
106                         }
107                 }
108
109                 ret = xp_remote_memcpy((void *)buf, (void *)rp_pa, buf_len);
110                 if (ret != xpSuccess) {
111                         dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
112                         status = SALRET_ERROR;
113                         break;
114                 }
115         }
116
117         kfree(buf_base);
118
119         if (status != SALRET_OK)
120                 rp_pa = 0;
121
122         dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
123         return rp_pa;
124 }
125
126 /*
127  * Fill the partition reserved page with the information needed by
128  * other partitions to discover we are alive and establish initial
129  * communications.
130  */
131 struct xpc_rsvd_page *
132 xpc_setup_rsvd_page(void)
133 {
134         struct xpc_rsvd_page *rp;
135         u64 rp_pa;
136         unsigned long new_stamp;
137
138         /* get the local reserved page's address */
139
140         preempt_disable();
141         rp_pa = xpc_get_rsvd_page_pa(cpuid_to_nasid(smp_processor_id()));
142         preempt_enable();
143         if (rp_pa == 0) {
144                 dev_err(xpc_part, "SAL failed to locate the reserved page\n");
145                 return NULL;
146         }
147         rp = (struct xpc_rsvd_page *)__va(rp_pa);
148
149         if (rp->SAL_version < 3) {
150                 /* SAL_versions < 3 had a SAL_partid defined as a u8 */
151                 rp->SAL_partid &= 0xff;
152         }
153         BUG_ON(rp->SAL_partid != sn_partition_id);
154
155         if (rp->SAL_partid < 0 || rp->SAL_partid >= xp_max_npartitions) {
156                 dev_err(xpc_part, "the reserved page's partid of %d is outside "
157                         "supported range (< 0 || >= %d)\n", rp->SAL_partid,
158                         xp_max_npartitions);
159                 return NULL;
160         }
161
162         rp->version = XPC_RP_VERSION;
163         rp->max_npartitions = xp_max_npartitions;
164
165         /* establish the actual sizes of the nasid masks */
166         if (rp->SAL_version == 1) {
167                 /* SAL_version 1 didn't set the nasids_size field */
168                 rp->SAL_nasids_size = 128;
169         }
170         xpc_sizeof_nasid_mask = rp->SAL_nasids_size;
171         xpc_nasid_mask_words = DIV_ROUND_UP(xpc_sizeof_nasid_mask,
172                                             BYTES_PER_WORD);
173
174         /* setup the pointers to the various items in the reserved page */
175         xpc_part_nasids = XPC_RP_PART_NASIDS(rp);
176         xpc_mach_nasids = XPC_RP_MACH_NASIDS(rp);
177
178         if (xpc_rsvd_page_init(rp) != xpSuccess)
179                 return NULL;
180
181         /*
182          * Set timestamp of when reserved page was setup by XPC.
183          * This signifies to the remote partition that our reserved
184          * page is initialized.
185          */
186         new_stamp = jiffies;
187         if (new_stamp == 0 || new_stamp == rp->stamp)
188                 new_stamp++;
189         rp->stamp = new_stamp;
190
191         return rp;
192 }
193
194 /*
195  * Get a copy of a portion of the remote partition's rsvd page.
196  *
197  * remote_rp points to a buffer that is cacheline aligned for BTE copies and
198  * is large enough to contain a copy of their reserved page header and
199  * part_nasids mask.
200  */
201 enum xp_retval
202 xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
203                   struct xpc_rsvd_page *remote_rp, u64 *remote_rp_pa)
204 {
205         int i;
206         enum xp_retval ret;
207
208         /* get the reserved page's physical address */
209
210         *remote_rp_pa = xpc_get_rsvd_page_pa(nasid);
211         if (*remote_rp_pa == 0)
212                 return xpNoRsvdPageAddr;
213
214         /* pull over the reserved page header and part_nasids mask */
215         ret = xp_remote_memcpy(remote_rp, (void *)*remote_rp_pa,
216                                XPC_RP_HEADER_SIZE + xpc_sizeof_nasid_mask);
217         if (ret != xpSuccess)
218                 return ret;
219
220         if (discovered_nasids != NULL) {
221                 u64 *remote_part_nasids = XPC_RP_PART_NASIDS(remote_rp);
222
223                 for (i = 0; i < xpc_nasid_mask_words; i++)
224                         discovered_nasids[i] |= remote_part_nasids[i];
225         }
226
227         /* see if the reserved page has been set up by XPC */
228         if (remote_rp->stamp == 0)
229                 return xpRsvdPageNotSet;
230
231         if (XPC_VERSION_MAJOR(remote_rp->version) !=
232             XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
233                 return xpBadVersion;
234         }
235
236         /* check that both remote and local partids are valid for each side */
237         if (remote_rp->SAL_partid < 0 ||
238             remote_rp->SAL_partid >= xp_max_npartitions ||
239             remote_rp->max_npartitions <= sn_partition_id) {
240                 return xpInvalidPartid;
241         }
242
243         if (remote_rp->SAL_partid == sn_partition_id)
244                 return xpLocalPartid;
245
246         return xpSuccess;
247 }
248
249 /*
250  * See if the other side has responded to a partition deactivate request
251  * from us. Though we requested the remote partition to deactivate with regard
252  * to us, we really only need to wait for the other side to disengage from us.
253  */
254 int
255 xpc_partition_disengaged(struct xpc_partition *part)
256 {
257         short partid = XPC_PARTID(part);
258         int disengaged;
259
260         disengaged = !xpc_partition_engaged(partid);
261         if (part->disengage_timeout) {
262                 if (!disengaged) {
263                         if (time_is_after_jiffies(part->disengage_timeout)) {
264                                 /* timelimit hasn't been reached yet */
265                                 return 0;
266                         }
267
268                         /*
269                          * Other side hasn't responded to our deactivate
270                          * request in a timely fashion, so assume it's dead.
271                          */
272
273                         dev_info(xpc_part, "deactivate request to remote "
274                                  "partition %d timed out\n", partid);
275                         xpc_disengage_timedout = 1;
276                         xpc_assume_partition_disengaged(partid);
277                         disengaged = 1;
278                 }
279                 part->disengage_timeout = 0;
280
281                 /* cancel the timer function, provided it's not us */
282                 if (!in_interrupt())
283                         del_singleshot_timer_sync(&part->disengage_timer);
284
285                 DBUG_ON(part->act_state != XPC_P_DEACTIVATING &&
286                         part->act_state != XPC_P_INACTIVE);
287                 if (part->act_state != XPC_P_INACTIVE)
288                         xpc_wakeup_channel_mgr(part);
289
290                 xpc_cancel_partition_deactivation_request(part);
291         }
292         return disengaged;
293 }
294
295 /*
296  * Mark specified partition as active.
297  */
298 enum xp_retval
299 xpc_mark_partition_active(struct xpc_partition *part)
300 {
301         unsigned long irq_flags;
302         enum xp_retval ret;
303
304         dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
305
306         spin_lock_irqsave(&part->act_lock, irq_flags);
307         if (part->act_state == XPC_P_ACTIVATING) {
308                 part->act_state = XPC_P_ACTIVE;
309                 ret = xpSuccess;
310         } else {
311                 DBUG_ON(part->reason == xpSuccess);
312                 ret = part->reason;
313         }
314         spin_unlock_irqrestore(&part->act_lock, irq_flags);
315
316         return ret;
317 }
318
319 /*
320  * Start the process of deactivating the specified partition.
321  */
322 void
323 xpc_deactivate_partition(const int line, struct xpc_partition *part,
324                          enum xp_retval reason)
325 {
326         unsigned long irq_flags;
327
328         spin_lock_irqsave(&part->act_lock, irq_flags);
329
330         if (part->act_state == XPC_P_INACTIVE) {
331                 XPC_SET_REASON(part, reason, line);
332                 spin_unlock_irqrestore(&part->act_lock, irq_flags);
333                 if (reason == xpReactivating) {
334                         /* we interrupt ourselves to reactivate partition */
335                         xpc_request_partition_reactivation(part);
336                 }
337                 return;
338         }
339         if (part->act_state == XPC_P_DEACTIVATING) {
340                 if ((part->reason == xpUnloading && reason != xpUnloading) ||
341                     reason == xpReactivating) {
342                         XPC_SET_REASON(part, reason, line);
343                 }
344                 spin_unlock_irqrestore(&part->act_lock, irq_flags);
345                 return;
346         }
347
348         part->act_state = XPC_P_DEACTIVATING;
349         XPC_SET_REASON(part, reason, line);
350
351         spin_unlock_irqrestore(&part->act_lock, irq_flags);
352
353         /* ask remote partition to deactivate with regard to us */
354         xpc_request_partition_deactivation(part);
355
356         /* set a timelimit on the disengage phase of the deactivation request */
357         part->disengage_timeout = jiffies + (xpc_disengage_timelimit * HZ);
358         part->disengage_timer.expires = part->disengage_timeout;
359         add_timer(&part->disengage_timer);
360
361         dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n",
362                 XPC_PARTID(part), reason);
363
364         xpc_partition_going_down(part, reason);
365 }
366
367 /*
368  * Mark specified partition as inactive.
369  */
370 void
371 xpc_mark_partition_inactive(struct xpc_partition *part)
372 {
373         unsigned long irq_flags;
374
375         dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
376                 XPC_PARTID(part));
377
378         spin_lock_irqsave(&part->act_lock, irq_flags);
379         part->act_state = XPC_P_INACTIVE;
380         spin_unlock_irqrestore(&part->act_lock, irq_flags);
381         part->remote_rp_pa = 0;
382 }
383
384 /*
385  * SAL has provided a partition and machine mask.  The partition mask
386  * contains a bit for each even nasid in our partition.  The machine
387  * mask contains a bit for each even nasid in the entire machine.
388  *
389  * Using those two bit arrays, we can determine which nasids are
390  * known in the machine.  Each should also have a reserved page
391  * initialized if they are available for partitioning.
392  */
393 void
394 xpc_discovery(void)
395 {
396         void *remote_rp_base;
397         struct xpc_rsvd_page *remote_rp;
398         u64 remote_rp_pa;
399         int region;
400         int region_size;
401         int max_regions;
402         int nasid;
403         struct xpc_rsvd_page *rp;
404         u64 *discovered_nasids;
405         enum xp_retval ret;
406
407         remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RP_HEADER_SIZE +
408                                                   xpc_sizeof_nasid_mask,
409                                                   GFP_KERNEL, &remote_rp_base);
410         if (remote_rp == NULL)
411                 return;
412
413         discovered_nasids = kzalloc(sizeof(u64) * xpc_nasid_mask_words,
414                                     GFP_KERNEL);
415         if (discovered_nasids == NULL) {
416                 kfree(remote_rp_base);
417                 return;
418         }
419
420         rp = (struct xpc_rsvd_page *)xpc_rsvd_page;
421
422         /*
423          * The term 'region' in this context refers to the minimum number of
424          * nodes that can comprise an access protection grouping. The access
425          * protection is in regards to memory, IOI and IPI.
426          */
427         max_regions = 64;
428         region_size = sn_region_size;
429
430         switch (region_size) {
431         case 128:
432                 max_regions *= 2;
433         case 64:
434                 max_regions *= 2;
435         case 32:
436                 max_regions *= 2;
437                 region_size = 16;
438                 DBUG_ON(!is_shub2());
439         }
440
441         for (region = 0; region < max_regions; region++) {
442
443                 if (xpc_exiting)
444                         break;
445
446                 dev_dbg(xpc_part, "searching region %d\n", region);
447
448                 for (nasid = (region * region_size * 2);
449                      nasid < ((region + 1) * region_size * 2); nasid += 2) {
450
451                         if (xpc_exiting)
452                                 break;
453
454                         dev_dbg(xpc_part, "checking nasid %d\n", nasid);
455
456                         if (XPC_NASID_IN_ARRAY(nasid, xpc_part_nasids)) {
457                                 dev_dbg(xpc_part, "PROM indicates Nasid %d is "
458                                         "part of the local partition; skipping "
459                                         "region\n", nasid);
460                                 break;
461                         }
462
463                         if (!(XPC_NASID_IN_ARRAY(nasid, xpc_mach_nasids))) {
464                                 dev_dbg(xpc_part, "PROM indicates Nasid %d was "
465                                         "not on Numa-Link network at reset\n",
466                                         nasid);
467                                 continue;
468                         }
469
470                         if (XPC_NASID_IN_ARRAY(nasid, discovered_nasids)) {
471                                 dev_dbg(xpc_part, "Nasid %d is part of a "
472                                         "partition which was previously "
473                                         "discovered\n", nasid);
474                                 continue;
475                         }
476
477                         /* pull over the rsvd page header & part_nasids mask */
478
479                         ret = xpc_get_remote_rp(nasid, discovered_nasids,
480                                                 remote_rp, &remote_rp_pa);
481                         if (ret != xpSuccess) {
482                                 dev_dbg(xpc_part, "unable to get reserved page "
483                                         "from nasid %d, reason=%d\n", nasid,
484                                         ret);
485
486                                 if (ret == xpLocalPartid)
487                                         break;
488
489                                 continue;
490                         }
491
492                         xpc_request_partition_activation(remote_rp,
493                                                          remote_rp_pa, nasid);
494                 }
495         }
496
497         kfree(discovered_nasids);
498         kfree(remote_rp_base);
499 }
500
501 /*
502  * Given a partid, get the nasids owned by that partition from the
503  * remote partition's reserved page.
504  */
505 enum xp_retval
506 xpc_initiate_partid_to_nasids(short partid, void *nasid_mask)
507 {
508         struct xpc_partition *part;
509         u64 part_nasid_pa;
510
511         part = &xpc_partitions[partid];
512         if (part->remote_rp_pa == 0)
513                 return xpPartitionDown;
514
515         memset(nasid_mask, 0, xpc_sizeof_nasid_mask);
516
517         part_nasid_pa = (u64)XPC_RP_PART_NASIDS(part->remote_rp_pa);
518
519         return xp_remote_memcpy(nasid_mask, (void *)part_nasid_pa,
520                                 xpc_sizeof_nasid_mask);
521 }