Skip to content

Commit 4b06e4a

Browse files
Dyre Tjeldvolldahlerlend
authored andcommitted
Bug#30183865: HUGE MALLOC WHEN OPEN FILE LIMIT IS HIGH
Problem: Setting max_open_files to a high value, or setting it when the OS' rlimit had a large value (not equal to RLIM_INF) could cause the server run out of memory. Root cause was that the server would allocate an a array with capacity equal to the max_open_files setting at startup, and in particular that the OS rlim setting would override the setting supplied by the user, even if that value was much larger. Solution: A simple solution would be to not let the OS override the setting provided by the user. But this is a change in behavior which could potentially break existing applications. Moreover, it would not protect against the user setting the limit to an excessively large value. Furthermore, allocating space for the largest possible number of open files, is a pessimisation, since it is unlikely that all those array slots will be needed. In this patch the issue is addressed by changing the array into a vector and letting it grow as needed when files are opened. Additionally, on Windows the same file_info array was used to provide a mapping between Windows HANDLEs and pseudo file descriptors so that the mysys file functions could use integers to refer to files also on Windows. In order to hide this functionality in Windows-specific code and reduce the number of #ifdefs this mapping was moved into its own vector and data structure. In the process the mysys functions emulating posix functions with Windows API functions was re-factored and error handling tightened. The latter was necessary since negative testing using invalid pseudo file descriptors had to be intercepted earlier to avoid stepping outside the bounds of the vector mapping pseudo fds to HANDLEs. Change-Id: Ib255a509e8c538d79f316d642165263609961e78
1 parent 6b8878d commit 4b06e4a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1288
-990
lines changed

client/mysqlbinlog.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2868,7 +2868,6 @@ int main(int argc, char **argv) {
28682868
if (result_file && (result_file != stdout)) my_fclose(result_file, MYF(0));
28692869
cleanup();
28702870

2871-
my_free_open_file_info();
28722871
load_processor.destroy();
28732872
/* We cannot free DBUG, it is used in global destructors after exit(). */
28742873
my_end(my_end_arg | MY_DONT_FREE_DBUG);

client/mysqltest.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7674,7 +7674,7 @@ void str_to_file2(const char *fname, char *str, size_t size, bool append) {
76747674
fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
76757675

76767676
if (!append) flags |= O_TRUNC;
7677-
if ((fd = my_open(buff, flags, MYF(MY_WME | MY_FFNF))) < 0)
7677+
if ((fd = my_open(buff, flags, MYF(MY_WME))) < 0)
76787678
die("Could not open '%s' for writing, errno: %d", buff, errno);
76797679
if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
76807680
die("Could not find end of file '%s', errno: %d", buff, errno);

config.h.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@
9797
#cmakedefine HAVE_GETPASSPHRASE 1
9898
#cmakedefine HAVE_GETPWNAM 1
9999
#cmakedefine HAVE_GETPWUID 1
100-
#cmakedefine HAVE_GETRLIMIT 1
101100
#cmakedefine HAVE_GETRUSAGE 1
102101
#cmakedefine HAVE_INITGROUPS 1
103102
#cmakedefine HAVE_ISSETUGID 1
@@ -316,6 +315,7 @@
316315
/*
317316
* NDB
318317
*/
318+
#cmakedefine HAVE_GETRLIMIT 1
319319
#cmakedefine WITH_NDBCLUSTER_STORAGE_ENGINE 1
320320
#cmakedefine HAVE_PTHREAD_SETSCHEDPARAM 1
321321

configure.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ CHECK_FUNCTION_EXISTS (getpass HAVE_GETPASS)
276276
CHECK_FUNCTION_EXISTS (getpassphrase HAVE_GETPASSPHRASE)
277277
CHECK_FUNCTION_EXISTS (getpwnam HAVE_GETPWNAM)
278278
CHECK_FUNCTION_EXISTS (getpwuid HAVE_GETPWUID)
279-
CHECK_FUNCTION_EXISTS (getrlimit HAVE_GETRLIMIT)
280279
CHECK_FUNCTION_EXISTS (getrusage HAVE_GETRUSAGE)
281280
CHECK_FUNCTION_EXISTS (initgroups HAVE_INITGROUPS)
282281
CHECK_FUNCTION_EXISTS (issetugid HAVE_ISSETUGID)

include/my_dbug.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,24 @@ extern void _db_flush_gcov_();
328328
do { \
329329
} while (0)
330330
#endif /* DBUG_OFF */
331-
#endif /* __cplusplus */
332331

332+
/**
333+
A type-safe interface to DBUG_EXECUTE_IF, where the debug action to
334+
activate when the keyword is provided is given as a callable object
335+
(typically a lambda).
336+
337+
@note The body of the callable will be checked by the compiler even
338+
in optimized mode.
339+
340+
@param keyword String literal which will enable this debug action.
341+
@param clos Callable object taking no arguments which will be
342+
called in debug mode if the keyword is enabled.
343+
*/
344+
template <class DBGCLOS>
345+
inline void dbug(const char *keyword MY_ATTRIBUTE((unused)),
346+
DBGCLOS &&clos MY_ATTRIBUTE((unused))) {
347+
DBUG_EXECUTE_IF(keyword, clos(););
348+
}
349+
350+
#endif /* __cplusplus */
333351
#endif /* MY_DBUG_INCLUDED */

include/my_sys.h

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct MEM_ROOT;
109109
#define MY_FILE_ERROR ((size_t)-1)
110110

111111
/* General bitmaps for my_func's */
112-
#define MY_FFNF 1 /* Fatal if file not found */
112+
// 1 used to be MY_FFNF which has been removed
113113
#define MY_FNABP 2 /* Fatal if not all bytes read/writen */
114114
#define MY_NABP 4 /* Error if not all bytes read/writen */
115115
#define MY_FAE 8 /* Fatal if any error */
@@ -189,7 +189,9 @@ extern PSI_memory_key key_memory_max_alloca;
189189
if (size > max_alloca_sz) my_free(ptr)
190190

191191
#if defined(ENABLED_DEBUG_SYNC)
192-
extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
192+
using DebugSyncCallbackFp = void (*)(const char *, size_t);
193+
extern DebugSyncCallbackFp debug_sync_C_callback_ptr;
194+
193195
#define DEBUG_SYNC_C(_sync_point_name_) \
194196
do { \
195197
if (debug_sync_C_callback_ptr != NULL) \
@@ -219,7 +221,7 @@ extern void (*error_handler_hook)(uint my_err, const char *str, myf MyFlags);
219221
extern void (*fatal_error_handler_hook)(uint my_err, const char *str,
220222
myf MyFlags);
221223
extern void (*local_message_hook)(enum loglevel ll, uint ecode, va_list args);
222-
extern uint my_file_limit;
224+
223225
extern MYSQL_PLUGIN_IMPORT ulong my_thread_stack_size;
224226

225227
/*
@@ -260,7 +262,9 @@ extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *all_charsets[MY_ALL_CHARSETS_SIZE];
260262
extern CHARSET_INFO compiled_charsets[];
261263

262264
/* statistics */
263-
extern ulong my_file_opened, my_stream_opened, my_tmp_file_created;
265+
extern ulong my_tmp_file_created;
266+
extern ulong my_file_opened;
267+
extern ulong my_stream_opened;
264268
extern ulong my_file_total_opened;
265269
extern bool my_init_done;
266270

@@ -293,31 +297,6 @@ enum flush_type {
293297
FLUSH_FORCE_WRITE
294298
};
295299

296-
/*
297-
How was this file opened (for debugging purposes).
298-
The important part is whether it is UNOPEN or not.
299-
*/
300-
enum file_type {
301-
UNOPEN = 0,
302-
FILE_BY_OPEN,
303-
FILE_BY_CREATE,
304-
STREAM_BY_FOPEN,
305-
STREAM_BY_FDOPEN,
306-
FILE_BY_MKSTEMP,
307-
FILE_BY_O_TMPFILE
308-
};
309-
310-
struct st_my_file_info {
311-
char *name;
312-
#ifdef _WIN32
313-
HANDLE fhandle; /* win32 file handle */
314-
int oflag; /* open flags, e.g O_APPEND */
315-
#endif
316-
enum file_type type;
317-
};
318-
319-
extern struct st_my_file_info *my_file_info;
320-
321300
struct DYNAMIC_ARRAY {
322301
uchar *buffer{nullptr};
323302
uint elements{0}, max_element{0};
@@ -584,13 +563,11 @@ extern void *my_once_alloc(size_t Size, myf MyFlags);
584563
extern void my_once_free(void);
585564
extern char *my_once_strdup(const char *src, myf myflags);
586565
extern void *my_once_memdup(const void *src, size_t len, myf myflags);
587-
extern File my_open(const char *FileName, int Flags, myf MyFlags);
588-
extern File my_register_filename(File fd, const char *FileName,
589-
enum file_type type_of_file,
590-
uint error_message_number, myf MyFlags);
566+
extern File my_open(const char *filename, int Flags, myf MyFlags);
567+
591568
extern File my_create(const char *FileName, int CreateFlags, int AccessFlags,
592569
myf MyFlags);
593-
extern int my_close(File Filedes, myf MyFlags);
570+
extern int my_close(File fd, myf MyFlags);
594571
extern int my_mkdir(const char *dir, int Flags, myf MyFlags);
595572
extern int my_readlink(char *to, const char *filename, myf MyFlags);
596573
extern int my_is_symlink(const char *filename, ST_FILE_ID *file_id);
@@ -665,11 +642,11 @@ extern void my_osmaperr(unsigned long last_error);
665642

666643
extern const char *get_global_errmsg(int nr);
667644
extern void wait_for_free_space(const char *filename, int errors);
668-
extern FILE *my_fopen(const char *FileName, int Flags, myf MyFlags);
669-
extern FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags);
670-
extern FILE *my_freopen(const char *path, const char *mode, FILE *stream);
671-
extern int my_fclose(FILE *fd, myf MyFlags);
672-
extern File my_fileno(FILE *fd);
645+
extern FILE *my_fopen(const char *filename, int Flags, myf MyFlags);
646+
extern FILE *my_fdopen(File fd, const char *filename, int Flags, myf MyFlags);
647+
extern FILE *my_freopen(const char *filename, const char *mode, FILE *stream);
648+
extern int my_fclose(FILE *stream, myf MyFlags);
649+
extern File my_fileno(FILE *stream);
673650
extern int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags);
674651
extern int my_fallocator(File fd, my_off_t newlength, int filler, myf MyFlags);
675652
extern void thr_set_sync_wait_callback(void (*before_sync)(void),
@@ -689,18 +666,31 @@ extern void my_message(uint my_err, const char *str, myf MyFlags);
689666
extern void my_message_stderr(uint my_err, const char *str, myf MyFlags);
690667
void my_message_local_stderr(enum loglevel, uint ecode, va_list args);
691668
extern void my_message_local(enum loglevel ll, uint ecode, ...);
669+
670+
/**
671+
Convenience wrapper for OS error messages which report
672+
errno/my_errno with %d followed by strerror as %s, as the last
673+
conversions in the error message.
674+
675+
The OS error message (my_errno) is formatted in a stack buffer and
676+
the errno value and a pointer to the buffer is added to the end of
677+
the parameter pack passed to my_error().
678+
679+
@param errno_val errno/my_errno number.
680+
@param ppck parameter pack of additional arguments to pass to my_error().
681+
*/
682+
template <class... Ts>
683+
inline void MyOsError(int errno_val, Ts... ppck) {
684+
char errbuf[MYSYS_STRERROR_SIZE];
685+
my_error(ppck..., errno_val, my_strerror(errbuf, sizeof(errbuf), errno_val));
686+
}
687+
692688
extern bool my_init(void);
693689
extern void my_end(int infoflag);
694690
extern const char *my_filename(File fd);
695691
extern MY_MODE get_file_perm(ulong perm_flags);
696692
extern bool my_chmod(const char *filename, ulong perm_flags, myf my_flags);
697693

698-
#ifdef EXTRA_DEBUG
699-
void my_print_open_files(void);
700-
#else
701-
#define my_print_open_files()
702-
#endif
703-
704694
extern bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist);
705695
extern char *my_tmpdir(MY_TMPDIR *tmpdir);
706696
extern void free_tmpdir(MY_TMPDIR *tmpdir);
@@ -817,7 +807,6 @@ extern uchar *my_compress_alloc(mysql_compress_context *comp_ctx,
817807
extern ha_checksum my_checksum(ha_checksum crc, const uchar *mem, size_t count);
818808

819809
extern uint my_set_max_open_files(uint files);
820-
void my_free_open_file_info(void);
821810

822811
extern bool my_gethwaddr(uchar *to);
823812

mysys/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ SET(MYSYS_SOURCES
7171
my_copy.cc
7272
my_create.cc
7373
my_delete.cc
74-
my_div.cc
7574
my_error.cc
7675
my_fallocator.cc
7776
my_file.cc

mysys/mf_tempfile.cc

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686

8787
File create_temp_file(char *to, const char *dir, const char *prefix,
8888
int mode MY_ATTRIBUTE((unused)),
89-
UnlinkOrKeepFile unlink_or_keep, myf MyFlags) {
89+
UnlinkOrKeepFile unlink_or_keep,
90+
myf MyFlags MY_ATTRIBUTE((unused))) {
9091
File file = -1;
9192
#ifdef _WIN32
9293
TCHAR path_buf[MAX_PATH - 14];
@@ -149,19 +150,24 @@ File create_temp_file(char *to, const char *dir, const char *prefix,
149150
/* Explicitly don't use O_EXCL here as it has a different
150151
meaning with O_TMPFILE.
151152
*/
152-
file = open(dirname_buf, O_RDWR | O_TMPFILE | O_CLOEXEC, S_IRUSR | S_IWUSR);
153+
file = mysys_priv::RetryOnEintr(
154+
[&]() {
155+
return open(dirname_buf, O_RDWR | O_TMPFILE | O_CLOEXEC,
156+
S_IRUSR | S_IWUSR);
157+
},
158+
-1);
159+
153160
if (file >= 0) {
154161
sprintf(to, "%s%.20sfd=%d", dirname_buf, prefix ? prefix : "tmp.", file);
155-
file = my_register_filename(file, to, FILE_BY_O_TMPFILE,
156-
EE_CANTCREATEFILE, MyFlags);
162+
file_info::RegisterFilename(file, to,
163+
file_info::OpenType::FILE_BY_O_TMPFILE);
157164
}
158165
}
159166
// Fall through, in case open() failed above (or we have KEEP_FILE).
160167
#endif /* HAVE_O_TMPFILE */
161168
if (file == -1) {
162169
char prefix_buff[30];
163170
uint pfx_len;
164-
File org_file;
165171

166172
pfx_len = (uint)(my_stpcpy(my_stpnmov(prefix_buff, prefix ? prefix : "tmp.",
167173
sizeof(prefix_buff) - 7),
@@ -174,22 +180,13 @@ File create_temp_file(char *to, const char *dir, const char *prefix,
174180
return file;
175181
}
176182
my_stpcpy(convert_dirname(to, dir, NullS), prefix_buff);
177-
org_file = mkstemp(to);
178-
file = my_register_filename(org_file, to, FILE_BY_MKSTEMP,
179-
EE_CANTCREATEFILE, MyFlags);
180-
/* If we didn't manage to register the name, remove the temp file */
181-
if (org_file >= 0 && file < 0) {
182-
int tmp = my_errno();
183-
close(org_file);
184-
(void)my_delete(to, MYF(MY_WME));
185-
set_my_errno(tmp);
186-
return file;
187-
}
183+
file = mkstemp(to);
184+
file_info::RegisterFilename(file, to, file_info::OpenType::FILE_BY_MKSTEMP);
188185
if (unlink_or_keep == UNLINK_FILE) {
189186
unlink(to);
190187
}
191188
}
192-
#endif
189+
#endif /* _WIN32 */
193190
if (file >= 0) {
194191
mysql_mutex_lock(&THR_LOCK_open);
195192
my_tmp_file_created++;

mysys/my_chmod.cc

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ MY_MODE get_file_perm(ulong perm_flags) {
8585
@retval false : File permission changed successfully
8686
*/
8787

88-
bool my_chmod(const char *filename, ulong perm_flags, myf my_flags) {
88+
bool my_chmod(const char *filename, ulong perm_flags, myf MyFlags) {
8989
int ret_val;
9090
MY_MODE file_perm;
9191
DBUG_TRACE;
@@ -98,11 +98,9 @@ bool my_chmod(const char *filename, ulong perm_flags, myf my_flags) {
9898
ret_val = chmod(filename, file_perm);
9999
#endif
100100

101-
if (ret_val && (my_flags & (MY_FAE + MY_WME))) {
102-
char errbuf[MYSYS_STRERROR_SIZE];
101+
if (ret_val && (MyFlags & (MY_FAE | MY_WME))) {
103102
set_my_errno(errno);
104-
my_error(EE_CHANGE_PERMISSIONS, MYF(0), filename, errno,
105-
my_strerror(errbuf, sizeof(errbuf), errno));
103+
MyOsError(my_errno(), EE_CHANGE_PERMISSIONS, MYF(0), filename);
106104
}
107105

108106
return ret_val ? true : false;

mysys/my_chsize.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) {
108108

109109
err:
110110
if (MyFlags & MY_WME) {
111-
char errbuf[MYSYS_STRERROR_SIZE];
112-
my_error(EE_CANT_CHSIZE, MYF(0), my_errno(),
113-
my_strerror(errbuf, sizeof(errbuf), my_errno()));
111+
MyOsError(my_errno(), EE_CANT_CHSIZE, MYF(0));
114112
}
115113
return 1;
116114
}

0 commit comments

Comments
 (0)