1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #ifndef SQUID_IPC_STORE_MAP_H
10 #define SQUID_IPC_STORE_MAP_H
11 
12 #include "Debug.h"
13 #include "ipc/mem/FlexibleArray.h"
14 #include "ipc/mem/Pointer.h"
15 #include "ipc/ReadWriteLock.h"
16 #include "sbuf/SBuf.h"
17 #include "store/forward.h"
18 #include "store_key_md5.h"
19 #include "tools.h"
20 
21 #include <atomic>
22 
23 namespace Ipc
24 {
25 
26 // The MEMMAP_SLOT_KEY_SIZE and MEMMAP_SLOT_DATA_SIZE must be enough big
27 // to hold cached keys and data. Currently MemMap used only to store SSL
28 // shared session data which have keys of 32bytes and at most 10K data
29 #define MEMMAP_SLOT_KEY_SIZE 32
30 #define MEMMAP_SLOT_DATA_SIZE 10*1024
31 
32 /// a MemMap basic element, holding basic shareable memory block info
33 class MemMapSlot
34 {
35 public:
36     MemMapSlot();
size()37     size_t size() const {return sizeof(MemMapSlot);}
keySize()38     size_t keySize() const {return sizeof(key);}
39     bool sameKey(const cache_key *const aKey) const;
40     void set(const unsigned char *aKey, const void *block, size_t blockSize, time_t expire = 0);
41     bool empty() const;
reading()42     bool reading() const { return lock.readers; }
writing()43     bool writing() const { return lock.writing; }
44 
45     std::atomic<uint8_t> waitingToBeFreed; ///< may be accessed w/o a lock
46     mutable ReadWriteLock lock; ///< protects slot data below
47     unsigned char key[MEMMAP_SLOT_KEY_SIZE]; ///< The entry key
48     unsigned char p[MEMMAP_SLOT_DATA_SIZE]; ///< The memory block;
49     size_t pSize;
50     time_t expire;
51 };
52 
53 class MemMapCleaner;
54 
55 /// A map of MemMapSlots indexed by their keys, with read/write slot locking.
56 class MemMap
57 {
58 public:
59     typedef MemMapSlot Slot;
60 
61     /// data shared across maps in different processes
62     class Shared
63     {
64     public:
65         Shared(const int aLimit, const size_t anExtrasSize);
66         size_t sharedMemorySize() const;
67         static size_t SharedMemorySize(const int limit, const size_t anExtrasSize);
68         ~Shared();
69 
70         const int limit; ///< maximum number of map slots
71         const size_t extrasSize; ///< size of slot extra data
72         std::atomic<int> count; ///< current number of map slots
73         Ipc::Mem::FlexibleArray<Slot> slots; ///< storage
74     };
75 
76 public:
77     typedef Mem::Owner<Shared> Owner;
78 
79     /// initialize shared memory
80     static Owner *Init(const char *const path, const int limit);
81 
82     MemMap(const char *const aPath);
83 
84     /// finds, locks and return a slot for an empty key position,
85     /// erasing the old entry (if any)
86     Slot *openForWriting(const cache_key *const key, sfileno &fileno);
87 
88     /// locks and returns a slot for the empty fileno position; if
89     /// overwriteExisting is false and the position is not empty, returns nil
90     Slot *openForWritingAt(sfileno fileno, bool overwriteExisting = true);
91 
92     /// successfully finish writing the entry
93     void closeForWriting(const sfileno fileno);
94 
95     /// stop writing the locked entry and start reading it
96     void switchWritingToReading(const sfileno fileno);
97 
98     /// only works on locked entries; returns nil unless the slot is readable
99     const Slot *peekAtReader(const sfileno fileno) const;
100 
101     /// mark the slot as waiting to be freed and, if possible, free it
102     void free(const sfileno fileno);
103 
104     /// open slot for reading, increments read level
105     const Slot *openForReading(const cache_key *const key, sfileno &fileno);
106 
107     /// open slot for reading, increments read level
108     const Slot *openForReadingAt(const sfileno fileno);
109 
110     /// close slot after reading, decrements read level
111     void closeForReading(const sfileno fileno);
112 
113     bool full() const; ///< there are no empty slots left
114     bool valid(const int n) const; ///< whether n is a valid slot coordinate
115     int entryCount() const; ///< number of used slots
116     int entryLimit() const; ///< maximum number of slots that can be used
117 
118     /// adds approximate current stats to the supplied ones
119     void updateStats(ReadWriteLockStats &stats) const;
120 
121     /// The cleaner MemMapCleaner::noteFreeMapSlot method called when a
122     /// readable entry is freed.
123     MemMapCleaner *cleaner;
124 
125 protected:
126     static Owner *Init(const char *const path, const int limit, const size_t extrasSize);
127 
128     const SBuf path; ///< cache_dir path, used for logging
129     Mem::Pointer<Shared> shared;
130 
131 private:
132     int slotIndexByKey(const cache_key *const key) const;
133     Slot &slotByKey(const cache_key *const key);
134 
135     Slot *openForReading(Slot &s);
136     void abortWriting(const sfileno fileno);
137     void freeIfNeeded(Slot &s);
138     void freeLocked(Slot &s, bool keepLocked);
139 };
140 
141 /// API for adjusting external state when dirty map slot is being freed
142 class MemMapCleaner
143 {
144 public:
~MemMapCleaner()145     virtual ~MemMapCleaner() {}
146 
147     /// adjust slot-linked state before a locked Readable slot is erased
148     virtual void noteFreeMapSlot(const sfileno slotId) = 0;
149 };
150 
151 } // namespace Ipc
152 
153 #endif /* SQUID_IPC_STORE_MAP_H */
154 
155