MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ndbd_malloc_impl.cpp
1 /*
2  Copyright (c) 2006, 2010, 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 as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 
19 
20 #include "ndbd_malloc_impl.hpp"
21 #include <ndb_global.h>
22 #include <EventLogger.hpp>
23 #include <portlib/NdbMem.h>
24 
25 #ifdef NDB_WIN
26 void *sbrk(int increment)
27 {
28  return (void*)-1;
29 }
30 #endif
31 
32 extern EventLogger * g_eventLogger;
33 
34 static int f_method_idx = 0;
35 #ifdef NDBD_MALLOC_METHOD_SBRK
36 static const char * f_method = "SMsm";
37 #else
38 static const char * f_method = "MSms";
39 #endif
40 #define MAX_CHUNKS 10
41 
42 #ifdef VM_TRACE
43 #ifndef NDBD_RANDOM_START_PAGE
44 #define NDBD_RANDOM_START_PAGE
45 #endif
46 #endif
47 
48 #ifdef NDBD_RANDOM_START_PAGE
49 static Uint32 g_random_start_page_id = 0;
50 #endif
51 
52 /*
53  * For muti-threaded ndbd, these calls are used for locking around
54  * memory allocation operations.
55  *
56  * For single-threaded ndbd, they are no-ops (but still called, to avoid
57  * having to compile this file twice).
58  */
59 extern void mt_mem_manager_init();
60 extern void mt_mem_manager_lock();
61 extern void mt_mem_manager_unlock();
62 
63 #define ZONE_LO 0
64 #define ZONE_HI 1
65 
69 #define ZONE_LO_BOUND (1u << 19)
70 
71 #include <NdbOut.hpp>
72 
73 extern void ndbd_alloc_touch_mem(void * p, size_t sz, volatile Uint32 * watchCounter);
74 
75 static
76 bool
77 do_malloc(Uint32 pages,
78  InitChunk* chunk,
79  Uint32 *watchCounter,
80  void * baseaddress)
81 {
82  pages += 1;
83  void * ptr = 0;
84  Uint32 sz = pages;
85 
86 retry:
87  if (watchCounter)
88  *watchCounter = 9;
89 
90  char method = f_method[f_method_idx];
91  switch(method){
92  case 0:
93  return false;
94  case 'S':
95  case 's':
96  {
97  ptr = 0;
98  while (ptr == 0)
99  {
100  if (watchCounter)
101  *watchCounter = 9;
102 
103  ptr = sbrk(sizeof(Alloc_page) * sz);
104 
105  if (ptr == (void*)-1)
106  {
107  if (method == 'S')
108  {
109  f_method_idx++;
110  goto retry;
111  }
112 
113  ptr = 0;
114  sz = 1 + (9 * sz) / 10;
115  if (pages >= 32 && sz < 32)
116  {
117  sz = pages;
118  f_method_idx++;
119  goto retry;
120  }
121  }
122  else if (UintPtr(ptr) < UintPtr(baseaddress))
123  {
127  ndbout_c("sbrk(%lluMb) => %p which is less than baseaddress!!",
128  Uint64((sizeof(Alloc_page) * sz) >> 20), ptr);
129  f_method_idx++;
130  goto retry;
131  }
132  }
133  break;
134  }
135  case 'M':
136  case 'm':
137  {
138  ptr = 0;
139  while (ptr == 0)
140  {
141  if (watchCounter)
142  *watchCounter = 9;
143 
144  ptr = malloc(sizeof(Alloc_page) * sz);
145  if (UintPtr(ptr) < UintPtr(baseaddress))
146  {
147  ndbout_c("malloc(%lluMb) => %p which is less than baseaddress!!",
148  Uint64((sizeof(Alloc_page) * sz) >> 20), ptr);
149  free(ptr);
150  ptr = 0;
151  }
152 
153  if (ptr == 0)
154  {
155  if (method == 'M')
156  {
157  f_method_idx++;
158  goto retry;
159  }
160 
161  sz = 1 + (9 * sz) / 10;
162  if (pages >= 32 && sz < 32)
163  {
164  f_method_idx++;
165  goto retry;
166  }
167  }
168  }
169  break;
170  }
171  default:
172  return false;
173  }
174 
175  chunk->m_cnt = sz;
176  chunk->m_ptr = (Alloc_page*)ptr;
177  const UintPtr align = sizeof(Alloc_page) - 1;
178  if (UintPtr(ptr) & align)
179  {
180  chunk->m_cnt--;
181  chunk->m_ptr = (Alloc_page*)((UintPtr(ptr) + align) & ~align);
182  }
183 
184 #ifdef UNIT_TEST
185  ndbout_c("do_malloc(%d) -> %p %d", pages, ptr, chunk->m_cnt);
186  if (1)
187  {
188  Uint32 sum = 0;
189  Alloc_page* page = chunk->m_ptr;
190  for (Uint32 i = 0; i<chunk->m_cnt; i++, page++)
191  {
192  page->m_data[0*1024] = 0;
193  page->m_data[1*1024] = 0;
194  page->m_data[2*1024] = 0;
195  page->m_data[3*1024] = 0;
196  page->m_data[4*1024] = 0;
197  page->m_data[5*1024] = 0;
198  page->m_data[6*1024] = 0;
199  page->m_data[7*1024] = 0;
200  }
201  }
202 #endif
203 
204  return true;
205 }
206 
207 Uint32
209 {
210  if (input > 65535)
211  return 16;
212  input = input | (input >> 8);
213  input = input | (input >> 4);
214  input = input | (input >> 2);
215  input = input | (input >> 1);
216  Uint32 output = (input & 0x5555) + ((input >> 1) & 0x5555);
217  output = (output & 0x3333) + ((output >> 2) & 0x3333);
218  output = output + (output >> 4);
219  output = (output & 0xf) + ((output >> 8) & 0xf);
220  return output;
221 }
222 
223 Ndbd_mem_manager::Ndbd_mem_manager()
224 {
225  m_base_page = 0;
226  memset(m_buddy_lists, 0, sizeof(m_buddy_lists));
227  memset(m_resource_limit, 0, sizeof(m_resource_limit));
228 
229  if (sizeof(Free_page_data) != (4 * (1 << FPD_2LOG)))
230  {
231  g_eventLogger->error("Invalid build, ndbd_malloc_impl.cpp:%d", __LINE__);
232  abort();
233  }
234  mt_mem_manager_init();
235 }
236 
237 void*
238 Ndbd_mem_manager::get_memroot() const
239 {
240 #ifdef NDBD_RANDOM_START_PAGE
241  return (void*)(m_base_page - g_random_start_page_id);
242 #else
243  return (void*)m_base_page;
244 #endif
245 }
246 
262 void
264 {
265  Uint32 id = rl.m_resource_id;
266  assert(id < XX_RL_COUNT);
267 
268  Uint32 reserve = id ? rl.m_min : 0;
269  mt_mem_manager_lock();
270  Uint32 current_reserved = m_resource_limit[0].m_min;
271 
272  m_resource_limit[id] = rl;
273  m_resource_limit[id].m_curr = 0;
274  m_resource_limit[0].m_min = current_reserved + reserve;
275  mt_mem_manager_unlock();
276 }
277 
278 bool
279 Ndbd_mem_manager::get_resource_limit(Uint32 id, Resource_limit& rl) const
280 {
281  if (id < XX_RL_COUNT)
282  {
283  mt_mem_manager_lock();
284  rl = m_resource_limit[id];
285  mt_mem_manager_unlock();
286  return true;
287  }
288  return false;
289 }
290 
291 static
292 inline
293 void
294 check_resource_limits(Resource_limit* rl)
295 {
296 #ifdef VM_TRACE
297  Uint32 curr = 0;
298  Uint32 res_alloc = 0;
299  Uint32 shared_alloc = 0;
300  Uint32 sumres = 0;
301  for (Uint32 i = 1; i<XX_RL_COUNT; i++)
302  {
303  curr += rl[i].m_curr;
304  sumres += rl[i].m_min;
305  assert(rl[i].m_max == 0 || rl[i].m_curr <= rl[i].m_max);
306  if (rl[i].m_curr > rl[i].m_min)
307  {
308  shared_alloc += rl[i].m_curr - rl[i].m_min;
309  res_alloc += rl[i].m_min;
310  }
311  else
312  {
313  res_alloc += rl[i].m_curr;
314  }
315  }
316  assert(curr == rl[0].m_curr);
317  assert(res_alloc + shared_alloc == curr);
318  assert(res_alloc <= sumres);
319  assert(sumres == res_alloc + rl[0].m_min);
320  assert(rl[0].m_curr <= rl[0].m_max);
321 #endif
322 }
323 
324 int
325 cmp_chunk(const void * chunk_vptr_1, const void * chunk_vptr_2)
326 {
327  InitChunk * ptr1 = (InitChunk*)chunk_vptr_1;
328  InitChunk * ptr2 = (InitChunk*)chunk_vptr_2;
329  if (ptr1->m_ptr < ptr2->m_ptr)
330  return -1;
331  if (ptr1->m_ptr > ptr2->m_ptr)
332  return 1;
333  assert(false);
334  return 0;
335 }
336 
337 bool
338 Ndbd_mem_manager::init(Uint32 *watchCounter, bool alloc_less_memory)
339 {
340  assert(m_base_page == 0);
341 
342  if (watchCounter)
343  *watchCounter = 9;
344 
345  Uint32 pages = 0;
346  Uint32 max_page = 0;
347  Uint32 reserved = m_resource_limit[0].m_min;
348  if (m_resource_limit[0].m_max)
349  {
350  pages = m_resource_limit[0].m_max;
351  }
352  else
353  {
354  pages = m_resource_limit[0].m_min; // reserved
355  }
356 
357  if (m_resource_limit[0].m_min == 0)
358  {
359  m_resource_limit[0].m_min = pages;
360  }
361 
362  const Uint64 pg = Uint64(sizeof(Alloc_page));
363  g_eventLogger->info("Ndbd_mem_manager::init(%d) min: %lluMb initial: %lluMb",
364  alloc_less_memory,
365  (pg*m_resource_limit[0].m_min)>>20,
366  (pg*pages) >> 20);
367 
368  if (pages == 0)
369  {
370  return false;
371  }
372 
373 #if SIZEOF_CHARP == 4
374  Uint64 sum = (pg*pages);
375  if (sum >= (Uint64(1) << 32))
376  {
377  g_eventLogger->error("Trying to allocate more that 4Gb with 32-bit binary!!");
378  return false;
379  }
380 #endif
381 
382 #ifdef NDBD_RANDOM_START_PAGE
383 
389  Uint32 max_rand_start = ZONE_LO_BOUND - 1;
390  if (max_rand_start > pages)
391  {
392  max_rand_start -= pages;
393  if (max_rand_start > 0x10000)
394  g_random_start_page_id = 0x10000 + (rand() % (max_rand_start - 0x10000));
395  else if (max_rand_start)
396  g_random_start_page_id = rand() % max_rand_start;
397 
398  assert(Uint64(pages) + Uint64(g_random_start_page_id) <= 0xFFFFFFFF);
399 
400  ndbout_c("using g_random_start_page_id: %u (%.8x)",
401  g_random_start_page_id, g_random_start_page_id);
402  }
403 #endif
404 
408  Uint32 allocated = 0;
409  while (m_unmapped_chunks.size() < MAX_CHUNKS && allocated < pages)
410  {
411  InitChunk chunk;
412  memset(&chunk, 0, sizeof(chunk));
413 
414  if (do_malloc(pages - allocated, &chunk, watchCounter, m_base_page))
415  {
416  if (watchCounter)
417  *watchCounter = 9;
418 
419  m_unmapped_chunks.push_back(chunk);
420  allocated += chunk.m_cnt;
421  }
422  else
423  {
424  break;
425  }
426  }
427 
428  if (allocated < m_resource_limit[0].m_min)
429  {
430  g_eventLogger->
431  error("Unable to alloc min memory from OS: min: %lldMb "
432  " allocated: %lldMb",
433  (Uint64)(sizeof(Alloc_page)*m_resource_limit[0].m_min) >> 20,
434  (Uint64)(sizeof(Alloc_page)*allocated) >> 20);
435  return false;
436  }
437  else if (allocated < pages)
438  {
439  g_eventLogger->
440  warning("Unable to alloc requested memory from OS: min: %lldMb"
441  " requested: %lldMb allocated: %lldMb",
442  (Uint64)(sizeof(Alloc_page)*m_resource_limit[0].m_min)>>20,
443  (Uint64)(sizeof(Alloc_page)*m_resource_limit[0].m_max)>>20,
444  (Uint64)(sizeof(Alloc_page)*allocated)>>20);
445  if (!alloc_less_memory)
446  return false;
447  }
448 
452  qsort(m_unmapped_chunks.getBase(), m_unmapped_chunks.size(),
453  sizeof(InitChunk), cmp_chunk);
454 
455  m_base_page = m_unmapped_chunks[0].m_ptr;
456 
457  for (Uint32 i = 0; i<m_unmapped_chunks.size(); i++)
458  {
459  UintPtr start = UintPtr(m_unmapped_chunks[i].m_ptr) - UintPtr(m_base_page);
460  start >>= (2 + BMW_2LOG);
461  assert((Uint64(start) >> 32) == 0);
462  m_unmapped_chunks[i].m_start = Uint32(start);
463  Uint64 last64 = start + m_unmapped_chunks[i].m_cnt;
464  assert((last64 >> 32) == 0);
465  Uint32 last = Uint32(last64);
466 
467  if (last > max_page)
468  max_page = last;
469  }
470 
471  m_resource_limit[0].m_resource_id = max_page;
472  m_resource_limit[0].m_min = reserved;
473  m_resource_limit[0].m_max = 0;
474 
475  return true;
476 }
477 
478 void
479 Ndbd_mem_manager::map(Uint32 * watchCounter, bool memlock, Uint32 resources[])
480 {
481  Uint32 limit = ~(Uint32)0;
482  Uint32 sofar = 0;
483 
484  if (resources != 0)
485  {
486  limit = 0;
487  for (Uint32 i = 0; resources[i] ; i++)
488  {
489  limit += m_resource_limit[resources[i]].m_min;
490  }
491  }
492 
493  while (m_unmapped_chunks.size() && sofar < limit)
494  {
495  Uint32 remain = limit - sofar;
496 
497  unsigned idx = m_unmapped_chunks.size() - 1;
498  InitChunk * chunk = &m_unmapped_chunks[idx];
499  if (watchCounter)
500  *watchCounter = 9;
501 
502  if (chunk->m_cnt > remain)
503  {
507  Uint32 extra = chunk->m_cnt - remain;
508  chunk->m_cnt = remain;
509 
510  InitChunk newchunk;
511  newchunk.m_start = chunk->m_start + remain;
512  newchunk.m_ptr = m_base_page + newchunk.m_start;
513  newchunk.m_cnt = extra;
514  m_unmapped_chunks.push_back(newchunk);
515 
516  // pointer could have changed after m_unmapped_chunks.push_back
517  chunk = &m_unmapped_chunks[idx];
518  }
519 
520  ndbd_alloc_touch_mem(chunk->m_ptr,
521  chunk->m_cnt * sizeof(Alloc_page),
522  watchCounter);
523 
524  if (memlock)
525  {
529  if (watchCounter)
530  *watchCounter = 9;
531 
536  const Alloc_page * start = chunk->m_ptr;
537  Uint32 cnt = chunk->m_cnt;
538  while (cnt > 32768) // 1G
539  {
540  if (watchCounter)
541  *watchCounter = 9;
542 
543  NdbMem_MemLock(start, 32768 * sizeof(Alloc_page));
544  start += 32768;
545  cnt -= 32768;
546  }
547  if (watchCounter)
548  *watchCounter = 9;
549 
550  NdbMem_MemLock(start, cnt * sizeof(Alloc_page));
551  }
552 
553  grow(chunk->m_start, chunk->m_cnt);
554  sofar += chunk->m_cnt;
555 
556  m_unmapped_chunks.erase(idx);
557  }
558 
559  mt_mem_manager_lock();
560  check_resource_limits(m_resource_limit);
561  mt_mem_manager_unlock();
562 
563  if (resources == 0 && memlock)
564  {
565  NdbMem_MemLockAll(1);
566  }
567 }
568 
569 #include <NdbOut.hpp>
570 
571 void
572 Ndbd_mem_manager::grow(Uint32 start, Uint32 cnt)
573 {
574  assert(cnt);
575  Uint32 start_bmp = start >> BPP_2LOG;
576  Uint32 last_bmp = (start + cnt - 1) >> BPP_2LOG;
577 
578 #if SIZEOF_CHARP == 4
579  assert(start_bmp == 0 && last_bmp == 0);
580 #endif
581 
582  if (start_bmp != last_bmp)
583  {
584  Uint32 tmp = ((start_bmp + 1) << BPP_2LOG) - start;
585  grow(start, tmp);
586  grow((start_bmp + 1) << BPP_2LOG, cnt - tmp);
587  return;
588  }
589 
590  if ((start + cnt) == ((start_bmp + 1) << BPP_2LOG))
591  {
592  cnt--; // last page is always marked as empty
593  }
594 
595  for (Uint32 i = 0; i<m_used_bitmap_pages.size(); i++)
596  if (m_used_bitmap_pages[i] == start_bmp)
597  goto found;
598 
599  if (start != (start_bmp << BPP_2LOG))
600  {
601 
602  ndbout_c("ndbd_malloc_impl.cpp:%d:grow(%d, %d) %d!=%d not using %uMb"
603  " - Unable to use due to bitmap pages missaligned!!",
604  __LINE__, start, cnt, start, (start_bmp << BPP_2LOG),
605  (cnt >> (20 - 15)));
606  g_eventLogger->error("ndbd_malloc_impl.cpp:%d:grow(%d, %d) not using %uMb"
607  " - Unable to use due to bitmap pages missaligned!!",
608  __LINE__, start, cnt,
609  (cnt >> (20 - 15)));
610 
611  dump();
612  return;
613  }
614 
615 #ifdef UNIT_TEST
616  ndbout_c("creating bitmap page %d", start_bmp);
617 #endif
618 
619  {
620  Alloc_page* bmp = m_base_page + start;
621  memset(bmp, 0, sizeof(Alloc_page));
622  cnt--;
623  start++;
624  }
625  m_used_bitmap_pages.push_back(start_bmp);
626 
627 found:
628  if (cnt)
629  {
630  mt_mem_manager_lock();
631  m_resource_limit[0].m_curr += cnt;
632  m_resource_limit[0].m_max += cnt;
633  if (start >= ZONE_LO_BOUND)
634  {
635  Uint64 mbytes = ((Uint64(cnt) * 32) + 1023) / 1024;
636  ndbout_c("Adding %uMb to ZONE_HI (%u,%u)", (Uint32)mbytes, start, cnt);
637  release(start, cnt);
638  }
639  else if (start + cnt <= ZONE_LO_BOUND)
640  {
641  Uint64 mbytes = ((Uint64(cnt)*32) + 1023) / 1024;
642  ndbout_c("Adding %uMb to ZONE_LO (%u,%u)", (Uint32)mbytes, start, cnt);
643  release(start, cnt);
644  }
645  else
646  {
647  Uint32 cnt0 = ZONE_LO_BOUND - start;
648  Uint32 cnt1 = start + cnt - ZONE_LO_BOUND;
649  Uint64 mbytes0 = ((Uint64(cnt0)*32) + 1023) / 1024;
650  Uint64 mbytes1 = ((Uint64(cnt1)*32) + 1023) / 1024;
651  ndbout_c("Adding %uMb to ZONE_LO (split %u,%u)", (Uint32)mbytes0,
652  start, cnt0);
653  ndbout_c("Adding %uMb to ZONE_HI (split %u,%u)", (Uint32)mbytes1,
654  ZONE_LO_BOUND, cnt1);
655  release(start, cnt0);
656  release(ZONE_LO_BOUND, cnt1);
657  }
658  mt_mem_manager_unlock();
659  }
660 }
661 
662 void
663 Ndbd_mem_manager::release(Uint32 start, Uint32 cnt)
664 {
665  assert(m_resource_limit[0].m_curr >= cnt);
666  assert(start);
667  m_resource_limit[0].m_curr -= cnt;
668 
669  set(start, start+cnt-1);
670 
671  Uint32 zone = start < ZONE_LO_BOUND ? 0 : 1;
672  release_impl(zone, start, cnt);
673 }
674 
675 void
676 Ndbd_mem_manager::release_impl(Uint32 zone, Uint32 start, Uint32 cnt)
677 {
678  assert(start);
679 
680  Uint32 test = check(start-1, start+cnt);
681  if (start != ZONE_LO_BOUND && test & 1)
682  {
683  Free_page_data *fd = get_free_page_data(m_base_page + start - 1,
684  start - 1);
685  Uint32 sz = fd->m_size;
686  Uint32 left = start - sz;
687  remove_free_list(zone, left, fd->m_list);
688  cnt += sz;
689  start = left;
690  }
691 
692  Uint32 right = start + cnt;
693  if (right != ZONE_LO_BOUND && test & 2)
694  {
695  Free_page_data *fd = get_free_page_data(m_base_page+right, right);
696  Uint32 sz = fd->m_size;
697  remove_free_list(zone, right, fd->m_list);
698  cnt += sz;
699  }
700 
701  insert_free_list(zone, start, cnt);
702 }
703 
704 void
705 Ndbd_mem_manager::alloc(AllocZone zone,
706  Uint32* ret, Uint32 *pages, Uint32 min)
707 {
708  if (zone == NDB_ZONE_ANY)
709  {
710  Uint32 save = * pages;
711  alloc_impl(ZONE_HI, ret, pages, min);
712  if (*pages)
713  return;
714  * pages = save;
715  }
716 
717  alloc_impl(ZONE_LO, ret, pages, min);
718 }
719 
720 void
721 Ndbd_mem_manager::alloc_impl(Uint32 zone,
722  Uint32* ret, Uint32 *pages, Uint32 min)
723 {
724  Int32 i;
725  Uint32 start;
726  Uint32 cnt = * pages;
727  Uint32 list = ndb_log2(cnt - 1);
728 
729  assert(cnt);
730  assert(list <= 16);
731 
732  for (i = list; i < 16; i++)
733  {
734  if ((start = m_buddy_lists[zone][i]))
735  {
736 /* ---------------------------------------------------------------- */
737 /* PROPER AMOUNT OF PAGES WERE FOUND. NOW SPLIT THE FOUND */
738 /* AREA AND RETURN THE PART NOT NEEDED. */
739 /* ---------------------------------------------------------------- */
740 
741  Uint32 sz = remove_free_list(zone, start, i);
742  Uint32 extra = sz - cnt;
743  assert(sz >= cnt);
744  if (extra)
745  {
746  insert_free_list(zone, start + cnt, extra);
747  clear_and_set(start, start+cnt-1);
748  }
749  else
750  {
751  clear(start, start+cnt-1);
752  }
753  * ret = start;
754  assert(m_resource_limit[0].m_curr + cnt <= m_resource_limit[0].m_max);
755  return;
756  }
757  }
758 
764  Int32 min_list = ndb_log2(min - 1);
765  assert((Int32)list >= min_list);
766  for (i = list - 1; i >= min_list; i--)
767  {
768  if ((start = m_buddy_lists[zone][i]))
769  {
770  Uint32 sz = remove_free_list(zone, start, i);
771  Uint32 extra = sz - cnt;
772  if (sz > cnt)
773  {
774  insert_free_list(zone, start + cnt, extra);
775  sz -= extra;
776  clear_and_set(start, start+sz-1);
777  }
778  else
779  {
780  clear(start, start+sz-1);
781  }
782 
783  * ret = start;
784  * pages = sz;
785  assert(m_resource_limit[0].m_curr + sz <= m_resource_limit[0].m_max);
786  return;
787  }
788  }
789  * pages = 0;
790 }
791 
792 void
793 Ndbd_mem_manager::insert_free_list(Uint32 zone, Uint32 start, Uint32 size)
794 {
795  Uint32 list = ndb_log2(size) - 1;
796  Uint32 last = start + size - 1;
797 
798  Uint32 head = m_buddy_lists[zone][list];
799  Free_page_data* fd_first = get_free_page_data(m_base_page+start,
800  start);
801  fd_first->m_list = list;
802  fd_first->m_next = head;
803  fd_first->m_prev = 0;
804  fd_first->m_size = size;
805 
806  Free_page_data* fd_last = get_free_page_data(m_base_page+last, last);
807  fd_last->m_list = list;
808  fd_last->m_next = head;
809  fd_last->m_prev = 0;
810  fd_last->m_size = size;
811 
812  if (head)
813  {
814  Free_page_data* fd = get_free_page_data(m_base_page+head, head);
815  assert(fd->m_prev == 0);
816  assert(fd->m_list == list);
817  fd->m_prev = start;
818  }
819 
820  m_buddy_lists[zone][list] = start;
821 }
822 
823 Uint32
824 Ndbd_mem_manager::remove_free_list(Uint32 zone, Uint32 start, Uint32 list)
825 {
826  Free_page_data* fd = get_free_page_data(m_base_page+start, start);
827  Uint32 size = fd->m_size;
828  Uint32 next = fd->m_next;
829  Uint32 prev = fd->m_prev;
830  assert(fd->m_list == list);
831 
832  if (prev)
833  {
834  assert(m_buddy_lists[zone][list] != start);
835  fd = get_free_page_data(m_base_page+prev, prev);
836  assert(fd->m_next == start);
837  assert(fd->m_list == list);
838  fd->m_next = next;
839  }
840  else
841  {
842  assert(m_buddy_lists[zone][list] == start);
843  m_buddy_lists[zone][list] = next;
844  }
845 
846  if (next)
847  {
848  fd = get_free_page_data(m_base_page+next, next);
849  assert(fd->m_list == list);
850  assert(fd->m_prev == start);
851  fd->m_prev = prev;
852  }
853 
854  return size;
855 }
856 
857 void
858 Ndbd_mem_manager::dump() const
859 {
860  mt_mem_manager_lock();
861  for (Uint32 zone = 0; zone < 2; zone ++)
862  {
863  for (Uint32 i = 0; i<16; i++)
864  {
865  printf(" list: %d - ", i);
866  Uint32 head = m_buddy_lists[zone][i];
867  while(head)
868  {
869  Free_page_data* fd = get_free_page_data(m_base_page+head, head);
870  printf("[ i: %d prev %d next %d list %d size %d ] ",
871  head, fd->m_prev, fd->m_next, fd->m_list, fd->m_size);
872  head = fd->m_next;
873  }
874  printf("EOL\n");
875  }
876 
877  for (Uint32 i = 0; i<XX_RL_COUNT; i++)
878  {
879  printf("ri: %d min: %d curr: %d max: %d\n",
880  i,
881  m_resource_limit[i].m_min,
882  m_resource_limit[i].m_curr,
883  m_resource_limit[i].m_max);
884  }
885  }
886  mt_mem_manager_unlock();
887 }
888 
889 void*
890 Ndbd_mem_manager::alloc_page(Uint32 type, Uint32* i, AllocZone zone)
891 {
892  Uint32 idx = type & RG_MASK;
893  assert(idx && idx < XX_RL_COUNT);
894  mt_mem_manager_lock();
895  Resource_limit tot = m_resource_limit[0];
896  Resource_limit rl = m_resource_limit[idx];
897 
898  Uint32 cnt = 1;
899  Uint32 res0 = (rl.m_curr < rl.m_min) ? 1 : 0;
900  Uint32 limit = (rl.m_max == 0 || rl.m_curr < rl.m_max) ? 0 : 1; // Over limit
901  Uint32 free = (tot.m_min + tot.m_curr < tot.m_max) ? 1 : 0; // Has free
902 
903  assert(tot.m_min >= res0);
904 
905  if (likely(res0 == 1 || (limit == 0 && free == 1)))
906  {
907  alloc(zone, i, &cnt, 1);
908  if (likely(cnt))
909  {
910  m_resource_limit[0].m_curr = tot.m_curr + cnt;
911  m_resource_limit[0].m_min = tot.m_min - res0;
912  m_resource_limit[idx].m_curr = rl.m_curr + cnt;
913 
914  check_resource_limits(m_resource_limit);
915  mt_mem_manager_unlock();
916 #ifdef NDBD_RANDOM_START_PAGE
917  *i += g_random_start_page_id;
918  return m_base_page + *i - g_random_start_page_id;
919 #else
920  return m_base_page + *i;
921 #endif
922  }
923  }
924  mt_mem_manager_unlock();
925  return 0;
926 }
927 
928 void
929 Ndbd_mem_manager::release_page(Uint32 type, Uint32 i)
930 {
931  Uint32 idx = type & RG_MASK;
932  assert(idx && idx < XX_RL_COUNT);
933  mt_mem_manager_lock();
934  Resource_limit tot = m_resource_limit[0];
935  Resource_limit rl = m_resource_limit[idx];
936 
937 #ifdef NDBD_RANDOM_START_PAGE
938  i -= g_random_start_page_id;
939 #endif
940 
941  Uint32 sub = (rl.m_curr <= rl.m_min) ? 1 : 0; // Over min ?
942  release(i, 1);
943  m_resource_limit[0].m_curr = tot.m_curr - 1;
944  m_resource_limit[0].m_min = tot.m_min + sub;
945  m_resource_limit[idx].m_curr = rl.m_curr - 1;
946 
947  check_resource_limits(m_resource_limit);
948  mt_mem_manager_unlock();
949 }
950 
951 void
952 Ndbd_mem_manager::alloc_pages(Uint32 type, Uint32* i, Uint32 *cnt, Uint32 min)
953 {
954  Uint32 idx = type & RG_MASK;
955  assert(idx && idx < XX_RL_COUNT);
956  mt_mem_manager_lock();
957  Resource_limit tot = m_resource_limit[0];
958  Resource_limit rl = m_resource_limit[idx];
959 
960  Uint32 req = *cnt;
961 
962  Uint32 max = rl.m_max - rl.m_curr;
963  Uint32 res0 = rl.m_min - rl.m_curr;
964  Uint32 free_shared = tot.m_max - (tot.m_min + tot.m_curr);
965 
966  Uint32 res1;
967  if (rl.m_curr + req <= rl.m_min)
968  {
969  // all is reserved...
970  res0 = req;
971  res1 = 0;
972  }
973  else
974  {
975  req = rl.m_max ? max : req;
976  res0 = (rl.m_curr > rl.m_min) ? 0 : res0;
977  res1 = req - res0;
978 
979  if (unlikely(res1 > free_shared))
980  {
981  res1 = free_shared;
982  req = res0 + res1;
983  }
984  }
985 
986  // req = pages to alloc
987  // res0 = portion that is reserved
988  // res1 = part that is over reserver
989  assert (res0 + res1 == req);
990  assert (tot.m_min >= res0);
991 
992  if (likely(req))
993  {
994  // Hi order allocations can always use any zone
995  alloc(NDB_ZONE_ANY, i, &req, min);
996  * cnt = req;
997  if (unlikely(req < res0)) // Got min than what was reserved :-(
998  {
999  res0 = req;
1000  }
1001  assert(tot.m_min >= res0);
1002  assert(tot.m_curr + req <= tot.m_max);
1003 
1004  m_resource_limit[0].m_curr = tot.m_curr + req;
1005  m_resource_limit[0].m_min = tot.m_min - res0;
1006  m_resource_limit[idx].m_curr = rl.m_curr + req;
1007  check_resource_limits(m_resource_limit);
1008  mt_mem_manager_unlock();
1009 #ifdef NDBD_RANDOM_START_PAGE
1010  *i += g_random_start_page_id;
1011 #endif
1012  return ;
1013  }
1014  mt_mem_manager_unlock();
1015  * cnt = req;
1016 #ifdef NDBD_RANDOM_START_PAGE
1017  *i += g_random_start_page_id;
1018 #endif
1019  return;
1020 }
1021 
1022 void
1023 Ndbd_mem_manager::release_pages(Uint32 type, Uint32 i, Uint32 cnt)
1024 {
1025  Uint32 idx = type & RG_MASK;
1026  assert(idx && idx < XX_RL_COUNT);
1027  mt_mem_manager_lock();
1028  Resource_limit tot = m_resource_limit[0];
1029  Resource_limit rl = m_resource_limit[idx];
1030 
1031 #ifdef NDBD_RANDOM_START_PAGE
1032  i -= g_random_start_page_id;
1033 #endif
1034 
1035  release(i, cnt);
1036 
1037  Uint32 currnew = rl.m_curr - cnt;
1038  if (rl.m_curr > rl.m_min)
1039  {
1040  if (currnew < rl.m_min)
1041  {
1042  m_resource_limit[0].m_min = tot.m_min + (rl.m_min - currnew);
1043  }
1044  }
1045  else
1046  {
1047  m_resource_limit[0].m_min = tot.m_min + cnt;
1048  }
1049  m_resource_limit[0].m_curr = tot.m_curr - cnt;
1050  m_resource_limit[idx].m_curr = currnew;
1051  check_resource_limits(m_resource_limit);
1052  mt_mem_manager_unlock();
1053 }
1054 
1055 #ifdef UNIT_TEST
1056 
1057 #include <Vector.hpp>
1058 #include <NdbTick.h>
1059 
1060 struct Chunk {
1061  Uint32 pageId;
1062  Uint32 pageCount;
1063 };
1064 
1065 struct Timer
1066 {
1067  Uint64 sum;
1068  Uint32 cnt;
1069 
1070  Timer() { sum = cnt = 0;}
1071 
1072  struct timeval st;
1073 
1074  void start() {
1075  gettimeofday(&st, 0);
1076  }
1077 
1078  Uint64 calc_diff() {
1079  struct timeval st2;
1080  gettimeofday(&st2, 0);
1081  Uint64 diff = st2.tv_sec;
1082  diff -= st.tv_sec;
1083  diff *= 1000000;
1084  diff += st2.tv_usec;
1085  diff -= st.tv_usec;
1086  return diff;
1087  }
1088 
1089  void stop() {
1090  add(calc_diff());
1091  }
1092 
1093  void add(Uint64 diff) { sum += diff; cnt++;}
1094 
1095  void print(const char * title) const {
1096  float ps = sum;
1097  ps /= cnt;
1098  printf("%s %fus/call %lld %d\n", title, ps, sum, cnt);
1099  }
1100 };
1101 
1102 int
1103 main(int argc, char** argv)
1104 {
1105  int sz = 1*32768;
1106  int run_time = 30;
1107  if (argc > 1)
1108  sz = 32*atoi(argv[1]);
1109 
1110  if (argc > 2)
1111  run_time = atoi(argv[2]);
1112 
1113  char buf[255];
1114  Timer timer[4];
1115  printf("Startar modul test av Page Manager %dMb %ds\n",
1116  (sz >> 5), run_time);
1117  g_eventLogger->createConsoleHandler();
1118  g_eventLogger->setCategory("keso");
1119  g_eventLogger->enable(Logger::LL_ON, Logger::LL_INFO);
1120  g_eventLogger->enable(Logger::LL_ON, Logger::LL_CRITICAL);
1121  g_eventLogger->enable(Logger::LL_ON, Logger::LL_ERROR);
1122  g_eventLogger->enable(Logger::LL_ON, Logger::LL_WARNING);
1123 
1124 #define DEBUG 0
1125 
1126  Ndbd_mem_manager mem;
1127  Resource_limit rl;
1128  rl.m_min = 0;
1129  rl.m_max = sz;
1130  rl.m_curr = 0;
1131  rl.m_resource_id = 0;
1132  mem.set_resource_limit(rl);
1133  rl.m_min = sz < 16384 ? sz : 16384;
1134  rl.m_max = 0;
1135  rl.m_resource_id = 1;
1136  mem.set_resource_limit(rl);
1137 
1138  mem.init(NULL);
1139  mem.dump();
1140  printf("pid: %d press enter to continue\n", getpid());
1141  fgets(buf, sizeof(buf), stdin);
1142  Vector<Chunk> chunks;
1143  time_t stop = time(0) + run_time;
1144  for(Uint32 i = 0; time(0) < stop; i++){
1145  //mem.dump();
1146 
1147  // Case
1148  Uint32 c = (rand() % 100);
1149  if (c < 50)
1150  {
1151  c = 0;
1152  }
1153  else if (c < 93)
1154  {
1155  c = 1;
1156  }
1157  else
1158  {
1159  c = 2;
1160  }
1161 
1162  Uint32 alloc = 1 + rand() % 3200;
1163 
1164  if(chunks.size() == 0 && c == 0)
1165  {
1166  c = 1 + rand() % 2;
1167  }
1168 
1169  if(DEBUG)
1170  printf("loop=%d ", i);
1171  switch(c){
1172  case 0:{ // Release
1173  const int ch = rand() % chunks.size();
1174  Chunk chunk = chunks[ch];
1175  chunks.erase(ch);
1176  timer[0].start();
1177  Uint64 start = NdbTick_CurrentMillisecond();
1178  mem.release(chunk.pageId, chunk.pageCount);
1179  timer[0].stop();
1180  if(DEBUG)
1181  printf(" release %d %d\n", chunk.pageId, chunk.pageCount);
1182  }
1183  break;
1184  case 2: { // Seize(n) - fail
1185  alloc += sz;
1186  // Fall through
1187  }
1188  case 1: { // Seize(n) (success)
1189  Chunk chunk;
1190  chunk.pageCount = alloc;
1191  if (DEBUG)
1192  {
1193  printf(" alloc %d -> ", alloc); fflush(stdout);
1194  }
1195  timer[0].start();
1196  mem.alloc(&chunk.pageId, &chunk.pageCount, 1);
1197  Uint64 diff = timer[0].calc_diff();
1198 
1199  if (DEBUG)
1200  printf("%d %d", chunk.pageId, chunk.pageCount);
1201  assert(chunk.pageCount <= alloc);
1202  if(chunk.pageCount != 0){
1203  chunks.push_back(chunk);
1204  if(chunk.pageCount != alloc) {
1205  timer[2].add(diff);
1206  if (DEBUG)
1207  printf(" - Tried to allocate %d - only allocated %d - free: %d",
1208  alloc, chunk.pageCount, 0);
1209  }
1210  else
1211  {
1212  timer[1].add(diff);
1213  }
1214  } else {
1215  timer[3].add(diff);
1216  if (DEBUG)
1217  printf(" Failed to alloc %d pages with %d pages free",
1218  alloc, 0);
1219  }
1220  if (DEBUG)
1221  printf("\n");
1222  }
1223  break;
1224  }
1225  }
1226  if (!DEBUG)
1227  while(chunks.size() > 0){
1228  Chunk chunk = chunks.back();
1229  mem.release(chunk.pageId, chunk.pageCount);
1230  chunks.erase(chunks.size() - 1);
1231  }
1232 
1233  const char *title[] = {
1234  "release ",
1235  "alloc full",
1236  "alloc part",
1237  "alloc fail"
1238  };
1239  for(Uint32 i = 0; i<4; i++)
1240  timer[i].print(title[i]);
1241 
1242  mem.dump();
1243 }
1244 
1245 template class Vector<Chunk>;
1246 
1247 #endif
1248 
1249 template class Vector<InitChunk>;