source: soft/giet_vm/giet_common/kernel_locks.c @ 495

Last change on this file since 495 was 495, checked in by alain, 9 years ago

Introduce quad tree for distributed locks and barriers.

File size: 17.0 KB
RevLine 
[455]1///////////////////////////////////////////////////////////////////////////////////
[495]2// File     : kernel_locks.c
[455]3// Date     : 01/12/2014
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
7
[495]8#include "kernel_locks.h"
[455]9#include "giet_config.h"
[466]10#include "hard_config.h"
[455]11#include "utils.h"
[466]12#include "tty0.h"
13#include "kernel_malloc.h"
[495]14#include "io.h"
[455]15
16///////////////////////////////////////////////////
17unsigned int _atomic_increment( unsigned int* ptr,
[495]18                                int           increment )
[455]19{
20    unsigned int value;
21
22    asm volatile (
23        "1234:                         \n"
24        "move $10,   %1                \n"   /* $10 <= ptr               */
25        "move $11,   %2                \n"   /* $11 <= increment         */
26        "ll   $12,   0($10)            \n"   /* $12 <= *ptr              */
27        "addu $13,   $11,    $12       \n"   /* $13 <= *ptr + increment  */
28        "sc   $13,   0($10)            \n"   /* M[ptr] <= new            */ 
29        "beqz $13,   1234b             \n"   /* retry if failure         */
30        "move %0,    $12               \n"   /* value <= *ptr if success */
31        : "=r" (value) 
32        : "r" (ptr), "r" (increment)
33        : "$10", "$11", "$12", "$13", "memory" );
34
35    return value;
36}
37
[495]38
[466]39///////////////////////////////////////////////////////////////////////////////////
40//      Simple lock access functions
41///////////////////////////////////////////////////////////////////////////////////
42
43////////////////////////////////////////////////
44void _simple_lock_acquire( simple_lock_t* lock )
[455]45{
[466]46
47#if GIET_DEBUG_SIMPLE_LOCK
48unsigned int    gpid = _get_procid();
49unsigned int    x    = gpid >> (Y_WIDTH + P_WIDTH);
50unsigned int    y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
51unsigned int    l    = gpid & ((1<<P_WIDTH)-1);
52_nolock_printf("\n[DEBUG SIMPLE_LOCK] P[%d,%d,%d] enters acquire() at cycle %d\n",
53               x , y , l , _get_proctime() );
54#endif
55
56    asm volatile ( "1515:                   \n"
[495]57                       "lw   $2,    0(%0)       \n"
58                       "bnez $2,    1515b       \n"
59                   "ll   $2,    0(%0)       \n"
60                   "bnez $2,    1515b       \n"
61                   "li   $3,    1           \n"
62                   "sc   $3,    0(%0)       \n"
63                   "beqz $3,    1515b       \n"
[466]64                   :
65                   : "r"(lock)
66                   : "$2", "$3", "memory" );
67
68#if GIET_DEBUG_SIMPLE_LOCK
69_nolock_printf("\n[DEBUG SIMPLE_LOCK] P[%d,%d,%d] exit acquire() at cycle %d\n",
70               x , y , l , _get_proctime() );
71#endif
72
73}
74
75////////////////////////////////////////////////
76void _simple_lock_release( simple_lock_t* lock )
77{
[495]78    asm volatile ( "sync" );   // for consistency
[466]79
[495]80    lock->value = 0;
81
[466]82#if GIET_DEBUG_SIMPLE_LOCK
83unsigned int    gpid = _get_procid();
84unsigned int    x    = gpid >> (Y_WIDTH + P_WIDTH);
85unsigned int    y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
86unsigned int    l    = gpid & ((1<<P_WIDTH)-1);
87_nolock_printf("\n[DEBUG SIMPLE_LOCK] P[%d,%d,%d] release() at cycle %d\n",
88               x , y , l , _get_proctime() );
89#endif
90
91}
92
93
94///////////////////////////////////////////////////////////////////////////////////
95//      Queuing Lock access functions
96///////////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////
99void _spin_lock_init( spin_lock_t* lock )
100{
[455]101    lock->current = 0;
102    lock->free    = 0;
103
[466]104#if GIET_DEBUG_SPIN_LOCK
[455]105unsigned int    gpid = _get_procid();
106unsigned int    x    = gpid >> (Y_WIDTH + P_WIDTH);
107unsigned int    y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
108unsigned int    l    = gpid & ((1<<P_WIDTH)-1);
[495]109_nolock_printf("\n[DEBUG SPIN_LOCK] P[%d,%d,%d] initializes lock %x at cycle %d\n",
110               x, y, l, (unsigned int)lock, _get_proctime() );
[455]111#endif
112
113}
114
115
[466]116////////////////////////////////////////////
117void _spin_lock_acquire( spin_lock_t* lock )
[455]118{
119    // get next free slot index fromlock
120    unsigned int ticket = _atomic_increment( &lock->free, 1 );
121
[466]122#if GIET_DEBUG_SPIN_LOCK
[455]123unsigned int    gpid = _get_procid();
124unsigned int    x    = gpid >> (Y_WIDTH + P_WIDTH);
125unsigned int    y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
126unsigned int    l    = gpid & ((1<<P_WIDTH)-1);
[495]127_nolock_printf("\n[DEBUG SPIN_LOCK] P[%d,%d,%d] get ticket %d for lock %x at cycle %d"
128               " / current = %d / free = %d\n",
129               x, y, l, ticket, (unsigned int)lock, _get_proctime(),
130               lock->current, lock->free );
[455]131#endif
132
133    // poll the spin_lock current slot index
[495]134    while ( ioread32( &lock->current ) != ticket ) asm volatile ("nop");
[455]135
[466]136#if GIET_DEBUG_SPIN_LOCK
[495]137_nolock_printf("\n[DEBUG SPIN_LOCK] P[%d,%d,%d] get lock %x at cycle %d"
138               " / current = %d / free = %d\n",
139               x, y, l, (unsigned int)lock, _get_proctime(),
140               lock->current, lock->free );
[455]141#endif
142
143}
144
[466]145////////////////////////////////////////////
146void _spin_lock_release( spin_lock_t* lock )
[455]147{
[495]148    asm volatile ( "sync" );   // for consistency
[455]149
[495]150    lock->current = lock->current + 1;
[455]151
[466]152#if GIET_DEBUG_SPIN_LOCK
[495]153_nolock_printf("\n[DEBUG SPIN_LOCK] P[%d,%d,%d] release lock %x at cycle %d"
154               " / current = %d / free = %d\n",
155               x, y, l, (unsigned int)lock, _get_proctime(),
156               lock->current, lock->free );
[455]157#endif
158
159}
160
[495]161
162
[466]163///////////////////////////////////////////////////////////////////////////////////
[495]164//      SQT lock access functions
[466]165///////////////////////////////////////////////////////////////////////////////////
[455]166
[466]167///////////////////////////////////////////////////////////////////////////////////
[495]168// This recursive function is used by the _sqt_lock_init() function
169// to initializes the SQT nodes (mainly the parent and child pointers).
170// It traverses the SQT from top to bottom.
171// The SQT can be uncomplete (when xmax or ymax are not power of 2),
172// and the recursion stops when the (x,y) coordinates exceed the footprint.
[466]173///////////////////////////////////////////////////////////////////////////////////
[495]174static 
175void _sqt_lock_build( sqt_lock_t*      lock,      // pointer on the SQT lock
176                      unsigned int     x,         // node X coordinate
177                      unsigned int     y,         // node Y coordinate
178                      unsigned int     level,     // node level
179                      sqt_lock_node_t* parent,    // pointer on parent node
180                      unsigned int     xmax,      // SQT X size
181                      unsigned int     ymax )     // SQT Y size
[466]182{
[455]183
[495]184#if GIET_DEBUG_SQT_LOCK
[466]185unsigned int    gpid = _get_procid();
186unsigned int    px   = gpid >> (Y_WIDTH + P_WIDTH);
187unsigned int    py   = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
188unsigned int    pl   = gpid & ((1<<P_WIDTH)-1);
189#endif
[455]190
[466]191    // get target node pointer
[495]192    sqt_lock_node_t* node = lock->node[x][y][level];
[466]193   
194    if (level == 0 )        // terminal case
195    {
196        // initializes target node
[495]197        node->current  = 0;   
198        node->free     = 0;
199        node->level    = 0;
[466]200        node->parent   = parent;
[495]201        node->child[0] = NULL;
202        node->child[1] = NULL;
203        node->child[2] = NULL;
204        node->child[3] = NULL;
[455]205
[495]206#if GIET_DEBUG_SQT_LOCK
207_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] initialises SQT node[%d,%d,%d] : \n"
208      " parent = %x / childO = %x / child1 = %x / child2 = %x / child3 = %x\n",
209      px , py , pl , x , y , level , 
210      (unsigned int)node->parent , 
211      (unsigned int)node->child[0] , 
212      (unsigned int)node->child[1] , 
213      (unsigned int)node->child[2] , 
214      (unsigned int)node->child[3] );
[466]215#endif
[455]216
[466]217    }
218    else                   // non terminal case
219    {
[495]220        unsigned int cx[4];      // x coordinate for children
221        unsigned int cy[4];      // y coordinate for children
222        unsigned int i;          // child index
[455]223
[466]224        // the child0 coordinates are equal to the parent coordinates
[495]225        // other childs coordinates are incremented depending on the level value
226        cx[0] = x;
227        cy[0] = y;
228
229        cx[1] = x + (1 << (level-1));
230        cy[1] = y;
231
232        cx[2] = x;
233        cy[2] = y + (1 << (level-1));
234
235        cx[3] = x + (1 << (level-1));
236        cy[3] = y + (1 << (level-1));
237
238        // initializes target node
239        for ( i = 0 ; i < 4 ; i++ )
[466]240        {
[495]241            if ( (cx[i] < xmax) && (cy[i] < ymax) ) 
242                node->child[i] = lock->node[cx[i]][cy[i]][level-1];
243            else 
244                node->child[i] = NULL;
[466]245        }
[495]246        node->current  = 0;
247        node->free     = 0;
[466]248        node->level    = level;
249        node->parent   = parent;
250
[495]251#if GIET_DEBUG_SQT_LOCK
252_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] initialises SQT node[%d,%d,%d] : \n"
253      " parent = %x / childO = %x / child1 = %x / child2 = %x / child3 = %x\n",
[466]254      px , py , pl , x , y , level , 
[495]255      (unsigned int)node->parent , 
256      (unsigned int)node->child[0] , 
257      (unsigned int)node->child[1] , 
258      (unsigned int)node->child[2] , 
259      (unsigned int)node->child[3] );
[466]260#endif
261
[495]262       // recursive calls for children nodes
263        for ( i = 0 ; i < 4 ; i++ )
264        {
265            if ( (cx[i] < xmax) && (cy[i] < ymax) ) 
266                _sqt_lock_build( lock, 
267                                 cx[i], 
268                                 cy[i], 
269                                 level-1, 
270                                 node, 
271                                 xmax, 
272                                 ymax );
273        }
[466]274    }
[495]275}  // end _sqt_lock_build()
[466]276
277/////////////////////////////////////////////////////////////////////////////////
[495]278// This external function initialises the distributed SQT lock.
279// It allocates memory for the distributed SQT nodes in clusters,
280// and initializes the SQT nodes pointers array (stored in cluster[0][0].
281// The SQT can be "uncomplete" as SQT lock nodes are only build in clusters
282// containing processors.
283// The actual number of SQT locks nodes in a cluster[x][y] depends on (x,y):
284// At least 1 node / at most 5 nodes per cluster:
285// - lock arbitrating between all processors of   1 cluster  has level 0,
286// - lock arbitrating between all processors of   4 clusters has level 1,
287// - lock arbitrating between all processors of  16 clusters has level 2,
288// - lock arbitrating between all processors of  64 clusters has level 3,
289// - lock arbitrating between all processors of 256 clusters has level 4,
[466]290/////////////////////////////////////////////////////////////////////////////////
[495]291void _sqt_lock_init( sqt_lock_t*  lock )
[466]292{
[495]293    unsigned int levels;
294    unsigned int xmax;
295    unsigned int ymax;
[466]296
[495]297    // compute the smallest SQT covering all processors
298    _get_sqt_footprint( &xmax, &ymax, &levels );
[466]299
300
[495]301#if GIET_DEBUG_SQT_LOCK
[466]302unsigned int    gpid = _get_procid();
303unsigned int    px   = gpid >> (Y_WIDTH + P_WIDTH);
304unsigned int    py   = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
305unsigned int    pl   = gpid & ((1<<P_WIDTH)-1);
[495]306_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] initialises SQT lock %x : \n"
307               " xmax = %d / ymax = %d / levels = %d\n",
308               px , py , pl , (unsigned int)lock , 
309               xmax , ymax , levels );
[466]310#endif
311
[495]312   
313    unsigned int x;              // x coordinate for one SQT node
314    unsigned int y;              // y coordinate for one SQT node
315    unsigned int l;              // level for one SQT node
316
317    for ( x = 0 ; x < xmax ; x++ )
[466]318    {
[495]319        for ( y = 0 ; y < ymax ; y++ )
[466]320        {
321            for ( l = 0 ; l < levels ; l++ )             // level 0 nodes
322            {
323               
324                if ( ( (l == 0) && ((x&0x00) == 0) && ((y&0x00) == 0) ) ||
[495]325                     ( (l == 1) && ((x&0x01) == 0) && ((y&0x01) == 0) ) ||
326                     ( (l == 2) && ((x&0x03) == 0) && ((y&0x03) == 0) ) ||
327                     ( (l == 3) && ((x&0x07) == 0) && ((y&0x07) == 0) ) ||
328                     ( (l == 4) && ((x&0x0F) == 0) && ((y&0x0F) == 0) ) )
[466]329                 {
[495]330                     lock->node[x][y][l] = 
331                     (sqt_lock_node_t*)_remote_malloc( sizeof(sqt_lock_node_t),
332                                                       x, y );
[466]333
[495]334#if GIET_DEBUG_SQT_LOCK
335_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] allocates SQT node[%d,%d,%d]"
336               " : vaddr = %x\n",
[466]337               px , py , pl , x , y , l , (unsigned int)lock->node[x][y][l] );
338#endif
339                 }
340            }
341        }
342    }
343           
[495]344    // recursively initialize all SQT nodes from root to bottom
345    _sqt_lock_build( lock,       // pointer on the SQT lock descriptor
[466]346                     0,          // x coordinate
347                     0,          // y coordinate
[495]348                     levels-1,   // level in SQT
349                     NULL,       // pointer on the parent node
350                     xmax,       // SQT footprint X size
351                     ymax );     // SQT footprint X size
[466]352
353    asm volatile ("sync" ::: "memory");
354
[495]355#if GIET_DEBUG_SQT_LOCK
356_nolock_printf("\n[DEBUG SQT_LOCK] SQT nodes initialisation completed\n"); 
[466]357#endif
358
[495]359} // end _sqt_lock_init()
[466]360
361//////////////////////////////////////////////////////////////////////////////////
[495]362// This recursive function is used by the sqt_lock_acquire() function to get
363// a distributed SQT lock: It tries to get each local queuing lock on the path
364// from bottom to top, and starting from bottom.
365// It is blocking : it polls each "partial lock until it can be taken.
366// The lock is finally obtained when all locks, at all levels are taken.
367//////////////////////////////////////////////////////////////////////////////////
368static 
369void _sqt_lock_take( sqt_lock_node_t* node )
370{
371    // get next free ticket from local lock
372    unsigned int ticket = _atomic_increment( &node->free, 1 );
373
374#if GIET_DEBUG_SQT_LOCK
375unsigned int    gpid = _get_procid();
376unsigned int    x    = gpid >> (Y_WIDTH + P_WIDTH);
377unsigned int    y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
378unsigned int    l    = gpid & ((1<<P_WIDTH)-1);
379_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] get ticket %d for SQT lock %x"
380               " / level = %d / current = %d / free = %d\n",
381               x , y , l , ticket , (unsigned int)node ,
382               node->level , node->current , node->free );
383#endif
384
385    // poll the local lock current index
386    while ( ioread32( &node->current ) != ticket ) asm volatile( "nop" );
387
388#if GIET_DEBUG_SQT_LOCK
389_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] get SQT lock %x"
390               " / level = %d / current = %d / free = %d\n",
391               x , y , l , (unsigned int)node ,
392               node->level , node->current , node->free );
393#endif
394
395    // try to take the parent node lock until top is reached
396    if ( node->parent != NULL ) _sqt_lock_take( node->parent );
397
398} // end _sqt_lock_take()
399   
400//////////////////////////////////////////////////////////////////////////////////
401// This external function get thes SQT lock.
[466]402// Returns only when the lock has been taken.
403/////////////////////////////////////////////////////////////////////////////////
[495]404void _sqt_lock_acquire( sqt_lock_t*  lock )
[466]405{
406    // get cluster coordinates
407    unsigned int gpid = _get_procid();
408    unsigned int x    = (gpid >> (Y_WIDTH + P_WIDTH)) & ((1<<X_WIDTH)-1);
409    unsigned int y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
410
[495]411    // try to recursively take the distributed locks (from bottom to top)
412    _sqt_lock_take( lock->node[x][y][0] );
[455]413}
414
[466]415
416/////////////////////////////////////////////////////////////////////////////////
[495]417// This recursive function is used by the sqt_lock_release() function to
418// release distributed SQT lock: It releases all local locks on the path from
419// bottom to top, using a normal read/write, and starting from bottom.
[466]420/////////////////////////////////////////////////////////////////////////////////
[495]421static 
422void _sqt_lock_give( sqt_lock_node_t* node )
[455]423{
[495]424    // release the local lock
425    node->current = node->current + 1;
426
427#if GIET_DEBUG_SQT_LOCK
428unsigned int    gpid = _get_procid();
429unsigned int    x   = gpid >> (Y_WIDTH + P_WIDTH);
430unsigned int    y   = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
431unsigned int    l   = gpid & ((1<<P_WIDTH)-1);
432_nolock_printf("\n[DEBUG SQT_LOCK] P[%d,%d,%d] release SQT lock %x"
433               " / level = %d / current = %d / free = %d\n",
434               x , y , l , (unsigned int)node, 
435               node->level , node->current , node->free );
436#endif
437
438    // reset parent node until top is reached
439    if ( node->parent != NULL ) _sqt_lock_give( node->parent );
440
441} // end _sqt_lock_give()
442
443
444/////////////////////////////////////////////////////////////////////////////////
445// This external function releases the SQT lock.
446/////////////////////////////////////////////////////////////////////////////////
447void _sqt_lock_release( sqt_lock_t*  lock )
448{
449    asm volatile ( "sync" );   // for consistency
450
[466]451    // get cluster coordinates
452    unsigned int gpid = _get_procid();
453    unsigned int x    = (gpid >> (Y_WIDTH + P_WIDTH)) & ((1<<X_WIDTH)-1);
454    unsigned int y    = (gpid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
455
[495]456    // recursively reset the distributed locks (from bottom to top)
457    _sqt_lock_give( lock->node[x][y][0] );
[455]458}
459
460// Local Variables:
461// tab-width: 4
462// c-basic-offset: 4
463// c-file-offsets:((innamespace . 0)(inline-open . 0))
464// indent-tabs-mode: nil
465// End:
466// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
467
Note: See TracBrowser for help on using the repository browser.