1 /*
2 Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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
26 #define DBTUP_C
27 #define DBTUP_PAGE_MAP_CPP
28 #include "Dbtup.hpp"
29 #include <RefConvert.hpp>
30 #include <ndb_limits.h>
31 #include <pc.hpp>
32 #include <signaldata/RestoreImpl.hpp>
33
34 #define JAM_FILE_ID 415
35
36
37 #define DBUG_PAGE_MAP 0
38
39 //
40 // PageMap is a service used by Dbtup to map logical page id's to physical
41 // page id's. The mapping is needs the fragment and the logical page id to
42 // provide the physical id.
43 //
44 // This is a part of Dbtup which is the exclusive user of a certain set of
45 // variables on the fragment record and it is the exclusive user of the
46 // struct for page ranges.
47 //
48 //
49 // The following methods operate on the data handled by the page map class.
50 //
51 // Public methods
52 // insertPageRange(Uint32 startPageId, # In
53 // Uint32 noPages) # In
54 // Inserts a range of pages into the mapping structure.
55 //
56 // void releaseFragPages()
57 // Releases all pages and their mappings belonging to a fragment.
58 //
59 // Uint32 allocFragPages(Uint32 tafpNoAllocRequested)
60 // Allocate a set of pages to the fragment from the page manager
61 //
62 // Uint32 getEmptyPage()
63 // Get an empty page from the pool of empty pages on the fragment.
64 // It returns the physical page id of the empty page.
65 // Returns RNIL if no empty page is available.
66 //
67 // Uint32 getRealpid(Uint32 logicalPageId)
68 // Return the physical page id provided the logical page id
69 //
70 // void initializePageRange()
71 // Initialise free list of page ranges and initialise the page raneg records.
72 //
73 // void initFragRange()
74 // Initialise the fragment variables when allocating a fragment to a table.
75 //
76 // void initPageRangeSize(Uint32 size)
77 // Initialise the number of page ranges.
78 //
79 // Uint32 getNoOfPages()
80 // Get the number of pages on the fragment currently.
81 //
82 //
83 // Private methods
84 // Uint32 leafPageRangeFull(PageRangePtr currPageRangePtr)
85 //
86 // void errorHandler()
87 // Method to crash NDB kernel in case of weird data set-up
88 //
89 // void allocMoreFragPages()
90 // When no more empty pages are attached to the fragment and we need more
91 // we allocate more pages from the page manager using this method.
92 //
93 // Private data
94 // On the fragment record
95 // currentPageRange # The current page range where to insert the next range
96 // rootPageRange # The root of the page ranges owned
97 // nextStartRange # The next page id to assign when expanding the
98 // # page map
99 // noOfPages # The number of pages in the fragment
100 // emptyPrimPage # The first page of the empty pages in the fragment
101 //
102 // The full page range struct
103
getRealpid(Fragrecord * regFragPtr,Uint32 logicalPageId)104 Uint32 Dbtup::getRealpid(Fragrecord* regFragPtr, Uint32 logicalPageId)
105 {
106 DynArr256 map(c_page_map_pool, regFragPtr->m_page_map);
107 Uint32 * ptr = map.get(2 * logicalPageId);
108 if (likely(ptr != 0))
109 {
110 return * ptr;
111 }
112 ndbrequire(false);
113 return RNIL;
114 }
115
116 Uint32
getRealpidCheck(Fragrecord * regFragPtr,Uint32 logicalPageId)117 Dbtup::getRealpidCheck(Fragrecord* regFragPtr, Uint32 logicalPageId)
118 {
119 DynArr256 map(c_page_map_pool, regFragPtr->m_page_map);
120 // logicalPageId might not be mapped yet,
121 // get_dirty returns NULL also in debug in this case.
122 Uint32 * ptr = map.get_dirty(2 * logicalPageId);
123 if (likely(ptr != 0))
124 {
125 Uint32 val = * ptr;
126 if ((val & FREE_PAGE_BIT) != 0)
127 return RNIL;
128 else
129 return val;
130 }
131 return RNIL;
132 }
133
getNoOfPages(Fragrecord * const regFragPtr)134 Uint32 Dbtup::getNoOfPages(Fragrecord* const regFragPtr)
135 {
136 return regFragPtr->noOfPages;
137 }//Dbtup::getNoOfPages()
138
139 void
init_page(Fragrecord * regFragPtr,PagePtr pagePtr,Uint32 pageId)140 Dbtup::init_page(Fragrecord* regFragPtr, PagePtr pagePtr, Uint32 pageId)
141 {
142 pagePtr.p->page_state = ~0;
143 pagePtr.p->frag_page_id = pageId;
144 pagePtr.p->physical_page_id = pagePtr.i;
145 pagePtr.p->nextList = RNIL;
146 pagePtr.p->prevList = RNIL;
147 pagePtr.p->m_flags = 0;
148 }
149
150 #ifdef VM_TRACE
151 #define do_check_page_map(x) check_page_map(x)
152 #if DBUG_PAGE_MAP
153 bool
find_page_id_in_list(Fragrecord * fragPtrP,Uint32 pageId)154 Dbtup::find_page_id_in_list(Fragrecord* fragPtrP, Uint32 pageId)
155 {
156 DynArr256 map(c_page_map_pool, fragPtrP->m_page_map);
157
158 Uint32 prev = FREE_PAGE_RNIL;
159 Uint32 curr = fragPtrP->m_free_page_id_list | FREE_PAGE_BIT;
160
161 while (curr != FREE_PAGE_RNIL)
162 {
163 ndbrequire((curr & FREE_PAGE_BIT) != 0);
164 curr &= ~(Uint32)FREE_PAGE_BIT;
165 const Uint32 * prevPtr = map.get(2 * curr + 1);
166 ndbrequire(prevPtr != 0);
167 ndbrequire(prev == *prevPtr);
168
169 if (curr == pageId)
170 return true;
171
172 Uint32 * nextPtr = map.get(2 * curr);
173 ndbrequire(nextPtr != 0);
174 prev = curr | FREE_PAGE_BIT;
175 curr = (* nextPtr);
176 }
177
178 return false;
179 }
180
181 void
check_page_map(Fragrecord * fragPtrP)182 Dbtup::check_page_map(Fragrecord* fragPtrP)
183 {
184 Uint32 max = fragPtrP->m_max_page_cnt;
185 DynArr256 map(c_page_map_pool, fragPtrP->m_page_map);
186
187 for (Uint32 i = 0; i<max; i++)
188 {
189 const Uint32 * ptr = map.get(2*i);
190 if (ptr == 0)
191 {
192 ndbrequire(find_page_id_in_list(fragPtrP, i) == false);
193 }
194 else
195 {
196 Uint32 realpid = *ptr;
197 if (realpid == RNIL)
198 {
199 ndbrequire(find_page_id_in_list(fragPtrP, i) == false);
200 }
201 else if (realpid & FREE_PAGE_BIT)
202 {
203 ndbrequire(find_page_id_in_list(fragPtrP, i) == true);
204 }
205 else
206 {
207 PagePtr pagePtr;
208 c_page_pool.getPtr(pagePtr, realpid);
209 ndbrequire(pagePtr.p->frag_page_id == i);
210 ndbrequire(pagePtr.p->physical_page_id == realpid);
211 }
212 }
213 }
214 }
215 #else
check_page_map(Fragrecord *)216 void Dbtup::check_page_map(Fragrecord*) {}
217 #endif
218 #else
219 #define do_check_page_map(x)
220 #endif
221
222 Uint32
allocFragPage(EmulatedJamBuffer * jamBuf,Uint32 * err,Fragrecord * regFragPtr)223 Dbtup::allocFragPage(EmulatedJamBuffer* jamBuf,
224 Uint32 * err,
225 Fragrecord* regFragPtr)
226 {
227 PagePtr pagePtr;
228 Uint32 noOfPagesAllocated = 0;
229 Uint32 list = regFragPtr->m_free_page_id_list;
230 Uint32 max = regFragPtr->m_max_page_cnt;
231 Uint32 cnt = regFragPtr->noOfPages;
232
233 allocConsPages(jamBuf, 1, noOfPagesAllocated, pagePtr.i);
234 if (noOfPagesAllocated == 0)
235 {
236 thrjam(jamBuf);
237 * err = ZMEM_NOMEM_ERROR;
238 return RNIL;
239 }//if
240
241 Uint32 pageId;
242 DynArr256 map(c_page_map_pool, regFragPtr->m_page_map);
243 if (list == FREE_PAGE_RNIL)
244 {
245 thrjam(jamBuf);
246 pageId = max;
247 if (!Local_key::isShort(pageId))
248 {
249 /**
250 * TODO: remove when ACC supports 48 bit references
251 */
252 thrjam(jamBuf);
253 * err = 889;
254 return RNIL;
255 }
256 Uint32 * ptr = map.set(2 * pageId);
257 if (unlikely(ptr == 0))
258 {
259 thrjam(jamBuf);
260 returnCommonArea(pagePtr.i, noOfPagesAllocated);
261 * err = ZMEM_NOMEM_ERROR;
262 return RNIL;
263 }
264 ndbrequire(* ptr == RNIL);
265 * ptr = pagePtr.i;
266 regFragPtr->m_max_page_cnt = max + 1;
267 }
268 else
269 {
270 thrjam(jamBuf);
271 pageId = list;
272 Uint32 * ptr = map.set(2 * pageId);
273 ndbrequire(ptr != 0);
274 Uint32 next = * ptr;
275 * ptr = pagePtr.i;
276
277 if (next != FREE_PAGE_RNIL)
278 {
279 thrjam(jamBuf);
280 ndbrequire((next & FREE_PAGE_BIT) != 0);
281 next &= ~FREE_PAGE_BIT;
282 Uint32 * nextPrevPtr = map.set(2 * next + 1);
283 ndbrequire(nextPrevPtr != 0);
284 * nextPrevPtr = FREE_PAGE_RNIL;
285 }
286 regFragPtr->m_free_page_id_list = next;
287 }
288
289 regFragPtr->noOfPages = cnt + 1;
290 c_page_pool.getPtr(pagePtr);
291 init_page(regFragPtr, pagePtr, pageId);
292
293 if (DBUG_PAGE_MAP)
294 ndbout_c("alloc -> (%u %u max: %u)", pageId, pagePtr.i,
295 regFragPtr->m_max_page_cnt);
296
297 do_check_page_map(regFragPtr);
298 return pagePtr.i;
299 }//Dbtup::allocFragPage()
300
301 Uint32
allocFragPage(Uint32 * err,Tablerec * tabPtrP,Fragrecord * fragPtrP,Uint32 page_no)302 Dbtup::allocFragPage(Uint32 * err,
303 Tablerec* tabPtrP, Fragrecord* fragPtrP, Uint32 page_no)
304 {
305 PagePtr pagePtr;
306 DynArr256 map(c_page_map_pool, fragPtrP->m_page_map);
307 Uint32 * ptr = map.set(2 * page_no);
308 if (unlikely(ptr == 0))
309 {
310 jam();
311 * err = ZMEM_NOMEM_ERROR;
312 return RNIL;
313 }
314 const Uint32 * prevPtr = map.set(2 * page_no + 1);
315
316 pagePtr.i = * ptr;
317 if (likely(pagePtr.i != RNIL && (pagePtr.i & FREE_PAGE_BIT) == 0))
318 {
319 jam();
320 return pagePtr.i;
321 }
322
323 LocalDLFifoList<Page> free_pages(c_page_pool, fragPtrP->thFreeFirst);
324 Uint32 cnt = fragPtrP->noOfPages;
325 Uint32 max = fragPtrP->m_max_page_cnt;
326 Uint32 list = fragPtrP->m_free_page_id_list;
327 Uint32 noOfPagesAllocated = 0;
328 Uint32 next = pagePtr.i;
329
330 allocConsPages(jamBuffer(), 1, noOfPagesAllocated, pagePtr.i);
331 if (unlikely(noOfPagesAllocated == 0))
332 {
333 jam();
334 * err = ZMEM_NOMEM_ERROR;
335 return RNIL;
336 }
337
338 if (DBUG_PAGE_MAP)
339 ndbout_c("alloc(%u %u max: %u)", page_no, pagePtr.i, max);
340
341 * ptr = pagePtr.i;
342 if (next == RNIL)
343 {
344 jam();
345 }
346 else
347 {
348 jam();
349 ndbrequire(prevPtr != 0);
350 Uint32 prev = * prevPtr;
351
352 if (next == FREE_PAGE_RNIL)
353 {
354 jam();
355 // This should be end of list...
356 if (prev == FREE_PAGE_RNIL)
357 {
358 jam();
359 ndbrequire(list == page_no); // page_no is both head and tail...
360 fragPtrP->m_free_page_id_list = FREE_PAGE_RNIL;
361 }
362 else
363 {
364 jam();
365 Uint32 * prevNextPtr = map.set(2 * (prev & ~(Uint32)FREE_PAGE_BIT));
366 ndbrequire(prevNextPtr != 0);
367 Uint32 prevNext = * prevNextPtr;
368 ndbrequire(prevNext == (page_no | FREE_PAGE_BIT));
369 * prevNextPtr = FREE_PAGE_RNIL;
370 }
371 }
372 else
373 {
374 jam();
375 next &= ~(Uint32)FREE_PAGE_BIT;
376 Uint32 * nextPrevPtr = map.set(2 * next + 1);
377 ndbrequire(nextPrevPtr != 0);
378 ndbrequire(* nextPrevPtr == (page_no | FREE_PAGE_BIT));
379 * nextPrevPtr = prev;
380 if (prev == FREE_PAGE_RNIL)
381 {
382 jam();
383 ndbrequire(list == page_no); // page_no is head
384 fragPtrP->m_free_page_id_list = next;
385 }
386 else
387 {
388 jam();
389 Uint32 * prevNextPtr = map.get(2 * (prev & ~(Uint32)FREE_PAGE_BIT));
390 ndbrequire(prevNextPtr != 0);
391 ndbrequire(* prevNextPtr == (page_no | FREE_PAGE_BIT));
392 * prevNextPtr = next | FREE_PAGE_BIT;
393 }
394 }
395 }
396
397 fragPtrP->noOfPages = cnt + 1;
398 if (page_no + 1 > max)
399 {
400 jam();
401 fragPtrP->m_max_page_cnt = page_no + 1;
402 if (DBUG_PAGE_MAP)
403 ndbout_c("new max: %u", fragPtrP->m_max_page_cnt);
404 }
405
406 Uint32 lcp_scan_ptr_i = fragPtrP->m_lcp_scan_op;
407 c_page_pool.getPtr(pagePtr);
408 init_page(fragPtrP, pagePtr, page_no);
409 if (lcp_scan_ptr_i != RNIL)
410 {
411 jam();
412 ScanOpPtr scanOp;
413 c_scanOpPool.getPtr(scanOp, lcp_scan_ptr_i);
414 if (page_no < scanOp.p->m_endPage)
415 {
416 Local_key lcp_key = scanOp.p->m_scanPos.m_key;
417 if (page_no > lcp_key.m_page_no)
418 {
419 jam();
420 /**
421 * We allocated a page during an LCP, it was within the pages that
422 * will be checked during the LCP scan. The page has also not yet
423 * been scanned by the LCP. Given that we know that the page will
424 * only contain rows that would set the LCP_SKIP bit we will
425 * set the LCP skip on the page level instead to speed up LCP
426 * processing.
427 */
428 pagePtr.p->set_page_to_skip_lcp();
429 }
430 }
431 }
432 convertThPage((Fix_page*)pagePtr.p, tabPtrP, MM);
433 pagePtr.p->page_state = ZTH_MM_FREE;
434 free_pages.addFirst(pagePtr);
435
436 do_check_page_map(fragPtrP);
437
438 return pagePtr.i;
439 }
440
441 void
releaseFragPage(Fragrecord * fragPtrP,Uint32 logicalPageId,PagePtr pagePtr)442 Dbtup::releaseFragPage(Fragrecord* fragPtrP,
443 Uint32 logicalPageId, PagePtr pagePtr)
444 {
445 Uint32 list = fragPtrP->m_free_page_id_list;
446 Uint32 cnt = fragPtrP->noOfPages;
447 DynArr256 map(c_page_map_pool, fragPtrP->m_page_map);
448 Uint32 * next = map.set(2 * logicalPageId);
449 Uint32 * prev = map.set(2 * logicalPageId + 1);
450 ndbrequire(next != 0 && prev != 0);
451
452 returnCommonArea(pagePtr.i, 1);
453
454 /**
455 * Add to head or tail of list...
456 */
457 const char * where = 0;
458 if (list == FREE_PAGE_RNIL)
459 {
460 jam();
461 * next = * prev = FREE_PAGE_RNIL;
462 fragPtrP->m_free_page_id_list = logicalPageId;
463 where = "empty";
464 }
465 else
466 {
467 jam();
468 * next = list | FREE_PAGE_BIT;
469 * prev = FREE_PAGE_RNIL;
470 fragPtrP->m_free_page_id_list = logicalPageId;
471 Uint32 * nextPrevPtr = map.set(2 * list + 1);
472 ndbrequire(nextPrevPtr != 0);
473 ndbrequire(*nextPrevPtr == FREE_PAGE_RNIL);
474 * nextPrevPtr = logicalPageId | FREE_PAGE_BIT;
475 where = "head";
476 }
477
478 fragPtrP->noOfPages = cnt - 1;
479 if (DBUG_PAGE_MAP)
480 ndbout_c("release(%u %u)@%s", logicalPageId, pagePtr.i, where);
481 do_check_page_map(fragPtrP);
482 }
483
errorHandler(Uint32 errorCode)484 void Dbtup::errorHandler(Uint32 errorCode)
485 {
486 switch (errorCode) {
487 case 0:
488 jam();
489 break;
490 case 1:
491 jam();
492 break;
493 case 2:
494 jam();
495 break;
496 default:
497 jam();
498 }
499 ndbrequire(false);
500 }//Dbtup::errorHandler()
501
502 void
rebuild_page_free_list(Signal * signal)503 Dbtup::rebuild_page_free_list(Signal* signal)
504 {
505 Ptr<Fragoperrec> fragOpPtr;
506 fragOpPtr.i = signal->theData[1];
507 Uint32 pageId = signal->theData[2];
508 Uint32 tail = signal->theData[3];
509 ptrCheckGuard(fragOpPtr, cnoOfFragoprec, fragoperrec);
510
511 Ptr<Fragrecord> fragPtr;
512 fragPtr.i= fragOpPtr.p->fragPointer;
513 ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord);
514
515 if (pageId == fragPtr.p->m_max_page_cnt)
516 {
517 RestoreLcpConf* conf = (RestoreLcpConf*)signal->getDataPtrSend();
518 conf->senderRef = reference();
519 conf->senderData = fragOpPtr.p->m_senderData;
520 sendSignal(fragOpPtr.p->m_senderRef,
521 GSN_RESTORE_LCP_CONF, signal,
522 RestoreLcpConf::SignalLength, JBB);
523
524 releaseFragoperrec(fragOpPtr);
525 return;
526 }
527
528 DynArr256 map(c_page_map_pool, fragPtr.p->m_page_map);
529 Uint32* nextPtr = map.set(2 * pageId);
530 Uint32* prevPtr = map.set(2 * pageId + 1);
531
532 // Out of memory ?? Should nto be possible here/now
533 ndbrequire(nextPtr != 0 && prevPtr != 0);
534
535 if (* nextPtr == RNIL)
536 {
537 jam();
538 /**
539 * An unallocated page id...put in free list
540 */
541 #if DBUG_PAGE_MAP
542 char * where;
543 #endif
544 if (tail == RNIL)
545 {
546 jam();
547 ndbrequire(fragPtr.p->m_free_page_id_list == FREE_PAGE_RNIL);
548 fragPtr.p->m_free_page_id_list = pageId;
549 *nextPtr = FREE_PAGE_RNIL;
550 *prevPtr = FREE_PAGE_RNIL;
551 #if DBUG_PAGE_MAP
552 where = "head";
553 #endif
554 }
555 else
556 {
557 jam();
558 ndbrequire(fragPtr.p->m_free_page_id_list != FREE_PAGE_RNIL);
559
560 *nextPtr = FREE_PAGE_RNIL;
561 *prevPtr = tail | FREE_PAGE_BIT;
562
563 Uint32 * prevNextPtr = map.set(2 * tail);
564 ndbrequire(prevNextPtr != 0);
565 ndbrequire(* prevNextPtr == FREE_PAGE_RNIL);
566 * prevNextPtr = pageId | FREE_PAGE_BIT;
567 #if DBUG_PAGE_MAP
568 where = "tail";
569 #endif
570 }
571 tail = pageId;
572 #if DBUG_PAGE_MAP
573 ndbout_c("adding page %u to free list @ %s", pageId, where);
574 #endif
575 }
576
577 signal->theData[0] = ZREBUILD_FREE_PAGE_LIST;
578 signal->theData[1] = fragOpPtr.i;
579 signal->theData[2] = pageId + 1;
580 signal->theData[3] = tail;
581 sendSignal(reference(), GSN_CONTINUEB, signal, 4, JBB);
582 }
583
584