sn1_smp.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:13k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * SN1 Platform specific SMP Support
  3.  *
  4.  * Copyright (C) 2000-2002 Silicon Graphics, Inc. All rights reserved.
  5.  * 
  6.  * This program is free software; you can redistribute it and/or modify it 
  7.  * under the terms of version 2 of the GNU General Public License 
  8.  * as published by the Free Software Foundation.
  9.  * 
  10.  * This program is distributed in the hope that it would be useful, but 
  11.  * WITHOUT ANY WARRANTY; without even the implied warranty of 
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
  13.  * 
  14.  * Further, this software is distributed without any warranty that it is 
  15.  * free of the rightful claim of any third person regarding infringement 
  16.  * or the like.  Any license provided herein, whether implied or 
  17.  * otherwise, applies only to this software file.  Patent licenses, if 
  18.  * any, provided herein do not apply to combinations of this program with 
  19.  * other software, or any other product whatsoever.
  20.  * 
  21.  * You should have received a copy of the GNU General Public 
  22.  * License along with this program; if not, write the Free Software 
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  24.  * 
  25.  * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
  26.  * Mountain View, CA  94043, or:
  27.  * 
  28.  * http://www.sgi.com 
  29.  * 
  30.  * For further information regarding this notice, see: 
  31.  * 
  32.  * http://oss.sgi.com/projects/GenInfo/NoticeExplan
  33.  */
  34. #include <linux/config.h>
  35. #include <linux/init.h>
  36. #include <linux/kernel.h>
  37. #include <linux/spinlock.h>
  38. #include <linux/threads.h>
  39. #include <linux/sched.h>
  40. #include <linux/smp.h>
  41. #include <linux/interrupt.h>
  42. #include <linux/irq.h>
  43. #include <linux/mmzone.h>
  44. #include <asm/processor.h>
  45. #include <asm/irq.h>
  46. #include <asm/sal.h>
  47. #include <asm/system.h>
  48. #include <asm/io.h>
  49. #include <asm/smp.h>
  50. #include <asm/hw_irq.h>
  51. #include <asm/current.h>
  52. #include <asm/delay.h>
  53. #include <asm/sn/sn_cpuid.h>
  54. /*
  55.  * The following structure is used to pass params thru smp_call_function
  56.  * to other cpus for flushing TLB ranges.
  57.  */
  58. typedef struct {
  59. unsigned long start;
  60. unsigned long end;
  61. unsigned long nbits;
  62. unsigned int rid;
  63. atomic_t unfinished_count;
  64. } ptc_params_t;
  65. #define NUMPTC 512
  66. static ptc_params_t ptcParamArray[NUMPTC] __attribute__((__aligned__(128)));
  67. /* use separate cache lines on ptcParamsNextByCpu to avoid false sharing */
  68. static ptc_params_t *ptcParamsNextByCpu[NR_CPUS*16] __attribute__((__aligned__(128)));
  69. static volatile ptc_params_t *ptcParamsEmpty __cacheline_aligned;
  70. /*REFERENCED*/
  71. static spinlock_t ptcParamsLock __cacheline_aligned = SPIN_LOCK_UNLOCKED;
  72. static int ptcInit = 0;
  73. #ifdef PTCDEBUG
  74. static int ptcParamsAllBusy = 0; /* debugging/statistics */
  75. static int ptcCountBacklog = 0;
  76. static int ptcBacklog[NUMPTC+1];
  77. static char ptcParamsCounts[NR_CPUS][NUMPTC] __attribute__((__aligned__(128)));
  78. static char ptcParamsResults[NR_CPUS][NUMPTC] __attribute__((__aligned__(128)));
  79. #endif
  80. /*
  81.  * Make smp_send_flush_tlbsmp_send_flush_tlb() a weak reference,
  82.  * so that we get a clean compile with the ia64 patch without the
  83.  * actual SN1 specific code in arch/ia64/kernel/smp.c.
  84.  */
  85. extern void smp_send_flush_tlb (void) __attribute((weak));
  86. /*
  87.  * The following table/struct is for remembering PTC coherency domains. It
  88.  * is also used to translate sapicid into cpuids. We dont want to start 
  89.  * cpus unless we know their cache domain.
  90.  */
  91. #ifdef PTC_NOTYET
  92. sn_sapicid_info_t sn_sapicid_info[NR_CPUS];
  93. #endif
  94. /**
  95.  * sn1_ptc_l_range - purge local translation cache
  96.  * @start: start of virtual address range
  97.  * @end: end of virtual address range
  98.  * @nbits: specifies number of bytes to purge per instruction (num = 1<<(nbits & 0xfc))
  99.  *
  100.  * Purges the range specified from the local processor's translation cache
  101.  * (as opposed to the translation registers).  Note that more than the specified
  102.  * range *may* be cleared from the cache by some processors.
  103.  *
  104.  * This is probably not good enough, but I don't want to try to make it better 
  105.  * until I get some statistics on a running system. At a minimum, we should only 
  106.  * send IPIs to 1 processor in each TLB domain & have it issue a ptc.g on it's 
  107.  * own FSB. Also, we only have to serialize per FSB, not globally.
  108.  *
  109.  * More likely, we will have to do some work to reduce the frequency of calls to
  110.  * this routine.
  111.  */
  112. static inline void
  113. sn1_ptc_l_range(unsigned long start, unsigned long end, unsigned long nbits)
  114. {
  115. do {
  116. __asm__ __volatile__ ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory");
  117. start += (1UL << nbits);
  118. } while (start < end);
  119. ia64_srlz_d();
  120. }
  121. /**
  122.  * sn1_received_flush_tlb - cpu tlb flush routine
  123.  *
  124.  * Flushes the TLB of a given processor.
  125.  */
  126. void
  127. sn1_received_flush_tlb(void)
  128. {
  129. unsigned long start, end, nbits;
  130. unsigned int rid, saved_rid;
  131. int cpu = smp_processor_id();
  132. int result;
  133. ptc_params_t *ptcParams;
  134. ptcParams = ptcParamsNextByCpu[cpu*16];
  135. if (ptcParams == ptcParamsEmpty)
  136. return;
  137. do {
  138. start = ptcParams->start;
  139. saved_rid = (unsigned int) ia64_get_rr(start);
  140. end = ptcParams->end;
  141. nbits = ptcParams->nbits;
  142. rid = ptcParams->rid;
  143. if (saved_rid != rid) {
  144. ia64_set_rr(start, (unsigned long)rid);
  145. ia64_srlz_d();
  146. }
  147. sn1_ptc_l_range(start, end, nbits);
  148. if (saved_rid != rid) 
  149. ia64_set_rr(start, (unsigned long)saved_rid);
  150. ia64_srlz_i();
  151. result = atomic_dec(&ptcParams->unfinished_count);
  152. #ifdef PTCDEBUG
  153. {
  154.     int i = ptcParams-&ptcParamArray[0];
  155.     ptcParamsResults[cpu][i] = (char) result;
  156.     ptcParamsCounts[cpu][i]++;
  157. }
  158. #endif /* PTCDEBUG */
  159. if (++ptcParams == &ptcParamArray[NUMPTC])
  160. ptcParams = &ptcParamArray[0];
  161. } while (ptcParams != ptcParamsEmpty);
  162. ptcParamsNextByCpu[cpu*16] = ptcParams;
  163. }
  164. /**
  165.  * sn1_global_tlb_purge - flush a translation cache range on all processors
  166.  * @start: start of virtual address range to flush
  167.  * @end: end of virtual address range
  168.  * @nbits: specifies number of bytes to purge per instruction (num = 1<<(nbits & 0xfc))
  169.  *
  170.  * Flushes the translation cache of all processors from @start to @end.
  171.  */
  172. void
  173. sn1_global_tlb_purge (unsigned long start, unsigned long end, unsigned long nbits)
  174. {
  175. ptc_params_t *params;
  176. ptc_params_t *next;
  177. unsigned long irqflags;
  178. #ifdef PTCDEBUG
  179. ptc_params_t *nextnext;
  180. int backlog = 0;
  181. #endif
  182. if (smp_num_cpus == 1) {
  183. sn1_ptc_l_range(start, end, nbits);
  184. return;
  185. }
  186. if (in_interrupt()) {
  187. /*
  188.  *  If at interrupt level and cannot get spinlock, 
  189.  *  then do something useful by flushing own tlbflush queue
  190.  *  so as to avoid a possible deadlock.
  191.  */
  192. while (!spin_trylock(&ptcParamsLock)) {
  193. local_irq_save(irqflags);
  194. sn1_received_flush_tlb();
  195. local_irq_restore(irqflags);
  196. udelay(10); /* take it easier on the bus */
  197. }
  198. } else {
  199. spin_lock(&ptcParamsLock);
  200. }
  201. if (!ptcInit) {
  202. int cpu;
  203. ptcInit = 1;
  204. memset(ptcParamArray, 0, sizeof(ptcParamArray));
  205. ptcParamsEmpty = &ptcParamArray[0];
  206. for (cpu=0; cpu<NR_CPUS; cpu++)
  207. ptcParamsNextByCpu[cpu*16] = &ptcParamArray[0];
  208. #ifdef PTCDEBUG
  209. memset(ptcBacklog, 0, sizeof(ptcBacklog));
  210. memset(ptcParamsCounts, 0, sizeof(ptcParamsCounts));
  211. memset(ptcParamsResults, 0, sizeof(ptcParamsResults));
  212. #endif /* PTCDEBUG */
  213. }
  214. params = (ptc_params_t *) ptcParamsEmpty;
  215. next = (ptc_params_t *) ptcParamsEmpty + 1;
  216. if (next == &ptcParamArray[NUMPTC])
  217. next = &ptcParamArray[0];
  218. #ifdef PTCDEBUG
  219. nextnext = next + 1;
  220. if (nextnext == &ptcParamArray[NUMPTC])
  221. nextnext = &ptcParamArray[0];
  222. if (ptcCountBacklog) {
  223. /* quick count of backlog */
  224. ptc_params_t *ptr;
  225. /* check the current pointer to the beginning */
  226. ptr = params;
  227. while(--ptr >= &ptcParamArray[0]) {
  228. if (atomic_read(&ptr->unfinished_count) == 0)
  229. break;
  230. ++backlog;
  231. }
  232. if (backlog) {
  233. /* check the end of the array */
  234. ptr = &ptcParamArray[NUMPTC];
  235. while (--ptr > params) {
  236. if (atomic_read(&ptr->unfinished_count) == 0)
  237. break;
  238. ++backlog;
  239. }
  240. }
  241. ptcBacklog[backlog]++;
  242. }
  243. #endif /* PTCDEBUG */
  244. /* wait for the next entry to clear...should be rare */
  245. if (atomic_read(&next->unfinished_count) > 0) {
  246. #ifdef PTCDEBUG
  247. ptcParamsAllBusy++;
  248. if (atomic_read(&nextnext->unfinished_count) == 0) {
  249.     if (atomic_read(&next->unfinished_count) > 0) {
  250. panic("nnonzero next zero nextnext %lx %lxn",
  251.     (long)next, (long)nextnext);
  252.     }
  253. }
  254. #endif
  255. /* it could be this cpu that is behind */
  256. local_irq_save(irqflags);
  257. sn1_received_flush_tlb();
  258. local_irq_restore(irqflags);
  259. /* now we know it's not this cpu, so just wait */
  260. while (atomic_read(&next->unfinished_count) > 0) {
  261. barrier();
  262. }
  263. }
  264. params->start = start;
  265. params->end = end;
  266. params->nbits = nbits;
  267. params->rid = (unsigned int) ia64_get_rr(start);
  268. atomic_set(&params->unfinished_count, smp_num_cpus);
  269. /* The atomic_set above can hit memory *after* the update
  270.  * to ptcParamsEmpty below, which opens a timing window
  271.  * that other cpus can squeeze into!
  272.  */
  273. mb();
  274. /* everything is ready to process:
  275.  * -- global lock is held
  276.  * -- new entry + 1 is free
  277.  * -- new entry is set up
  278.  * so now:
  279.  * -- update the global next pointer
  280.  * -- unlock the global lock
  281.  * -- send IPI to notify other cpus
  282.  * -- process the data ourselves
  283.  */
  284. ptcParamsEmpty = next;
  285. spin_unlock(&ptcParamsLock);
  286. smp_send_flush_tlb();
  287. local_irq_save(irqflags);
  288. sn1_received_flush_tlb();
  289. local_irq_restore(irqflags);
  290. /* Currently we don't think global TLB purges need to be atomic.
  291.  * All CPUs get sent IPIs, so if they haven't done the purge,
  292.  * they're busy with interrupts that are at the IPI level, which is
  293.  * priority 15.  We're asserting that any code at that level
  294.  * shouldn't be using user TLB entries.  To change this to wait
  295.  * for all the flushes to complete, enable the following code.
  296.  */
  297. #ifdef SN1_SYNCHRONOUS_GLOBAL_TLB_PURGE
  298. /* this code is not tested */
  299. /* wait for the flush to complete */
  300. while (atomic_read(&params.unfinished_count) > 1)
  301. barrier();
  302. atomic_set(&params->unfinished_count, 0);
  303. #endif
  304. }
  305. /**
  306.  * sn_send_IPI_phys - send an IPI to a Nasid and slice
  307.  * @physid: physical cpuid to receive the interrupt.
  308.  * @vector: command to send
  309.  * @delivery_mode: delivery mechanism
  310.  *
  311.  * Sends an IPI (interprocessor interrupt) to the processor specified by
  312.  * @physid
  313.  *
  314.  * @delivery_mode can be one of the following
  315.  *
  316.  * %IA64_IPI_DM_INT - pend an interrupt
  317.  * %IA64_IPI_DM_PMI - pend a PMI
  318.  * %IA64_IPI_DM_NMI - pend an NMI
  319.  * %IA64_IPI_DM_INIT - pend an INIT interrupt
  320.  */
  321. void
  322. sn_send_IPI_phys(long physid, int vector, int delivery_mode)
  323. {
  324. long *p;
  325. long nasid, slice;
  326. static int  off[4] = {0x1800080, 0x1800088, 0x1a00080, 0x1a00088};
  327. nasid = cpu_physical_id_to_nasid(physid);
  328.         slice = cpu_physical_id_to_slice(physid);
  329. p = (long*)(0xc0000a0000000000LL | (nasid<<33) | off[slice]);
  330. #if defined(ZZZBRINGUP)
  331. {
  332. static int count=0;
  333. if (count++ < 10) printk("ZZ sendIPI 0x%x vec %d, nasid 0x%lx, slice %ld, adr 0x%lxn",
  334. smp_processor_id(), vector, nasid, slice, (long)p);
  335. }
  336. #endif
  337. mb();
  338. *p = (delivery_mode << 8) | (vector & 0xff);
  339. }
  340. /**
  341.  * sn1_send_IPI - send an IPI to a processor
  342.  * @cpuid: target of the IPI
  343.  * @vector: command to send
  344.  * @delivery_mode: delivery mechanism
  345.  * @redirect: redirect the IPI?
  346.  *
  347.  * Sends an IPI (interprocessor interrupt) to the processor specified by
  348.  * @cpuid.  @delivery_mode can be one of the following
  349.  *
  350.  * %IA64_IPI_DM_INT - pend an interrupt
  351.  * %IA64_IPI_DM_PMI - pend a PMI
  352.  * %IA64_IPI_DM_NMI - pend an NMI
  353.  * %IA64_IPI_DM_INIT - pend an INIT interrupt
  354.  */
  355. void
  356. sn1_send_IPI(int cpuid, int vector, int delivery_mode, int redirect)
  357. {
  358. long physid;
  359. physid = cpu_physical_id(cpuid);
  360. sn_send_IPI_phys(physid, vector, delivery_mode);
  361. }
  362. #ifdef CONFIG_SMP
  363. #ifdef PTC_NOTYET
  364. static void __init
  365. process_sal_ptc_domain_info(ia64_sal_ptc_domain_info_t *di, int domain)
  366. {
  367. ia64_sal_ptc_domain_proc_entry_t *pe;
  368. int  i, sapicid, cpuid;
  369. pe = __va(di->proc_list);
  370. for (i=0; i<di->proc_count; i++, pe++) {
  371. sapicid = id_eid_to_sapicid(pe->id, pe->eid);
  372. cpuid = cpu_logical_id(sapicid);
  373. sn_sapicid_info[cpuid].domain = domain;
  374. sn_sapicid_info[cpuid].sapicid = sapicid;
  375. }
  376. }
  377. static void __init
  378. process_sal_desc_ptc(ia64_sal_desc_ptc_t *ptc)
  379. {
  380. ia64_sal_ptc_domain_info_t *di;
  381. int i;
  382. di = __va(ptc->domain_info);
  383. for (i=0; i<ptc->num_domains; i++, di++) {
  384. process_sal_ptc_domain_info(di, i);
  385. }
  386. }
  387. #endif /* PTC_NOTYET */
  388. /**
  389.  * init_sn1_smp_config - setup PTC domains per processor
  390.  */
  391. void __init
  392. init_sn1_smp_config(void)
  393. {
  394. if (!ia64_ptc_domain_info)  {
  395. printk("SMP: Can't find PTC domain info. Forcing UP moden");
  396. smp_num_cpus = 1;
  397. return;
  398. }
  399. #ifdef PTC_NOTYET
  400. memset (sn_sapicid_info, -1, sizeof(sn_sapicid_info));
  401. process_sal_desc_ptc(ia64_ptc_domain_info);
  402. #endif
  403. }
  404. #else /* CONFIG_SMP */
  405. void __init
  406. init_sn1_smp_config(void)
  407. {
  408. #ifdef PTC_NOTYET
  409. sn_sapicid_info[0].sapicid = hard_smp_processor_id();
  410. #endif
  411. }
  412. #endif /* CONFIG_SMP */