1 /*
2    Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include "RWPool.hpp"
26 #include <ndbd_exit_codes.h>
27 #include <NdbOut.hpp>
28 
29 #define JAM_FILE_ID 278
30 
31 
32 #define REC_NIL GLOBAL_PAGE_SIZE_WORDS
33 
RWPool()34 RWPool::RWPool()
35 {
36   memset(this, 0, sizeof(* this));
37   m_current_pos = RWPage::RWPAGE_WORDS;
38   m_current_first_free = REC_NIL;
39   m_first_free_page = RNIL;
40 }
41 
42 void
init(const Record_info & ri,const Pool_context & pc)43 RWPool::init(const Record_info& ri, const Pool_context& pc)
44 {
45   m_ctx = pc;
46   m_record_info = ri;
47   m_record_info.m_size = ((ri.m_size + 3) >> 2); // Align to word boundary
48   m_record_info.m_offset_magic = ((ri.m_offset_magic + 3) >> 2);
49   m_record_info.m_offset_next_pool = ((ri.m_offset_next_pool + 3) >> 2);
50   m_memroot = (RWPage*)m_ctx.get_memroot();
51 #ifdef VM_TRACE
52   ndbout_c("RWPool::init(%x, %d)",ri.m_type_id, m_record_info.m_size);
53 #endif
54 }
55 
56 bool
seize(Ptr<void> & ptr)57 RWPool::seize(Ptr<void>& ptr)
58 {
59   Uint32 pos = m_current_pos;
60   Uint32 size = m_record_info.m_size;
61   Uint32 off = m_record_info.m_offset_magic;
62   RWPage *pageP = m_current_page;
63   if (likely(m_current_first_free != REC_NIL))
64   {
65 seize_free:
66     pos = m_current_first_free;
67     ptr.i = (m_current_page_no << POOL_RECORD_BITS) + pos;
68     ptr.p = pageP->m_data + pos;
69     pageP->m_data[pos+off] = ~(Uint32)m_record_info.m_type_id;
70     m_current_ref_count++;
71     m_current_first_free = pageP->m_data[pos+m_record_info.m_offset_next_pool];
72     return true;
73   }
74   else if (pos + size < RWPage::RWPAGE_WORDS)
75   {
76 seize_first:
77     ptr.i = (m_current_page_no << POOL_RECORD_BITS) + pos;
78     ptr.p = (pageP->m_data + pos);
79     pageP->m_data[pos+off] = ~(Uint32)m_record_info.m_type_id;
80     m_current_ref_count++;
81     m_current_pos = pos + size;
82     return true;
83   }
84 
85   if (m_current_page)
86   {
87     m_current_page->m_first_free = REC_NIL;
88     m_current_page->m_next_page = RNIL;
89     m_current_page->m_prev_page = RNIL;
90     m_current_page->m_type_id = m_record_info.m_type_id;
91     m_current_page->m_ref_count = m_current_ref_count;
92   }
93 
94   if (m_first_free_page != RNIL)
95   {
96     pageP = m_current_page = m_memroot + m_first_free_page;
97     m_current_page_no = m_first_free_page;
98     m_current_pos = RWPage::RWPAGE_WORDS;
99     m_current_first_free = m_current_page->m_first_free;
100     m_first_free_page = m_current_page->m_next_page;
101     m_current_ref_count = m_current_page->m_ref_count;
102     if (m_first_free_page != RNIL)
103     {
104       (m_memroot + m_first_free_page)->m_prev_page = RNIL;
105     }
106     goto seize_free;
107   }
108 
109   m_current_ref_count = 0;
110 
111   RWPage* page;
112   Uint32 page_no = RNIL;
113   if ((page = (RWPage*)m_ctx.alloc_page(m_record_info.m_type_id, &page_no)))
114   {
115     pos = 0;
116     m_current_page_no = page_no;
117     pageP = m_current_page = page;
118     m_current_first_free = REC_NIL;
119     page->m_type_id = m_record_info.m_type_id;
120     goto seize_first;
121   }
122 
123   m_current_page = 0;
124   m_current_page_no = RNIL;
125   m_current_pos = RWPage::RWPAGE_WORDS;
126   m_current_first_free = REC_NIL;
127 
128   return false;
129 }
130 
131 void
release(Ptr<void> ptr)132 RWPool::release(Ptr<void> ptr)
133 {
134   Uint32 cur_page = m_current_page_no;
135   Uint32 ptr_page = ptr.i >> POOL_RECORD_BITS;
136   Uint32 *record_ptr = (Uint32*)ptr.p;
137   Uint32 magic_val = * (record_ptr + m_record_info.m_offset_magic);
138 
139   if (likely(magic_val == ~(Uint32)m_record_info.m_type_id))
140   {
141     * (record_ptr + m_record_info.m_offset_magic) = 0;
142     if (cur_page == ptr_page)
143     {
144       * (record_ptr + m_record_info.m_offset_next_pool) = m_current_first_free;
145       assert(m_current_ref_count);
146       m_current_ref_count--;
147       m_current_first_free = ptr.i & POOL_RECORD_MASK;
148       return;
149     }
150 
151     // Cache miss on page...
152     RWPage* page = m_memroot + ptr_page;
153     Uint32 ref_cnt = page->m_ref_count;
154     Uint32 ff = page->m_first_free;
155 
156     * (record_ptr + m_record_info.m_offset_next_pool) = ff;
157     page->m_first_free = ptr.i & POOL_RECORD_MASK;
158     page->m_ref_count = ref_cnt - 1;
159 
160     if (ff == REC_NIL)
161     {
162       /**
163        * It was full...add to free page list
164        */
165       Uint32 ffp = m_first_free_page;
166       if (ffp != RNIL)
167       {
168 	RWPage* next = (m_memroot + ffp);
169 	assert(next->m_prev_page == RNIL);
170 	next->m_prev_page = ptr_page;
171       }
172       page->m_next_page = ffp;
173       page->m_prev_page = RNIL;
174       m_first_free_page = ptr_page;
175       return;
176     }
177     else if(ref_cnt == 1)
178     {
179       /**
180        * It's now empty...release it
181        */
182       Uint32 prev = page->m_prev_page;
183       Uint32 next = page->m_next_page;
184       if (prev != RNIL)
185       {
186 	(m_memroot + prev)->m_next_page = next;
187       }
188       else
189       {
190 	assert(m_first_free_page == ptr_page);
191 	m_first_free_page = next;
192       }
193 
194       if (next != RNIL)
195       {
196 	(m_memroot + next)->m_prev_page = prev;
197       }
198       m_ctx.release_page(m_record_info.m_type_id, ptr_page);
199       return;
200     }
201     return;
202   }
203   handle_invalid_release(ptr);
204 }
205 
206 void
handle_invalid_release(Ptr<void> ptr)207 RWPool::handle_invalid_release(Ptr<void> ptr)
208 {
209   char buf[255];
210 
211   Uint32 pos = ptr.i & POOL_RECORD_MASK;
212   Uint32 pageI = ptr.i >> POOL_RECORD_BITS;
213   Uint32 * record_ptr_p = (Uint32*)ptr.p;
214   Uint32 * record_ptr_i = (m_memroot+pageI)->m_data + pos;
215 
216   Uint32 magic = * (record_ptr_p + m_record_info.m_offset_magic);
217   BaseString::snprintf(buf, sizeof(buf),
218 	   "Invalid memory release: ptr (%x %p %p) magic: (%.8x %.8x) memroot: %p page: %x",
219 	   ptr.i, ptr.p, record_ptr_i, magic, m_record_info.m_type_id,
220 	   m_memroot,
221 	   (m_memroot+pageI)->m_type_id);
222 
223   m_ctx.handleAbort(NDBD_EXIT_PRGERR, buf);
224 }
225 
226 void
handle_invalid_get_ptr(Uint32 ptrI)227 RWPool::handle_invalid_get_ptr(Uint32 ptrI)
228 {
229   char buf[255];
230 
231   Uint32 pos = ptrI & POOL_RECORD_MASK;
232   Uint32 pageI = ptrI >> POOL_RECORD_BITS;
233   Uint32 * record_ptr_i = (m_memroot+pageI)->m_data + pos;
234 
235   Uint32 magic = * (record_ptr_i + m_record_info.m_offset_magic);
236   BaseString::snprintf(buf, sizeof(buf),
237 	   "Invalid memory access: ptr (%x %p) magic: (%.8x %.8x) memroot: %p page: %x",
238 	   ptrI, record_ptr_i, magic, m_record_info.m_type_id,
239 	   m_memroot,
240 	   (m_memroot+pageI)->m_type_id);
241 
242   m_ctx.handleAbort(NDBD_EXIT_PRGERR, buf);
243 }
244