directory.c
9.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
* Copyright (C) 1996-1998 by the Board of Trustees
* of Leland Stanford Junior University.
*
* This file is part of the SimOS distribution.
* See LICENSE file for terms of the license.
*
*/
/****************************************************************
* directory.c
*
* $Author: blythe $
* $Date: 2002/05/29 01:09:10 $
*****************************************************************/
#include <bstring.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include "embra.h"
#include "stats.h"
#include "directory.h"
#include "qc.h"
#include "driver.h"
#include "mem_control.h"
#include "cache.h"
#include "simutil.h"
/* Directory Structure */
/* Bit 31 is the pending bit */
/* Bit 30 is the dirty bit */
/* the remaining 30 bits are allocated one per processor */
#define DIR_ENT_PENDING(_dir_ent) ((unsigned)(_dir_ent) >> 31)
#define DIR_ENT_EXCL(_dir_ent) ((unsigned)(_dir_ent) & 0x40000000 )
#define DIR_ENT_MAKE_EXCL(_dir_ent) ((unsigned)(_dir_ent) | 0x40000000 )
#define DIR_ENT_CPUS(_dir_ent) (((unsigned)((unsigned)(_dir_ent) <<2))>>2)
#define DIR_ENT_ERASE_PENDING(_dir_ent) ((unsigned)(_dir_ent) & 0x7fffffff)
/* Defining this causes all sorts of check that the directory remains */
/* consistent */
/*#define DEBUG_DIR*/
Dir_Entry *directory[MAX_MACHINES];
/* used for range checking */
Dir_Entry *max_directory[MAX_MACHINES];
/* These are (local) arrays with the values for all processors */
/* This is just an optimization as we could compute the addresses each */
/* time, but that would slow interventions (if our compilier weren't */
/* really good at moving out loop invariant code ) */
/* I don't trust malloc */
volatile unsigned* cc_addr[SIM_MAXCPUS];
/* Local Functions */
static void Do_Intervention( int cpuNum, PA pAddr, VA vAddr,
EmVQCMemState state, Dir_Entry cpu_bits );
static Dir_Entry dir_get( int cpuNum, PA pAddr, VA vAddr,
EmVQCMemState state );
static Dir_Entry dir_getx( int cpuNum, PA pAddr, VA vAddr,
EmVQCMemState state );
/* Only for multiprocessors */
void Directory_Init(void)
{
static int FirstTime = 1;
int machine;
if( embra.emode == EMBRA_PAGE ) {
return;
}
if (FirstTime) {
FirstTime = 0;
for (machine = 0; machine < NUM_MACHINES; machine++) {
if (NUM_CPUS(machine) > 1) {
directory[machine] =
(Dir_Entry*) ZALLOC_PERM(DIRECTORY_SIZE(machine),
"EmbraDir");
max_directory[machine] =
(Dir_Entry*)(directory[machine] +
DIRECTORY_SIZE(machine));
CPUPrint("0x%x D Machine %d Directory_BASE 0x%x\n",
directory[machine],machine,
max_directory[machine] );
}
}
}
}
static unsigned cc_counts[SIM_MAXCPUS];
/* Note this procedure and the directory stucture limit us to 30 CPUs */
static void Do_Intervention( int cpuNum, PA pAddr, VA vAddr,
EmVQCMemState state, Dir_Entry cpu_bits )
{
static int count;
register int i;
int machine = M_FROM_CPU(cpuNum);
/* Eliminate us from the cpu_bits */
/* We could be in it if we have a read shared copy and we are upgrading */
cpu_bits &= ~(EMP[cpuNum].myBit);
/*Clobber the other guy's quick check, forcing synchronization at */
/*the directory, then clobber his cache tags */
Cache_Clobber( machine, cpuNum, pAddr, cpu_bits, state);
if( !embra.MPinUP ) {
/* Confirm that every other CPU is not in a race condition */
/* Initial pass notes if other procs are out of TC , or if not, then */
/* it notes their CC */
for( i = FIRST_CPU(machine); i <= LAST_CPU(machine); i++ )
{
if( i != EMP[cpuNum].myNum )
{
if( EMP[i].outTC || !EMP[i].outOfSlaveLoop )
cpu_bits &= (~(1<<i));
else
cc_counts[i] = EMP[i].cycleCountdown;
}
}
i = FIRST_CPU(machine);
while( cpu_bits )
{
if( i++ == LAST_CPU(machine) )
i = 0;
if( (cpu_bits>>i) & 0x1 )
{
if( EMP[i].outTC )
cpu_bits &= (~(1<<i));
else
if( cc_counts[i] != EMP[i].cycleCountdown )
cpu_bits &= (~(1<<i));
}
}
}
}
static Dir_Entry dir_get( int cpuNum, PA pAddr, VA vAddr, EmVQCMemState state )
{
int machine = M_FROM_CPU(cpuNum);
unsigned dir_entry = directory[machine][ADDR2SLINE(pAddr)];
ASSERT( &directory[machine][ADDR2SLINE(pAddr)] < max_directory[machine] );
if( !embra.MPinUP ) {
ASSERT( DIR_ENT_PENDING(dir_entry) );
}
/* If I am in directory then either I own line exclusive (in which */
/* case this action will downgrade me), or I have a read shared */
/* copy, which in this case won't change anything */
#ifdef DEBUG_DIR
if( DIR_ENT_CPUS(dir_entry) == EMP[cpuNum].myBit )
{
qc_insure_other_qc_invalid(pAddr);
return EMP[cpuNum].myBit;
}
#else
if( dir_entry & EMP[cpuNum].myBit )
return DIR_ENT_CPUS(dir_entry);
#endif
#ifdef DEBUG_DIR
if( !DIR_ENT_EXCL( dir_entry ) )
qc_insure_other_qc_invalid_or_read(pAddr);
#endif
/* Owned exclusively by someone else --intervene*/
if( DIR_ENT_EXCL( dir_entry ) ) {
Do_Intervention( cpuNum, pAddr, vAddr, state,
DIR_ENT_CPUS(dir_entry) );
}
/* else this is a read shared line, and we just want to enter the list*/
return (DIR_ENT_CPUS(dir_entry) | EMP[cpuNum].myBit); /* new dir entry */
}
static Dir_Entry dir_getx( int cpuNum, PA pAddr, VA vAddr,
EmVQCMemState state )
{
int machine = M_FROM_CPU(cpuNum);
Dir_Entry dir_entry = directory[machine][ADDR2SLINE(pAddr)];
ASSERT( &directory[machine][ADDR2SLINE(pAddr)] < max_directory[machine] );
if( !embra.MPinUP ) {
ASSERT( DIR_ENT_PENDING(dir_entry) );
}
/* Someone else has dibs (excl or read shared) on the line, so intervene */
if( DIR_ENT_CPUS(dir_entry) != EMP[cpuNum].myBit ) {
Do_Intervention( cpuNum, pAddr, vAddr, state,
DIR_ENT_CPUS(dir_entry) );
}
#ifdef DEBUG_DIR
/* We are the only users for the line, either exclusive (and we forgot) */
/* or read shared (in which case this is an upgrade) */
qc_insure_other_qc_invalid(pAddr);
#endif
return DIR_ENT_MAKE_EXCL(EMP[cpuNum].myBit);
}
void Directory_NoLock_Modify( int cpuNum, PA pAddr, VA vAddr, int state )
{
Dir_Entry dir_ent;
int machine = M_FROM_CPU(cpuNum);
ASSERT( NUM_CPUS(machine) > 1 );
switch( state ) {
case MEM_D_EXCLUSIVE:
case MEM_I_EXCLUSIVE:
dir_ent = dir_getx( cpuNum, pAddr, vAddr, (EmVQCMemState)state );
break;
case MEM_D_SHARED:
case MEM_I_SHARED:
dir_ent = dir_get( cpuNum, pAddr, vAddr, (EmVQCMemState)state );
break;
default:
ASSERT(0);
}
ASSERT( !DIR_ENT_PENDING(dir_ent) );
directory[machine][ADDR2SLINE(pAddr)] = dir_ent;
}
Dir_Entry Directory_Lock( int cpuNum, PA pAddr, VA vAddr, int state)
{
int machine = M_FROM_CPU(cpuNum);
ASSERT( NUM_CPUS(machine) > 1 );
switch( state ) {
case MEM_D_EXCLUSIVE:
case MEM_I_EXCLUSIVE:
Dir_Lock_Line( &directory[machine][ADDR2SLINE(pAddr)] );
return dir_getx( cpuNum, pAddr, vAddr, (EmVQCMemState)state );
case MEM_D_SHARED:
case MEM_I_SHARED:
Dir_Lock_Line( &directory[machine][ADDR2SLINE(pAddr)] );
return dir_get( cpuNum, pAddr, vAddr, (EmVQCMemState)state );
default:
ASSERT(0);
}
return 0;
}
void Directory_Free( int cpuNum, PA pAddr, Dir_Entry dir_ent )
{
int machine = M_FROM_CPU(cpuNum);
ASSERT( NUM_CPUS(machine) > 1 );
ASSERT( dir_ent && !DIR_ENT_PENDING(dir_ent) );
directory[machine][ADDR2SLINE(pAddr)] = dir_ent;
}
/* AKA replacement hint */
void Directory_Eliminate( int cpuNum, PLN pline, unsigned cpu_bit )
{
unsigned dir_entry;
int machine = M_FROM_CPU(cpuNum);
/* If the tag is invalid, don't bother to affect the directory */
/* NOTE: this check is now done in cache.c */
/* Replacement hints not necessary for uniprocessor */
/*if( pline == INVALID_TAG || NUM_CPUS(machine) == 1 )*/
if( NUM_CPUS(machine) == 1 )
return;
dir_entry = Dir_Lock_Line( &directory[machine][pline] );
ASSERT( DIR_ENT_PENDING(dir_entry) );
/* We want to assert that our interventions are actually knocking */
/* out the correct line, but because we do the replacement hint */
/* outside of the directory lock (to avoid deadlock) we can be in a */
/* state where we are issuing a replacement hint to a line which has */
/* been stolen. Therefore we can not assert this */
#ifdef COMMENTOUT
{
extern PLN* pline_tag; /* cache.c */
if( !(dir_entry & cpu_bit) &&
pline_tag[pline%LINES_PER_CACHE] != INVALID_TAG ) {
CPUPut("%d: pline 0x%x, tag 0x%x, direntry 0x%x cpu_bit 0x%x\n",
EMP[cpuNum].myNum, pline, pline_tag[pline%LINES_PER_CACHE], dir_entry, cpu_bit);
}
ASSERT( (dir_entry & cpu_bit) ||
pline_tag[pline%LINES_PER_CACHE] == INVALID_TAG )
}
#endif
/* If I am the only user, clear the entry */
if( DIR_ENT_CPUS(dir_entry) == cpu_bit ) {
directory[machine][pline] = 0;
return;
}
directory[machine][pline] = DIR_ENT_ERASE_PENDING(dir_entry & (~cpu_bit) );
}