Imported Upstream version 3.2.2
[debian/gnuradio] / gcell / lib / runtime / spu / gc_spu_jd_queue.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008,2009 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 // keep track of stats
33 int jdq_ok;
34 int jdq_empty;
35 int jdq_locked;
36
37
38 #define INITIAL_BACKOFF    32.0
39 #define MAX_BACKOFF      8192.0         /* 2.6us */
40 #define RANDOM_WEIGHT       0.2
41
42 static float
43 next_backoff(float backoff)
44 {
45   // exponential with random
46   float t = backoff * 2.0;
47   if (t > MAX_BACKOFF)
48     t = MAX_BACKOFF;
49
50   float r = (RANDOM_WEIGHT * (2.0 * (gc_uniform_deviate() - 0.5)));
51   t = t * (1.0 + r);
52
53   return t;
54 }
55
56 gc_dequeue_status_t
57 gc_jd_queue_dequeue(gc_eaddr_t q, gc_eaddr_t *item_ea,
58                     int jd_tag, gc_job_desc_t *item)
59 {
60   int   status;
61   char  _tmp[256];
62   gc_jd_queue_t *local_q =
63     (gc_jd_queue_t *) ALIGN(_tmp, 128);         // get cache-aligned buffer
64   
65   float backoff = next_backoff(INITIAL_BACKOFF);
66
67   do {
68     // Copy the queue structure in and get a lock line reservation.
69     // (The structure is 128-byte aligned and completely fills a cache-line)
70
71     mfc_getllar(local_q, q, 0, 0);
72     spu_readch(MFC_RdAtomicStat);
73
74     if (local_q->mutex != 0){           // somebody else has it locked
75       jdq_locked++;
76       return GCQ_LOCKED;
77     }
78
79     if (local_q->head == 0){            // the queue is empty
80       jdq_empty++;
81       return GCQ_EMPTY;
82     }
83
84     // Try to acquire the lock
85
86     local_q->mutex = 1;
87     mfc_putllc(local_q, q, 0, 0);
88     status = spu_readch(MFC_RdAtomicStat);
89
90     if (status != 0){
91       gc_cdelay((int) backoff);
92       backoff = next_backoff(backoff);
93     }
94
95   } while (status != 0);
96
97   // we're now holding the lock
98     
99   // copy in job descriptor at head of queue
100   *item_ea = local_q->head;
101   
102   // We must use the fence with the jd_tag to ensure that any
103   // previously initiated put of a job desc is locally ordered before
104   // the get of the new one.
105   mfc_getf(item, local_q->head, sizeof(gc_job_desc_t), jd_tag, 0, 0);
106   mfc_write_tag_mask(1 << jd_tag);      // the tag we're interested in
107   mfc_read_tag_status_all();            // wait for DMA to complete
108
109   local_q->head = item->sys.next;
110   item->sys.next = 0;
111   if (local_q->head == 0)               // now empty?
112     local_q->tail = 0;
113
114   // Copy the queue struct back out and unlock the mutex in one fell swoop.
115   // We use the unconditional put since it's faster and we own the lock.
116
117   local_q->mutex = 0;
118   mfc_putlluc(local_q, q, 0, 0);
119   spu_readch(MFC_RdAtomicStat);
120
121   jdq_ok++;
122   return GCQ_OK;
123 }