bzr branch
http://suren.me/webbzr/alps/pcitool
277.1.1
by zilio nicolas
clean version for locks |
1 |
#define _XOPEN_SOURCE 700
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
2 |
#include <stdio.h> |
3 |
#include <stdlib.h> |
|
4 |
#include <string.h> |
|
5 |
#include <strings.h> |
|
6 |
#include <assert.h> |
|
295
by Suren A. Chilingaryan
Fix compilation of the driver |
7 |
#include <stdint.h> |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
8 |
#include <sys/file.h> |
277.1.1
by zilio nicolas
clean version for locks |
9 |
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
10 |
#include "locking.h" |
277.1.1
by zilio nicolas
clean version for locks |
11 |
#include "error.h" |
12 |
#include "pci.h" |
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
13 |
#include "kmem.h" |
277.1.1
by zilio nicolas
clean version for locks |
14 |
|
15 |
/*
|
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
16 |
* this function allocates the kernel memory for the locks for software registers
|
277.1.1
by zilio nicolas
clean version for locks |
17 |
*/
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
18 |
int pcilib_init_locking(pcilib_t* ctx) { |
19 |
int i; |
|
20 |
int err; |
|
21 |
pcilib_kmem_reuse_state_t reused; |
|
22 |
||
23 |
assert(PCILIB_LOCK_PAGES * PCILIB_KMEM_PAGE_SIZE >= PCILIB_MAX_LOCKS * PCILIB_LOCK_SIZE); |
|
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
24 |
|
25 |
/*protection against multiple creations of kernel space*/
|
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
26 |
err = pcilib_lock_global(ctx); |
27 |
if (err) return err; |
|
28 |
||
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
29 |
/* by default, this kernel space is persistent and will be reused, in order to avoid the big initialization times for robust mutexes each time we run pcitool*/
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
30 |
ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT); |
31 |
if (!ctx->locks.kmem) { |
|
32 |
pcilib_unlock_global(ctx); |
|
33 |
pcilib_error("Allocation of kernel memory for locking subsystem has failed"); |
|
34 |
return PCILIB_ERROR_FAILED; |
|
35 |
}
|
|
36 |
||
37 |
reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem); |
|
38 |
if (reused & PCILIB_KMEM_REUSE_PARTIAL) { |
|
39 |
pcilib_unlock_global(ctx); |
|
40 |
pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)"); |
|
41 |
return PCILIB_ERROR_INVALID_STATE; |
|
42 |
}
|
|
43 |
||
44 |
if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) { |
|
45 |
for (i = 0; i < PCILIB_LOCK_PAGES; i++) { |
|
370
by Suren A. Chilingaryan
RPM generation |
46 |
void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i); |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
47 |
memset(addr, 0, PCILIB_KMEM_PAGE_SIZE); |
48 |
}
|
|
49 |
}
|
|
50 |
||
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
51 |
/* the lock that has been used for the creation of kernel space is declared unlocked, has we shouldnot use it anymore*/
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
52 |
ctx->locks.locking = pcilib_get_lock(ctx, PCILIB_LOCK_FLAG_UNLOCKED, "locking"); |
53 |
||
54 |
pcilib_unlock_global(ctx); |
|
55 |
||
285
by Suren A. Chilingaryan
Use global locks to protect kmem allocation to prevent race while allocating simmultaneously locking kmem pages and any other type of kmem |
56 |
if ((!ctx->locks.locking)) { |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
57 |
pcilib_error("Locking subsystem has failed to initialized mandatory global locks"); |
58 |
return PCILIB_ERROR_FAILED; |
|
59 |
}
|
|
60 |
||
61 |
return 0; |
|
277.1.1
by zilio nicolas
clean version for locks |
62 |
}
|
63 |
||
277.1.3
by zilio nicolas
last modification+comments update |
64 |
/*
|
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
65 |
* this function free the kernel memory allocated for them and destroys locks by setting memory to 0
|
277.1.3
by zilio nicolas
last modification+comments update |
66 |
*/
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
67 |
void pcilib_free_locking(pcilib_t *ctx) { |
68 |
if (ctx->locks.locking) |
|
69 |
pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->locks.locking); |
|
70 |
||
71 |
if (ctx->locks.kmem) { |
|
72 |
pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE); |
|
73 |
}
|
|
74 |
||
75 |
memset(&ctx->locks, 0, sizeof(pcilib_locking_t)); |
|
76 |
}
|
|
77 |
||
78 |
int pcilib_lock_global(pcilib_t *ctx) { |
|
79 |
int err; |
|
80 |
||
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
81 |
/* we flock() on the board's device file to make sure to not have two initialization in the same time (possible long time to init) */
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
82 |
if ((err = flock(ctx->handle, LOCK_EX))==-1) { |
83 |
pcilib_error("Can't get flock on device file"); |
|
84 |
return PCILIB_ERROR_FAILED; |
|
85 |
}
|
|
86 |
||
87 |
return 0; |
|
88 |
}
|
|
89 |
||
90 |
void pcilib_unlock_global(pcilib_t *ctx) { |
|
91 |
if (flock(ctx->handle, LOCK_UN) == -1) |
|
92 |
pcilib_warning("Could not correctly remove lock from the device file"); |
|
93 |
}
|
|
94 |
||
95 |
pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id) { |
|
96 |
int page = id / PCILIB_LOCKS_PER_PAGE; |
|
97 |
int offset = id - page * PCILIB_LOCKS_PER_PAGE; |
|
370
by Suren A. Chilingaryan
RPM generation |
98 |
volatile void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, page); |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
99 |
pcilib_lock_t *lock = (pcilib_lock_t*)(addr + offset * PCILIB_LOCK_SIZE); |
100 |
||
101 |
return lock; |
|
102 |
}
|
|
103 |
||
104 |
pcilib_lock_t *pcilib_get_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, const char *lock_id, ...) { |
|
367
by Suren A. Chilingaryan
Further improvements of Python scripting and web-interface API for register manipulations by Vasiliy Chernov |
105 |
pcilib_lock_id_t i; |
106 |
int err, ret; |
|
107 |
||
108 |
pcilib_lock_t *lock; |
|
109 |
char buffer[PCILIB_LOCK_SIZE]; |
|
110 |
||
111 |
/* we construct the complete lock_id given the parameters of the function*/
|
|
112 |
va_list pa; |
|
113 |
va_start(pa, lock_id); |
|
114 |
ret = vsnprintf(buffer, PCILIB_LOCK_SIZE, lock_id, pa); |
|
115 |
va_end(pa); |
|
116 |
||
117 |
if (ret < 0) { |
|
118 |
pcilib_error("Failed to construct the lock id, probably arguments does not match the format string (%s)...", lock_id); |
|
119 |
return NULL; |
|
120 |
}
|
|
121 |
||
122 |
||
123 |
/* we iterate through locks to see if there is one already with the same name*/
|
|
124 |
// Would be nice to have hash here
|
|
125 |
for (i = 0; i < PCILIB_MAX_LOCKS; i++) { |
|
126 |
lock = pcilib_get_lock_by_id(ctx, i); |
|
127 |
||
128 |
const char *name = pcilib_lock_get_name(lock); |
|
129 |
if (!name) break; |
|
130 |
||
131 |
if (!strcmp(buffer, name)) { |
|
132 |
if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) { |
|
133 |
if (flags&PCILIB_LOCK_FLAG_PERSISTENT) |
|
134 |
pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name); |
|
135 |
else
|
|
136 |
pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name); |
|
137 |
return NULL; |
|
138 |
}
|
|
139 |
||
140 |
#ifndef HAVE_STDATOMIC_H
|
|
141 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) { |
|
142 |
err = pcilib_lock(ctx->locks.locking); |
|
143 |
if (err) { |
|
144 |
pcilib_error("Error (%i) obtaining global lock", err); |
|
145 |
return NULL; |
|
146 |
}
|
|
147 |
}
|
|
148 |
#endif /* ! HAVE_STDATOMIC_H */ |
|
149 |
/* if yes, we increment its ref variable*/
|
|
150 |
pcilib_lock_ref(lock); |
|
151 |
#ifndef HAVE_STDATOMIC_H
|
|
152 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
153 |
pcilib_unlock(ctx->locks.locking); |
|
154 |
#endif /* ! HAVE_STDATOMIC_H */ |
|
155 |
||
156 |
return lock; |
|
157 |
}
|
|
158 |
}
|
|
159 |
||
160 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) { |
|
161 |
err = pcilib_lock(ctx->locks.locking); |
|
162 |
if (err) { |
|
163 |
pcilib_error("Error (%i) obtaining global lock", err); |
|
164 |
return NULL; |
|
165 |
}
|
|
166 |
}
|
|
167 |
||
168 |
// Make sure it was not allocated meanwhile
|
|
169 |
for (; i < PCILIB_MAX_LOCKS; i++) { |
|
170 |
lock = pcilib_get_lock_by_id(ctx, i); |
|
171 |
||
172 |
const char *name = pcilib_lock_get_name(lock); |
|
173 |
if (!name) break; |
|
174 |
||
175 |
if (!strcmp(buffer, name)) { |
|
176 |
if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) { |
|
177 |
if (flags&PCILIB_LOCK_FLAG_PERSISTENT) |
|
178 |
pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name); |
|
179 |
else
|
|
180 |
pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name); |
|
181 |
||
182 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
183 |
pcilib_unlock(ctx->locks.locking); |
|
184 |
return NULL; |
|
185 |
}
|
|
186 |
||
187 |
pcilib_lock_ref(lock); |
|
188 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
189 |
pcilib_unlock(ctx->locks.locking); |
|
190 |
return lock; |
|
191 |
}
|
|
192 |
}
|
|
193 |
||
194 |
if (i == PCILIB_MAX_LOCKS) { |
|
195 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
196 |
pcilib_unlock(ctx->locks.locking); |
|
197 |
pcilib_error("Failed to create lock (%s), only %u locks is supported", buffer, PCILIB_MAX_LOCKS); |
|
198 |
return NULL; |
|
199 |
}
|
|
200 |
||
201 |
/* if the lock did not exist before, then we create it*/
|
|
202 |
err = pcilib_init_lock(lock, flags, buffer); |
|
203 |
||
204 |
if (err) { |
|
205 |
pcilib_error("Lock initialization failed with error %i", err); |
|
206 |
||
207 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
208 |
pcilib_unlock(ctx->locks.locking); |
|
209 |
||
210 |
return NULL; |
|
211 |
}
|
|
212 |
||
213 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
214 |
pcilib_unlock(ctx->locks.locking); |
|
215 |
||
216 |
return lock; |
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
217 |
}
|
218 |
||
219 |
void pcilib_return_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, pcilib_lock_t *lock) { |
|
220 |
#ifndef HAVE_STDATOMIC_H
|
|
277.1.1
by zilio nicolas
clean version for locks |
221 |
int err; |
222 |
||
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
223 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) { |
224 |
err = pcilib_lock(ctx->locks.locking); |
|
225 |
if (err) { |
|
226 |
pcilib_error("Error (%i) obtaining global lock", err); |
|
227 |
return; |
|
277.1.1
by zilio nicolas
clean version for locks |
228 |
}
|
229 |
}
|
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
230 |
#endif /* ! HAVE_STDATOMIC_H */ |
231 |
pcilib_lock_unref(lock); |
|
232 |
#ifndef HAVE_STDATOMIC_H
|
|
233 |
if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) |
|
234 |
pcilib_unlock(ctx->locks.locking); |
|
235 |
#endif /* ! HAVE_STDATOMIC_H */ |
|
236 |
}
|
|
237 |
||
238 |
||
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
239 |
/*
|
240 |
* Destroy all existing locks. This is unsafe call as this and other running applications
|
|
241 |
* will still have all initialized lock pointers. It is user responsibility to issue this
|
|
242 |
* command when no other application is running.
|
|
243 |
*/
|
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
244 |
int pcilib_destroy_all_locks(pcilib_t *ctx, int force) { |
245 |
int err; |
|
246 |
pcilib_lock_id_t i; |
|
247 |
pcilib_kmem_reuse_state_t reused; |
|
248 |
||
249 |
if (strcasecmp(ctx->model, "maintenance")) { |
|
250 |
pcilib_error("Can't destroy locks while locking subsystem is initialized, use maintenance model"); |
|
251 |
return PCILIB_ERROR_INVALID_STATE; |
|
252 |
}
|
|
253 |
||
254 |
err = pcilib_lock_global(ctx); |
|
255 |
if (err) return err; |
|
256 |
||
257 |
// ToDo: We should check here that no other instances of pcitool are running, the driver can provide this information
|
|
258 |
||
259 |
ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT); |
|
260 |
if (!ctx->locks.kmem) { |
|
261 |
pcilib_unlock_global(ctx); |
|
262 |
pcilib_error("Failed to allocate kernel memory of locking subsystem"); |
|
263 |
return PCILIB_ERROR_FAILED; |
|
264 |
}
|
|
265 |
||
266 |
reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem); |
|
267 |
if (reused & PCILIB_KMEM_REUSE_PARTIAL) { |
|
268 |
pcilib_unlock_global(ctx); |
|
269 |
pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)"); |
|
270 |
return PCILIB_ERROR_INVALID_STATE; |
|
271 |
}
|
|
272 |
||
273 |
if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) { |
|
274 |
pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT); |
|
275 |
pcilib_unlock_global(ctx); |
|
277.1.1
by zilio nicolas
clean version for locks |
276 |
return 0; |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
277 |
}
|
278 |
||
305.1.5
by zilio nicolas
reviewd old tasks comments for doxygen and update |
279 |
/* if we run in non-forced case, then if it may be still processes that can have access to the locks, they are not destroyed*/
|
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
280 |
if (!force) { |
281 |
for (i = 0; i < PCILIB_MAX_LOCKS; i++) { |
|
282 |
pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i); |
|
283 |
||
284 |
const char *name = pcilib_lock_get_name(lock); |
|
285 |
if (!name) break; |
|
286 |
||
287 |
size_t refs = pcilib_lock_get_refs(lock); |
|
288 |
||
289 |
if (refs > 0) { |
|
290 |
char *stmp = strdup(name); |
|
291 |
pcilib_free_locking(ctx); |
|
292 |
pcilib_unlock_global(ctx); |
|
293 |
pcilib_error("Lock (%s) has %zu references, destroying references may result in crashes and data corruption", stmp, refs); |
|
294 |
free(stmp); |
|
295 |
return PCILIB_ERROR_BUSY; |
|
296 |
}
|
|
297 |
}
|
|
298 |
}
|
|
299 |
||
300 |
// Do we really need this? I guess zeroing should be enough
|
|
301 |
for (i = 0; i < PCILIB_MAX_LOCKS; i++) { |
|
302 |
pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i); |
|
303 |
||
304 |
const char *name = pcilib_lock_get_name(lock); |
|
305 |
if (!name) break; |
|
306 |
||
307 |
pcilib_free_lock(lock); |
|
308 |
}
|
|
309 |
||
310 |
for (i = 0; i < PCILIB_LOCK_PAGES; i++) { |
|
370
by Suren A. Chilingaryan
RPM generation |
311 |
void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i); |
280
by Suren A. Chilingaryan
Integrate locking subsystem from Nicolas Zilio |
312 |
memset(addr, 0, PCILIB_KMEM_PAGE_SIZE); |
313 |
}
|
|
314 |
||
315 |
pcilib_free_locking(ctx); |
|
316 |
pcilib_unlock_global(ctx); |
|
317 |
||
318 |
return 0; |
|
277.1.1
by zilio nicolas
clean version for locks |
319 |
}
|