source: svn/trunk/newcon3bcm2_21bu/BSEAV/lib/bfile/bfile_buffer.c @ 7

Last change on this file since 7 was 2, checked in by jglee, 11 years ago

first commit

  • Property svn:executable set to *
File size: 26.9 KB
Line 
1/***************************************************************************
2 *     Copyright (c) 2007-2010, Broadcom Corporation
3 *     All Rights Reserved
4 *     Confidential Property of Broadcom Corporation
5 *
6 *  THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE
7 *  AGREEMENT  BETWEEN THE USER AND BROADCOM.  YOU HAVE NO RIGHT TO USE OR
8 *  EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT.
9 *
10 * $brcm_Workfile: bfile_buffer.c $
11 * $brcm_Revision: 21 $
12 * $brcm_Date: 5/6/10 3:37p $
13 *
14 * Module Description:
15 *
16 * Block based cached file I/O
17 *
18 * Revision History:
19 *
20 * $brcm_Log: /BSEAV/lib/bfile/bfile_buffer.c $
21 *
22 * 21   5/6/10 3:37p vsilyaev
23 * SW7405-3773: fixed debug message
24 *
25 * 20   2/22/10 5:41p vsilyaev
26 * SW3556-913: Fixed handling of seek errors
27 *
28 * 19   10/28/09 1:18p vsilyaev
29 * SW7405-3311: Recycle unused entries if there was partial read
30 *
31 * 18   6/8/09 7:09p vsilyaev
32 * PR 55554: Fixed handling of huge frames in MP4 stream
33 *
34 * 17   6/4/09 4:56p vsilyaev
35 * PR 55417: Fixed free list traversing
36 *
37 * 16   6/4/09 2:44p vsilyaev
38 * PR 55417: Improved buffer search for the new buffer assigment scheme
39 *
40 * 15   6/2/09 7:38p vsilyaev
41 * PR 55417: Added support for read method returning no data or completing
42 * with a partial read
43 *
44 * 14   12/19/08 3:50p vsilyaev
45 * PR 47854: Return avaliable data on aborted request
46 *
47 * 13   12/18/08 7:19p vsilyaev
48 * PR 47854: Increased default buffer size
49 *
50 * 12   12/18/08 7:04p vsilyaev
51 * PR 47854: Improved handling of conditions where asynchronous read
52 * exhaust allocated buffer
53 *
54 * 11   11/26/08 3:56p vsilyaev
55 * PR 47650: Added function to get bounds
56 *
57 * 10   9/18/08 3:27p vsilyaev
58 * PR 47105: Added support for layered buffer cache
59 *
60 * 9   4/3/08 2:01p vsilyaev
61 * PR 39818: Optimized MKV handling
62 *
63 * 8   3/18/08 1:04p vsilyaev
64 * PR 39818: Fixed handling of active segments
65 *
66 * 7   7/26/07 4:16p vsilyaev
67 * PR 32813: Fixed memory leak
68 *
69 * 6   7/20/07 11:33p vsilyaev
70 * PR 32813: Properly return EOF result
71 *
72 * 5   7/18/07 11:42p vsilyaev
73 * PR 32813: Another fix for handling of end of file condition
74 *
75 * 4   7/9/07 4:09p vsilyaev
76 * PR 32813: Fixed buffer leak when read reached end of file
77 *
78 * 3   6/14/07 12:49p vsilyaev
79 * PR 32162: Serializes async disk I/O for bfile_buffer. Fixed unitilized
80 * ref_cnt in bfile_buffer.c
81 *
82 * 2   6/6/07 5:23p vsilyaev
83 * PR 31887: Use asynchronous I/O for MP4 container
84 *
85 * 1   5/16/07 9:53p vsilyaev
86 * PR 28631: Added block based file cache/buffer
87 *
88 *******************************************************************************/
89
90#include "bstd.h"
91#include "bkni.h"
92#include "bfile_buffer.h"
93#include "blst_queue.h"
94
95BDBG_MODULE(bfile_buffer);
96BDBG_OBJECT_ID(bfile_buffer_t);
97
98#define BDBG_MSG_TRACE(x) /* BDBG_MSG(x)  */
99
100typedef struct b_file_buffer_segment {
101        BLST_Q_ENTRY(b_file_buffer_segment)  link;
102        off_t off;
103        size_t  size;
104        unsigned ref_cnt;
105        void    *base;
106} b_file_buffer_segment;
107
108BLST_Q_HEAD(b_file_buffer_list, b_file_buffer_segment);
109
110struct bfile_buffer
111{
112        BDBG_OBJECT(bfile_buffer_t)
113        struct b_file_buffer_list busy; /* blocks with ref_cnt > 0 */
114        struct b_file_buffer_list free; /* blocks with ref_cnt == 0 */
115        size_t segment_size;
116        batom_accum_t acc;
117        struct {
118                off_t off;
119                size_t size;
120                b_file_buffer_segment *seg_read;
121                bool wait_for_seg;
122                bool active;
123                void *cntx;
124                void (*read_complete)(void *cntx, batom_t atom, bfile_buffer_result result);
125        } async_state;
126        bfile_buffer_cfg cfg;
127        b_file_buffer_segment segs[1]; /* variable length array, shall be last entry */
128};
129
130#define B_FILE_BUFFER_SEG(buf, base) (&(buf)->segs[(((uint8_t *)(base) - (uint8_t *)(buf)->cfg.buf)/(buf)->segment_size)])
131#define B_FILE_BUFFER_BASE(buf, seg) ((uint8_t *)(buf)->cfg.buf + ((seg)-(buf)->segs)*(buf)->segment_size)
132/* #define B_FILE_BUFFER_OFF(buf, off)  ((off)-((off)%(buf)->segment_size)) */
133#define B_FILE_BUFFER_OFF(buf, off)  (off - (off % (buf)->segment_size))
134
135static void b_file_buffer_read_complete(void *cont, ssize_t size);
136static void b_file_buffer_insert_segment(bfile_buffer_t buf, b_file_buffer_segment *seg);
137static void b_file_buffer_async_complete(bfile_buffer_t buf, bfile_buffer_result result);
138static void b_file_buffer_recycle_empty(bfile_buffer_t buf, b_file_buffer_segment *seg);
139
140#if 0
141static void
142b_file_buffer_dump(bfile_buffer_t buf, const char *name)
143{
144        b_file_buffer_segment *seg;
145        unsigned busy_cnt=0;
146        unsigned free_cnt=0;
147
148        for(seg=BLST_Q_FIRST(&buf->busy); seg; seg=BLST_Q_NEXT(seg, link)) {
149                BDBG_MSG_TRACE(("b_file_buffer_dump:%s %#lx seg:%#lx(%lu:%u) ref_cnt:%u", name, (unsigned long)buf, (unsigned long)seg,(unsigned long)seg->off, (unsigned)seg->size, seg->ref_cnt));
150                busy_cnt++;
151        }
152        for(seg=BLST_Q_FIRST(&buf->free); seg; seg=BLST_Q_NEXT(seg, link)) {
153                free_cnt++;
154        }
155        BDBG_MSG_TRACE(("b_file_buffer_dump:%s %#lx busy:%u free:%u total:%u(%u)", name, (unsigned long)buf, busy_cnt, free_cnt, busy_cnt+free_cnt, buf->cfg.nsegs));
156        return;
157}
158#else
159#define b_file_buffer_dump(buf, name)
160#endif
161
162static void
163b_file_buffer_init_segment(bfile_buffer_t buf, b_file_buffer_segment *seg, off_t off)
164{
165        off_t read_off;
166
167    BSTD_UNUSED(buf);
168
169        read_off = B_FILE_BUFFER_OFF(buf, off);
170
171        BDBG_ASSERT(read_off <= off);
172        BDBG_ASSERT(read_off + (int)buf->segment_size >= off);
173    seg->off = read_off;
174    seg->size = 0;
175    seg->ref_cnt = 0;
176    return;
177}
178
179static bfile_buffer_result
180b_file_buffer_async_read_to_segment(bfile_buffer_t buf, b_file_buffer_segment *seg)
181{
182        uint8_t *base;
183        off_t seek_off;
184        off_t seek_result;
185
186        BDBG_ASSERT(seg);
187    BDBG_ASSERT(seg->size < buf->segment_size);
188        buf->async_state.active=true;
189        buf->async_state.seg_read = seg;
190        base = B_FILE_BUFFER_BASE(buf,seg);
191    base += seg->size;
192    seek_off = seg->off + seg->size;
193        BDBG_MSG_TRACE(("b_file_buffer_async_read_to_segment: %#lx seg:%#lx:%lx %lld:%lld:%u", (unsigned long)buf, (unsigned long)seg, (unsigned long)base, seg->off, seek_off, (unsigned)buf->segment_size-seg->size));
194        seek_result = buf->cfg.fd->seek(buf->cfg.fd, seek_off, SEEK_SET);
195        if(seek_result==seek_off) {
196                buf->cfg.async_read(buf->cfg.sync_cnxt, buf->cfg.fd, base, buf->segment_size - seg->size, b_file_buffer_read_complete, buf);
197        return bfile_buffer_result_async;
198        } else {
199        buf->async_state.active=false;
200        b_file_buffer_recycle_empty(buf, seg);
201        return bfile_buffer_result_read_error;
202        }
203}
204
205static ssize_t
206b_file_buffer_read_to_segment(bfile_buffer_t buf, b_file_buffer_segment *seg)
207{
208        uint8_t *base;
209        off_t seek_off;
210        off_t seek_result;
211        ssize_t read_result;
212
213        BDBG_ASSERT(seg);
214    BDBG_ASSERT(seg->size < buf->segment_size);
215        buf->async_state.seg_read = seg;
216        base = B_FILE_BUFFER_BASE(buf,seg);
217    base += seg->size;
218    seek_off = seg->off + seg->size;
219        BDBG_MSG_TRACE(("b_file_buffer_read_to_segment: %#lx seg:%#lx:%lx %lld:%lld:%u", (unsigned long)buf, (unsigned long)seg, (unsigned long)base, seg->off, seek_off, (unsigned)buf->segment_size-seg->size));
220        seek_result = buf->cfg.fd->seek(buf->cfg.fd, seek_off, SEEK_SET);
221        if(seek_result==seek_off) {
222                read_result = buf->cfg.fd->read(buf->cfg.fd, base, buf->segment_size-seg->size);
223        } else {
224                read_result = -1;
225        }
226    b_file_buffer_dump(buf, "b_file_buffer_read_to_segment");
227        BDBG_MSG(("b_file_buffer_read_to_segment: %#lx %lld:%d", (unsigned long)seg, seek_off, (int)read_result));
228        BDBG_MSG_TRACE(("b_file_buffer_read_to_segment<: %#lx seg:%#lx:%lx %lld:%lld -> %d", (unsigned long)buf, (unsigned long)seg, (unsigned long)base, seg->off, seek_off, (int)read_result));
229        return read_result;
230}
231
232
233static void
234b_file_buffer_atom_free(batom_t atom, void *user)
235{
236        bfile_buffer_t buf = *(bfile_buffer_t*)user;
237        b_file_buffer_segment *seg, *buf_id;
238        unsigned nvec;
239        const batom_vec *vec;
240        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
241        BDBG_MSG_TRACE(("b_file_buffer_atom_free: %#lx recycle atom:%#lx", (unsigned long)buf, (unsigned long)atom));
242        for(nvec = 0; NULL!=(vec=batom_get_vec(atom, nvec));nvec++) {
243                BDBG_ASSERT(vec->base >= buf->cfg.buf && (uint8_t *)vec->base <= (uint8_t *)buf->cfg.buf+buf->cfg.buf_len);
244                buf_id = B_FILE_BUFFER_SEG(buf, vec->base);
245#if BDBG_DEBUG_BUILD
246                {
247                        for(seg=BLST_Q_FIRST(&buf->busy); seg; seg=BLST_Q_NEXT(seg, link)) {
248                        /* verify that buf_id belongs to tbe buffer */
249                                if(buf_id==seg) {
250                                        break;
251                                }
252                        }
253                        if(seg==NULL) {
254                                BDBG_ERR(("b_file_buffer_atom_free: %#lx unknown buf_id:%lx", (unsigned long)buf, (unsigned long)buf_id));
255                                return;
256                        }
257                }
258#endif
259                seg = buf_id;
260                BDBG_MSG_TRACE(("b_file_buffer_atom_free: %#lx recycle atom:%#lx seg:%#lx(%lu:%u) ref_cnt:%u", (unsigned long)buf, (unsigned long)atom, (unsigned long)seg,(unsigned long)seg->off, (unsigned)seg->size, seg->ref_cnt));
261                BDBG_ASSERT(seg->ref_cnt>0);
262                seg->ref_cnt--;
263                if(seg->ref_cnt==0) {
264                        BLST_Q_REMOVE(&buf->busy, seg, link);
265                        if(!buf->async_state.wait_for_seg) {
266                                BLST_Q_INSERT_HEAD(&buf->free, seg, link);
267                        } else {
268                bfile_buffer_result result;
269                                BDBG_MSG_TRACE(("b_file_buffer_atom_free: %#lx restart_io seg:%#lx", (unsigned long)buf, (unsigned long)seg));
270                                buf->async_state.wait_for_seg = false;
271                b_file_buffer_init_segment(buf, seg, buf->async_state.off);
272                b_file_buffer_insert_segment(buf, seg);
273                                result = b_file_buffer_async_read_to_segment(buf, seg);
274                if(result!=bfile_buffer_result_async) {
275                    b_file_buffer_async_complete(buf, result);
276                }
277                        }
278                }
279        }
280        return;
281}
282
283static const batom_user b_file_buffer_atom = {
284        b_file_buffer_atom_free,
285        sizeof(bfile_buffer_t)
286};
287
288static void
289b_file_buffer_insert_segment(bfile_buffer_t buf, b_file_buffer_segment *seg)
290{
291        b_file_buffer_segment *prev,*s;
292        off_t off = seg->off;
293        BDBG_MSG_TRACE(("b_file_buffer_insert_segment>: %#lx seg:%#lx %u", (unsigned long)buf, (unsigned long)seg, (unsigned)seg->off));
294        for(prev=NULL, s=BLST_Q_FIRST(&buf->busy); s; s=BLST_Q_NEXT(s, link)) {
295            BDBG_MSG_TRACE(("b_file_buffer_insert_segment: %#lx seg:%#lx off:%u s->off:%u prev:%#lx", (unsigned long)buf, (unsigned long)seg, (unsigned)off, (unsigned)s->off, (unsigned long)prev));
296                if(off > s->off) {
297                        break;
298                }
299                prev = s;
300        }
301        if(prev==NULL) {
302            BDBG_MSG_TRACE(("b_file_buffer_insert_segment<: %#lx seg:%#lx TOP %u", (unsigned long)buf, (unsigned long)seg, (unsigned)off));
303                BLST_Q_INSERT_HEAD(&buf->busy, seg, link);
304        } else {
305            BDBG_MSG_TRACE(("b_file_buffer_insert_segment<: %#lx seg:%#lx prev:%u %u", (unsigned long)buf, (unsigned long)seg, (unsigned)prev->off, (unsigned)off));
306                BLST_Q_INSERT_AFTER(&buf->busy, prev, seg, link);
307        }
308        return;
309}
310
311static b_file_buffer_segment *
312b_file_buffer_segment_find(bfile_buffer_t buf, off_t off)
313{
314        b_file_buffer_segment *seg;
315    off_t read_off;
316
317        BDBG_MSG_TRACE(("b_file_buffer_segment_find>:%#lx %lld", (unsigned long)buf, off));
318        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
319
320        read_off = B_FILE_BUFFER_OFF(buf, off);
321
322        /* 1. Validate that segment in the range of 'busy' segments (busy segments are sorted by offset) */
323        seg = BLST_Q_LAST(&buf->busy); /* min offset */
324    BDBG_MSG_TRACE(("seg:%p LAST off:%u %u", seg, (unsigned)off, seg?(unsigned)(seg->off):0));
325        if(seg && read_off>=seg->off) {
326                seg=BLST_Q_FIRST(&buf->busy); /* max offset */
327        BDBG_ASSERT(seg);
328        BDBG_MSG_TRACE(("seg:%p FIRST off:%u %u", seg, (unsigned)off, (unsigned)(seg->off+(int)seg->size)));
329                if(read_off<=seg->off) {
330            /* 2. Try to find segment in the sorted (high to low )list */
331                        do {
332                BDBG_MSG_TRACE(("seg:%p search[1] off:%u %u", seg, (unsigned)off, (unsigned)(seg->off)));
333                                BDBG_ASSERT(seg->size < 1u<<31);
334                                if(read_off <= seg->off) {
335                    BDBG_MSG_TRACE(("seg:%p search[2] off:%u %u", seg, (unsigned)off, (unsigned)(seg->off+(int)seg->size)));
336                    if(read_off == seg->off) {
337                                        /* bingo, found segment */
338                                        BDBG_MSG_TRACE(("b_file_buffer_segment_find<:%#lx %lld seg:%#lx %lld:%u:%u", (unsigned long)buf, off, (unsigned long)seg, seg->off, (unsigned)seg->size, seg->ref_cnt));
339                                        return seg;
340                    }  else {
341                        /* because segments are sorted stop  search here */
342                        break;
343                    }
344                } 
345                        } while (NULL!=(seg=BLST_Q_NEXT(seg, link)));
346                }
347        }
348    /* 3. Do a search in the free list */
349    for(seg=BLST_Q_FIRST(&buf->free);seg;seg=BLST_Q_NEXT(seg, link)) {
350        BDBG_ASSERT(seg->size < 1u<<31);
351        if(seg->size && seg->off == read_off) {
352            BLST_Q_REMOVE(&buf->free, seg, link);
353            b_file_buffer_insert_segment(buf, seg);
354            BDBG_MSG_TRACE(("b_file_buffer_segment_find<:%#lx %lld seg:%#lx %lld:%u:%u", (unsigned long)buf, off, (unsigned long)seg, seg->off, (unsigned)seg->size, seg->ref_cnt));
355            return seg;
356        }
357    }
358    /* nothing found */
359        BDBG_MSG_TRACE(("b_file_buffer_segment_find<:%#lx %lld NOT FOUND", (unsigned long)buf, off));
360        return NULL;
361}
362
363
364static size_t
365b_file_buffer_add_segment(bfile_buffer_t buf, b_file_buffer_segment *seg, off_t off, size_t size)
366{
367        unsigned buf_off;
368        uint8_t *base=NULL;
369        size_t atom_size=0;
370
371        BDBG_ASSERT(seg);
372        BDBG_ASSERT(off >= seg->off);
373        BDBG_MSG_TRACE(("b_file_buffer_add_segment>: %#lx seg:%#lx(%u):%lld:%u %lld:%u", (unsigned long)buf, (unsigned long)seg, seg->ref_cnt, seg->off, (unsigned)seg->size, off, (unsigned)size));
374        buf_off = off-seg->off;
375    if(seg->size > buf_off) {
376        atom_size = seg->size - buf_off;
377        if(atom_size>size) {
378            atom_size = size;
379        }
380        if(atom_size>BATOM_VEC_MAX_SIZE) {
381            atom_size = BATOM_VEC_MAX_SIZE;
382        }
383        seg->ref_cnt++;
384        base = B_FILE_BUFFER_BASE(buf, seg);
385        batom_accum_add_range(buf->acc, base+buf_off, atom_size);
386    }
387        BDBG_MSG_TRACE(("b_file_buffer_add_segment<: %#lx seg:%#lx(%u) %lld:%u -> %#lx:%u", (unsigned long)buf, (unsigned long)seg, seg->ref_cnt, off, (unsigned)size, (unsigned long)(base+buf_off), (unsigned)atom_size));
388        return atom_size;
389}
390
391static void
392b_file_buffer_recycle_empty(bfile_buffer_t buf, b_file_buffer_segment *seg)
393{
394    if(seg->ref_cnt==0) {
395        BLST_Q_REMOVE(&buf->busy, seg, link);
396        BLST_Q_INSERT_TAIL(&buf->free, seg, link);
397    }
398    return;
399}
400
401static b_file_buffer_segment *
402b_file_buffer_get_read_segment(bfile_buffer_t buf, off_t off)
403{
404    b_file_buffer_segment *seg;
405    seg = b_file_buffer_segment_find(buf, off);
406    BDBG_MSG_TRACE(("b_file_buffer_get_read_segment: %#lx %lld seg:%#lx", (unsigned long)buf, off, (unsigned long)seg));
407    if(seg==NULL) { /* read to the new segment */
408        if(BLST_Q_LAST(&buf->free)==NULL) {
409            b_file_buffer_segment *next;
410
411                        for(seg=BLST_Q_FIRST(&buf->busy); seg; ) {
412                next = BLST_Q_NEXT(seg,link);
413                if(seg->ref_cnt==0) {
414                    BLST_Q_REMOVE(&buf->busy, seg, link);
415                    BLST_Q_INSERT_TAIL(&buf->free, seg, link);
416                }
417                seg = next;
418            }
419        }
420        seg = BLST_Q_LAST(&buf->free); /* use element from the free queue */
421        if(seg) {
422            BLST_Q_REMOVE(&buf->free, seg, link);
423            /* initialize new segment */
424            b_file_buffer_init_segment(buf, seg, off);
425            b_file_buffer_insert_segment(buf, seg);
426        }
427    }
428    return seg;
429}
430
431static batom_t
432b_file_buffer_complete(bfile_buffer_t buf)
433{
434        batom_t atom;
435    if(batom_accum_len(buf->acc)>0) {
436            atom = batom_from_accum(buf->acc, &b_file_buffer_atom, &buf);
437    } else {
438        atom = NULL;
439    }
440        BDBG_MSG_TRACE(("b_file_buffer_complete: %#lx atom:%#lx(%u)", (unsigned long)buf, (unsigned long)atom, atom?batom_len(atom):0));
441        b_file_buffer_dump(buf, "b_file_buffer_complete");
442    return atom;
443}
444
445static void
446b_file_buffer_async_complete(bfile_buffer_t buf, bfile_buffer_result result)
447{
448        batom_t atom = b_file_buffer_complete(buf);
449    buf->async_state.active = false;
450        buf->async_state.read_complete(buf->async_state.cntx, atom, result);
451    return;
452}
453
454
455static batom_t
456b_file_buffer_async_read(bfile_buffer_t buf, off_t off, size_t length, bfile_buffer_result *sync_result)
457{
458        size_t left;
459        batom_t atom;
460    bfile_buffer_result result = bfile_buffer_result_ok;
461
462        BDBG_MSG_TRACE(("b_file_buffer_async_read>: %#lx %lld:%u", (unsigned long)buf, off, (unsigned)length));
463        for(left=length;left;) {
464        size_t atom_size;
465                b_file_buffer_segment *seg = b_file_buffer_get_read_segment(buf, off);
466        if(seg==NULL) {
467            switch(buf->cfg.async_overflow(buf->cfg.sync_cnxt)) {
468            default:
469            case bfile_buffer_overflow_action_abort:
470                            BDBG_MSG_TRACE(("b_file_buffer_async_read<: %#lx aborted request", (unsigned long)buf));
471                result = bfile_buffer_result_buffer_overflow;
472                goto done;
473            case bfile_buffer_overflow_action_wait:
474                BDBG_MSG_TRACE(("b_file_buffer_async_read<: %#lx waiting for segment", (unsigned long)buf));
475                buf->async_state.wait_for_seg = true;
476                buf->async_state.off = off;
477                buf->async_state.size = left;
478                if(sync_result) {
479                    *sync_result = bfile_buffer_result_async;
480                }
481                return NULL;
482            }
483        }
484        atom_size = b_file_buffer_add_segment(buf, seg, off, left);
485        BDBG_ASSERT(atom_size<=left);
486        left-=atom_size;
487        off+=atom_size;
488        if(left==0) {
489            goto done;
490        }
491        if(seg->size != buf->segment_size) { /* need to read data */
492                    b_file_buffer_dump(buf, "b_file_buffer_async_read");
493            buf->async_state.off = off;
494            buf->async_state.size = left;
495                        result = b_file_buffer_async_read_to_segment(buf, seg);
496            if(result!=bfile_buffer_result_async) { goto done;}
497            if(sync_result) {
498                *sync_result = result;
499                BDBG_MSG_TRACE(("b_file_buffer_async_read<: %#lx async_read", (unsigned long)buf));
500            }
501            return NULL;
502                } 
503        }
504done:
505        atom = b_file_buffer_complete(buf);
506    if(sync_result) {
507        *sync_result = result;
508        return atom;
509    } else {
510        buf->async_state.active = false;
511                buf->async_state.read_complete(buf->async_state.cntx, atom, result);
512        return NULL;
513    }
514}
515
516static bfile_buffer_result
517b_file_buffer_map_read_error(ssize_t result)
518{
519    switch(result) {
520    case 0:
521        return bfile_buffer_result_eof;
522    case BFILE_ERROR_NO_DATA:
523        return bfile_buffer_result_no_data;
524    default: 
525        return bfile_buffer_result_read_error; 
526    }
527}
528
529static void
530b_file_buffer_read_complete(void *cont, ssize_t read_size)
531{
532        bfile_buffer_t buf = cont;
533        b_file_buffer_segment *seg;
534    bfile_buffer_result result = bfile_buffer_result_ok;
535
536        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
537        BDBG_MSG_TRACE(("b_file_buffer_read_complete>: %#lx %d", (unsigned long)buf, (int)read_size));
538
539        seg = buf->async_state.seg_read;
540        BDBG_ASSERT(seg);
541    if(read_size>0) {
542            size_t atom_size;
543            off_t off = buf->async_state.off;
544            size_t left = buf->async_state.size;
545
546        seg->size += read_size;
547        BDBG_ASSERT(seg->size <= buf->segment_size);
548
549        atom_size = b_file_buffer_add_segment(buf, seg, off, left);
550        BDBG_ASSERT(atom_size<=left);
551        left-=atom_size;
552        off+=atom_size;
553        if(left!=0) {
554            if(seg->size == buf->segment_size) {  /* keep on reading */
555                b_file_buffer_async_read(buf, off, left, NULL);
556                return;
557            }
558            result = bfile_buffer_result_no_data;
559        }
560    } else {
561        b_file_buffer_recycle_empty(buf, seg);
562        result = b_file_buffer_map_read_error(read_size);
563    }
564    b_file_buffer_async_complete(buf, result);
565    return;
566}
567
568batom_t
569bfile_buffer_async_read(bfile_buffer_t buf, off_t off, size_t length, bfile_buffer_result *result, void (*read_complete)(void *, batom_t, bfile_buffer_result ), void *cntx)
570{
571        batom_t atom;
572        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
573        BDBG_MSG_TRACE(("bfile_buffer_async_read>: %#lx %lld:%u", (unsigned long)buf, off, (unsigned)length));
574
575        BDBG_ASSERT(buf->cfg.async);
576        BDBG_ASSERT(buf->cfg.async_read);
577        BDBG_ASSERT(batom_accum_len(buf->acc)==0); /* accumulator shall be empty */
578        BDBG_ASSERT(!buf->async_state.active); /* and we shall not be waiting for completion of asyncrhonous transaction */
579        buf->async_state.read_complete = read_complete;
580        buf->async_state.cntx = cntx;
581        b_file_buffer_dump(buf, "bfile_buffer_async_read+");
582        atom = b_file_buffer_async_read(buf, off, length, result);
583        b_file_buffer_dump(buf, "bfile_buffer_async_read-");
584        return atom;
585}
586
587static batom_t
588b_file_buffer_read(bfile_buffer_t buf, off_t off, size_t length, bfile_buffer_result *result)
589{
590        size_t left;
591        batom_t atom;
592
593        BDBG_MSG_TRACE(("b_file_buffer_read>: %#lx %lld:%u", (unsigned long)buf, off, (unsigned)length));
594    b_file_buffer_dump(buf, "b_file_buffer_read+");
595        *result = bfile_buffer_result_ok;
596
597        for(left=length;left;) {
598                b_file_buffer_segment *seg;
599                size_t atom_size;
600
601        seg = b_file_buffer_get_read_segment(buf, off);
602                BDBG_MSG_TRACE(("b_file_buffer_read: %#lx %lld:%u seg:%#lx", (unsigned long)buf, off, (unsigned)length, (unsigned long)seg));
603        if(seg==NULL) {
604            *result = bfile_buffer_result_buffer_overflow;
605            break;
606        }
607        /* add segment */
608        atom_size = b_file_buffer_add_segment(buf, seg, off, left);
609        BDBG_ASSERT(atom_size<=left);
610        left-=atom_size;
611        off+=atom_size;
612        if(left==0) {
613            break;
614        }
615        if(seg->size != buf->segment_size) { /* need to read data */
616            ssize_t read_result;
617            read_result = b_file_buffer_read_to_segment(buf, seg);
618            if(read_result>0) {
619                seg->size += read_result;
620                BDBG_ASSERT(seg->size <= buf->segment_size);
621                /* add segmenta (new read data) */
622                atom_size = b_file_buffer_add_segment(buf, seg, off, left);
623                BDBG_ASSERT(atom_size<=left);
624                left-=atom_size;
625                off+=atom_size;
626                if(left==0) {
627                    break;
628                }
629                if(seg->size != buf->segment_size) { 
630                    /* if read was not complete, then terminate loop and return */
631                    b_file_buffer_recycle_empty(buf, seg);
632                    *result = bfile_buffer_result_no_data;
633                    break;
634                }
635            } else {
636                b_file_buffer_recycle_empty(buf, seg);
637                *result = b_file_buffer_map_read_error(read_result);
638                break;
639            }
640        }
641        }
642    atom = b_file_buffer_complete(buf);
643        BDBG_MSG_TRACE(("b_file_buffer_read<: %#lx %lld:%u %#lx:%u", (unsigned long)buf, off, (unsigned)length, (unsigned long)atom, (unsigned)*result));
644        return atom;
645}
646
647batom_t
648bfile_buffer_read(bfile_buffer_t buf, off_t off, size_t length, bfile_buffer_result *result)
649{
650        batom_t atom;
651
652        BDBG_MSG_TRACE(("bfile_buffer_read>: %#lx %lld:%u", (unsigned long)buf, off, (unsigned)length));
653        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
654        BDBG_ASSERT(result);
655        BDBG_ASSERT(!buf->async_state.active); /* we shall not be waiting for the completion of asyncrhonous transaction */
656    BDBG_ASSERT(!buf->async_state.wait_for_seg);
657        buf->async_state.read_complete = NULL;
658        atom = b_file_buffer_read(buf, off, length, result);
659        BDBG_MSG_TRACE(("bfile_buffer_read<: %#lx %lld:%u %#lx:%u", (unsigned long)buf, off, (unsigned)length, (unsigned long)atom, (unsigned)*result));
660        return atom;
661}
662
663
664bfile_buffer_overflow_action
665b_file_default_async_overflow(void *sync_cnxt)
666{
667    BSTD_UNUSED(sync_cnxt);
668    return bfile_buffer_overflow_action_abort;
669}
670
671
672void
673bfile_buffer_default_cfg(bfile_buffer_cfg *cfg)
674{
675        cfg->buf = NULL;
676        cfg->async = false;
677        cfg->nsegs = 32;
678    /* to handle MP4/MKV streams this should be as large as largest frame in the stream */
679        cfg->buf_len = cfg->nsegs*(BIO_BLOCK_SIZE*15);
680        cfg->fd = NULL;
681        cfg->async_read = NULL;
682        cfg->sync_cnxt = NULL;
683    cfg->async_overflow = b_file_default_async_overflow;
684        return;
685}
686
687bfile_buffer_t
688bfile_buffer_create(batom_factory_t factory, bfile_buffer_cfg *cfg)
689{
690        bfile_buffer_t buf;
691        unsigned i;
692        size_t segment_size;
693
694        BDBG_ASSERT(factory);
695        BDBG_ASSERT(cfg);
696        if(!cfg->buf) {
697                BDBG_ERR(("bfile_buffer_create: buffer wasn't provided"));
698                goto err_param;
699        }
700        if(cfg->nsegs<=0) {
701                BDBG_ERR(("bfile_buffer_create: invalid number of segments"));
702                goto err_param;
703        }
704        if(cfg->buf_len<=0) {
705                BDBG_ERR(("bfile_buffer_create: invalid buffer size"));
706                goto err_param;
707        }
708        if(!cfg->fd) {
709                BDBG_ERR(("bfile_buffer_create: file descriptor wasn't provided"));
710                goto err_param;
711        }
712        if(cfg->async && !cfg->async_read) {
713                BDBG_ERR(("bfile_buffer_create: invalid options for asyn I/O"));
714                goto err_param;
715        }
716        segment_size = (cfg->buf_len/cfg->nsegs)&(~(BIO_BLOCK_SIZE-1));
717        if(segment_size<BIO_BLOCK_SIZE) {
718                BDBG_ERR(("bfile_buffer_create: segment size %u < %u (buf_len:%u nsegs:%u -> %u)", (unsigned)segment_size, (unsigned)BIO_BLOCK_SIZE, (unsigned)cfg->buf_len,  (unsigned)cfg->nsegs, (unsigned)cfg->buf_len/cfg->nsegs));
719                goto err_param;
720        }
721        buf = BKNI_Malloc(sizeof(*buf)+ sizeof(b_file_buffer_segment)*cfg->nsegs);
722    BDBG_MSG_TRACE(("bfile_buffer_create:%#lx  %#lx %u:%u(%u) %s", (unsigned long)buf, (unsigned long)cfg->buf, (unsigned)cfg->buf_len, (unsigned)cfg->nsegs, (unsigned)segment_size, cfg->async?"ASYNC":"")); 
723        if(!buf) {
724                BDBG_ERR(("bfile_buffer_create: can't allocate %u bytes", sizeof(*buf)+ sizeof(b_file_buffer_segment)*cfg->nsegs));
725                goto err_alloc;
726        }
727        BDBG_OBJECT_INIT(buf, bfile_buffer_t);
728        buf->cfg = *cfg;       
729        buf->segment_size = segment_size;
730        buf->async_state.active=false;
731    buf->async_state.wait_for_seg = false;
732        buf->acc = batom_accum_create(factory);
733        if(!buf->acc) {
734                goto err_acc;
735
736        }
737        BLST_Q_INIT(&buf->free);
738        BLST_Q_INIT(&buf->busy);
739        for(i=0;i<cfg->nsegs;i++) {
740                b_file_buffer_segment *seg=&buf->segs[i];
741
742                seg->off = 0;
743                seg->size = 0;
744                seg->ref_cnt = 0;
745
746                BLST_Q_INSERT_TAIL(&buf->free, seg, link);
747        }
748        return buf;
749err_acc:
750        BKNI_Free(buf);
751err_alloc:
752err_param:
753        return NULL;
754}
755
756void 
757bfile_buffer_clear(bfile_buffer_t buf)
758{
759        unsigned i;
760
761        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
762        if(buf->async_state.active) {
763                BDBG_WRN(("bfile_buffer_clear: %#lx clearing while doing async I/O", (unsigned long)buf));
764        }
765        if(BLST_Q_FIRST(&buf->busy)) {
766                BDBG_WRN(("bfile_buffer_clear: %#lx clearing while have active buffers", (unsigned long)buf));
767#if BDBG_DEBUG_BUILD
768                {
769                        b_file_buffer_segment *seg;
770                        for(seg=BLST_Q_FIRST(&buf->busy); seg; seg=BLST_Q_NEXT(seg, link)) {
771                                BDBG_WRN(("bfile_buffer_clear: %#lx buffers %#lx:%u %lld:%u", (unsigned long)buf, (unsigned long)seg, seg->ref_cnt, seg->off, seg->size ));
772                        }
773                }
774#endif
775        }
776        batom_accum_clear(buf->acc);
777        BLST_Q_INIT(&buf->free);
778        BLST_Q_INIT(&buf->busy);
779        for(i=0;i<buf->cfg.nsegs;i++) {
780                b_file_buffer_segment *seg=&buf->segs[i];
781
782                seg->off = -1;
783                seg->size = 0;
784                seg->ref_cnt = 0;
785
786                BLST_Q_INSERT_TAIL(&buf->free, seg, link);
787        }
788    return;
789}
790
791
792void 
793bfile_buffer_destroy(bfile_buffer_t buf)
794{
795        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
796    bfile_buffer_clear(buf);
797        batom_accum_destroy(buf->acc);
798        BDBG_OBJECT_DESTROY(buf, bfile_buffer_t);
799        BKNI_Free(buf);
800        return;
801}
802
803int 
804bfile_buffer_get_bounds(bfile_buffer_t buf, off_t *first, off_t *last)
805{
806        BDBG_OBJECT_ASSERT(buf, bfile_buffer_t);
807        return buf->cfg.fd->bounds(buf->cfg.fd, first, last);
808}
Note: See TracBrowser for help on using the repository browser.