3 #include "network_backends.h"
15 #define MMAP_CHUNK_SIZE (512*1024)
17 off_t
mmap_align_offset(off_t start
) {
18 static long pagesize
= 0;
20 pagesize
= sysconf(_SC_PAGESIZE
);
21 force_assert(pagesize
< MMAP_CHUNK_SIZE
);
23 force_assert(start
>= (start
% pagesize
));
24 return start
- (start
% pagesize
);
29 static volatile int sigbus_jmp_valid
;
30 static sigjmp_buf sigbus_jmp
;
32 static void sigbus_handler(int sig
) {
34 if (sigbus_jmp_valid
) siglongjmp(sigbus_jmp
, 1);
35 log_failed_assert(__FILE__
, __LINE__
, "SIGBUS");
39 /* read mmap()ed data into local buffer */
40 #define LOCAL_BUFFERING 1
43 int network_write_file_chunk_mmap(server
*srv
, connection
*con
, int fd
, chunkqueue
*cq
, off_t
*p_max_bytes
) {
44 chunk
* const c
= cq
->first
;
45 off_t offset
, toSend
, file_end
;
47 size_t mmap_offset
, mmap_avail
;
51 force_assert(NULL
!= c
);
52 force_assert(FILE_CHUNK
== c
->type
);
53 force_assert(c
->offset
>= 0 && c
->offset
<= c
->file
.length
);
55 offset
= c
->file
.start
+ c
->offset
;
56 toSend
= c
->file
.length
- c
->offset
;
57 if (toSend
> *p_max_bytes
) toSend
= *p_max_bytes
;
58 file_end
= c
->file
.start
+ c
->file
.length
; /* offset to file end in this chunk */
61 chunkqueue_remove_finished_chunks(cq
);
65 if (0 != chunkqueue_open_file_chunk(srv
, cq
)) return -1;
67 /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
68 if (0 != sigsetjmp(sigbus_jmp
, 1)) {
71 log_error_write(srv
, __FILE__
, __LINE__
, "sbd", "SIGBUS in mmap:",
72 c
->file
.name
, c
->file
.fd
);
74 munmap(c
->file
.mmap
.start
, c
->file
.mmap
.length
);
75 c
->file
.mmap
.start
= MAP_FAILED
;
76 #ifdef LOCAL_BUFFERING
83 signal(SIGBUS
, sigbus_handler
);
85 /* mmap the buffer if offset is outside old mmap area or not mapped at all */
86 if (MAP_FAILED
== c
->file
.mmap
.start
87 || offset
< c
->file
.mmap
.offset
88 || offset
>= (off_t
)(c
->file
.mmap
.offset
+ c
->file
.mmap
.length
)) {
90 if (MAP_FAILED
!= c
->file
.mmap
.start
) {
91 munmap(c
->file
.mmap
.start
, c
->file
.mmap
.length
);
92 c
->file
.mmap
.start
= MAP_FAILED
;
95 /* Optimizations for the future:
97 * adaptive mem-mapping
99 * we mmap() the whole file. If someone has alot large files and 32bit
100 * machine the virtual address area will be unrun and we will have a failing
103 * only mmap 16M in one chunk and move the window as soon as we have finished
106 * read-ahead buffering
108 * sending out several large files in parallel trashes the read-ahead of the
109 * kernel leading to long wait-for-seek times.
110 * solutions: (increasing complexity)
112 * 2. use a internal read-ahead buffer in the chunk-structure
113 * 3. use non-blocking IO for file-transfers
116 c
->file
.mmap
.offset
= mmap_align_offset(offset
);
118 /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */
119 c
->file
.mmap
.length
= MMAP_CHUNK_SIZE
;
120 if (c
->file
.mmap
.offset
> file_end
- (off_t
)c
->file
.mmap
.length
) {
121 c
->file
.mmap
.length
= file_end
- c
->file
.mmap
.offset
;
124 if (MAP_FAILED
== (c
->file
.mmap
.start
= mmap(NULL
, c
->file
.mmap
.length
, PROT_READ
, MAP_SHARED
, c
->file
.fd
, c
->file
.mmap
.offset
))) {
125 log_error_write(srv
, __FILE__
, __LINE__
, "ssbdoo", "mmap failed:",
126 strerror(errno
), c
->file
.name
, c
->file
.fd
, c
->file
.mmap
.offset
, (off_t
) c
->file
.mmap
.length
);
130 #if defined(LOCAL_BUFFERING)
131 sigbus_jmp_valid
= 1;
132 buffer_copy_string_len(c
->mem
, c
->file
.mmap
.start
, c
->file
.mmap
.length
);
133 sigbus_jmp_valid
= 0;
135 # if defined(HAVE_MADVISE)
136 /* don't advise files < 64Kb */
137 if (c
->file
.mmap
.length
> (64*1024)) {
138 /* darwin 7 is returning EINVAL all the time and I don't know how to
139 * detect this at runtime.
141 * ignore the return value for now */
142 madvise(c
->file
.mmap
.start
, c
->file
.mmap
.length
, MADV_WILLNEED
);
148 force_assert(offset
>= c
->file
.mmap
.offset
);
149 mmap_offset
= offset
- c
->file
.mmap
.offset
;
150 force_assert(c
->file
.mmap
.length
> mmap_offset
);
151 mmap_avail
= c
->file
.mmap
.length
- mmap_offset
;
152 if (toSend
> (off_t
) mmap_avail
) toSend
= mmap_avail
;
154 #if defined(LOCAL_BUFFERING)
155 data
= c
->mem
->ptr
+ mmap_offset
;
157 data
= c
->file
.mmap
.start
+ mmap_offset
;
160 sigbus_jmp_valid
= 1;
162 r
= send(fd
, data
, toSend
, 0);
164 r
= write(fd
, data
, toSend
);
166 sigbus_jmp_valid
= 0;
170 int lastError
= WSAGetLastError();
177 case WSAECONNABORTED
:
180 log_error_write(srv
, __FILE__
, __LINE__
, "sdd",
181 "send failed: ", lastError
, fd
);
195 log_error_write(srv
, __FILE__
, __LINE__
, "ssd",
196 "write failed:", strerror(errno
), fd
);
204 chunkqueue_mark_written(cq
, r
);
207 return (r
> 0 && r
== toSend
) ? 0 : -3;
210 #endif /* USE_MMAP */