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