]> granicus.if.org Git - postgresql/blob - src/backend/port/sysv_shmem.c
sysv_shmem.c patch is to correct a bug that prevents the postmaster
[postgresql] / src / backend / port / sysv_shmem.c
1 /*-------------------------------------------------------------------------
2  *
3  * sysv_shmem.c
4  *        Implement shared memory using SysV facilities
5  *
6  * These routines represent a fairly thin layer on top of SysV shared
7  * memory functionality.
8  *
9  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.34 2004/05/06 19:23:25 momjian Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <unistd.h>
22 #include <sys/file.h>
23 #ifdef HAVE_SYS_IPC_H
24 #include <sys/ipc.h>
25 #endif
26 #ifdef HAVE_SYS_SHM_H
27 #include <sys/shm.h>
28 #endif
29 #ifdef HAVE_KERNEL_OS_H
30 #include <kernel/OS.h>
31 #endif
32
33 #include "miscadmin.h"
34 #include "storage/ipc.h"
35 #include "storage/pg_shmem.h"
36
37
38 typedef key_t IpcMemoryKey;             /* shared memory key passed to shmget(2) */
39 typedef int IpcMemoryId;                /* shared memory ID returned by shmget(2) */
40
41 #define IPCProtection   (0600)  /* access/modify by user only */
42
43
44 unsigned long UsedShmemSegID = 0;
45 void       *UsedShmemSegAddr = NULL;
46
47 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size);
48 static void IpcMemoryDetach(int status, Datum shmaddr);
49 static void IpcMemoryDelete(int status, Datum shmId);
50 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
51                                          IpcMemoryId *shmid);
52
53
54 /*
55  *      InternalIpcMemoryCreate(memKey, size)
56  *
57  * Attempt to create a new shared memory segment with the specified key.
58  * Will fail (return NULL) if such a segment already exists.  If successful,
59  * attach the segment to the current process and return its attached address.
60  * On success, callbacks are registered with on_shmem_exit to detach and
61  * delete the segment when on_shmem_exit is called.
62  *
63  * If we fail with a failure code other than collision-with-existing-segment,
64  * print out an error and abort.  Other types of errors are not recoverable.
65  */
66 static void *
67 InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size)
68 {
69         IpcMemoryId shmid;
70         void       *memAddress;
71
72         shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
73
74         if (shmid < 0)
75         {
76                 /*
77                  * Fail quietly if error indicates a collision with existing
78                  * segment. One would expect EEXIST, given that we said IPC_EXCL,
79                  * but perhaps we could get a permission violation instead?  Also,
80                  * EIDRM might occur if an old seg is slated for destruction but
81                  * not gone yet.
82                  */
83                 if (errno == EEXIST || errno == EACCES
84 #ifdef EIDRM
85                         || errno == EIDRM
86 #endif
87                         )
88                         return NULL;
89
90                 /*
91                  * Else complain and abort
92                  */
93                 ereport(FATAL,
94                                 (errmsg("could not create shared memory segment: %m"),
95                         errdetail("Failed system call was shmget(key=%lu, size=%u, 0%o).",
96                                           (unsigned long) memKey, size,
97                                           IPC_CREAT | IPC_EXCL | IPCProtection),
98                                  (errno == EINVAL) ?
99                                  errhint("This error usually means that PostgreSQL's request for a shared memory "
100                                                  "segment exceeded your kernel's SHMMAX parameter.  You can either "
101                                                  "reduce the request size or reconfigure the kernel with larger SHMMAX.  "
102                                                  "To reduce the request size (currently %u bytes), reduce "
103                                                  "PostgreSQL's shared_buffers parameter (currently %d) and/or "
104                                                  "its max_connections parameter (currently %d).\n"
105                                                  "If the request size is already small, it's possible that it is less than "
106                                                  "your kernel's SHMMIN parameter, in which case raising the request size or "
107                                                  "reconfiguring SHMMIN is called for.\n"
108                                                  "The PostgreSQL documentation contains more information about shared "
109                                                  "memory configuration.",
110                                                  size, NBuffers, MaxBackends) : 0,
111                                  (errno == ENOMEM) ?
112                                  errhint("This error usually means that PostgreSQL's request for a shared "
113                            "memory segment exceeded available memory or swap space. "
114                            "To reduce the request size (currently %u bytes), reduce "
115                    "PostgreSQL's shared_buffers parameter (currently %d) and/or "
116                                                  "its max_connections parameter (currently %d).\n"
117                                                  "The PostgreSQL documentation contains more information about shared "
118                                                  "memory configuration.",
119                                                  size, NBuffers, MaxBackends) : 0,
120                                  (errno == ENOSPC) ?
121                                  errhint("This error does *not* mean that you have run out of disk space. "
122                                                  "It occurs either if all available shared memory IDs have been taken, "
123                                                  "in which case you need to raise the SHMMNI parameter in your kernel, "
124                                                  "or because the system's overall limit for shared memory has been "
125                          "reached.  If you cannot increase the shared memory limit, "
126                 "reduce PostgreSQL's shared memory request (currently %u bytes), "
127                 "by reducing its shared_buffers parameter (currently %d) and/or "
128                                                  "its max_connections parameter (currently %d).\n"
129                                                  "The PostgreSQL documentation contains more information about shared "
130                                                  "memory configuration.",
131                                                  size, NBuffers, MaxBackends) : 0));
132         }
133
134         /* Register on-exit routine to delete the new segment */
135         on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
136
137         /* OK, should be able to attach to the segment */
138 #ifdef SHM_SHARE_MMU
139         /* use intimate shared memory on Solaris */
140         memAddress = shmat(shmid, 0, SHM_SHARE_MMU);
141 #else
142
143 #ifdef EXEC_BACKEND
144         memAddress = shmat(shmid, UsedShmemSegAddr, 0);
145 #else
146         memAddress = shmat(shmid, 0, 0);
147 #endif
148 #endif
149
150         if (memAddress == (void *) -1)
151                 elog(FATAL, "shmat(id=%d) failed: %m", shmid);
152
153         /* Register on-exit routine to detach new segment before deleting */
154         on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
155
156         /* Record key and ID in lockfile for data directory. */
157         RecordSharedMemoryInLockFile((unsigned long) memKey,
158                                                                  (unsigned long) shmid);
159
160         return memAddress;
161 }
162
163 /****************************************************************************/
164 /*      IpcMemoryDetach(status, shmaddr)        removes a shared memory segment         */
165 /*                                                                              from process' address spaceq            */
166 /*      (called as an on_shmem_exit callback, hence funny argument list)                */
167 /****************************************************************************/
168 static void
169 IpcMemoryDetach(int status, Datum shmaddr)
170 {
171         if (shmdt(DatumGetPointer(shmaddr)) < 0)
172                 elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
173 }
174
175 /****************************************************************************/
176 /*      IpcMemoryDelete(status, shmId)          deletes a shared memory segment         */
177 /*      (called as an on_shmem_exit callback, hence funny argument list)                */
178 /****************************************************************************/
179 static void
180 IpcMemoryDelete(int status, Datum shmId)
181 {
182         if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
183                 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
184                          DatumGetInt32(shmId), IPC_RMID);
185 }
186
187 /*
188  * PGSharedMemoryIsInUse
189  *
190  * Is a previously-existing shmem segment still existing and in use?
191  */
192 bool
193 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
194 {
195         IpcMemoryId shmId = (IpcMemoryId) id2;
196         struct shmid_ds shmStat;
197
198         /*
199          * We detect whether a shared memory segment is in use by seeing
200          * whether it (a) exists and (b) has any processes are attached to it.
201          *
202          * If we are unable to perform the stat operation for a reason other than
203          * nonexistence of the segment (most likely, because it doesn't belong
204          * to our userid), assume it is in use.
205          */
206         if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
207         {
208                 /*
209                  * EINVAL actually has multiple possible causes documented in the
210                  * shmctl man page, but we assume it must mean the segment no
211                  * longer exists.
212                  */
213                 if (errno == EINVAL)
214                         return false;
215                 /* Else assume segment is in use */
216                 return true;
217         }
218         /* If it has attached processes, it's in use */
219         if (shmStat.shm_nattch != 0)
220                 return true;
221         return false;
222 }
223
224
225 /*
226  * PGSharedMemoryCreate
227  *
228  * Create a shared memory segment of the given size and initialize its
229  * standard header.  Also, register an on_shmem_exit callback to release
230  * the storage.  For an exec'ed backend, it just attaches.
231  *
232  * Dead Postgres segments are recycled if found, but we do not fail upon
233  * collision with non-Postgres shmem segments.  The idea here is to detect and
234  * re-use keys that may have been assigned by a crashed postmaster or backend.
235  *
236  * makePrivate means to always create a new segment, rather than attach to
237  * or recycle any existing segment.
238  *
239  * The port number is passed for possible use as a key (for SysV, we use
240  * it to generate the starting shmem key).      In a standalone backend,
241  * zero will be passed.
242  */
243 PGShmemHeader *
244 PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
245 {
246         IpcMemoryKey NextShmemSegID;
247         void       *memAddress;
248         PGShmemHeader *hdr;
249         IpcMemoryId shmid;
250
251 #ifdef EXEC_BACKEND
252         /* If Exec case, just attach and return the pointer */
253         if (UsedShmemSegAddr != NULL && !makePrivate && IsUnderPostmaster)
254         {
255                 void* origUsedShmemSegAddr = UsedShmemSegAddr;
256
257 #ifdef __CYGWIN__
258                 /* cygipc (currently) appears to not detach on exec. */
259                 PGSharedMemoryDetach();
260                 UsedShmemSegAddr = origUsedShmemSegAddr;
261 #endif
262                 elog(DEBUG3,"Attaching to %p",UsedShmemSegAddr);
263                 hdr = PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
264                 if (hdr == NULL)
265                         elog(FATAL, "could not attach to proper memory at fixed address: shmget(key=%d, addr=%p) failed: %m",
266                                  (int) UsedShmemSegID, UsedShmemSegAddr);
267                 if (hdr != origUsedShmemSegAddr)
268                         elog(FATAL,"attaching to shared mem returned unexpected address (got %p, expected %p)",
269                                  hdr,UsedShmemSegAddr);
270                 UsedShmemSegAddr = hdr;
271                 return hdr;
272         }
273 #endif
274
275         /* Room for a header? */
276         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
277
278         /* Make sure PGSharedMemoryAttach doesn't fail without need */
279         UsedShmemSegAddr = NULL;
280
281         /* Loop till we find a free IPC key */
282         NextShmemSegID = port * 1000;
283
284         for (NextShmemSegID++;; NextShmemSegID++)
285         {
286                 /* Try to create new segment */
287                 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
288                 if (memAddress)
289                         break;                          /* successful create and attach */
290
291                 /* Check shared memory and possibly remove and recreate */
292
293                 if (makePrivate)                /* a standalone backend shouldn't do this */
294                         continue;
295
296                 if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
297                         continue;                       /* can't attach, not one of mine */
298
299                 /*
300                  * If I am not the creator and it belongs to an extant process,
301                  * continue.
302                  */
303                 hdr = (PGShmemHeader *) memAddress;
304                 if (hdr->creatorPID != getpid())
305                 {
306                         if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
307                         {
308                                 shmdt(memAddress);
309                                 continue;               /* segment belongs to a live process */
310                         }
311                 }
312
313                 /*
314                  * The segment appears to be from a dead Postgres process, or from
315                  * a previous cycle of life in this same process.  Zap it, if
316                  * possible.  This probably shouldn't fail, but if it does, assume
317                  * the segment belongs to someone else after all, and continue
318                  * quietly.
319                  */
320                 shmdt(memAddress);
321                 if (shmctl(shmid, IPC_RMID, NULL) < 0)
322                         continue;
323
324                 /*
325                  * Now try again to create the segment.
326                  */
327                 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
328                 if (memAddress)
329                         break;                          /* successful create and attach */
330
331                 /*
332                  * Can only get here if some other process managed to create the
333                  * same shmem key before we did.  Let him have that one, loop
334                  * around to try next key.
335                  */
336         }
337
338         /*
339          * OK, we created a new segment.  Mark it as created by this process.
340          * The order of assignments here is critical so that another Postgres
341          * process can't see the header as valid but belonging to an invalid
342          * PID!
343          */
344         hdr = (PGShmemHeader *) memAddress;
345         hdr->creatorPID = getpid();
346         hdr->magic = PGShmemMagic;
347
348         /*
349          * Initialize space allocation status for segment.
350          */
351         hdr->totalsize = size;
352         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
353
354         /* Save info for possible future use */
355         UsedShmemSegAddr = memAddress;
356         UsedShmemSegID = (unsigned long) NextShmemSegID;
357
358         return hdr;
359 }
360
361 /*
362  * PGSharedMemoryDetach
363  *
364  * Detach from the shared memory segment, if still attached.  This is not
365  * intended for use by the process that originally created the segment
366  * (it will have an on_shmem_exit callback registered to do that).  Rather,
367  * this is for subprocesses that have inherited an attachment and want to
368  * get rid of it.
369  */
370 void
371 PGSharedMemoryDetach(void)
372 {
373         if (UsedShmemSegAddr != NULL)
374         {
375                 if ((shmdt(UsedShmemSegAddr) < 0)
376 #if (defined(EXEC_BACKEND) && defined(__CYGWIN__))
377                         /* Work-around for cygipc exec bug */
378                         && shmdt(NULL) < 0
379 #endif
380                         )
381                         elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
382                 UsedShmemSegAddr = NULL;
383         }
384 }
385
386
387 /*
388  * Attach to shared memory and make sure it has a Postgres header
389  *
390  * Returns attach address if OK, else NULL
391  */
392 static PGShmemHeader *
393 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
394 {
395         PGShmemHeader *hdr;
396
397         if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
398                 return NULL;
399
400         hdr = (PGShmemHeader *) shmat(*shmid,
401                                                                   UsedShmemSegAddr,
402 #ifdef SHM_SHARE_MMU
403         /* use intimate shared memory on Solaris */
404                                                                   SHM_SHARE_MMU
405 #else
406                                                                   0
407 #endif
408                 );
409
410         if (hdr == (PGShmemHeader *) -1)
411                 return NULL;                    /* failed: must be some other app's */
412
413         if (hdr->magic != PGShmemMagic)
414         {
415                 shmdt(hdr);
416                 return NULL;                    /* segment belongs to a non-Postgres app */
417         }
418
419         return hdr;
420 }