source: mainline/uspace/srv/fs/devfs/devfs_ops.c@ 4198f9c3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4198f9c3 was 2dfd9fa, checked in by Martin Decky <martin@…>, 16 years ago

avoid races during access to the shared hash table

  • Property mode set to 100644
File size: 12.5 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
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 fs
30 * @{
31 */
32
33/**
34 * @file devfs_ops.c
35 * @brief Implementation of VFS operations for the devfs file system server.
36 */
37
38#include <ipc/ipc.h>
39#include <bool.h>
40#include <errno.h>
41#include <malloc.h>
42#include <string.h>
43#include <libfs.h>
44#include <fibril_sync.h>
45#include <adt/hash_table.h>
46#include "devfs.h"
47#include "devfs_ops.h"
48
49#define PLB_GET_CHAR(pos) (devfs_reg.plb_ro[pos % PLB_SIZE])
50
51/** Opened devices structure */
52typedef struct {
53 dev_handle_t handle;
54 int phone;
55 size_t refcount;
56 link_t link;
57} device_t;
58
59/** Hash table of opened devices */
60static hash_table_t devices;
61
62/** Hash table mutex */
63static FIBRIL_MUTEX_INITIALIZE(devices_mutex);
64
65#define DEVICES_KEYS 1
66#define DEVICES_KEY_HANDLE 0
67#define DEVICES_BUCKETS 256
68
69/* Implementation of hash table interface for the nodes hash table. */
70static hash_index_t devices_hash(unsigned long key[])
71{
72 return key[DEVICES_KEY_HANDLE] % DEVICES_BUCKETS;
73}
74
75static int devices_compare(unsigned long key[], hash_count_t keys, link_t *item)
76{
77 device_t *dev = hash_table_get_instance(item, device_t, link);
78 return (dev->handle == (dev_handle_t) key[DEVICES_KEY_HANDLE]);
79}
80
81static void devices_remove_callback(link_t *item)
82{
83 free(hash_table_get_instance(item, device_t, link));
84}
85
86static hash_table_operations_t devices_ops = {
87 .hash = devices_hash,
88 .compare = devices_compare,
89 .remove_callback = devices_remove_callback
90};
91
92bool devfs_init(void)
93{
94 if (!hash_table_create(&devices, DEVICES_BUCKETS,
95 DEVICES_KEYS, &devices_ops))
96 return false;
97
98 if (devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING) < 0)
99 return false;
100
101 return true;
102}
103
104void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
105{
106 /* Accept the mount options */
107 ipc_callid_t callid;
108 size_t size;
109 if (!ipc_data_write_receive(&callid, &size)) {
110 ipc_answer_0(callid, EINVAL);
111 ipc_answer_0(rid, EINVAL);
112 return;
113 }
114
115 char *opts = malloc(size + 1);
116 if (!opts) {
117 ipc_answer_0(callid, ENOMEM);
118 ipc_answer_0(rid, ENOMEM);
119 return;
120 }
121
122 ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
123 if (retval != EOK) {
124 ipc_answer_0(rid, retval);
125 free(opts);
126 return;
127 }
128
129 free(opts);
130
131 ipc_answer_3(rid, EOK, 0, 0, 0);
132}
133
134void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
135{
136 ipc_answer_0(rid, ENOTSUP);
137}
138
139void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
140{
141 ipcarg_t first = IPC_GET_ARG1(*request);
142 ipcarg_t last = IPC_GET_ARG2(*request);
143 dev_handle_t dev_handle = IPC_GET_ARG3(*request);
144 ipcarg_t lflag = IPC_GET_ARG4(*request);
145 fs_index_t index = IPC_GET_ARG5(*request);
146
147 /* Hierarchy is flat, no altroot is supported */
148 if (index != 0) {
149 ipc_answer_0(rid, ENOENT);
150 return;
151 }
152
153 if ((lflag & L_LINK) || (lflag & L_UNLINK)) {
154 ipc_answer_0(rid, ENOTSUP);
155 return;
156 }
157
158 /* Eat slash */
159 if (PLB_GET_CHAR(first) == '/') {
160 first++;
161 first %= PLB_SIZE;
162 }
163
164 if (first >= last) {
165 /* Root entry */
166 if (lflag & L_DIRECTORY)
167 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, 0, 0, 0);
168 else
169 ipc_answer_0(rid, ENOENT);
170 } else {
171 if (lflag & L_FILE) {
172 size_t len;
173 if (last >= first)
174 len = last - first + 1;
175 else
176 len = first + PLB_SIZE - last + 1;
177
178 char *name = (char *) malloc(len + 1);
179 if (name == NULL) {
180 ipc_answer_0(rid, ENOMEM);
181 return;
182 }
183
184 size_t i;
185 for (i = 0; i < len; i++)
186 name[i] = PLB_GET_CHAR(first + i);
187
188 name[len] = 0;
189
190 dev_handle_t handle;
191 if (devmap_device_get_handle(name, &handle, 0) != EOK) {
192 free(name);
193 ipc_answer_0(rid, ENOENT);
194 return;
195 }
196
197 if (lflag & L_OPEN) {
198 unsigned long key[] = {
199 [DEVICES_KEY_HANDLE] = (unsigned long) handle
200 };
201
202 fibril_mutex_lock(&devices_mutex);
203 link_t *lnk = hash_table_find(&devices, key);
204 if (lnk == NULL) {
205 int phone = devmap_device_connect(handle, 0);
206 if (phone < 0) {
207 fibril_mutex_unlock(&devices_mutex);
208 free(name);
209 ipc_answer_0(rid, ENOENT);
210 return;
211 }
212
213 device_t *dev = (device_t *) malloc(sizeof(device_t));
214 if (dev == NULL) {
215 fibril_mutex_unlock(&devices_mutex);
216 free(name);
217 ipc_answer_0(rid, ENOMEM);
218 return;
219 }
220
221 dev->handle = handle;
222 dev->phone = phone;
223 dev->refcount = 1;
224
225 hash_table_insert(&devices, key, &dev->link);
226 } else {
227 device_t *dev = hash_table_get_instance(lnk, device_t, link);
228 dev->refcount++;
229 }
230 fibril_mutex_unlock(&devices_mutex);
231 }
232
233 free(name);
234
235 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1);
236 } else
237 ipc_answer_0(rid, ENOENT);
238 }
239}
240
241void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
242{
243 dev_handle_t handle = IPC_GET_ARG2(*request);
244
245 unsigned long key[] = {
246 [DEVICES_KEY_HANDLE] = (unsigned long) handle
247 };
248
249 fibril_mutex_lock(&devices_mutex);
250 link_t *lnk = hash_table_find(&devices, key);
251 if (lnk == NULL) {
252 int phone = devmap_device_connect(handle, 0);
253 if (phone < 0) {
254 fibril_mutex_unlock(&devices_mutex);
255 ipc_answer_0(rid, ENOENT);
256 return;
257 }
258
259 device_t *dev = (device_t *) malloc(sizeof(device_t));
260 if (dev == NULL) {
261 fibril_mutex_unlock(&devices_mutex);
262 ipc_answer_0(rid, ENOMEM);
263 return;
264 }
265
266 dev->handle = handle;
267 dev->phone = phone;
268 dev->refcount = 1;
269
270 hash_table_insert(&devices, key, &dev->link);
271 } else {
272 device_t *dev = hash_table_get_instance(lnk, device_t, link);
273 dev->refcount++;
274 }
275 fibril_mutex_unlock(&devices_mutex);
276
277 ipc_answer_3(rid, EOK, 0, 1, L_FILE);
278}
279
280void devfs_device(ipc_callid_t rid, ipc_call_t *request)
281{
282 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
283
284 if (index != 0) {
285 unsigned long key[] = {
286 [DEVICES_KEY_HANDLE] = (unsigned long) index
287 };
288
289 fibril_mutex_lock(&devices_mutex);
290 link_t *lnk = hash_table_find(&devices, key);
291 if (lnk == NULL) {
292 fibril_mutex_unlock(&devices_mutex);
293 ipc_answer_0(rid, ENOENT);
294 return;
295 }
296 fibril_mutex_unlock(&devices_mutex);
297
298 ipc_answer_1(rid, EOK, (ipcarg_t) index);
299 } else
300 ipc_answer_0(rid, ENOTSUP);
301}
302
303void devfs_read(ipc_callid_t rid, ipc_call_t *request)
304{
305 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
306 off_t pos = (off_t) IPC_GET_ARG3(*request);
307
308 if (index != 0) {
309 unsigned long key[] = {
310 [DEVICES_KEY_HANDLE] = (unsigned long) index
311 };
312
313 fibril_mutex_lock(&devices_mutex);
314 link_t *lnk = hash_table_find(&devices, key);
315 if (lnk == NULL) {
316 fibril_mutex_unlock(&devices_mutex);
317 ipc_answer_0(rid, ENOENT);
318 return;
319 }
320
321 device_t *dev = hash_table_get_instance(lnk, device_t, link);
322
323 ipc_callid_t callid;
324 if (!ipc_data_read_receive(&callid, NULL)) {
325 fibril_mutex_unlock(&devices_mutex);
326 ipc_answer_0(callid, EINVAL);
327 ipc_answer_0(rid, EINVAL);
328 return;
329 }
330
331 /* Make a request at the driver */
332 ipc_call_t answer;
333 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
334 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
335 IPC_GET_ARG3(*request), &answer);
336
337 /* Forward the IPC_M_DATA_READ request to the driver */
338 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
339 fibril_mutex_unlock(&devices_mutex);
340
341 /* Wait for reply from the driver. */
342 ipcarg_t rc;
343 async_wait_for(msg, &rc);
344 size_t bytes = IPC_GET_ARG1(answer);
345
346 /* Driver reply is the final result of the whole operation */
347 ipc_answer_1(rid, rc, bytes);
348 } else {
349 ipc_callid_t callid;
350 size_t size;
351 if (!ipc_data_read_receive(&callid, &size)) {
352 ipc_answer_0(callid, EINVAL);
353 ipc_answer_0(rid, EINVAL);
354 return;
355 }
356
357 size_t count = devmap_device_get_count();
358 dev_desc_t *desc = malloc(count * sizeof(dev_desc_t));
359 if (desc == NULL) {
360 ipc_answer_0(callid, ENOMEM);
361 ipc_answer_1(rid, ENOMEM, 0);
362 return;
363 }
364
365 size_t max = devmap_device_get_devices(count, desc);
366
367 if (pos < max) {
368 ipc_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
369 } else {
370 ipc_answer_0(callid, ENOENT);
371 ipc_answer_1(rid, ENOENT, 0);
372 return;
373 }
374
375 free(desc);
376
377 ipc_answer_1(rid, EOK, 1);
378 }
379}
380
381void devfs_write(ipc_callid_t rid, ipc_call_t *request)
382{
383 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
384 off_t pos = (off_t) IPC_GET_ARG3(*request);
385
386 if (index != 0) {
387 unsigned long key[] = {
388 [DEVICES_KEY_HANDLE] = (unsigned long) index
389 };
390
391 fibril_mutex_lock(&devices_mutex);
392 link_t *lnk = hash_table_find(&devices, key);
393 if (lnk == NULL) {
394 fibril_mutex_unlock(&devices_mutex);
395 ipc_answer_0(rid, ENOENT);
396 return;
397 }
398
399 device_t *dev = hash_table_get_instance(lnk, device_t, link);
400
401 ipc_callid_t callid;
402 if (!ipc_data_write_receive(&callid, NULL)) {
403 fibril_mutex_unlock(&devices_mutex);
404 ipc_answer_0(callid, EINVAL);
405 ipc_answer_0(rid, EINVAL);
406 return;
407 }
408
409 /* Make a request at the driver */
410 ipc_call_t answer;
411 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
412 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
413 IPC_GET_ARG3(*request), &answer);
414
415 /* Forward the IPC_M_DATA_WRITE request to the driver */
416 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
417
418 fibril_mutex_unlock(&devices_mutex);
419
420 /* Wait for reply from the driver. */
421 ipcarg_t rc;
422 async_wait_for(msg, &rc);
423 size_t bytes = IPC_GET_ARG1(answer);
424
425 /* Driver reply is the final result of the whole operation */
426 ipc_answer_1(rid, rc, bytes);
427 } else {
428 /* Read-only filesystem */
429 ipc_answer_0(rid, ENOTSUP);
430 }
431}
432
433void devfs_truncate(ipc_callid_t rid, ipc_call_t *request)
434{
435 ipc_answer_0(rid, ENOTSUP);
436}
437
438void devfs_close(ipc_callid_t rid, ipc_call_t *request)
439{
440 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
441
442 if (index != 0) {
443 unsigned long key[] = {
444 [DEVICES_KEY_HANDLE] = (unsigned long) index
445 };
446
447 fibril_mutex_lock(&devices_mutex);
448 link_t *lnk = hash_table_find(&devices, key);
449 if (lnk == NULL) {
450 fibril_mutex_unlock(&devices_mutex);
451 ipc_answer_0(rid, ENOENT);
452 return;
453 }
454
455 device_t *dev = hash_table_get_instance(lnk, device_t, link);
456 dev->refcount--;
457
458 if (dev->refcount == 0) {
459 ipc_hangup(dev->phone);
460 hash_table_remove(&devices, key, DEVICES_KEYS);
461 }
462
463 fibril_mutex_unlock(&devices_mutex);
464
465 ipc_answer_0(rid, EOK);
466 } else
467 ipc_answer_0(rid, ENOTSUP);
468}
469
470void devfs_sync(ipc_callid_t rid, ipc_call_t *request)
471{
472 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
473
474 if (index != 0) {
475 unsigned long key[] = {
476 [DEVICES_KEY_HANDLE] = (unsigned long) index
477 };
478
479 fibril_mutex_lock(&devices_mutex);
480 link_t *lnk = hash_table_find(&devices, key);
481 if (lnk == NULL) {
482 fibril_mutex_unlock(&devices_mutex);
483 ipc_answer_0(rid, ENOENT);
484 return;
485 }
486
487 device_t *dev = hash_table_get_instance(lnk, device_t, link);
488
489 /* Make a request at the driver */
490 ipc_call_t answer;
491 aid_t msg = async_send_2(dev->phone, IPC_GET_METHOD(*request),
492 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer);
493
494 fibril_mutex_unlock(&devices_mutex);
495
496 /* Wait for reply from the driver */
497 ipcarg_t rc;
498 async_wait_for(msg, &rc);
499
500 /* Driver reply is the final result of the whole operation */
501 ipc_answer_0(rid, rc);
502 } else
503 ipc_answer_0(rid, ENOTSUP);
504}
505
506void devfs_destroy(ipc_callid_t rid, ipc_call_t *request)
507{
508 ipc_answer_0(rid, ENOTSUP);
509}
510
511/**
512 * @}
513 */
Note: See TracBrowser for help on using the repository browser.