]> granicus.if.org Git - postgresql/commitdiff
Create an fd.c entry point that is just like plain open(2) except that
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Jun 2000 03:58:34 +0000 (03:58 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 2 Jun 2000 03:58:34 +0000 (03:58 +0000)
it will close VFDs if necessary to surmount ENFILE or EMFILE failures.
Make use of this in md.c, xlog.c, and user.c routines that were
formerly vulnerable to these failures.  In particular, this should
handle failures of mdblindwrt() that have been observed under heavy
load conditions.  (By golly, every other process on the system may
crash after Postgres eats up all the kernel FDs, but Postgres will
keep going!)

src/backend/access/transam/xlog.c
src/backend/commands/user.c
src/backend/storage/file/fd.c
src/backend/storage/smgr/md.c
src/include/storage/fd.h

index b672cf2c7a9621fbab60fa58ae37870be2e06718..07a8392c4e443905cc6f36378bf9ae89d9236d05 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.13 2000/04/12 17:14:53 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.14 2000/06/02 03:58:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,8 +44,6 @@ bool          StopIfError = false;
 SPINLOCK       ControlFileLockId;
 SPINLOCK       XidGenLockId;
 
-extern bool ReleaseDataFile(void);
-
 extern VariableCache ShmemVariableCache;
 
 #define MinXLOGbuffers 4
@@ -738,22 +736,13 @@ XLogFileInit(uint32 log, uint32 seg)
        XLogFileName(path, log, seg);
        unlink(path);
 
-tryAgain:
 #ifndef __CYGWIN__
-       fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
 #else
-       fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
+       fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
 #endif
-       if (fd < 0 && (errno == EMFILE || errno == ENFILE))
-       {
-               fd = errno;
-               if (!ReleaseDataFile())
-                       elog(STOP, "Create(logfile %u seg %u) failed: %d (and no one data file can be closed)",
-                                logId, logSeg, fd);
-               goto tryAgain;
-       }
        if (fd < 0)
-               elog(STOP, "Init(logfile %u seg %u) failed: %d",
+               elog(STOP, "Open(logfile %u seg %u) failed: %d",
                         logId, logSeg, errno);
 
        if (lseek(fd, XLogSegSize - 1, SEEK_SET) != (off_t) (XLogSegSize - 1))
@@ -783,20 +772,11 @@ XLogFileOpen(uint32 log, uint32 seg, bool econt)
 
        XLogFileName(path, log, seg);
 
-tryAgain:
 #ifndef __CYGWIN__
-       fd = open(path, O_RDWR);
+       fd = BasicOpenFile(path, O_RDWR, S_IRUSR | S_IWUSR);
 #else
-       fd = open(path, O_RDWR | O_BINARY);
+       fd = BasicOpenFile(path, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
 #endif
-       if (fd < 0 && (errno == EMFILE || errno == ENFILE))
-       {
-               fd = errno;
-               if (!ReleaseDataFile())
-                       elog(STOP, "Open(logfile %u seg %u) failed: %d (and no one data file can be closed)",
-                                logId, logSeg, fd);
-               goto tryAgain;
-       }
        if (fd < 0)
        {
                if (econt && errno == ENOENT)
@@ -1102,20 +1082,11 @@ UpdateControlFile()
 {
        int                     fd;
 
-tryAgain:
 #ifndef __CYGWIN__
-       fd = open(ControlFilePath, O_RDWR);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR, S_IRUSR | S_IWUSR);
 #else
-       fd = open(ControlFilePath, O_RDWR | O_BINARY);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
 #endif
-       if (fd < 0 && (errno == EMFILE || errno == ENFILE))
-       {
-               fd = errno;
-               if (!ReleaseDataFile())
-                       elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)",
-                                fd);
-               goto tryAgain;
-       }
        if (fd < 0)
                elog(STOP, "Open(cntlfile) failed: %d", errno);
 
@@ -1174,9 +1145,9 @@ BootStrapXLOG()
 #endif
 
 #ifndef __CYGWIN__
-       fd = open(ControlFilePath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
 #else
-       fd = open(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
 #endif
        if (fd < 0)
                elog(STOP, "BootStrapXLOG failed to create control file (%s): %d",
@@ -1288,20 +1259,11 @@ StartupXLOG()
        /*
         * Open/read Control file
         */
-tryAgain:
 #ifndef __CYGWIN__
-       fd = open(ControlFilePath, O_RDWR);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR, S_IRUSR | S_IWUSR);
 #else
-       fd = open(ControlFilePath, O_RDWR | O_BINARY);
+       fd = BasicOpenFile(ControlFilePath, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
 #endif
-       if (fd < 0 && (errno == EMFILE || errno == ENFILE))
-       {
-               fd = errno;
-               if (!ReleaseDataFile())
-                       elog(STOP, "Open(\"%s\") failed: %d (and no one data file can be closed)",
-                                ControlFilePath, fd);
-               goto tryAgain;
-       }
        if (fd < 0)
                elog(STOP, "Open(\"%s\") failed: %d", ControlFilePath, errno);
 
index 160fa5353bedc334726f919ad8735302ae8e3312..04303a54b088cc70c2edd56a2e47d4bdf8ae4910 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.56 2000/05/30 00:49:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.57 2000/06/02 03:58:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -146,13 +146,13 @@ write_password_file(Relation rel)
        /*
         * Create a flag file the postmaster will detect the next time it
         * tries to authenticate a user.  The postmaster will know to reload
-        * the pg_pwd file contents.  Note: we used to elog(ERROR) if the
-        * creat() call failed, but it's a little silly to abort the transaction
+        * the pg_pwd file contents.  Note: we used to elog(ERROR) if the file
+        * creation failed, but it's a little silly to abort the transaction
         * at this point, so let's just make it a NOTICE.
         */
        filename = crypt_getpwdreloadfilename();
-       flagfd = creat(filename, S_IRUSR | S_IWUSR);
-       if (flagfd == -1)
+       flagfd = BasicOpenFile(filename, O_WRONLY | O_CREAT, 0600);
+       if (flagfd < 0)
                elog(NOTICE, "%s: %m", filename);
        else
                close(flagfd);
index 2a6c9466363f0cb0150a61a4c3fdfa290d641475..2d0d733a7c321ad0e47b626b230a2db77466e2ad 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.57 2000/05/31 00:28:27 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.58 2000/06/02 03:58:32 tgl Exp $
  *
  * NOTES:
  *
@@ -202,6 +202,45 @@ pg_fsync(int fd)
                return 0;
 }
 
+/*
+ * BasicOpenFile --- same as open(2) except can free other FDs if needed
+ *
+ * This is exported for use by places that really want a plain kernel FD,
+ * but need to be proof against running out of FDs.  Once an FD has been
+ * successfully returned, it is the caller's responsibility to ensure that
+ * it will not be leaked on elog()!  Most users should *not* call this
+ * routine directly, but instead use the VFD abstraction level, which
+ * provides protection against descriptor leaks as well as management of
+ * files that need to be open for more than a short period of time.
+ *
+ * Ideally this should be the *only* direct call of open() in the backend.
+ * In practice, the postmaster calls open() directly, and there are some
+ * direct open() calls done early in backend startup.  Those are OK since
+ * this module wouldn't have any open files to close at that point anyway.
+ */
+int
+BasicOpenFile(FileName fileName, int fileFlags, int fileMode)
+{
+       int             fd;
+
+tryAgain:
+       fd = open(fileName, fileFlags, fileMode);
+
+       if (fd >= 0)
+               return fd;                              /* success! */
+
+       if ((errno == EMFILE || errno == ENFILE) && nfile > 0)
+       {
+               DO_DB(elog(DEBUG, "BasicOpenFile: not enough descs, retry, er= %d",
+                                  errno));
+               errno = 0;
+               ReleaseLruFile();
+               goto tryAgain;
+       }
+
+       return -1;                                      /* failure */
+}
+
 /*
  * pg_nofile: determine number of filedescriptors that fd.c is allowed to use
  */
@@ -348,7 +387,6 @@ LruInsert(File file)
 
        if (FileIsNotOpen(file))
        {
-
                while (nfile + numAllocatedFiles >= pg_nofile())
                        ReleaseLruFile();
 
@@ -357,15 +395,8 @@ LruInsert(File file)
                 * to overall system file table being full.  So, be prepared to
                 * release another FD if necessary...
                 */
-tryAgain:
-               vfdP->fd = open(vfdP->fileName, vfdP->fileFlags, vfdP->fileMode);
-               if (vfdP->fd < 0 && (errno == EMFILE || errno == ENFILE))
-               {
-                       errno = 0;
-                       ReleaseLruFile();
-                       goto tryAgain;
-               }
-
+               vfdP->fd = BasicOpenFile(vfdP->fileName, vfdP->fileFlags,
+                                                                vfdP->fileMode);
                if (vfdP->fd < 0)
                {
                        DO_DB(elog(DEBUG, "RE_OPEN FAILED: %d",
@@ -411,22 +442,6 @@ ReleaseLruFile()
        LruDelete(VfdCache[0].lruMoreRecently);
 }
 
-/*
- * Force one kernel file descriptor to be released (temporarily).
- */
-bool
-ReleaseDataFile()
-{
-       DO_DB(elog(DEBUG, "ReleaseDataFile. Opened %d", nfile));
-
-       if (nfile <= 0)
-               return (false);
-       Assert(VfdCache[0].lruMoreRecently != 0);
-       LruDelete(VfdCache[0].lruMoreRecently);
-
-       return (true);
-}
-
 static File
 AllocateVfd()
 {
@@ -617,16 +632,7 @@ fileNameOpenFile(FileName fileName,
        while (nfile + numAllocatedFiles >= pg_nofile())
                ReleaseLruFile();
 
-tryAgain:
-       vfdP->fd = open(fileName, fileFlags, fileMode);
-       if (vfdP->fd < 0 && (errno == EMFILE || errno == ENFILE))
-       {
-               DO_DB(elog(DEBUG, "fileNameOpenFile: not enough descs, retry, er= %d",
-                                  errno));
-               errno = 0;
-               ReleaseLruFile();
-               goto tryAgain;
-       }
+       vfdP->fd = BasicOpenFile(fileName, fileFlags, fileMode);
 
        if (vfdP->fd < 0)
        {
@@ -990,6 +996,8 @@ FileMarkDirty(File file)
  * fd.c will automatically close all files opened with AllocateFile at
  * transaction commit or abort; this prevents FD leakage if a routine
  * that calls AllocateFile is terminated prematurely by elog(ERROR).
+ *
+ * Ideally this should be the *only* direct call of fopen() in the backend.
  */
 
 FILE *
@@ -1005,7 +1013,7 @@ AllocateFile(char *name, char *mode)
 TryAgain:
        if ((file = fopen(name, mode)) == NULL)
        {
-               if (errno == EMFILE || errno == ENFILE)
+               if ((errno == EMFILE || errno == ENFILE) && nfile > 0)
                {
                        DO_DB(elog(DEBUG, "AllocateFile: not enough descs, retry, er= %d",
                                           errno));
index e4fd3120d2ca74518421425a2e1409460c70ab21..4c1cac547249e8bed7701b39ed636cb88ce3334c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.68 2000/05/25 23:30:20 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.69 2000/06/02 03:58:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1129,10 +1129,11 @@ _mdfd_blind_getseg(char *dbname, char *relname, Oid dbid, Oid relid,
        }
 #endif
 
+       /* call fd.c to allow other FDs to be closed if needed */
 #ifndef __CYGWIN32__
-       fd = open(path, O_RDWR, 0600);
+       fd = BasicOpenFile(path, O_RDWR, 0600);
 #else
-       fd = open(path, O_RDWR | O_BINARY, 0600);
+       fd = BasicOpenFile(path, O_RDWR | O_BINARY, 0600);
 #endif
 
        if (fd < 0)
index fb45e204564aaff58f9212a99a712e5c71df30af..5456f18372af461f34d4e04dfbe9051467089d85 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fd.h,v 1.21 2000/04/12 17:16:51 momjian Exp $
+ * $Id: fd.h,v 1.22 2000/06/02 03:58:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,7 @@
  * Use them for all file activity...
  *
  *     File fd;
- *     fd = FilePathOpenFile("foo", O_RDONLY);
+ *     fd = FilePathOpenFile("foo", O_RDONLY, 0600);
  *
  *     AllocateFile();
  *     FreeFile();
@@ -64,8 +64,10 @@ extern void FileMarkDirty(File file);
 extern FILE *AllocateFile(char *name, char *mode);
 extern void FreeFile(FILE *);
 
+/* If you've really really gotta have a plain kernel FD, use this */
+extern int     BasicOpenFile(FileName fileName, int fileFlags, int fileMode);
+
 /* Miscellaneous support routines */
-extern bool ReleaseDataFile(void);
 extern void closeAllVfds(void);
 extern void AtEOXact_Files(void);
 extern int     pg_fsync(int fd);