/alps/pcitool

To get this branch, use:
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
}