00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #if !defined(q_malloc) && !(defined F_MALLOC)
00039 #define q_malloc
00040
00041 #include <stdlib.h>
00042 #include <string.h>
00043
00044 #include "q_malloc.h"
00045 #include "../dprint.h"
00046 #include "../globals.h"
00047 #include "../statistics.h"
00048
00049
00050
00051 #define FRAG_END(f) \
00052 ((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
00053 (f)->size))
00054
00055 #define FRAG_NEXT(f) \
00056 ((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
00057 sizeof(struct qm_frag_end)))
00058
00059 #define FRAG_PREV(f) \
00060 ( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
00061 ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
00062 sizeof(struct qm_frag) ) )
00063
00064 #define PREV_FRAG_END(f) \
00065 ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
00066
00067
00068 #define FRAG_OVERHEAD (sizeof(struct qm_frag)+sizeof(struct qm_frag_end))
00069
00070
00071 #define ROUNDTO_MASK (~((unsigned long)ROUNDTO-1))
00072 #define ROUNDUP(s) (((s)+(ROUNDTO-1))&ROUNDTO_MASK)
00073 #define ROUNDDOWN(s) ((s)&ROUNDTO_MASK)
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 #define GET_HASH(s) ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\
00084 (unsigned long)(s)/ROUNDTO: \
00085 QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
00086 QM_MALLOC_OPTIMIZE_FACTOR+1 )
00087
00088 #define UN_HASH(h) ( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\
00089 (unsigned long)(h)*ROUNDTO: \
00090 1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\
00091 QM_MALLOC_OPTIMIZE_FACTOR-1)\
00092 )
00093
00094
00095
00096 #define FRAG_MARK_USED(f)
00097 #define FRAG_CLEAR_USED(f)
00098 #define FRAG_WAS_USED(f) (1)
00099
00100
00101
00102
00103
00104
00105 #define MEM_FRAG_AVOIDANCE
00106
00107
00108
00109
00110 inline static unsigned long big_hash_idx(unsigned long s)
00111 {
00112 int idx;
00113
00114
00115
00116
00117 idx=sizeof(long)*8-1;
00118 for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
00119 return idx;
00120 }
00121
00122
00123 #ifdef DBG_QM_MALLOC
00124 #define ST_CHECK_PATTERN 0xf0f0f0f0
00125 #define END_CHECK_PATTERN1 0xc0c0c0c0
00126 #define END_CHECK_PATTERN2 0xabcdefed
00127
00128
00129 static void qm_debug_frag(struct qm_block* qm, struct qm_frag* f)
00130 {
00131 if (f->check!=ST_CHECK_PATTERN){
00132 LM_CRIT("qm_*: fragm. %p (address %p) "
00133 "beginning overwritten(%lx)!\n",
00134 f, (char*)f+sizeof(struct qm_frag),
00135 f->check);
00136 qm_status(qm);
00137 abort();
00138 };
00139 if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)||
00140 (FRAG_END(f)->check2!=END_CHECK_PATTERN2)){
00141 LM_CRIT("qm_*: fragm. %p (address %p)"
00142 " end overwritten(%lx, %lx)!\n",
00143 f, (char*)f+sizeof(struct qm_frag),
00144 FRAG_END(f)->check1, FRAG_END(f)->check2);
00145 qm_status(qm);
00146 abort();
00147 }
00148 if ((f>qm->first_frag)&&
00149 ((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) ||
00150 (PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){
00151 LM_CRIT(" qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p]!\n",
00152 PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f,
00153 (char*)f+sizeof(struct qm_frag));
00154 qm_status(qm);
00155 abort();
00156 }
00157 }
00158 #endif
00159
00160
00161
00162 static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag)
00163 {
00164 struct qm_frag* f;
00165 struct qm_frag* prev;
00166 int hash;
00167
00168 hash=GET_HASH(frag->size);
00169 for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head);
00170 f=f->u.nxt_free){
00171 if (frag->size <= f->size) break;
00172 }
00173
00174 prev=FRAG_END(f)->prev_free;
00175 prev->u.nxt_free=frag;
00176 FRAG_END(frag)->prev_free=prev;
00177 frag->u.nxt_free=f;
00178 FRAG_END(f)->prev_free=frag;
00179 qm->free_hash[hash].no++;
00180 }
00181
00182
00183
00184
00185 struct qm_block* qm_malloc_init(char* address, unsigned long size)
00186 {
00187 char* start;
00188 char* end;
00189 struct qm_block* qm;
00190 unsigned long init_overhead;
00191 int h;
00192
00193
00194 start=(char*)ROUNDUP((unsigned long) address);
00195 LM_DBG("QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
00196 QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO);
00197 LM_DBG("QM_HASH_SIZE=%lu, qm_block size=%lu\n",
00198 QM_HASH_SIZE, (long)sizeof(struct qm_block));
00199 LM_DBG("params (%p, %lu), start=%p\n", address, size, start);
00200 if (size<start-address) return 0;
00201 size-=(start-address);
00202 if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
00203 size=ROUNDDOWN(size);
00204
00205 init_overhead=ROUNDUP(sizeof(struct qm_block))+sizeof(struct qm_frag)+
00206 sizeof(struct qm_frag_end);
00207 LM_DBG("size= %lu, init_overhead=%lu\n", size, init_overhead);
00208
00209 if (size < init_overhead)
00210 {
00211
00212 return 0;
00213 }
00214 end=start+size;
00215 qm=(struct qm_block*)start;
00216 memset(qm, 0, sizeof(struct qm_block));
00217 qm->size=size;
00218 qm->real_used=init_overhead;
00219 qm->max_real_used=qm->real_used;
00220 size-=init_overhead;
00221
00222 qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block)));
00223 qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end));
00224
00225 qm->first_frag->size=size;
00226 qm->last_frag_end->size=size;
00227
00228 #ifdef DBG_QM_MALLOC
00229 qm->first_frag->check=ST_CHECK_PATTERN;
00230 qm->last_frag_end->check1=END_CHECK_PATTERN1;
00231 qm->last_frag_end->check2=END_CHECK_PATTERN2;
00232 #endif
00233
00234 for (h=0; h<QM_HASH_SIZE;h++){
00235 qm->free_hash[h].head.u.nxt_free=&(qm->free_hash[h].head);
00236 qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head);
00237 qm->free_hash[h].head.size=0;
00238 qm->free_hash[h].tail.size=0;
00239 }
00240
00241
00242
00243 qm_insert_free(qm, qm->first_frag);
00244
00245
00246
00247
00248
00249
00250 return qm;
00251 }
00252
00253
00254
00255 static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag)
00256 {
00257 struct qm_frag *prev;
00258 struct qm_frag *next;
00259
00260 prev=FRAG_END(frag)->prev_free;
00261 next=frag->u.nxt_free;
00262 prev->u.nxt_free=next;
00263 FRAG_END(next)->prev_free=prev;
00264
00265 }
00266
00267
00268
00269 #ifdef DBG_QM_MALLOC
00270 static inline struct qm_frag* qm_find_free(struct qm_block* qm,
00271 unsigned long size,
00272 int *h,
00273 unsigned int *count)
00274 #else
00275 static inline struct qm_frag* qm_find_free(struct qm_block* qm,
00276 unsigned long size,
00277 int* h)
00278 #endif
00279 {
00280 int hash;
00281 struct qm_frag* f;
00282
00283 for (hash=GET_HASH(size); hash<QM_HASH_SIZE; hash++){
00284 for (f=qm->free_hash[hash].head.u.nxt_free;
00285 f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){
00286 #ifdef DBG_QM_MALLOC
00287 *count+=1;
00288 #endif
00289 if (f->size>=size){ *h=hash; return f; }
00290 }
00291
00292 }
00293
00294 return 0;
00295 }
00296
00297
00298
00299
00300 static inline
00301 #ifdef DBG_QM_MALLOC
00302 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size,
00303 const char* file, const char* func, unsigned int line)
00304 #else
00305 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size)
00306 #endif
00307 {
00308 unsigned long rest;
00309 struct qm_frag* n;
00310 struct qm_frag_end* end;
00311
00312 rest=f->size-new_size;
00313 #ifdef MEM_FRAG_AVOIDANCE
00314 if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))||
00315 (rest>=(FRAG_OVERHEAD+new_size))){
00316 #else
00317 if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
00318 #endif
00319 f->size=new_size;
00320
00321 end=FRAG_END(f);
00322 end->size=new_size;
00323 n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end));
00324 n->size=rest-FRAG_OVERHEAD;
00325 FRAG_END(n)->size=n->size;
00326 FRAG_CLEAR_USED(n);
00327 qm->real_used+=FRAG_OVERHEAD;
00328 #ifdef DBG_QM_MALLOC
00329 end->check1=END_CHECK_PATTERN1;
00330 end->check2=END_CHECK_PATTERN2;
00331
00332 n->file=file;
00333 n->func=func;
00334 n->line=line;
00335 n->check=ST_CHECK_PATTERN;
00336 #endif
00337
00338 qm_insert_free(qm, n);
00339 return 0;
00340 }else{
00341
00342 return -1;
00343 }
00344 }
00345
00346
00347
00348 #ifdef DBG_QM_MALLOC
00349 void* qm_malloc(struct qm_block* qm, unsigned long size,
00350 const char* file, const char* func, unsigned int line)
00351 #else
00352 void* qm_malloc(struct qm_block* qm, unsigned long size)
00353 #endif
00354 {
00355 struct qm_frag* f;
00356 int hash;
00357
00358 #ifdef DBG_QM_MALLOC
00359 unsigned int list_cntr;
00360
00361 list_cntr = 0;
00362 LM_GEN1( memlog, "params (%p, %lu), called from %s: %s(%d)\n",
00363 qm, size, file, func, line);
00364 #endif
00365
00366 size=ROUNDUP(size);
00367 if (size>(qm->size-qm->real_used)) return 0;
00368
00369
00370 #ifdef DBG_QM_MALLOC
00371 if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
00372 #else
00373 if ((f=qm_find_free(qm, size, &hash))!=0){
00374 #endif
00375
00376
00377 #ifdef DBG_QM_MALLOC
00378 qm_debug_frag(qm, f);
00379 #endif
00380 qm_detach_free(qm, f);
00381
00382 f->u.is_free=0;
00383 qm->free_hash[hash].no--;
00384
00385 #ifdef DBG_QM_MALLOC
00386 split_frag(qm, f, size, file, "fragm. from qm_malloc", line);
00387 #else
00388 split_frag(qm, f, size);
00389 #endif
00390 qm->real_used+=f->size;
00391 qm->used+=f->size;
00392 if (qm->max_real_used<qm->real_used)
00393 qm->max_real_used=qm->real_used;
00394 #ifdef DBG_QM_MALLOC
00395 f->file=file;
00396 f->func=func;
00397 f->line=line;
00398 f->check=ST_CHECK_PATTERN;
00399
00400
00401 LM_GEN1( memlog, "params (%p, %lu), returns address %p frag. %p "
00402 "(size=%lu) on %d -th hit\n",
00403 qm, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr );
00404 #endif
00405 return (char*)f+sizeof(struct qm_frag);
00406 }
00407 return 0;
00408 }
00409
00410
00411
00412 #ifdef DBG_QM_MALLOC
00413 void qm_free(struct qm_block* qm, void* p, const char* file, const char* func,
00414 unsigned int line)
00415 #else
00416 void qm_free(struct qm_block* qm, void* p)
00417 #endif
00418 {
00419 struct qm_frag* f;
00420 struct qm_frag* prev;
00421 struct qm_frag* next;
00422 unsigned long size;
00423
00424 #ifdef DBG_QM_MALLOC
00425 LM_GEN1( memlog, "params(%p, %p), called from %s: %s(%d)\n",
00426 qm, p, file, func, line);
00427 if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){
00428 LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p);
00429 abort();
00430 }
00431 #endif
00432 if (p==0) {
00433 LM_WARN("free(0) called\n");
00434 return;
00435 }
00436 prev=next=0;
00437 f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
00438 #ifdef DBG_QM_MALLOC
00439 qm_debug_frag(qm, f);
00440 if (f->u.is_free){
00441 LM_CRIT("freeing already freed pointer,"
00442 " first free: %s: %s(%ld) - aborting\n",
00443 f->file, f->func, f->line);
00444 abort();
00445 }
00446 LM_GEN1( memlog, "freeing frag. %p alloc'ed from %s: %s(%ld)\n",
00447 f, f->file, f->func, f->line);
00448 #endif
00449 size=f->size;
00450 qm->used-=size;
00451 qm->real_used-=size;
00452
00453 #ifdef QM_JOIN_FREE
00454
00455 next=FRAG_NEXT(f);
00456 if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){
00457
00458 #ifdef DBG_QM_MALLOC
00459 qm_debug_frag(qm, next);
00460 #endif
00461 qm_detach_free(qm, next);
00462 size+=next->size+FRAG_OVERHEAD;
00463 qm->real_used-=FRAG_OVERHEAD;
00464 qm->free_hash[GET_HASH(next->size)].no--;
00465 }
00466
00467 if (f > qm->first_frag){
00468 prev=FRAG_PREV(f);
00469
00470
00471 #ifdef DBG_QM_MALLOC
00472 qm_debug_frag(qm, prev);
00473 #endif
00474 if (prev->u.is_free){
00475
00476 qm_detach_free(qm, prev);
00477 size+=prev->size+FRAG_OVERHEAD;
00478 qm->real_used-=FRAG_OVERHEAD;
00479 qm->free_hash[GET_HASH(prev->size)].no--;
00480 f=prev;
00481 }
00482 }
00483 f->size=size;
00484 FRAG_END(f)->size=f->size;
00485 #endif
00486 #ifdef DBG_QM_MALLOC
00487 f->file=file;
00488 f->func=func;
00489 f->line=line;
00490 #endif
00491 qm_insert_free(qm, f);
00492 }
00493
00494
00495
00496 #ifdef DBG_QM_MALLOC
00497 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size,
00498 const char* file, const char* func, unsigned int line)
00499 #else
00500 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size)
00501 #endif
00502 {
00503 struct qm_frag* f;
00504 unsigned long diff;
00505 unsigned long orig_size;
00506 struct qm_frag* n;
00507 void* ptr;
00508
00509
00510 #ifdef DBG_QM_MALLOC
00511 LM_GEN1( memlog, "params (%p, %p, %lu), called from %s: %s(%d)\n",
00512 qm, p, size, file, func, line);
00513 if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){
00514 LM_CRIT("bad pointer %p (out of memory block!) - aborting\n", p);
00515 abort();
00516 }
00517 #endif
00518
00519 if (size==0) {
00520 if (p)
00521 #ifdef DBG_QM_MALLOC
00522 qm_free(qm, p, file, func, line);
00523 #else
00524 qm_free(qm, p);
00525 #endif
00526 return 0;
00527 }
00528 if (p==0)
00529 #ifdef DBG_QM_MALLOC
00530 return qm_malloc(qm, size, file, func, line);
00531 #else
00532 return qm_malloc(qm, size);
00533 #endif
00534 f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
00535 #ifdef DBG_QM_MALLOC
00536 qm_debug_frag(qm, f);
00537 LM_GEN1( memlog, "realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
00538 f, f->file, f->func, f->line);
00539 if (f->u.is_free){
00540 LM_CRIT("trying to realloc an already freed "
00541 "pointer %p , fragment %p -- aborting\n", p, f);
00542 abort();
00543 }
00544 #endif
00545
00546 size=ROUNDUP(size);
00547 if (f->size > size){
00548 orig_size=f->size;
00549
00550 #ifdef DBG_QM_MALLOC
00551 LM_GEN1(memlog,"shrinking from %lu to %lu\n", f->size, size);
00552 if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){
00553 LM_GEN1(memlog,"shrinked successful\n");
00554 #else
00555 if(split_frag(qm, f, size)!=0){
00556 #endif
00557
00558 qm->real_used-=(orig_size-f->size-FRAG_OVERHEAD);
00559 qm->used-=(orig_size-f->size);
00560 }
00561
00562 }else if (f->size < size){
00563
00564 #ifdef DBG_QM_MALLOC
00565 LM_GEN1( memlog, "growing from %lu to %lu\n", f->size, size);
00566 #endif
00567 orig_size=f->size;
00568 diff=size-f->size;
00569 n=FRAG_NEXT(f);
00570 if (((char*)n < (char*)qm->last_frag_end) &&
00571 (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
00572
00573 qm_detach_free(qm, n);
00574 qm->free_hash[GET_HASH(n->size)].no--;
00575 f->size+=n->size+FRAG_OVERHEAD;
00576 qm->real_used-=FRAG_OVERHEAD;
00577 FRAG_END(f)->size=f->size;
00578
00579
00580 if (f->size > size ){
00581 #ifdef DBG_QM_MALLOC
00582 split_frag(qm, f, size, file, "fragm. from qm_realloc",
00583 line);
00584 #else
00585 split_frag(qm, f, size);
00586 #endif
00587 }
00588 qm->real_used+=(f->size-orig_size);
00589 qm->used+=(f->size-orig_size);
00590 }else{
00591
00592 #ifdef DBG_QM_MALLOC
00593 ptr=qm_malloc(qm, size, file, func, line);
00594 #else
00595 ptr=qm_malloc(qm, size);
00596 #endif
00597 if (ptr) {
00598
00599 memcpy(ptr, p, orig_size);
00600 #ifdef DBG_QM_MALLOC
00601 qm_free(qm, p, file, func, line);
00602 #else
00603 qm_free(qm, p);
00604 #endif
00605 }
00606 p=ptr;
00607 }
00608 }else{
00609
00610 #ifdef DBG_QM_MALLOC
00611 LM_GEN1(memlog,"doing nothing, same size: %lu - %lu\n", f->size, size);
00612 #endif
00613 }
00614 #ifdef DBG_QM_MALLOC
00615 LM_GEN1(memlog,"returning %p\n", p);
00616 #endif
00617 return p;
00618 }
00619
00620
00621
00622
00623 void qm_status(struct qm_block* qm)
00624 {
00625 struct qm_frag* f;
00626 int i,j;
00627 int h;
00628 int unused;
00629
00630 LM_GEN1(memlog, "qm_status (%p):\n", qm);
00631 if (!qm) return;
00632
00633 LM_GEN1(memlog, " heap size= %lu\n", qm->size);
00634 LM_GEN1(memlog, " used= %lu, used+overhead=%lu, free=%lu\n",
00635 qm->used, qm->real_used, qm->size-qm->real_used);
00636 LM_GEN1(memlog, " max used (+overhead)= %lu\n", qm->max_real_used);
00637
00638 LM_GEN1(memlog, "dumping all alloc'ed. fragments:\n");
00639 for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;f=FRAG_NEXT(f)
00640 ,i++){
00641 if (! f->u.is_free){
00642 LM_GEN1(memlog," %3d. %c address=%p frag=%p size=%lu used=%d\n",
00643 i,
00644 (f->u.is_free)?'a':'N',
00645 (char*)f+sizeof(struct qm_frag), f, f->size, FRAG_WAS_USED(f));
00646 #ifdef DBG_QM_MALLOC
00647 LM_GEN1(memlog, " %s from %s: %s(%ld)\n",
00648 (f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
00649 LM_GEN1(memlog, " start check=%lx, end check= %lx, %lx\n",
00650 f->check, FRAG_END(f)->check1, FRAG_END(f)->check2);
00651 #endif
00652 }
00653 }
00654 LM_GEN1(memlog, "dumping free list stats :\n");
00655 for(h=0,i=0;h<QM_HASH_SIZE;h++){
00656 unused=0;
00657 for (f=qm->free_hash[h].head.u.nxt_free,j=0;
00658 f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){
00659 if (!FRAG_WAS_USED(f)){
00660 unused++;
00661 #ifdef DBG_QM_MALLOC
00662 LM_GEN1(memlog, "unused fragm.: hash = %3d, fragment %p,"
00663 " address %p size %lu, created from %s: %s(%lu)\n",
00664 h, f, (char*)f+sizeof(struct qm_frag), f->size,
00665 f->file, f->func, f->line);
00666 #endif
00667 }
00668 }
00669
00670 if (j) LM_GEN1(memlog, "hash= %3d. fragments no.: %5d, unused: %5d\n"
00671 "\t\t bucket size: %9lu - %9ld (first %9lu)\n",
00672 h, j, unused, UN_HASH(h),
00673 ((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h),
00674 qm->free_hash[h].head.u.nxt_free->size
00675 );
00676 if (j!=qm->free_hash[h].no){
00677 LM_CRIT("different free frag. count: %d!=%lu"
00678 " for hash %3d\n", j, qm->free_hash[h].no, h);
00679 }
00680
00681 }
00682 LM_GEN1(memlog, "-----------------------------\n");
00683 }
00684
00685
00686
00687
00688 void qm_info(struct qm_block* qm, struct mem_info* info)
00689 {
00690 int r;
00691 long total_frags;
00692
00693 total_frags=0;
00694 memset(info,0, sizeof(*info));
00695 info->total_size=qm->size;
00696 info->min_frag=MIN_FRAG_SIZE;
00697 info->free=qm->size-qm->real_used;
00698 info->used=qm->used;
00699 info->real_used=qm->real_used;
00700 info->max_used=qm->max_real_used;
00701 for(r=0;r<QM_HASH_SIZE; r++){
00702 total_frags+=qm->free_hash[r].no;
00703 }
00704 info->total_frags=total_frags;
00705 }
00706
00707
00708
00709 #endif