~vojtech-horky/helenos/numa

« back to all changes in this revision

Viewing changes to kernel/generic/src/mm/tlb.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2001-2004 Jakub Jermar
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * - Redistributions of source code must retain the above copyright
 
10
 *   notice, this list of conditions and the following disclaimer.
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 *   notice, this list of conditions and the following disclaimer in the
 
13
 *   documentation and/or other materials provided with the distribution.
 
14
 * - The name of the author may not be used to endorse or promote products
 
15
 *   derived from this software without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 */
 
28
 
 
29
/** @addtogroup genericmm
 
30
 * @{
 
31
 */
 
32
 
 
33
/**
 
34
 * @file
 
35
 * @brief       Generic TLB shootdown algorithm.
 
36
 *
 
37
 * The algorithm implemented here is based on the CMU TLB shootdown
 
38
 * algorithm and is further simplified (e.g. all CPUs receive all TLB
 
39
 * shootdown messages).
 
40
 */
 
41
 
 
42
#include <mm/tlb.h>
 
43
#include <mm/asid.h>
 
44
#include <arch/mm/tlb.h>
 
45
#include <smp/ipi.h>
 
46
#include <synch/spinlock.h>
 
47
#include <atomic.h>
 
48
#include <arch/interrupt.h>
 
49
#include <config.h>
 
50
#include <arch.h>
 
51
#include <panic.h>
 
52
#include <debug.h>
 
53
#include <cpu.h>
 
54
 
 
55
/**
 
56
 * This lock is used for synchronisation between sender and
 
57
 * recipients of TLB shootdown message. It must be acquired
 
58
 * before CPU structure lock.
 
59
 */
 
60
SPINLOCK_INITIALIZE(tlblock);
 
61
 
 
62
void tlb_init(void)
 
63
{
 
64
        tlb_arch_init();
 
65
}
 
66
 
 
67
#ifdef CONFIG_SMP
 
68
 
 
69
/** Send TLB shootdown message.
 
70
 *
 
71
 * This function attempts to deliver TLB shootdown message
 
72
 * to all other processors.
 
73
 *
 
74
 * This function must be called with interrupts disabled.
 
75
 *
 
76
 * @param type Type describing scope of shootdown.
 
77
 * @param asid Address space, if required by type.
 
78
 * @param page Virtual page address, if required by type.
 
79
 * @param count Number of pages, if required by type.
 
80
 */
 
81
void tlb_shootdown_start(tlb_invalidate_type_t type, asid_t asid,
 
82
    uintptr_t page, size_t count)
 
83
{
 
84
        unsigned int i;
 
85
 
 
86
        CPU->tlb_active = 0;
 
87
        spinlock_lock(&tlblock);
 
88
        
 
89
        for (i = 0; i < config.cpu_count; i++) {
 
90
                cpu_t *cpu;
 
91
                
 
92
                if (i == CPU->id)
 
93
                        continue;
 
94
 
 
95
                cpu = &cpus[i];
 
96
                spinlock_lock(&cpu->lock);
 
97
                if (cpu->tlb_messages_count == TLB_MESSAGE_QUEUE_LEN) {
 
98
                        /*
 
99
                         * The message queue is full.
 
100
                         * Erase the queue and store one TLB_INVL_ALL message.
 
101
                         */
 
102
                        cpu->tlb_messages_count = 1;
 
103
                        cpu->tlb_messages[0].type = TLB_INVL_ALL;
 
104
                        cpu->tlb_messages[0].asid = ASID_INVALID;
 
105
                        cpu->tlb_messages[0].page = 0;
 
106
                        cpu->tlb_messages[0].count = 0;
 
107
                } else {
 
108
                        /*
 
109
                         * Enqueue the message.
 
110
                         */
 
111
                        size_t idx = cpu->tlb_messages_count++;
 
112
                        cpu->tlb_messages[idx].type = type;
 
113
                        cpu->tlb_messages[idx].asid = asid;
 
114
                        cpu->tlb_messages[idx].page = page;
 
115
                        cpu->tlb_messages[idx].count = count;
 
116
                }
 
117
                spinlock_unlock(&cpu->lock);
 
118
        }
 
119
        
 
120
        tlb_shootdown_ipi_send();
 
121
 
 
122
busy_wait:      
 
123
        for (i = 0; i < config.cpu_count; i++)
 
124
                if (cpus[i].tlb_active)
 
125
                        goto busy_wait;
 
126
}
 
127
 
 
128
/** Finish TLB shootdown sequence. */
 
129
void tlb_shootdown_finalize(void)
 
130
{
 
131
        spinlock_unlock(&tlblock);
 
132
        CPU->tlb_active = 1;
 
133
}
 
134
 
 
135
void tlb_shootdown_ipi_send(void)
 
136
{
 
137
        ipi_broadcast(VECTOR_TLB_SHOOTDOWN_IPI);
 
138
}
 
139
 
 
140
/** Receive TLB shootdown message. */
 
141
void tlb_shootdown_ipi_recv(void)
 
142
{
 
143
        tlb_invalidate_type_t type;
 
144
        asid_t asid;
 
145
        uintptr_t page;
 
146
        size_t count;
 
147
        unsigned int i;
 
148
        
 
149
        ASSERT(CPU);
 
150
        
 
151
        CPU->tlb_active = 0;
 
152
        spinlock_lock(&tlblock);
 
153
        spinlock_unlock(&tlblock);
 
154
        
 
155
        spinlock_lock(&CPU->lock);
 
156
        ASSERT(CPU->tlb_messages_count <= TLB_MESSAGE_QUEUE_LEN);
 
157
 
 
158
        for (i = 0; i < CPU->tlb_messages_count; CPU->tlb_messages_count--) {
 
159
                type = CPU->tlb_messages[i].type;
 
160
                asid = CPU->tlb_messages[i].asid;
 
161
                page = CPU->tlb_messages[i].page;
 
162
                count = CPU->tlb_messages[i].count;
 
163
 
 
164
                switch (type) {
 
165
                case TLB_INVL_ALL:
 
166
                        tlb_invalidate_all();
 
167
                        break;
 
168
                case TLB_INVL_ASID:
 
169
                        tlb_invalidate_asid(asid);
 
170
                        break;
 
171
                case TLB_INVL_PAGES:
 
172
                        ASSERT(count);
 
173
                        tlb_invalidate_pages(asid, page, count);
 
174
                        break;
 
175
                default:
 
176
                        panic("Unknown type (%d).", type);
 
177
                        break;
 
178
                }
 
179
                if (type == TLB_INVL_ALL)
 
180
                        break;
 
181
        }
 
182
        
 
183
        spinlock_unlock(&CPU->lock);
 
184
        CPU->tlb_active = 1;
 
185
}
 
186
 
 
187
#endif /* CONFIG_SMP */
 
188
 
 
189
/** @}
 
190
 */