|
|
1.1 root 1: /* $Id: posix-disk.c,v 1.3 2003/10/16 03:02:11 fredette Exp $ */
2:
3: /* host/posix/posix-disk.c - implementation of disks on a POSIX system: */
4:
5: /*
6: * Copyright (c) 2003 Matt Fredette
7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Matt Fredette.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33: * POSSIBILITY OF SUCH DAMAGE.
34: */
35:
36: #include <tme/common.h>
37: _TME_RCSID("$Id: posix-disk.c,v 1.3 2003/10/16 03:02:11 fredette Exp $");
38:
39: /* includes: */
40: #include <tme/generic/disk.h>
41: #include <tme/generic/bus.h>
42: #include <fcntl.h>
43: #include <stdlib.h>
44: #include <strings.h>
45: #include <sys/stat.h>
46: #include <sys/uio.h>
47: #ifdef HAVE_MMAP
48: #include <sys/types.h>
49: #include <sys/mman.h>
50: #endif /* HAVE_MMAP */
51: #ifdef HAVE_STDARG_H
52: #include <stdarg.h>
53: #else /* HAVE_STDARG_H */
54: #include <varargs.h>
55: #endif /* HAVE_STDARG_H */
56:
57: /* macros: */
58:
59: /* the maximum block size: */
60: #define TME_POSIX_DISK_BLOCK_SIZE_MAX (16384)
61:
62: /* disk flags: */
63: #define TME_POSIX_DISK_FLAG_RO TME_BIT(0)
64:
65: /* buffer flags: */
66: #define TME_POSIX_DISK_BUFFER_READABLE TME_BIT(0)
67: #define TME_POSIX_DISK_BUFFER_DIRTY TME_BIT(1)
68: #define TME_POSIX_DISK_BUFFER_MMAPPED TME_BIT(2)
69:
70: /* default buffer parameters: */
71: #define TME_POSIX_DISK_BUFFER_DEFAULT_COUNT (16)
72: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE (128UL * 1024UL)
73: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST (1UL * 1024UL * 1024UL)
74:
75: /* types: */
76:
77: /* a posix disk buffer: */
78: struct tme_posix_disk_buffer {
79:
80: /* buffers are kept on a doubly linked list: */
81: struct tme_posix_disk_buffer *tme_posix_disk_buffer_next;
82: struct tme_posix_disk_buffer **tme_posix_disk_buffer_prev;
83:
84: /* buffer flags: */
85: int tme_posix_disk_buffer_flags;
86:
87: /* the file position, size, and data of this buffer. a size of zero
88: means this buffer is free: */
89:
90: /* we're paranoid about autoconf's ability to actually determine if
91: off_t and size_t are available, and it's possible that a system
92: that predates off_t and size_t may use other types to achieve
93: large file support anyways, that autoconf's default off_t = long
94: and size_t = unsigned definitions may not match. the bizarre
95: solution is to declare structs with appropriately-typed members,
96: and then use those members: */
97: struct stat _tme_posix_disk_buffer_stat;
98: struct iovec _tme_posix_disk_buffer_iov;
99: #define tme_posix_disk_buffer_pos _tme_posix_disk_buffer_stat.st_size
100: #define tme_posix_disk_buffer_size _tme_posix_disk_buffer_iov.iov_len
101: #define tme_posix_disk_buffer_data _tme_posix_disk_buffer_iov.iov_base
102: };
103:
104: /* a posix disk: */
105: struct tme_posix_disk {
106:
107: /* backpointer to our element: */
108: struct tme_element *tme_posix_disk_element;
109:
110: /* our mutex: */
111: tme_mutex_t tme_posix_disk_mutex;
112:
113: /* our flags: */
114: int tme_posix_disk_flags;
115:
116: /* the file descriptor: */
117: int tme_posix_disk_fd;
118:
119: /* the stat buffer: */
120: struct stat tme_posix_disk_stat;
121:
122: /* our connection: */
123: struct tme_disk_connection *tme_posix_disk_connection;
124:
125: /* our disk buffers. the most-recently-used buffer
126: is at the front of the list: */
127: struct tme_posix_disk_buffer *tme_posix_disk_buffers;
128:
129: /* how much we aggregate behind and ahead when we will
130: a new buffer: */
131: struct stat _tme_posix_disk_stat0;
132: struct stat _tme_posix_disk_stat1;
133: #define tme_posix_disk_buffer_agg_pre _tme_posix_disk_stat0.st_size
134: #define tme_posix_disk_buffer_agg_post _tme_posix_disk_stat1.st_size
135: };
136:
137: /* this gets a buffer: */
138: static int
139: _tme_posix_disk_buffer_get(struct tme_posix_disk *posix_disk,
140: const union tme_value64 *_pos,
141: unsigned long _size,
142: int readable,
143: tme_uint8_t **_buffer)
144: {
145: struct stat statbufs[3];
146: #define pos_least statbufs[0].st_size
147: #define pos_most statbufs[1].st_size
148: #define pos_last statbufs[2].st_size
149: struct iovec iovecs[3];
150: #define data iovecs[0].iov_base
151: #define size iovecs[0].iov_len
152: #define size_agg iovecs[1].iov_len
153: #define ssize iovecs[2].iov_len
154: unsigned long agg_pre, agg_post;
155: struct tme_posix_disk_buffer *buffer;
156: struct tme_posix_disk_buffer *buffer_free_nosize;
157: struct tme_posix_disk_buffer *buffer_free_sized;
158: int have_least, have_most;
159: int rc;
160:
161: /* form the size, and least and most positions: */
162: size = _size;
163: assert (size > 0);
164: #ifdef TME_HAVE_INT64_T
165: pos_least = _pos->tme_value64_int;
166: #else /* !TME_HAVE_INT64_T */
167: pos_least = _pos->tme_value64_int32_hi;
168: pos_least
169: = ((pos_least << 32)
170: | _pos->tme_value64_int32_lo);
171: #endif /* !TME_HAVE_INT64_T */
172: pos_most = (pos_least + size) - 1;
173:
174: /* if we have to fill a new buffer, decide from where and how much
175: we are going to fill: */
176: agg_pre
177: = TME_MIN(pos_least,
178: posix_disk->tme_posix_disk_buffer_agg_pre);
179: agg_pre
180: += ((pos_least - agg_pre)
181: & (posix_disk->tme_posix_disk_stat.st_blksize - 1));
182: agg_post
183: = posix_disk->tme_posix_disk_buffer_agg_post;
184: size_agg
185: = (((agg_pre + size) + agg_post
186: + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
187: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
188: agg_post = (size_agg - (agg_pre + size));
189:
190: /* start with no best free buffers: */
191: buffer_free_nosize = NULL;
192: buffer_free_sized = NULL;
193:
194: /* walk all of the buffers: */
195: for (buffer = posix_disk->tme_posix_disk_buffers;
196: buffer != NULL; ) {
197:
198: /* a buffer with no size is free: */
199: if (buffer->tme_posix_disk_buffer_size == 0) {
200:
201: /* remember this nosize free buffer and continue: */
202: buffer_free_nosize = buffer;
203: buffer = buffer->tme_posix_disk_buffer_next;
204: continue;
205: }
206:
207: /* a buffer with some size, but no flags, is also free: */
208: else if (buffer->tme_posix_disk_buffer_flags == 0) {
209:
210: /* take this buffer as our best sized free buffer, */
211:
212: /* if we have no best sized free buffer yet: */
213: if (buffer_free_sized == NULL
214:
215: || ((buffer->tme_posix_disk_buffer_size
216: >= buffer_free_sized->tme_posix_disk_buffer_size)
217:
218: /* if this buffer is bigger than our best sized free
219: buffer, and the best sized free buffer is smaller
220: than the new-fill aggregate buffer size: */
221: ? (buffer_free_sized->tme_posix_disk_buffer_size
222: < size_agg)
223:
224: /* if this buffer is smaller than our best sized
225: free buffer, but it is still at least as big as
226: the new-fill aggregate buffer size: */
227: : (buffer->tme_posix_disk_buffer_size
228: >= size_agg))) {
229: buffer_free_sized = buffer;
230: }
231:
232: /* continue: */
233: buffer = buffer->tme_posix_disk_buffer_next;
234: continue;
235: }
236:
237: /* calculate the last position in this buffer: */
238: pos_last = ((buffer->tme_posix_disk_buffer_pos
239: + buffer->tme_posix_disk_buffer_size)
240: - 1);
241:
242: /* see if this buffer contains the least position we want: */
243: have_least
244: = (buffer->tme_posix_disk_buffer_pos <= pos_least
245: && pos_least <= pos_last);
246:
247: /* see if this buffer contains the most position we want: */
248: have_most
249: = (buffer->tme_posix_disk_buffer_pos <= pos_most
250: && pos_most <= pos_last);
251:
252: /* if this buffer covers all of the positions we want, and
253: is readable or we don't need a readable buffer, stop: */
254: if (have_least
255: && have_most
256: && ((buffer->tme_posix_disk_buffer_flags
257: & TME_POSIX_DISK_BUFFER_READABLE)
258: || !readable)) {
259: break;
260: }
261:
262: /* otherwise, if this buffer covers any of the positions we want,
263: or if this is the last buffer on the list and we don't have any
264: best free buffer, we need to free this buffer: */
265: else if (have_least
266: || have_most
267: || (buffer->tme_posix_disk_buffer_next == NULL
268: && buffer_free_nosize == NULL
269: && buffer_free_sized == NULL)) {
270:
271: #ifdef HAVE_MMAP
272: /* if this buffer is mmapped: */
273: if (buffer->tme_posix_disk_buffer_flags
274: & TME_POSIX_DISK_BUFFER_MMAPPED) {
275:
276: /* munmap the buffer: */
277: rc = munmap(buffer->tme_posix_disk_buffer_data,
278: buffer->tme_posix_disk_buffer_size);
279: assert (rc == 0);
280:
281: /* free this buffer: */
282: buffer->tme_posix_disk_buffer_size = 0;
283: }
284:
285: else
286: #endif /* HAVE_MMAP */
287: {
288:
289: /* if this buffer is dirty, we need to write it out: */
290: if (buffer->tme_posix_disk_buffer_flags
291: & TME_POSIX_DISK_BUFFER_DIRTY) {
292:
293: /* seek to the buffer's position: */
294: rc = (lseek(posix_disk->tme_posix_disk_fd,
295: buffer->tme_posix_disk_buffer_pos,
296: SEEK_SET) < 0);
297: assert (rc == 0);
298:
299: /* write out the buffer: */
300: ssize = write(posix_disk->tme_posix_disk_fd,
301: buffer->tme_posix_disk_buffer_data,
302: buffer->tme_posix_disk_buffer_size);
303: assert (ssize == buffer->tme_posix_disk_buffer_size);
304: }
305:
306: /* free this buffer: */
307: buffer->tme_posix_disk_buffer_flags = 0;
308: }
309:
310: /* continue without updating buffer, so that this now-free
311: buffer is revisited, to possibly become a best free buffer: */
312: }
313:
314: /* otherwise, this is a non-free buffer that doesn't
315: cover any of the positions we want. just continue: */
316: else {
317: buffer = buffer->tme_posix_disk_buffer_next;
318: }
319: }
320:
321: /* if we didn't find an applicable buffer: */
322: if (buffer == NULL) {
323:
324: /* we must have some free buffer: */
325: assert (buffer_free_nosize != NULL
326: || buffer_free_sized != NULL);
327:
328: #ifdef HAVE_MMAP
329:
330: /* try to mmap this region. if the map fails with more read-ahead
331: than is needed to meet the block size, try the map one more
332: time with just that needed read-ahead: */
333: data = mmap(NULL,
334: size_agg,
335: PROT_READ
336: | ((posix_disk->tme_posix_disk_flags
337: & TME_POSIX_DISK_FLAG_RO)
338: ? 0
339: : PROT_WRITE),
340: MAP_SHARED,
341: posix_disk->tme_posix_disk_fd,
342: (pos_least
343: - agg_pre));
344: if (data == MAP_FAILED) {
345: size_agg
346: = (((agg_pre + size)
347: + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
348: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
349: data = mmap(NULL,
350: size_agg,
351: PROT_READ
352: | ((posix_disk->tme_posix_disk_flags
353: & TME_POSIX_DISK_FLAG_RO)
354: ? 0
355: : PROT_WRITE),
356: MAP_SHARED,
357: posix_disk->tme_posix_disk_fd,
358: (pos_least
359: - agg_pre));
360: if (data == MAP_FAILED) {
361: size_agg = (agg_pre + size) + agg_post;
362: }
363: else {
364: agg_post = size_agg - (agg_pre + size);
365: }
366: }
367:
368: /* if we were able to mmap this region: */
369: if (data != MAP_FAILED) {
370:
371: /* if we have a free nosize buffer, reuse it, else free the
372: free sized buffer's data and reuse that: */
373: if (buffer_free_nosize != NULL) {
374: buffer = buffer_free_nosize;
375: }
376: else {
377: tme_free(buffer_free_sized->tme_posix_disk_buffer_data);
378: buffer = buffer_free_sized;
379: }
380:
381: /* do the mmapped-specific initialization of this buffer: */
382: buffer->tme_posix_disk_buffer_flags
383: = (TME_POSIX_DISK_BUFFER_READABLE
384: | TME_POSIX_DISK_BUFFER_MMAPPED);
385: }
386:
387: /* otherwise, we were unable to map this region: */
388: else
389: #endif /* HAVE_MMAP */
390: {
391:
392: /* if we have a free sized buffer, resize it, else malloc
393: data for the free nosize buffer: */
394: if (buffer_free_sized != NULL) {
395: data = buffer_free_sized->tme_posix_disk_buffer_data;
396: if (buffer_free_sized->tme_posix_disk_buffer_size
397: != size_agg) {
398: data = tme_realloc(data, size_agg);
399: }
400: buffer = buffer_free_sized;
401: }
402: else {
403: data = tme_malloc(size_agg);
404: buffer = buffer_free_nosize;
405: }
406:
407: /* do the malloced-specific initialization of this buffer: */
408: buffer->tme_posix_disk_buffer_flags = 0;
409: if (readable) {
410: buffer->tme_posix_disk_buffer_flags
411: = TME_POSIX_DISK_BUFFER_READABLE;
412:
413: /* seek to the buffer's position: */
414: rc = (lseek(posix_disk->tme_posix_disk_fd,
415: (pos_least
416: - agg_pre),
417: SEEK_SET) < 0);
418: assert (rc == 0);
419:
420: /* read in the buffer. if the read fails with more
421: read-ahead than is needed to meet the block size, try the
422: read one more time with just that needed read-ahead: */
423: for (;;) {
424: ssize = read(posix_disk->tme_posix_disk_fd,
425: data,
426: size_agg);
427: if (ssize == size_agg) {
428: break;
429: }
430: size_agg
431: = (((agg_pre + size)
432: + (posix_disk->tme_posix_disk_stat.st_blksize - 1))
433: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1));
434: assert (agg_post > (size_agg - (agg_pre + size)));
435: agg_post = (size_agg - (agg_pre + size));
436: }
437: }
438: }
439:
440: /* do the common initialization of this buffer: */
441: buffer->tme_posix_disk_buffer_pos
442: = (pos_least
443: - agg_pre);
444: buffer->tme_posix_disk_buffer_size
445: = size_agg;
446: buffer->tme_posix_disk_buffer_data
447: = data;
448: }
449:
450: /* if this buffer doesn't need to be readable, that means that it's
451: being written to, so mark it dirty: */
452: if (!readable) {
453: buffer->tme_posix_disk_buffer_flags
454: |= TME_POSIX_DISK_BUFFER_DIRTY;
455: }
456:
457: /* remove this buffer from the list: */
458: *buffer->tme_posix_disk_buffer_prev
459: = buffer->tme_posix_disk_buffer_next;
460: if (buffer->tme_posix_disk_buffer_next != NULL) {
461: buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev
462: = buffer->tme_posix_disk_buffer_prev;
463: }
464:
465: /* add this buffer to the front of the list: */
466: buffer->tme_posix_disk_buffer_prev
467: = &posix_disk->tme_posix_disk_buffers;
468: buffer->tme_posix_disk_buffer_next
469: = posix_disk->tme_posix_disk_buffers;
470: if (buffer->tme_posix_disk_buffer_next != NULL) {
471: buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev
472: = &buffer->tme_posix_disk_buffer_next;
473: }
474: *buffer->tme_posix_disk_buffer_prev
475: = buffer;
476:
477: /* return the desired pointer into the buffer: */
478: *_buffer
479: = (buffer->tme_posix_disk_buffer_data
480: + (pos_least
481: - buffer->tme_posix_disk_buffer_pos));
482:
483: return (TME_OK);
484: #undef pos_least
485: #undef pos_most
486: #undef data
487: #undef size
488: #undef size_agg
489: }
490:
491: /* this returns a read buffer: */
492: static int
493: _tme_posix_disk_read(struct tme_disk_connection *conn_disk,
494: const union tme_value64 *pos,
495: unsigned long size,
496: const tme_uint8_t **_buffer)
497: {
498: struct tme_posix_disk *posix_disk;
499: tme_uint8_t *buffer;
500: int rc;
501:
502: /* recover our data structure: */
503: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
504:
505: /* lock the mutex: */
506: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
507:
508: /* get the buffer: */
509: rc = _tme_posix_disk_buffer_get(posix_disk,
510: pos,
511: size,
512: TRUE,
513: &buffer);
514: assert (rc == TME_OK);
515:
516: /* unlock the mutex: */
517: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
518:
519: /* return the buffer: */
520: *_buffer = buffer;
521: return (TME_OK);
522: }
523:
524: /* this returns a write buffer: */
525: static int
526: _tme_posix_disk_write(struct tme_disk_connection *conn_disk,
527: const union tme_value64 *pos,
528: unsigned long size,
529: tme_uint8_t **_buffer)
530: {
531: struct tme_posix_disk *posix_disk;
532: int rc;
533:
534: /* recover our data structure: */
535: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
536:
537: /* lock the mutex: */
538: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
539:
540: /* get the buffer: */
541: rc = _tme_posix_disk_buffer_get(posix_disk,
542: pos,
543: size,
544: FALSE,
545: _buffer);
546: assert (rc == TME_OK);
547:
548: /* unlock the mutex: */
549: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
550:
551: return (TME_OK);
552: }
553:
554: /* the disk control handler: */
555: #ifdef HAVE_STDARG_H
556: static int _tme_posix_disk_control(struct tme_disk_connection *conn_disk,
557: unsigned int control,
558: ...)
559: #else /* HAVE_STDARG_H */
560: static int _tme_posix_disk_control(conn_disk, control, va_alist)
561: struct tme_disk_connection *conn_disk;
562: unsigned int control;
563: va_dcl
564: #endif /* HAVE_STDARG_H */
565: {
566: struct tme_posix_disk *posix_disk;
567:
568: /* recover our data structure: */
569: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private;
570:
571: /* lock the mutex: */
572: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
573:
574: /* unlock the mutex: */
575: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
576:
577: return (TME_OK);
578: }
579:
580: /* this breaks a posix disk connection: */
581: static int
582: _tme_posix_disk_connection_break(struct tme_connection *conn,
583: unsigned int state)
584: {
585: abort();
586: }
587:
588: /* this makes a posix disk connection: */
589: static int
590: _tme_posix_disk_connection_make(struct tme_connection *conn,
591: unsigned int state)
592: {
593: struct tme_posix_disk *posix_disk;
594: struct tme_disk_connection *conn_disk;
595:
596: /* recover our data structure: */
597: posix_disk = (struct tme_posix_disk *) conn->tme_connection_element->tme_element_private;
598:
599: /* both sides must be disk connections: */
600: assert(conn->tme_connection_type == TME_CONNECTION_DISK);
601: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_DISK);
602:
603: /* we're always set up to answer calls across the connection,
604: so we only have to do work when the connection has gone full,
605: namely taking the other side of the connection: */
606: if (state == TME_CONNECTION_FULL) {
607:
608: /* lock the mutex: */
609: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex);
610:
611: /* save this connection to our list of connections: */
612: conn_disk = (struct tme_disk_connection *) conn->tme_connection_other;
613: posix_disk->tme_posix_disk_connection = conn_disk;
614:
615: /* unlock the mutex: */
616: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex);
617: }
618:
619: return (TME_OK);
620: }
621:
622: /* this makes a new connection side for a posix disk: */
623: static int
624: _tme_posix_disk_connections_new(struct tme_element *element,
625: const char * const *args,
626: struct tme_connection **_conns,
627: char **_output)
628: {
629: struct tme_posix_disk *posix_disk;
630: struct tme_disk_connection *conn_disk;
631: struct tme_connection *conn;
632:
633: /* recover our data structure: */
634: posix_disk = (struct tme_posix_disk *) element->tme_element_private;
635:
636: /* if we already have a connection, there's nothing to do: */
637: if (posix_disk->tme_posix_disk_connection != NULL) {
638: return (TME_OK);
639: }
640:
641: /* create our side of a disk connection: */
642: conn_disk = tme_new0(struct tme_disk_connection, 1);
643: conn = &conn_disk->tme_disk_connection;
644:
645: /* fill in the generic connection: */
646: conn->tme_connection_next = *_conns;
647: conn->tme_connection_type = TME_CONNECTION_DISK;
648: conn->tme_connection_score = tme_disk_connection_score;
649: conn->tme_connection_make = _tme_posix_disk_connection_make;
650: conn->tme_connection_break = _tme_posix_disk_connection_break;
651:
652: /* fill in the disk connection: */
653: (void) tme_value64_set(&conn_disk->tme_disk_connection_size,
654: posix_disk->tme_posix_disk_stat.st_size);
655: conn_disk->tme_disk_connection_read
656: = _tme_posix_disk_read;
657: if (!(posix_disk->tme_posix_disk_flags
658: & TME_POSIX_DISK_FLAG_RO)) {
659: conn_disk->tme_disk_connection_write
660: = _tme_posix_disk_write;
661: }
662: conn_disk->tme_disk_connection_release
663: = NULL;
664: conn_disk->tme_disk_connection_control
665: = _tme_posix_disk_control;
666:
667: /* return the connection side possibility: */
668: *_conns = conn;
669: return (TME_OK);
670: }
671:
672: /* the new posix disk function: */
673: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,disk) {
674: const char *filename;
675: int flags;
676: int fd;
677: unsigned long agg_pre;
678: unsigned long agg_post;
679: int buffers;
680: struct stat statbuf;
681: tme_uint8_t *block;
682: struct tme_posix_disk *posix_disk;
683: struct tme_posix_disk_buffer *buffer, **_prev;
684: int arg_i;
685: int usage;
686:
687: /* check our arguments: */
688: filename = NULL;
689: flags = 0;
690: arg_i = 1;
691: buffers = TME_POSIX_DISK_BUFFER_DEFAULT_COUNT;
692: agg_pre = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE;
693: agg_post = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST;
694: usage = FALSE;
695:
696: /* loop reading our arguments: */
697: for (;;) {
698:
699: /* the filename: */
700: if (TME_ARG_IS(args[arg_i], "file")
701: && args[arg_i + 1] != NULL
702: && filename == NULL) {
703: filename = args[arg_i + 1];
704: arg_i += 2;
705: }
706:
707: /* the read-only flag: */
708: else if (TME_ARG_IS(args[arg_i], "read-only")) {
709: flags |= TME_POSIX_DISK_FLAG_RO;
710: arg_i++;
711: }
712:
713: /* the buffers count: */
714: else if (TME_ARG_IS(args[arg_i + 0], "buffers")
715: && args[arg_i + 1] != NULL
716: && (buffers = atoi(args[arg_i + 1])) > 0) {
717: arg_i += 2;
718: }
719:
720: /* the read-behind value, also called the aggregate-pre: */
721: else if (TME_ARG_IS(args[arg_i + 0], "read-behind")) {
722: agg_pre = tme_bus_addr_parse_any(args[arg_i + 1], &usage);
723: if (usage) {
724: break;
725: }
726: arg_i += 2;
727: }
728:
729: /* the read-ahead value, also called the aggregate-post: */
730: else if (TME_ARG_IS(args[arg_i + 0], "read-ahead")) {
731: agg_post = tme_bus_addr_parse_any(args[arg_i + 1], &usage);
732: if (usage) {
733: break;
734: }
735: arg_i += 2;
736: }
737:
738: /* if we've run out of arguments: */
739: else if (args[arg_i + 0] == NULL) {
740:
741: /* we must have been given a filename: */
742: if (filename == NULL) {
743: usage = TRUE;
744: }
745: break;
746: }
747:
748: /* this is a bad argument: */
749: else {
750: tme_output_append_error(_output,
751: "%s %s",
752: args[arg_i],
753: _("unexpected"));
754: usage = TRUE;
755: break;
756: }
757: }
758:
759: if (usage) {
760: tme_output_append_error(_output,
761: "%s %s file %s [read-only] [buffers %s] [read-behind %s] [read-ahead %s]",
762: _("usage:"),
763: args[0],
764: _("FILENAME"),
765: _("BUFFER-COUNT"),
766: _("BYTE-COUNT"),
767: _("BYTE-COUNT"));
768: return (EINVAL);
769: }
770:
771: /* open the file: */
772: fd = open(filename, ((flags
773: & TME_POSIX_DISK_FLAG_RO)
774: ? O_RDONLY
775: : O_RDWR));
776: if (fd < 0) {
777: tme_output_append_error(_output,
778: "%s",
779: filename);
780: return (errno);
781: }
782:
783: /* stat the file: */
784: if (fstat(fd, &statbuf) < 0) {
785: tme_output_append_error(_output,
786: "%s",
787: filename);
788: close(fd);
789: return (errno);
790: }
791:
792: /* we will handle a character device, but not a block device: */
793: if (S_ISBLK(statbuf.st_mode)) {
794: tme_output_append_error(_output,
795: "%s",
796: filename);
797: close(fd);
798: return (EINVAL);
799: }
800:
801: /* if this is a character device, determine its block size: */
802: statbuf.st_blksize = 1;
803: if (S_ISCHR(statbuf.st_mode)) {
804:
805: /* allocate space for the block: */
806: block = tme_new(tme_uint8_t, statbuf.st_blksize);
807:
808: /* loop trying to read a block at offset zero, doubling the block
809: size until we succeed: */
810: for (; statbuf.st_blksize <= TME_POSIX_DISK_BLOCK_SIZE_MAX; ) {
811:
812: /* do the read: */
813: if (read(fd, block, statbuf.st_blksize) >= 0) {
814: break;
815: }
816:
817: /* seek back to the beginning: */
818: if (lseek(fd, 0, SEEK_SET) < 0) {
819: tme_free(block);
820: tme_output_append_error(_output,
821: "%s",
822: filename);
823: close(fd);
824: return (errno);
825: }
826:
827: /* resize the block: */
828: statbuf.st_blksize <<= 1;
829: block = tme_renew(tme_uint8_t, block, statbuf.st_blksize);
830: }
831:
832: /* free the block: */
833: tme_free(block);
834:
835: /* if we failed: */
836: if (statbuf.st_blksize > TME_POSIX_DISK_BLOCK_SIZE_MAX) {
837: tme_output_append_error(_output,
838: "%s",
839: filename);
840: close(fd);
841: return (EINVAL);
842: }
843: }
844:
845: /* start the disk structure: */
846: posix_disk = tme_new0(struct tme_posix_disk, 1);
847: posix_disk->tme_posix_disk_element = element;
848: tme_mutex_init(&posix_disk->tme_posix_disk_mutex);
849: posix_disk->tme_posix_disk_flags = flags;
850: posix_disk->tme_posix_disk_fd = fd;
851: posix_disk->tme_posix_disk_stat = statbuf;
852: posix_disk->tme_posix_disk_buffer_agg_pre = agg_pre;
853: posix_disk->tme_posix_disk_buffer_agg_post = agg_post;
854:
855: /* allocate the buffers: */
856: for (_prev = &posix_disk->tme_posix_disk_buffers;
857: buffers-- > 0;
858: _prev = &buffer->tme_posix_disk_buffer_next) {
859: buffer = tme_new0(struct tme_posix_disk_buffer, 1);
860: buffer->tme_posix_disk_buffer_prev = _prev;
861: *buffer->tme_posix_disk_buffer_prev = buffer;
862: }
863: *_prev = NULL;
864:
865: /* fill the element: */
866: element->tme_element_private = posix_disk;
867: element->tme_element_connections_new = _tme_posix_disk_connections_new;
868:
869: return (TME_OK);
870: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.