1 /* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22
23 #include "ArenaPool.hpp"
24 #include <ndbd_exit_codes.h>
25 #include <NdbOut.hpp>
26
27 #define JAM_FILE_ID 309
28
29
30 static
31 Uint32
computeBlockSize(Uint32 blockSz,Uint32 wpp)32 computeBlockSize(Uint32 blockSz, Uint32 wpp)
33 {
34 Uint32 minspill = wpp % blockSz;
35 Uint32 minspill_bs = blockSz;
36
37 for (Uint32 i = 16; i<blockSz/4; i += 16)
38 {
39 Uint32 spillsz = wpp % (blockSz - i);
40 if (spillsz == 0)
41 {
42 return blockSz - i;
43 }
44 else if (spillsz < minspill)
45 {
46 minspill = spillsz;
47 minspill_bs = blockSz -i;
48 }
49 }
50 #ifdef VM_TRACE
51 ndbout_c("blockSz: %u, wpp: %u -> %u (%u)",
52 blockSz, wpp, minspill_bs, minspill);
53 #endif
54 return minspill_bs;
55 }
56
57 void
init(Uint32 sz,Uint32 type_id,const Pool_context & pc)58 ArenaAllocator::init(Uint32 sz, Uint32 type_id, const Pool_context& pc)
59 {
60 Uint32 blocksz = ArenaBlock::computeBlockSizeInWords(sz);
61 Uint32 wpp = m_pool.WORDS_PER_PAGE;
62
63 Uint32 bs = computeBlockSize(blocksz, wpp);
64 Record_info ri;
65 ri.m_size = 4 * bs;
66 {
67 ArenaBlock tmp;
68 const char * off_base = (char*)&tmp;
69 const char * off_next = (char*)&tmp.nextPool;
70 const char * off_magic = (char*)&tmp.m_magic;
71
72 ri.m_offset_next_pool = Uint32(off_next - off_base);
73 ri.m_offset_magic = Uint32(off_magic - off_base);
74 }
75 ri.m_type_id = type_id;
76 m_pool.init(ri, pc);
77 m_block_size = bs - ArenaBlock::HeaderSize;
78 }
79
80 bool
seize(ArenaHead & ah)81 ArenaAllocator::seize(ArenaHead& ah)
82 {
83 Ptr<void> tmp;
84 if (m_pool.seize(tmp))
85 {
86 ah.m_first_block = tmp.i;
87 ah.m_current_block = tmp.i;
88 ah.m_block_size = m_block_size;
89 ah.m_current_block_ptr = static_cast<ArenaBlock*>(tmp.p);
90 ah.m_current_block_ptr->m_next_block = RNIL;
91 return true;
92 }
93 return false;
94 }
95
96 void
release(ArenaHead & ah)97 ArenaAllocator::release(ArenaHead& ah)
98 {
99 Ptr<void> curr;
100 curr.i = ah.m_first_block;
101 while (curr.i != RNIL)
102 {
103 curr.p = m_pool.getPtr(curr.i);
104 Uint32 next = static_cast<ArenaBlock*>(curr.p)->m_next_block;
105 m_pool.release(curr);
106 curr.i = next;
107 }
108
109 new (&ah) ArenaHead();
110 }
111
112 void
init(ArenaAllocator * alloc,const Record_info & ri,const Pool_context &)113 ArenaPool::init(ArenaAllocator * alloc,
114 const Record_info& ri, const Pool_context&)
115 {
116 m_record_info = ri;
117 #if SIZEOF_CHARP == 4
118 m_record_info.m_size = ((ri.m_size + 3) >> 2); // Align to word boundary
119 #else
120 m_record_info.m_size = ((ri.m_size + 7) >> 3) << 1; // align 8-byte
121 #endif
122 m_record_info.m_offset_magic = ((ri.m_offset_magic + 3) >> 2);
123 m_record_info.m_offset_next_pool = ((ri.m_offset_next_pool + 3) >> 2);
124 m_allocator = alloc;
125 }
126
127 bool
seize(ArenaHead & ah,Ptr<void> & ptr)128 ArenaPool::seize(ArenaHead & ah, Ptr<void>& ptr)
129 {
130 Uint32 pos = ah.m_first_free;
131 Uint32 bs = ah.m_block_size;
132 Uint32 ptrI = ah.m_current_block;
133 ArenaBlock * block = ah.m_current_block_ptr;
134
135 Uint32 sz = m_record_info.m_size;
136 Uint32 off = m_record_info.m_offset_magic;
137
138 if (0)
139 ndbout_c("pos: %u sz: %u (sum: %u) bs: %u",
140 pos, sz, (pos + sz), bs);
141
142 if (pos + sz <= bs)
143 {
144 /**
145 * Alloc in this block
146 */
147 ptr.i =
148 ((ptrI >> POOL_RECORD_BITS) << POOL_RECORD_BITS) +
149 (ptrI & POOL_RECORD_MASK) + pos + ArenaBlock::HeaderSize;
150 ptr.p = block->m_data + pos;
151 block->m_data[pos+off] = ~(Uint32)m_record_info.m_type_id;
152
153 ah.m_first_free = pos + sz;
154 return true;
155 }
156 else
157 {
158 Ptr<void> tmp;
159 if (m_allocator->m_pool.seize(tmp))
160 {
161 ah.m_first_free = 0;
162 ah.m_current_block = tmp.i;
163 ah.m_current_block_ptr->m_next_block = tmp.i;
164 ah.m_current_block_ptr = static_cast<ArenaBlock*>(tmp.p);
165 ah.m_current_block_ptr->m_next_block = RNIL;
166 bool ret = seize(ah, ptr);
167 (void)ret;
168 assert(ret == true);
169 return true;
170 }
171 }
172 return false;
173 }
174
175 void
handle_invalid_release(Ptr<void> ptr)176 ArenaPool::handle_invalid_release(Ptr<void> ptr)
177 {
178 char buf[255];
179
180 //Uint32 pos = ptr.i & POOL_RECORD_MASK;
181 //Uint32 pageI = ptr.i >> POOL_RECORD_BITS;
182 Uint32 * record_ptr_p = (Uint32*)ptr.p;
183
184 Uint32 magic = * (record_ptr_p + m_record_info.m_offset_magic);
185 BaseString::snprintf(buf, sizeof(buf),
186 "Invalid memory release: ptr (%x %p) magic: (%.8x %.8x)",
187 ptr.i, ptr.p, magic, m_record_info.m_type_id);
188
189 m_allocator->m_pool.m_ctx.handleAbort(NDBD_EXIT_PRGERR, buf);
190 }
191