source: gpfs_3.1_ker2.6.20/lpp/mmfs/src/ibm-linux/cxiAtomic-plat.h @ 148

Last change on this file since 148 was 16, checked in by rock, 17 years ago
File size: 18.0 KB
Line 
1/***************************************************************************
2 *
3 * Copyright (C) 2001 International Business Machines
4 * All rights reserved.
5 *
6 * This file is part of the GPFS mmfslinux kernel module.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 *  1. Redistributions of source code must retain the above copyright notice,
13 *     this list of conditions and the following disclaimer.
14 *  2. Redistributions in binary form must reproduce the above copyright
15 *     notice, this list of conditions and the following disclaimer in the
16 *     documentation and/or other materials provided with the distribution.
17 *  3. The name of the author may not be used to endorse or promote products
18 *     derived from this software without specific prior written
19 *     permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *************************************************************************** */
33/* @(#)98       1.20  src/avs/fs/mmfs/ts/kernext/ibm-linux/cxiAtomic-plat.h, mmfs, avs_rgpfs24, rgpfs240610b 6/24/05 15:57:45 */
34/*
35 * Platform specific synchronization/atomic operations for Linux
36 *
37 * Note that these should not be directly invoked; instead use the
38 * ATOMIC_XXX and ATOMIC_XXXLP macros from <cxiAtomic.h>.
39 *
40 * Definitions for baseline atomic operations (long/pointer variants)
41 *     comp_and_swap(lp)
42 *
43 * Definitions for atomic operations
44 *     fetch_and_add(lp)
45 *     fetch_and_and(lp)
46 *     fetch_and_or(lp)
47 *     compare_and_swap(lp)
48 *     _check_lock
49 *
50 */
51
52#ifndef _h_cxiAtomic_plat
53#define _h_cxiAtomic_plat
54
55#ifndef _h_cxiAtomic
56#error Platform header (XXX-plat.h) should not be included directly
57#endif
58
59/* NOTE: need to further split this file into architecture specific headers. */
60
61#include <cxiTypes.h>
62
63
64/* Memory fencing operations for various architectures */
65
66#if defined(GPFS_ARCH_POWER) || defined(GPFS_ARCH_PPC64)
67#ifndef CONFIG_UP
68#define IMPORT_FENCE __asm__ __volatile__ ("isync" : : )
69#define EXPORT_FENCE __asm__ __volatile__ ("sync" : : )
70#else
71#define IMPORT_FENCE ((void)0)
72#define EXPORT_FENCE ((void)0)
73#endif
74/* A complete fence is defined as insuring that the most recently preceding
75   store is visible to all processors before any subsequent access completes
76   in storage.  For PowerPC MP, the implementations of COMPLETE_FENCE and
77   EXPORT_FENCE are the same. */
78#define COMPLETE_FENCE EXPORT_FENCE
79#endif /* GPFS_ARCH_POWER */
80
81#ifdef GPFS_ARCH_I386
82/* Memory in the I386 architecture is always consistent from all processors,
83   so explicit fence instructions are not needed. */
84#define IMPORT_FENCE ((void)0)
85#define EXPORT_FENCE ((void)0)
86#define COMPLETE_FENCE ((void)0)
87#endif  /* GPFS_ARCH_I386 */
88
89#ifdef GPFS_ARCH_IA64
90/* Only full/complete memory fence available */
91#define IMPORT_FENCE __asm__ __volatile__ ("mf" : : )
92#define EXPORT_FENCE __asm__ __volatile__ ("mf" : : )
93#define COMPLETE_FENCE __asm__ __volatile__ ("mf" : : )
94#endif
95
96#ifdef GPFS_ARCH_X86_64
97#define IMPORT_FENCE __asm__ __volatile__ ("sfence":::"memory")
98#define EXPORT_FENCE __asm__ __volatile__ ("mfence":::"memory")
99#define COMPLETE_FENCE EXPORT_FENCE
100#endif
101
102
103/* Baseline atomic operation for i386: comp_and_swap */
104
105#if defined(GPFS_ARCH_I386)
106
107/* Compare the contents of word_addr with the contents of old_val_addr.
108   If the values are equal, store new_val in word_addr and return 1.
109   Otherwise, set old_val_addr to the current value of word_addr and
110   return 0. See ppc64 comp_and_swaplp for details on exception table
111   code . */
112static inline int
113comp_and_swap(volatile int *word_addr, int *old_val_addr, int new_val)
114{
115   unsigned char result;
116
117   __asm__  __volatile__(
118            "1: lock; cmpxchg %3,%0           \n\
119             2: setz %2                       \n\
120             .section .fixup, \"ax\"          \n\
121             3: jmp 2b                        \n\
122             .previous                        \n\
123             .section __ex_table, \"a\"       \n\
124             .align 4                         \n\
125             .long 1b, 3b                     \n\
126             .previous"
127
128      :"=m" (*word_addr),
129       "=a" (*old_val_addr),
130       "=&b" (result)
131
132      :"r" (new_val),
133       "a" (*old_val_addr)
134
135      :"cc",
136       "memory");
137    return result;
138}
139
140#endif /* GPFS_ARCH_I386 */
141
142/* Baseline atomic operations for x86_64: comp_and_swap and comp_and_swaplp .
143   See ppc64 comp_and_swaplp for details on exception table code. */
144
145#ifdef GPFS_ARCH_X86_64
146
147/* Compare the contents of word_addr with the contents of old_val_addr.
148   If the values are equal, store new_val in word_addr and return 1.
149   Otherwise, set old_val_addr to the current value of word_addr and
150   return 0. See ppc64 comp_and_swaplp for details on exception table
151   code . */
152static inline int
153comp_and_swap(volatile int *word_addr, int *old_val_addr, int new_val)
154{
155   unsigned char result;
156
157   __asm__  __volatile__(
158            "lock; cmpxchg %3,%0           \n\
159             setz %2"
160
161      :"=m" (*word_addr),
162       "=a" (*old_val_addr),
163       "=&b" (result)
164
165      :"r" (new_val),
166       "a" (*old_val_addr)
167
168      :"cc",
169       "memory");
170    return result;
171}
172
173static inline int
174comp_and_swaplp(volatile long *word_addr, long *old_val_addr, long new_val)
175{
176   char result;
177
178   __asm__  __volatile__(
179            "1: lock; cmpxchgq %3,%0          \n\
180             2: setz %2                       \n\
181             .section .fixup, \"ax\"          \n\
182             3: jmp 2b                        \n\
183             .previous                        \n\
184             .section __ex_table, \"a\"       \n\
185             .align 8                         \n\
186             .quad 1b, 3b                     \n\
187             .previous"
188
189            :"=m" (*word_addr),
190             "=a" (*old_val_addr),
191             "=q" (result)
192
193            :"r" (new_val),
194             "a" (*old_val_addr)
195
196            :"cc",
197             "memory");
198    return result;
199}
200#endif /* GPFS_ARCH_X86_64 */
201
202
203/* Baseline atomic operation for power: comp_and_swap */
204
205#if defined(GPFS_ARCH_POWER) || defined(GPFS_ARCH_PPC64)
206
207/* Compare the contents of word_addr with the contents of old_val_addr.
208   If the values are equal, store new_val in word_addr and return 1.
209   Otherwise, set old_val_addr to the current value of word_addr and
210   return 0. */
211static inline int
212comp_and_swap(volatile int *word_addr, int *old_val_addr, int new_val)
213{
214        int result;
215
216        __asm__ __volatile__(
217"1:     lwarx   %0,0,%4        # result = *word_addr                    \n\
218        cmplw   cr0,%0,%3      # compare result to *old_val_addr        \n\
219        bne-    2f             # skip to 2: if mismatch                 \n\
220        stwcx.  %2,0,%4        # *word_addr = new_val                   \n\
221        bne-    1b             # repeat if reservation lost             \n\
222        li      %0,1           # result = 1                             \n\
223        b       3f                                                      \n\
2242:      stw     %0,%1          # *old_val_addr = result                 \n\
225        li      %0,0           # result = 0                             \n\
2263:"
227        // output values
228        : "=&r" (result),       // %0 return value + temp variable
229          "=m" (*old_val_addr)  // %1 changed if mismatch
230
231        // input values
232        : "r" (new_val),        // %2
233          "r" (*old_val_addr),  // %3
234          "r" (word_addr)       // %4
235        : "cr0", "memory");     // "memory" because we modify *word_addr
236
237  return result;
238}
239#endif /* GPFS_ARCH_POWER */
240
241#ifdef GPFS_ARCH_PPC64
242/* This is a regular comp_and_swap function, but with an added twist.
243 * In SLES9 SP1 ppc64, a patch has been added that modifies the page
244 * fault handler to search the exceptions table _before_ an actual
245 * exception happens, in the course of handling a minor page fault
246 * triggered by a store to a userspace address.  If the offending
247 * instruction is not found in the module exception table, the page
248 * fault will result in an Oops, even though the dereferenced address
249 * is actually OK, and would have resulted in a successful store had
250 * it been given a chance to proceed.  This problems occurs in
251 * internalAcquire, where we have to do some atomic store operations on
252 * the lockWord that may be located in the userspace.  To work around
253 * this check, we add exception handling code to all ppc64 atomic ops.
254 * This exception code does absolutely nothing (it transfers control
255 * back to the instruction following the one that triggered the fault),
256 * but that doesn't really matter, as we do not expect the exception
257 * handling code to ever be invoked, we only want search_exception_tables()
258 * not to return false.  If a bad address is passed to internalAcquire,
259 * we'll get an Oops or assert before getting a chance to run any atomic
260 * ops.  See LTC bugzilla 14533 for more details. */
261static inline int
262comp_and_swaplp(volatile long *word_addr, long *old_val_addr, long new_val)
263{ 
264        long result;
265
266        __asm__ __volatile__(
267"1:     ldarx   %0,0,%4        # result = *word_addr                    \n\
2688:      cmpld   cr0,%0,%3      # compare result to *old_val_addr        \n\
269        bne-    2f             # skip to 2: if mismatch                 \n\
2704:      stdcx.  %2,0,%4        # *word_addr = new_val                   \n\
271        .section .fixup, \"ax\"                                         \n\
2725:      b       6f                                                      \n\
2737:      b       8b                                                      \n\
274        .previous                                                       \n\
275        .section __ex_table, \"a\"                                      \n\
276        .align 3                                                        \n\
277        .llong 4b, 5b                                                   \n\
278        .llong 1b, 7b                                                   \n\
279        .previous                                                       \n\
2806:      bne-    1b             # repeat if reservation lost             \n\
281        li      %0,1           # result = 1                             \n\
282        b       3f                                                      \n\
2832:      std     %0,%1          # *old_val_addr = result                 \n\
284        li      %0,0           # result = 0                             \n\
2853:"
286        // output values
287        : "=&r" (result),       // %0 return value + temp variable
288          "=m" (*old_val_addr)  // %1 changed if mismatch
289
290        // input values
291        : "r" (new_val),        // %2
292          "r" (*old_val_addr),  // %3
293          "r" (word_addr)       // %4
294        : "cr0", "memory");     // "memory" because we modify *word_addr
295
296        return (int)result;
297}
298#endif
299
300
301/* Baseline atomic operations for ia64: comp_and_swap and comp_and_swaplp */
302
303/* Found the HP IA64 ISA guide very useful here:
304   http://devresource.hp.com/devresource/Docs/Refs/IA64ISA/ */
305
306#ifdef GPFS_ARCH_IA64
307
308#define MASK_LOWER32 0x00000000FFFFFFFFULL
309
310/* Compare the contents of word_addr with the contents of old_val_addr.
311   If the values are equal, store new_val in word_addr and return 1.
312   Otherwise, set old_val_addr to the current value of word_addr and
313   return 0. */
314
315/* compare and swap 4-byte halfword */
316static inline int
317comp_and_swap(volatile int *word_addr, int *old_val_addr, int new_val)
318{
319  UInt64 old_val = ((UInt64)*old_val_addr) & MASK_LOWER32;
320  UInt64 ret_val;
321
322  /* Ensure mov-to-AR[CCV] in separate instruction group/bundle from cmpxchg
323     to handle RAW dependency */
324  __asm__ __volatile__ ("mov ar.ccv=%0\n\
325                         ;;"
326                         :
327                         : "rO"(old_val));
328  /* Use acquire consistancy sem with cmpxchg
329     (memory write visible to all subsequent data memory accesses) */
330  __asm__ __volatile__ ("cmpxchg4.acq %0=[%1],%2,ar.ccv"
331
332                        : "=r"(ret_val)
333
334                        : "r"(word_addr),
335                          "r"(new_val)
336
337                        : "memory");
338
339  if (ret_val == old_val)
340    return 1;
341  else
342  {
343    *old_val_addr = (int)ret_val;
344    return 0;
345  }
346}
347
348/* compare and swap natural 8-byte word */
349static inline int
350comp_and_swaplp(volatile long *word_addr, long *old_val_addr, long new_val)
351{
352  long ret;
353
354  /* Ensure mov-to-AR[CCV] in separate instruction group/bundle from cmpxchg
355     to handle RAW dependency */
356  __asm__ __volatile__ ("mov ar.ccv=%0\n\
357                        ;;"
358                        :
359                        : "rO"(*old_val_addr));
360
361  /* Use acquire consistancy sem with cmpxchg
362     (memory write visible to all subsequent data memory accesses) */
363  __asm__ __volatile__ ("cmpxchg8.acq %0=[%1],%2,ar.ccv"
364
365                        : "=r"(ret)
366
367                        : "r"(word_addr),
368                          "r"(new_val)
369
370                        : "memory");
371
372  if (ret == *old_val_addr)
373    return 1;
374  else
375  {
376    *old_val_addr = ret;
377    return 0;
378  }
379}
380
381#endif /* GPFS_ARCH_IA64 */
382
383
384/* fetch_and_XXX and fetch_and_XXXlp operations */
385
386/* With inlined functions we cannot use the standard trace statements, so
387   for the atomic operations the USE_LOCK_TRACE must be toggled on to
388   debug these operations (which fortunately shouldn't happen often). */
389#undef USE_LOCK_TRACE
390
391#ifdef USE_LOCK_TRACE
392#ifdef _KERNEL
393#define LOCK_TRACE printk
394#else
395#define LOCK_TRACE printf
396#endif /* _KERNEL */
397#else
398#define LOCK_TRACE(X1,X2,X3,X4,X5,X6)
399#endif /* USE_LOCK_TRACE */
400
401static inline int 
402fetch_and_add(atomic_p wd, int i)
403{
404  int ret, oldVal, newVal;
405  oldVal = cxiSafeGetInt(wd);
406
407  do
408  {
409    newVal = oldVal + i;
410    ret = comp_and_swap((volatile int *)wd, &oldVal, newVal);
411
412    LOCK_TRACE(
413           "fetch_and_add: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
414           wd, *wd, oldVal, newVal, ret);
415
416  } while (ret == 0);
417
418  return oldVal;
419}
420
421#ifdef __64BIT__
422static inline long
423fetch_and_addlp(atomic_l wd, long i)
424{
425  long oldVal, newVal;
426  int  ret;
427
428  oldVal = cxiSafeGetLong(wd);
429
430  do
431  {
432    newVal = oldVal + i;
433    ret = comp_and_swaplp((volatile long *)wd, &oldVal, newVal);
434
435    LOCK_TRACE(
436           "fetch_and_addlp: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
437           wd, *wd, oldVal, newVal, ret);
438
439  } while (ret == 0);
440
441  return oldVal;
442}
443#endif /* __64BIT__ */
444
445static inline int 
446fetch_and_and(atomic_p wd, uint mask)
447{
448  int ret, oldVal,newVal;
449  oldVal = cxiSafeGetInt(wd);
450
451  do
452  {
453    newVal = oldVal & mask;
454    ret = comp_and_swap((volatile int *)wd, &oldVal, newVal);
455
456    LOCK_TRACE(
457           "fetch_and_and: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
458           wd, *wd, oldVal, newVal, ret);
459
460  } while (ret == 0);
461
462  return oldVal;
463}
464
465#ifdef __64BIT__
466static inline long
467fetch_and_andlp(atomic_l wd, ulong  mask)
468{
469  long oldVal,newVal;
470  int ret;
471  oldVal = cxiSafeGetLong(wd);
472
473  do
474  {
475    newVal = oldVal & mask;
476    ret = comp_and_swaplp((volatile long *)wd, &oldVal, newVal);
477
478    LOCK_TRACE(
479           "fetch_and_andlp: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
480           wd, *wd, oldVal, newVal, ret);
481
482  } while (ret == 0);
483
484  return oldVal;
485}
486#endif /* __64BIT__ */
487
488static inline int 
489fetch_and_or(atomic_p wd, uint mask)
490{
491  int ret, oldVal,newVal;
492  oldVal = cxiSafeGetInt(wd);
493
494  do
495  {
496    newVal = oldVal | mask;
497    ret = comp_and_swap((volatile int *)wd, &oldVal, newVal);
498
499    LOCK_TRACE(
500           "fetch_and_or: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
501           wd, *wd, oldVal, newVal, ret);
502
503  } while (ret == 0);
504
505  return oldVal;
506}
507
508#ifdef __64BIT__
509static inline long
510fetch_and_orlp(atomic_l wd, ulong  mask)
511{
512  long oldVal,newVal;
513  int ret;
514  oldVal = cxiSafeGetLong(wd);
515
516  do
517  {
518    newVal = oldVal | mask;
519    ret = comp_and_swaplp((volatile long *)wd, &oldVal, newVal);
520
521    LOCK_TRACE(
522           "fetch_and_orlp: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
523           wd, *wd, oldVal, newVal, ret);
524
525  } while (ret == 0);
526
527  return oldVal;
528}
529#endif /* __64BIT__ */
530
531static inline Boolean
532compare_and_swap(atomic_p wd, int *oldVal, int newVal)
533{
534  Boolean ret;
535
536  ret = comp_and_swap((volatile int *)wd, oldVal, newVal);
537
538  LOCK_TRACE(
539         "compare_and_swap out: wd 0x%lX *wd 0x%lX old 0x%lX "
540         "new 0x%lX ret %d\n", wd, *wd, *oldVal, newVal, ret);
541  return ret;
542}
543
544#ifdef __64BIT__
545static inline Boolean
546compare_and_swaplp(atomic_l wd, long *oldVal, long newVal)
547{
548  Boolean ret;
549
550  ret = comp_and_swaplp((volatile long *)wd, oldVal, newVal);
551
552  LOCK_TRACE(
553         "compare_and_swaplp out: wd 0x%lX *wd 0x%lX old 0x%lX "
554         "new 0x%lX ret %d\n", wd, *wd, *oldVal, newVal, ret);
555  return ret;
556}
557#endif /* __64BIT__ */
558
559static inline Boolean
560_check_lock(atomic_p wd, int oldVal, int newVal)
561{
562    int old_val_addr = oldVal;
563    Boolean  ret;
564
565    ret = comp_and_swap((volatile int *) wd, &old_val_addr, newVal);
566
567    LOCK_TRACE(
568         "_check_lock: wd 0x%X *wd 0x%X old 0x%X new 0x%X ret %d\n",
569          wd, *wd, old_val_addr, newVal, ret);
570
571    if (ret)
572    {
573      IMPORT_FENCE;
574      return 0;
575    }
576    else
577      return 1;
578}
579
580#ifdef __64BIT__
581static inline Boolean
582_check_locklp(atomic_l wd, long oldVal, long newVal)
583{
584    long old_val_addr = oldVal;
585    Boolean  ret;
586
587    ret = comp_and_swaplp((volatile long *) wd, &old_val_addr, newVal);
588
589    LOCK_TRACE(
590         "_check_locklp: wd 0x%lX *wd 0x%lX old 0x%lX new 0x%lX ret %d\n",
591          wd, *wd, old_val_addr, newVal, ret);
592
593    if (ret)
594    {
595      IMPORT_FENCE;
596      return 0;
597    }
598    else
599      return 1;
600}
601#endif /* __64BIT__ */
602
603#endif /* _h_cxiAtomic_plat */
Note: See TracBrowser for help on using the repository browser.