]> granicus.if.org Git - postgresql/blob - src/backend/port/sysv_shmem.c
fa107bd468b51d40ac95cead1fc6f3a62ba60b77
[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-2008, 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.53 2008/01/01 19:45:51 momjian Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18
19 #include <signal.h>
20 #include <unistd.h>
21 #include <sys/file.h>
22 #include <sys/stat.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 #ifdef SHM_SHARE_MMU                    /* use intimate shared memory on Solaris */
44 #define PG_SHMAT_FLAGS                  SHM_SHARE_MMU
45 #else
46 #define PG_SHMAT_FLAGS                  0
47 #endif
48
49
50 unsigned long UsedShmemSegID = 0;
51 void       *UsedShmemSegAddr = NULL;
52
53 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
54 static void IpcMemoryDetach(int status, Datum shmaddr);
55 static void IpcMemoryDelete(int status, Datum shmId);
56 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
57                                          IpcMemoryId *shmid);
58
59
60 /*
61  *      InternalIpcMemoryCreate(memKey, size)
62  *
63  * Attempt to create a new shared memory segment with the specified key.
64  * Will fail (return NULL) if such a segment already exists.  If successful,
65  * attach the segment to the current process and return its attached address.
66  * On success, callbacks are registered with on_shmem_exit to detach and
67  * delete the segment when on_shmem_exit is called.
68  *
69  * If we fail with a failure code other than collision-with-existing-segment,
70  * print out an error and abort.  Other types of errors are not recoverable.
71  */
72 static void *
73 InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
74 {
75         IpcMemoryId shmid;
76         void       *memAddress;
77
78         shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
79
80         if (shmid < 0)
81         {
82                 /*
83                  * Fail quietly if error indicates a collision with existing segment.
84                  * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
85                  * we could get a permission violation instead?  Also, EIDRM might
86                  * occur if an old seg is slated for destruction but not gone yet.
87                  */
88                 if (errno == EEXIST || errno == EACCES
89 #ifdef EIDRM
90                         || errno == EIDRM
91 #endif
92                         )
93                         return NULL;
94
95                 /*
96                  * Else complain and abort
97                  */
98                 ereport(FATAL,
99                                 (errmsg("could not create shared memory segment: %m"),
100                   errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
101                                         (unsigned long) memKey, (unsigned long) size,
102                                         IPC_CREAT | IPC_EXCL | IPCProtection),
103                                  (errno == EINVAL) ?
104                                  errhint("This error usually means that PostgreSQL's request for a shared memory "
105                   "segment exceeded your kernel's SHMMAX parameter.  You can either "
106                                                  "reduce the request size or reconfigure the kernel with larger SHMMAX.  "
107                                   "To reduce the request size (currently %lu bytes), reduce "
108                            "PostgreSQL's shared_buffers parameter (currently %d) and/or "
109                                                  "its max_connections parameter (currently %d).\n"
110                                                  "If the request size is already small, it's possible that it is less than "
111                                                  "your kernel's SHMMIN parameter, in which case raising the request size or "
112                                                  "reconfiguring SHMMIN is called for.\n"
113                 "The PostgreSQL documentation contains more information about shared "
114                                                  "memory configuration.",
115                                                  (unsigned long) size, NBuffers, MaxBackends) : 0,
116                                  (errno == ENOMEM) ?
117                                  errhint("This error usually means that PostgreSQL's request for a shared "
118                                    "memory segment exceeded available memory or swap space. "
119                                   "To reduce the request size (currently %lu bytes), reduce "
120                            "PostgreSQL's shared_buffers parameter (currently %d) and/or "
121                                                  "its max_connections parameter (currently %d).\n"
122                 "The PostgreSQL documentation contains more information about shared "
123                                                  "memory configuration.",
124                                                  (unsigned long) size, NBuffers, MaxBackends) : 0,
125                                  (errno == ENOSPC) ?
126                                  errhint("This error does *not* mean that you have run out of disk space. "
127                                                  "It occurs either if all available shared memory IDs have been taken, "
128                                                  "in which case you need to raise the SHMMNI parameter in your kernel, "
129                   "or because the system's overall limit for shared memory has been "
130                                  "reached.  If you cannot increase the shared memory limit, "
131                   "reduce PostgreSQL's shared memory request (currently %lu bytes), "
132                         "by reducing its shared_buffers parameter (currently %d) and/or "
133                                                  "its max_connections parameter (currently %d).\n"
134                 "The PostgreSQL documentation contains more information about shared "
135                                                  "memory configuration.",
136                                                  (unsigned long) size, NBuffers, MaxBackends) : 0));
137         }
138
139         /* Register on-exit routine to delete the new segment */
140         on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
141
142         /* OK, should be able to attach to the segment */
143         memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
144
145         if (memAddress == (void *) -1)
146                 elog(FATAL, "shmat(id=%d) failed: %m", shmid);
147
148         /* Register on-exit routine to detach new segment before deleting */
149         on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
150
151         /* Record key and ID in lockfile for data directory. */
152         RecordSharedMemoryInLockFile((unsigned long) memKey,
153                                                                  (unsigned long) shmid);
154
155         return memAddress;
156 }
157
158 /****************************************************************************/
159 /*      IpcMemoryDetach(status, shmaddr)        removes a shared memory segment         */
160 /*                                                                              from process' address spaceq            */
161 /*      (called as an on_shmem_exit callback, hence funny argument list)                */
162 /****************************************************************************/
163 static void
164 IpcMemoryDetach(int status, Datum shmaddr)
165 {
166         if (shmdt(DatumGetPointer(shmaddr)) < 0)
167                 elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
168 }
169
170 /****************************************************************************/
171 /*      IpcMemoryDelete(status, shmId)          deletes a shared memory segment         */
172 /*      (called as an on_shmem_exit callback, hence funny argument list)                */
173 /****************************************************************************/
174 static void
175 IpcMemoryDelete(int status, Datum shmId)
176 {
177         if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
178                 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
179                          DatumGetInt32(shmId), IPC_RMID);
180 }
181
182 /*
183  * PGSharedMemoryIsInUse
184  *
185  * Is a previously-existing shmem segment still existing and in use?
186  *
187  * The point of this exercise is to detect the case where a prior postmaster
188  * crashed, but it left child backends that are still running.  Therefore
189  * we only care about shmem segments that are associated with the intended
190  * DataDir.  This is an important consideration since accidental matches of
191  * shmem segment IDs are reasonably common.
192  */
193 bool
194 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
195 {
196         IpcMemoryId shmId = (IpcMemoryId) id2;
197         struct shmid_ds shmStat;
198         struct stat statbuf;
199         PGShmemHeader *hdr;
200
201         /*
202          * We detect whether a shared memory segment is in use by seeing whether
203          * it (a) exists and (b) has any processes attached to it.
204          */
205         if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
206         {
207                 /*
208                  * EINVAL actually has multiple possible causes documented in the
209                  * shmctl man page, but we assume it must mean the segment no longer
210                  * exists.
211                  */
212                 if (errno == EINVAL)
213                         return false;
214
215                 /*
216                  * EACCES implies that the segment belongs to some other userid, which
217                  * means it is not a Postgres shmem segment (or at least, not one that
218                  * is relevant to our data directory).
219                  */
220                 if (errno == EACCES)
221                         return false;
222
223                 /*
224                  * Some Linux kernel versions (in fact, all of them as of July 2007)
225                  * sometimes return EIDRM when EINVAL is correct.  The Linux kernel
226                  * actually does not have any internal state that would justify
227                  * returning EIDRM, so we can get away with assuming that EIDRM is
228                  * equivalent to EINVAL on that platform.
229                  */
230 #ifdef HAVE_LINUX_EIDRM_BUG
231                 if (errno == EIDRM)
232                         return false;
233 #endif
234
235                 /*
236                  * Otherwise, we had better assume that the segment is in use. The
237                  * only likely case is EIDRM, which implies that the segment has been
238                  * IPC_RMID'd but there are still processes attached to it.
239                  */
240                 return true;
241         }
242
243         /* If it has no attached processes, it's not in use */
244         if (shmStat.shm_nattch == 0)
245                 return false;
246
247         /*
248          * Try to attach to the segment and see if it matches our data directory.
249          * This avoids shmid-conflict problems on machines that are running
250          * several postmasters under the same userid.
251          */
252         if (stat(DataDir, &statbuf) < 0)
253                 return true;                    /* if can't stat, be conservative */
254
255         hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
256
257         if (hdr == (PGShmemHeader *) -1)
258                 return true;                    /* if can't attach, be conservative */
259
260         if (hdr->magic != PGShmemMagic ||
261                 hdr->device != statbuf.st_dev ||
262                 hdr->inode != statbuf.st_ino)
263         {
264                 /*
265                  * It's either not a Postgres segment, or not one for my data
266                  * directory.  In either case it poses no threat.
267                  */
268                 shmdt((void *) hdr);
269                 return false;
270         }
271
272         /* Trouble --- looks a lot like there's still live backends */
273         shmdt((void *) hdr);
274
275         return true;
276 }
277
278
279 /*
280  * PGSharedMemoryCreate
281  *
282  * Create a shared memory segment of the given size and initialize its
283  * standard header.  Also, register an on_shmem_exit callback to release
284  * the storage.
285  *
286  * Dead Postgres segments are recycled if found, but we do not fail upon
287  * collision with non-Postgres shmem segments.  The idea here is to detect and
288  * re-use keys that may have been assigned by a crashed postmaster or backend.
289  *
290  * makePrivate means to always create a new segment, rather than attach to
291  * or recycle any existing segment.
292  *
293  * The port number is passed for possible use as a key (for SysV, we use
294  * it to generate the starting shmem key).      In a standalone backend,
295  * zero will be passed.
296  */
297 PGShmemHeader *
298 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
299 {
300         IpcMemoryKey NextShmemSegID;
301         void       *memAddress;
302         PGShmemHeader *hdr;
303         IpcMemoryId shmid;
304         struct stat statbuf;
305
306         /* Room for a header? */
307         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
308
309         /* Make sure PGSharedMemoryAttach doesn't fail without need */
310         UsedShmemSegAddr = NULL;
311
312         /* Loop till we find a free IPC key */
313         NextShmemSegID = port * 1000;
314
315         for (NextShmemSegID++;; NextShmemSegID++)
316         {
317                 /* Try to create new segment */
318                 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
319                 if (memAddress)
320                         break;                          /* successful create and attach */
321
322                 /* Check shared memory and possibly remove and recreate */
323
324                 if (makePrivate)                /* a standalone backend shouldn't do this */
325                         continue;
326
327                 if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
328                         continue;                       /* can't attach, not one of mine */
329
330                 /*
331                  * If I am not the creator and it belongs to an extant process,
332                  * continue.
333                  */
334                 hdr = (PGShmemHeader *) memAddress;
335                 if (hdr->creatorPID != getpid())
336                 {
337                         if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
338                         {
339                                 shmdt(memAddress);
340                                 continue;               /* segment belongs to a live process */
341                         }
342                 }
343
344                 /*
345                  * The segment appears to be from a dead Postgres process, or from a
346                  * previous cycle of life in this same process.  Zap it, if possible.
347                  * This probably shouldn't fail, but if it does, assume the segment
348                  * belongs to someone else after all, and continue quietly.
349                  */
350                 shmdt(memAddress);
351                 if (shmctl(shmid, IPC_RMID, NULL) < 0)
352                         continue;
353
354                 /*
355                  * Now try again to create the segment.
356                  */
357                 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
358                 if (memAddress)
359                         break;                          /* successful create and attach */
360
361                 /*
362                  * Can only get here if some other process managed to create the same
363                  * shmem key before we did.  Let him have that one, loop around to try
364                  * next key.
365                  */
366         }
367
368         /*
369          * OK, we created a new segment.  Mark it as created by this process. The
370          * order of assignments here is critical so that another Postgres process
371          * can't see the header as valid but belonging to an invalid PID!
372          */
373         hdr = (PGShmemHeader *) memAddress;
374         hdr->creatorPID = getpid();
375         hdr->magic = PGShmemMagic;
376
377         /* Fill in the data directory ID info, too */
378         if (stat(DataDir, &statbuf) < 0)
379                 ereport(FATAL,
380                                 (errcode_for_file_access(),
381                                  errmsg("could not stat data directory \"%s\": %m",
382                                                 DataDir)));
383         hdr->device = statbuf.st_dev;
384         hdr->inode = statbuf.st_ino;
385
386         /*
387          * Initialize space allocation status for segment.
388          */
389         hdr->totalsize = size;
390         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
391
392         /* Save info for possible future use */
393         UsedShmemSegAddr = memAddress;
394         UsedShmemSegID = (unsigned long) NextShmemSegID;
395
396         return hdr;
397 }
398
399 #ifdef EXEC_BACKEND
400
401 /*
402  * PGSharedMemoryReAttach
403  *
404  * Re-attach to an already existing shared memory segment.      In the non
405  * EXEC_BACKEND case this is not used, because postmaster children inherit
406  * the shared memory segment attachment via fork().
407  *
408  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
409  * routine.  The caller must have already restored them to the postmaster's
410  * values.
411  */
412 void
413 PGSharedMemoryReAttach(void)
414 {
415         IpcMemoryId shmid;
416         void       *hdr;
417         void       *origUsedShmemSegAddr = UsedShmemSegAddr;
418
419         Assert(UsedShmemSegAddr != NULL);
420         Assert(IsUnderPostmaster);
421
422 #ifdef __CYGWIN__
423         /* cygipc (currently) appears to not detach on exec. */
424         PGSharedMemoryDetach();
425         UsedShmemSegAddr = origUsedShmemSegAddr;
426 #endif
427
428         elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
429         hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
430         if (hdr == NULL)
431                 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
432                          (int) UsedShmemSegID, UsedShmemSegAddr);
433         if (hdr != origUsedShmemSegAddr)
434                 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
435                          hdr, origUsedShmemSegAddr);
436
437         UsedShmemSegAddr = hdr;         /* probably redundant */
438 }
439 #endif   /* EXEC_BACKEND */
440
441 /*
442  * PGSharedMemoryDetach
443  *
444  * Detach from the shared memory segment, if still attached.  This is not
445  * intended for use by the process that originally created the segment
446  * (it will have an on_shmem_exit callback registered to do that).      Rather,
447  * this is for subprocesses that have inherited an attachment and want to
448  * get rid of it.
449  */
450 void
451 PGSharedMemoryDetach(void)
452 {
453         if (UsedShmemSegAddr != NULL)
454         {
455                 if ((shmdt(UsedShmemSegAddr) < 0)
456 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
457                 /* Work-around for cygipc exec bug */
458                         && shmdt(NULL) < 0
459 #endif
460                         )
461                         elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
462                 UsedShmemSegAddr = NULL;
463         }
464 }
465
466
467 /*
468  * Attach to shared memory and make sure it has a Postgres header
469  *
470  * Returns attach address if OK, else NULL
471  */
472 static PGShmemHeader *
473 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
474 {
475         PGShmemHeader *hdr;
476
477         if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
478                 return NULL;
479
480         hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
481
482         if (hdr == (PGShmemHeader *) -1)
483                 return NULL;                    /* failed: must be some other app's */
484
485         if (hdr->magic != PGShmemMagic)
486         {
487                 shmdt((void *) hdr);
488                 return NULL;                    /* segment belongs to a non-Postgres app */
489         }
490
491         return hdr;
492 }