diff options
Diffstat (limited to 'meta/recipes-devtools/squashfs-tools/patches/squashfs-fix-open-file-limit.patch')
| -rw-r--r-- | meta/recipes-devtools/squashfs-tools/patches/squashfs-fix-open-file-limit.patch | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/meta/recipes-devtools/squashfs-tools/patches/squashfs-fix-open-file-limit.patch b/meta/recipes-devtools/squashfs-tools/patches/squashfs-fix-open-file-limit.patch new file mode 100644 index 0000000000..c60f7b42af --- /dev/null +++ b/meta/recipes-devtools/squashfs-tools/patches/squashfs-fix-open-file-limit.patch | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | Upstream-Status: Backport | ||
| 2 | |||
| 3 | unsquashfs: fix open file limit | ||
| 4 | |||
| 5 | Previously Unsquashfs relied on the to_writer queue being | ||
| 6 | set to 1000 to limit the amount of open file read-ahead to a | ||
| 7 | maximum of 500. For the default process limit of 1024 open files | ||
| 8 | this was perhaps acceptable, but it obviously breaks if ulimit has | ||
| 9 | been used to set the open file limit to below 504 (this includes | ||
| 10 | stdin, stdout, stderr and the Squashfs filesystem being unsquashed). | ||
| 11 | |||
| 12 | More importantly setting the to_writer queue to 1000 to limit | ||
| 13 | the amount of files open has always been an inherent performance | ||
| 14 | hit because the to_writer queue queues blocks. It limits the | ||
| 15 | block readhead to 1000 blocks, irrespective of how many files | ||
| 16 | that represents. A single file containing more than 1000 blocks | ||
| 17 | will still be limited to a 1000 block readahead even though the | ||
| 18 | data block cache typically can buffer more than this (at the | ||
| 19 | default data cache size of 256 Mbytes and the default block size | ||
| 20 | of 128 Kbytes, it can buffer 2048 blocks). Obviously the | ||
| 21 | caches serve more than just a read-ahead role (they also | ||
| 22 | cache decompressed blocks in case they're referenced later e.g. | ||
| 23 | by duplicate files), but the artificial limit imposed on | ||
| 24 | the read-ahead due to setting the to_writer queue to 1000 is | ||
| 25 | unnecessary. | ||
| 26 | |||
| 27 | This commit does away with the need to limit the to_writer queue, | ||
| 28 | by introducing open_wait() and close_wake() calls which correctly | ||
| 29 | track the amount of open files. | ||
| 30 | |||
| 31 | Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk> | ||
| 32 | |||
| 33 | Signed-off-by: yanjun.zhu <yanjun.zhu@windriver.com> | ||
| 34 | |||
| 35 | diff -urpN a/unsquashfs.c b/unsquashfs.c | ||
| 36 | --- a/unsquashfs.c 2012-11-30 15:31:29.000000000 +0800 | ||
| 37 | +++ b/unsquashfs.c 2012-11-30 15:32:03.000000000 +0800 | ||
| 38 | @@ -31,6 +31,8 @@ | ||
| 39 | |||
| 40 | #include <sys/sysinfo.h> | ||
| 41 | #include <sys/types.h> | ||
| 42 | +#include <sys/time.h> | ||
| 43 | +#include <sys/resource.h> | ||
| 44 | |||
| 45 | struct cache *fragment_cache, *data_cache; | ||
| 46 | struct queue *to_reader, *to_deflate, *to_writer, *from_writer; | ||
| 47 | @@ -784,6 +786,46 @@ failure: | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | +pthread_mutex_t open_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| 52 | +pthread_cond_t open_empty = PTHREAD_COND_INITIALIZER; | ||
| 53 | +int open_unlimited, open_count; | ||
| 54 | +#define OPEN_FILE_MARGIN 10 | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +void open_init(int count) | ||
| 58 | +{ | ||
| 59 | + open_count = count; | ||
| 60 | + open_unlimited = count == -1; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +int open_wait(char *pathname, int flags, mode_t mode) | ||
| 65 | +{ | ||
| 66 | + if (!open_unlimited) { | ||
| 67 | + pthread_mutex_lock(&open_mutex); | ||
| 68 | + while (open_count == 0) | ||
| 69 | + pthread_cond_wait(&open_empty, &open_mutex); | ||
| 70 | + open_count --; | ||
| 71 | + pthread_mutex_unlock(&open_mutex); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + return open(pathname, flags, mode); | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | + | ||
| 78 | +void close_wake(int fd) | ||
| 79 | +{ | ||
| 80 | + close(fd); | ||
| 81 | + | ||
| 82 | + if (!open_unlimited) { | ||
| 83 | + pthread_mutex_lock(&open_mutex); | ||
| 84 | + open_count ++; | ||
| 85 | + pthread_cond_signal(&open_empty); | ||
| 86 | + pthread_mutex_unlock(&open_mutex); | ||
| 87 | + } | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | + | ||
| 91 | int write_file(struct inode *inode, char *pathname) | ||
| 92 | { | ||
| 93 | unsigned int file_fd, i; | ||
| 94 | @@ -794,8 +836,8 @@ int write_file(struct inode *inode, char | ||
| 95 | |||
| 96 | TRACE("write_file: regular file, blocks %d\n", inode->blocks); | ||
| 97 | |||
| 98 | - file_fd = open(pathname, O_CREAT | O_WRONLY | (force ? O_TRUNC : 0), | ||
| 99 | - (mode_t) inode->mode & 0777); | ||
| 100 | + file_fd = open_wait(pathname, O_CREAT | O_WRONLY | | ||
| 101 | + (force ? O_TRUNC : 0), (mode_t) inode->mode & 0777); | ||
| 102 | if(file_fd == -1) { | ||
| 103 | ERROR("write_file: failed to create file %s, because %s\n", | ||
| 104 | pathname, strerror(errno)); | ||
| 105 | @@ -1712,7 +1754,7 @@ void *writer(void *arg) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | - close(file_fd); | ||
| 110 | + close_wake(file_fd); | ||
| 111 | if(failed == FALSE) | ||
| 112 | set_attributes(file->pathname, file->mode, file->uid, | ||
| 113 | file->gid, file->time, file->xattr, force); | ||
| 114 | @@ -1803,9 +1845,9 @@ void *progress_thread(void *arg) | ||
| 115 | |||
| 116 | void initialise_threads(int fragment_buffer_size, int data_buffer_size) | ||
| 117 | { | ||
| 118 | - int i; | ||
| 119 | + struct rlimit rlim; | ||
| 120 | + int i, max_files, res; | ||
| 121 | sigset_t sigmask, old_mask; | ||
| 122 | - int all_buffers_size = fragment_buffer_size + data_buffer_size; | ||
| 123 | |||
| 124 | sigemptyset(&sigmask); | ||
| 125 | sigaddset(&sigmask, SIGINT); | ||
| 126 | @@ -1841,10 +1883,86 @@ void initialise_threads(int fragment_buf | ||
| 127 | EXIT_UNSQUASH("Out of memory allocating thread descriptors\n"); | ||
| 128 | deflator_thread = &thread[3]; | ||
| 129 | |||
| 130 | - to_reader = queue_init(all_buffers_size); | ||
| 131 | - to_deflate = queue_init(all_buffers_size); | ||
| 132 | - to_writer = queue_init(1000); | ||
| 133 | + /* | ||
| 134 | + * dimensioning the to_reader and to_deflate queues. The size of | ||
| 135 | + * these queues is directly related to the amount of block | ||
| 136 | + * read-ahead possible. To_reader queues block read requests to | ||
| 137 | + * the reader thread and to_deflate queues block decompression | ||
| 138 | + * requests to the deflate thread(s) (once the block has been read by | ||
| 139 | + * the reader thread). The amount of read-ahead is determined by | ||
| 140 | + * the combined size of the data_block and fragment caches which | ||
| 141 | + * determine the total number of blocks which can be "in flight" | ||
| 142 | + * at any one time (either being read or being decompressed) | ||
| 143 | + * | ||
| 144 | + * The maximum file open limit, however, affects the read-ahead | ||
| 145 | + * possible, in that for normal sizes of the fragment and data block | ||
| 146 | + * caches, where the incoming files have few data blocks or one fragment | ||
| 147 | + * only, the file open limit is likely to be reached before the | ||
| 148 | + * caches are full. This means the worst case sizing of the combined | ||
| 149 | + * sizes of the caches is unlikely to ever be necessary. However, is is | ||
| 150 | + * obvious read-ahead up to the data block cache size is always possible | ||
| 151 | + * irrespective of the file open limit, because a single file could | ||
| 152 | + * contain that number of blocks. | ||
| 153 | + * | ||
| 154 | + * Choosing the size as "file open limit + data block cache size" seems | ||
| 155 | + * to be a reasonable estimate. We can reasonably assume the maximum | ||
| 156 | + * likely read-ahead possible is data block cache size + one fragment | ||
| 157 | + * per open file. | ||
| 158 | + * | ||
| 159 | + * dimensioning the to_writer queue. The size of this queue is | ||
| 160 | + * directly related to the amount of block read-ahead possible. | ||
| 161 | + * However, unlike the to_reader and to_deflate queues, this is | ||
| 162 | + * complicated by the fact the to_writer queue not only contains | ||
| 163 | + * entries for fragments and data_blocks but it also contains | ||
| 164 | + * file entries, one per open file in the read-ahead. | ||
| 165 | + * | ||
| 166 | + * Choosing the size as "2 * (file open limit) + | ||
| 167 | + * data block cache size" seems to be a reasonable estimate. | ||
| 168 | + * We can reasonably assume the maximum likely read-ahead possible | ||
| 169 | + * is data block cache size + one fragment per open file, and then | ||
| 170 | + * we will have a file_entry for each open file. | ||
| 171 | + */ | ||
| 172 | + res = getrlimit(RLIMIT_NOFILE, &rlim); | ||
| 173 | + if (res == -1) { | ||
| 174 | + ERROR("failed to get open file limit! Defaulting to 1\n"); | ||
| 175 | + rlim.rlim_cur = 1; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + if (rlim.rlim_cur != RLIM_INFINITY) { | ||
| 179 | + /* | ||
| 180 | + * leave OPEN_FILE_MARGIN free (rlim_cur includes fds used by | ||
| 181 | + * stdin, stdout, stderr and filesystem fd | ||
| 182 | + */ | ||
| 183 | + if (rlim.rlim_cur <= OPEN_FILE_MARGIN) | ||
| 184 | + /* no margin, use minimum possible */ | ||
| 185 | + max_files = 1; | ||
| 186 | + else | ||
| 187 | + max_files = rlim.rlim_cur - OPEN_FILE_MARGIN; | ||
| 188 | + } else | ||
| 189 | + max_files = -1; | ||
| 190 | + | ||
| 191 | + /* set amount of available files for use by open_wait and close_wake */ | ||
| 192 | + open_init(max_files); | ||
| 193 | + | ||
| 194 | + /* | ||
| 195 | + * allocate to_reader, to_deflate and to_writer queues. Set based on | ||
| 196 | + * open file limit and cache size, unless open file limit is unlimited, | ||
| 197 | + * in which case set purely based on cache limits | ||
| 198 | + */ | ||
| 199 | + if (max_files != -1) { | ||
| 200 | + to_reader = queue_init(max_files + data_buffer_size); | ||
| 201 | + to_deflate = queue_init(max_files + data_buffer_size); | ||
| 202 | + to_writer = queue_init(max_files * 2 + data_buffer_size); | ||
| 203 | + } else { | ||
| 204 | + int all_buffers_size = fragment_buffer_size + data_buffer_size; | ||
| 205 | + | ||
| 206 | + to_reader = queue_init(all_buffers_size); | ||
| 207 | + to_deflate = queue_init(all_buffers_size); | ||
| 208 | + to_writer = queue_init(all_buffers_size * 2); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | from_writer = queue_init(1); | ||
| 212 | + | ||
| 213 | fragment_cache = cache_init(block_size, fragment_buffer_size); | ||
| 214 | data_cache = cache_init(block_size, data_buffer_size); | ||
| 215 | pthread_create(&thread[0], NULL, reader, NULL); | ||
