Merged eb/gcell-wip2 rev 10130:10152 into trunk.
[debian/gnuradio] / gcell / lib / runtime / spu / gc_spu_jd_queue.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <gcell/spu/gc_jd_queue.h>
23 #include <gcell/spu/gc_delay.h>
24 #include <gcell/spu/gc_random.h>
25 #include "mutex_lock.h"
26 #include "mutex_unlock.h"
27
28 #define MIN(a,b) ((a) < (b) ? (a) : (b))
29
30 extern int gc_sys_tag;
31
32 #define INITIAL_BACKOFF    32.0
33 #define MAX_BACKOFF     16384.0
34 #define RANDOM_WEIGHT       0.2
35
36 static float
37 next_backoff(float backoff)
38 {
39   // exponential with random
40   float t = backoff * 2.0;
41   if (t > MAX_BACKOFF)
42     t = MAX_BACKOFF;
43
44   float r = (RANDOM_WEIGHT * (2.0 * (gc_uniform_deviate() - 0.5)));
45   t = t * (1.0 + r);
46
47   return t;
48 }
49
50 bool
51 gc_jd_queue_dequeue(gc_eaddr_t q, gc_eaddr_t *item_ea,
52                     int jd_tag, gc_job_desc_t *item)
53 {
54   int   status;
55   char  _tmp[256];
56   gc_jd_queue_t *local_q =
57     (gc_jd_queue_t *) ALIGN(_tmp, 128);         // get cache-aligned buffer
58   
59   float backoff = next_backoff(INITIAL_BACKOFF);
60
61   do {
62     // Copy the queue structure in and get a lock line reservation.
63     // (The structure is 128-byte aligned and completely fills a cache-line)
64
65     mfc_getllar(local_q, q, 0, 0);
66     spu_readch(MFC_RdAtomicStat);
67
68     if (local_q->mutex != 0)            // somebody else has it locked
69       return false;
70
71     if (local_q->head == 0)             // the queue is empty
72       return false;
73
74     // Try to acquire the lock
75
76     local_q->mutex = 1;
77     mfc_putllc(local_q, q, 0, 0);
78     status = spu_readch(MFC_RdAtomicStat);
79
80     if (status != 0){
81       gc_cdelay((int) backoff);
82       backoff = next_backoff(backoff);
83     }
84
85   } while (status != 0);
86
87   // we're now holding the lock
88     
89   // copy in job descriptor at head of queue
90   *item_ea = local_q->head;
91   
92   // We must use the fence with the jd_tag to ensure that any
93   // previously initiated put of a job desc is locally ordered before
94   // the get of the new one.
95   mfc_getf(item, local_q->head, sizeof(gc_job_desc_t), jd_tag, 0, 0);
96   mfc_write_tag_mask(1 << jd_tag);      // the tag we're interested in
97   mfc_read_tag_status_all();            // wait for DMA to complete
98
99   local_q->head = item->sys.next;
100   item->sys.next = 0;
101   if (local_q->head == 0)               // now empty?
102     local_q->tail = 0;
103
104   // Copy the queue struct back out and unlock the mutex in one fell swoop.
105   // We use the unconditional put since it's faster and we own the lock.
106
107   local_q->mutex = 0;
108   mfc_putlluc(local_q, q, 0, 0);
109   spu_readch(MFC_RdAtomicStat);
110
111   return true;
112 }