summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortdeval <>2005-07-07 05:28:53 +0000
committertdeval <>2005-07-07 05:28:53 +0000
commit50b5ffa76644cb9dbead0b4113a3f540cfce026a (patch)
tree5b5abe06a5605319b7f294459e43717a9484af81 /src
parent39bb14827920f2df6ef79ef2646a815ff17ff665 (diff)
downloadopenbsd-50b5ffa76644cb9dbead0b4113a3f540cfce026a.tar.gz
openbsd-50b5ffa76644cb9dbead0b4113a3f540cfce026a.tar.bz2
openbsd-50b5ffa76644cb9dbead0b4113a3f540cfce026a.zip
Fix the unmapping of freed pages, leaving just 64k worth of cache pages.
Prodded by art@ and fgsch@, ok deraadt@
Diffstat (limited to 'src')
-rw-r--r--src/lib/libc/stdlib/malloc.c194
1 files changed, 136 insertions, 58 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c
index e3405df39a..5a328b0eb0 100644
--- a/src/lib/libc/stdlib/malloc.c
+++ b/src/lib/libc/stdlib/malloc.c
@@ -8,7 +8,7 @@
8 */ 8 */
9 9
10#if defined(LIBC_SCCS) && !defined(lint) 10#if defined(LIBC_SCCS) && !defined(lint)
11static char rcsid[] = "$OpenBSD: malloc.c,v 1.74 2005/06/07 04:42:42 tedu Exp $"; 11static char rcsid[] = "$OpenBSD: malloc.c,v 1.75 2005/07/07 05:28:53 tdeval Exp $";
12#endif /* LIBC_SCCS and not lint */ 12#endif /* LIBC_SCCS and not lint */
13 13
14/* 14/*
@@ -142,8 +142,9 @@ struct pgfree {
142/* A mask for the offset inside a page. */ 142/* A mask for the offset inside a page. */
143#define malloc_pagemask ((malloc_pagesize)-1) 143#define malloc_pagemask ((malloc_pagesize)-1)
144 144
145#define pageround(foo) (((foo) + (malloc_pagemask)) & ~malloc_pagemask) 145#define pageround(foo) (((foo) + (malloc_pagemask)) & ~malloc_pagemask)
146#define ptr2index(foo) (((u_long)(foo) >> malloc_pageshift)+malloc_pageshift) 146#define ptr2index(foo) (((u_long)(foo) >> malloc_pageshift)+malloc_pageshift)
147#define index2ptr(idx) ((void*)(((idx)-malloc_pageshift)<<malloc_pageshift))
147 148
148/* fd of /dev/zero */ 149/* fd of /dev/zero */
149#ifdef USE_DEV_ZERO 150#ifdef USE_DEV_ZERO
@@ -211,6 +212,7 @@ static int malloc_freeprot;
211 212
212/* use guard pages after allocations? */ 213/* use guard pages after allocations? */
213static int malloc_guard = 0; 214static int malloc_guard = 0;
215static int malloc_guarded;
214/* align pointers to end of page? */ 216/* align pointers to end of page? */
215static int malloc_ptrguard; 217static int malloc_ptrguard;
216 218
@@ -396,6 +398,7 @@ malloc_dump(FILE *fd)
396 fprintf(fd, "Pagesize\t%lu\n", (u_long)malloc_pagesize); 398 fprintf(fd, "Pagesize\t%lu\n", (u_long)malloc_pagesize);
397 fprintf(fd, "Pageshift\t%d\n", malloc_pageshift); 399 fprintf(fd, "Pageshift\t%d\n", malloc_pageshift);
398 fprintf(fd, "In use\t%lu\n", (u_long)malloc_used); 400 fprintf(fd, "In use\t%lu\n", (u_long)malloc_used);
401 fprintf(fd, "Guarded\t%lu\n", (u_long)malloc_guarded);
399} 402}
400#endif /* MALLOC_STATS */ 403#endif /* MALLOC_STATS */
401 404
@@ -472,9 +475,9 @@ map_pages(size_t pages)
472{ 475{
473 struct pdinfo *pi, *spi; 476 struct pdinfo *pi, *spi;
474 struct pginfo **pd; 477 struct pginfo **pd;
475 u_long pidx,lidx; 478 u_long idx, pidx, lidx;
476 void *result, *tail; 479 void *result, *tail;
477 u_long index; 480 u_long index, lindex;
478 481
479 pages <<= malloc_pageshift; 482 pages <<= malloc_pageshift;
480 result = MMAP(pages + malloc_guard); 483 result = MMAP(pages + malloc_guard);
@@ -485,23 +488,25 @@ map_pages(size_t pages)
485#endif /* MALLOC_EXTRA_SANITY */ 488#endif /* MALLOC_EXTRA_SANITY */
486 return (NULL); 489 return (NULL);
487 } 490 }
491 index = ptr2index(result);
488 tail = result + pages + malloc_guard; 492 tail = result + pages + malloc_guard;
493 lindex = ptr2index(tail) - 1;
489 if (malloc_guard) 494 if (malloc_guard)
490 mprotect(result + pages, malloc_guard, PROT_NONE); 495 mprotect(result + pages, malloc_guard, PROT_NONE);
491 496
492 if (tail > malloc_brk) 497 pidx = PI_IDX(index);
498 lidx = PI_IDX(lindex);
499
500 if (tail > malloc_brk) {
493 malloc_brk = tail; 501 malloc_brk = tail;
494 if ((index = ptr2index(tail) - 1) > last_index) 502 last_index = lindex;
495 last_index = index; 503 }
496 504
497 /* Insert directory pages, if needed. */ 505 /* Insert directory pages, if needed. */
498 pidx = PI_IDX(ptr2index(result)); 506 pdir_lookup(index, &pi);
499 lidx = PI_IDX(index);
500
501 pdir_lookup(ptr2index(result), &pi);
502 507
503 for (index=pidx,spi=pi;index<=lidx;index++) { 508 for (idx=pidx,spi=pi;idx<=lidx;idx++) {
504 if (pi == NULL || PD_IDX(pi->dirnum) != index) { 509 if (pi == NULL || PD_IDX(pi->dirnum) != idx) {
505 if ((pd = MMAP(malloc_pagesize)) == MAP_FAILED) { 510 if ((pd = MMAP(malloc_pagesize)) == MAP_FAILED) {
506 errno = ENOMEM; 511 errno = ENOMEM;
507 munmap(result, tail - result); 512 munmap(result, tail - result);
@@ -515,31 +520,31 @@ map_pages(size_t pages)
515 pi->base = pd; 520 pi->base = pd;
516 pi->prev = spi; 521 pi->prev = spi;
517 pi->next = spi->next; 522 pi->next = spi->next;
518 pi->dirnum = index * (malloc_pagesize/sizeof(struct pginfo *)); 523 pi->dirnum = idx * (malloc_pagesize/sizeof(struct pginfo *));
519 524
520 if (spi->next != NULL) 525 if (spi->next != NULL)
521 spi->next->prev = pi; 526 spi->next->prev = pi;
522 spi->next = pi; 527 spi->next = pi;
523 } 528 }
524 if (index > pidx && index < lidx) { 529 if (idx > pidx && idx < lidx) {
525 pi->dirnum += pdi_mod; 530 pi->dirnum += pdi_mod;
526 } else if (index == pidx) { 531 } else if (idx == pidx) {
527 if (pidx == lidx) { 532 if (pidx == lidx) {
528 pi->dirnum += (tail - result) >> malloc_pageshift; 533 pi->dirnum += (tail - result) >> malloc_pageshift;
529 } else { 534 } else {
530 pi->dirnum += pdi_mod - PI_OFF(ptr2index(result)); 535 pi->dirnum += pdi_mod - PI_OFF(index);
531 } 536 }
532 } else { 537 } else {
533 pi->dirnum += PI_OFF(ptr2index(tail - 1)) + 1; 538 pi->dirnum += PI_OFF(ptr2index(tail - 1)) + 1;
534 } 539 }
535#ifdef MALLOC_EXTRA_SANITY 540#ifdef MALLOC_EXTRA_SANITY
536 if (PD_OFF(pi->dirnum) > pdi_mod || PD_IDX(pi->dirnum) > index) { 541 if (PD_OFF(pi->dirnum) > pdi_mod || PD_IDX(pi->dirnum) > idx) {
537 wrterror("(ES): pages directory overflow\n"); 542 wrterror("(ES): pages directory overflow\n");
538 errno = EFAULT; 543 errno = EFAULT;
539 return (NULL); 544 return (NULL);
540 } 545 }
541#endif /* MALLOC_EXTRA_SANITY */ 546#endif /* MALLOC_EXTRA_SANITY */
542 if (index == pidx && pi != last_dir) { 547 if (idx == pidx && pi != last_dir) {
543 prev_dir = last_dir; 548 prev_dir = last_dir;
544 last_dir = pi; 549 last_dir = pi;
545 } 550 }
@@ -852,6 +857,7 @@ malloc_pages(size_t size)
852 } 857 }
853 858
854 malloc_used += size << malloc_pageshift; 859 malloc_used += size << malloc_pageshift;
860 malloc_guarded += malloc_guard;
855 861
856 if (malloc_junk) 862 if (malloc_junk)
857 memset(p, SOME_JUNK, size << malloc_pageshift); 863 memset(p, SOME_JUNK, size << malloc_pageshift);
@@ -1065,11 +1071,11 @@ malloc_bytes(size_t size)
1065} 1071}
1066 1072
1067/* 1073/*
1068 * magic so that malloc(sizeof(ptr)) is near the end of the page. 1074 * Magic so that malloc(sizeof(ptr)) is near the end of the page.
1069 */ 1075 */
1070#define PTR_GAP (malloc_pagesize - sizeof(void *)) 1076#define PTR_GAP (malloc_pagesize - sizeof(void *))
1071#define PTR_SIZE (sizeof(void *)) 1077#define PTR_SIZE (sizeof(void *))
1072#define PTR_ALIGNED(p) (((unsigned long)p & malloc_pagemask) == PTR_GAP) 1078#define PTR_ALIGNED(p) (((unsigned long)p & malloc_pagemask) == PTR_GAP)
1073 1079
1074/* 1080/*
1075 * Allocate a piece of memory 1081 * Allocate a piece of memory
@@ -1087,8 +1093,8 @@ imalloc(size_t size)
1087 abort(); 1093 abort();
1088 1094
1089 if (malloc_ptrguard && size == PTR_SIZE) { 1095 if (malloc_ptrguard && size == PTR_SIZE) {
1090 ptralloc = 1; 1096 ptralloc = 1;
1091 size = malloc_pagesize; 1097 size = malloc_pagesize;
1092 } 1098 }
1093 1099
1094 if ((size + malloc_pagesize) < size) { /* Check for overflow */ 1100 if ((size + malloc_pagesize) < size) { /* Check for overflow */
@@ -1107,7 +1113,7 @@ imalloc(size_t size)
1107 memset(result, 0, size); 1113 memset(result, 0, size);
1108 1114
1109 if (result && ptralloc) 1115 if (result && ptralloc)
1110 return ((char *)result + PTR_GAP); 1116 return ((char *)result + PTR_GAP);
1111 return (result); 1117 return (result);
1112} 1118}
1113 1119
@@ -1133,18 +1139,17 @@ irealloc(void *ptr, size_t size)
1133 } 1139 }
1134 1140
1135 if (malloc_ptrguard && PTR_ALIGNED(ptr)) { 1141 if (malloc_ptrguard && PTR_ALIGNED(ptr)) {
1136 if (size <= PTR_SIZE) 1142 if (size <= PTR_SIZE) {
1137 return (ptr); 1143 return (ptr);
1138 else { 1144 } else {
1139 p = imalloc(size); 1145 p = imalloc(size);
1140 if (p) 1146 if (p)
1141 memcpy(p, ptr, PTR_SIZE); 1147 memcpy(p, ptr, PTR_SIZE);
1142 ifree(ptr); 1148 ifree(ptr);
1143 return (p); 1149 return (p);
1144 } 1150 }
1145 } 1151 }
1146 1152
1147
1148 index = ptr2index(ptr); 1153 index = ptr2index(ptr);
1149 1154
1150 if (index < malloc_pageshift) { 1155 if (index < malloc_pageshift) {
@@ -1240,7 +1245,7 @@ irealloc(void *ptr, size_t size)
1240 } 1245 }
1241 1246
1242 } else { 1247 } else {
1243 wrtwarning("pointer to wrong page\n"); 1248 wrtwarning("irealloc: pointer to wrong page\n");
1244 return (NULL); 1249 return (NULL);
1245 } 1250 }
1246 1251
@@ -1268,7 +1273,7 @@ irealloc(void *ptr, size_t size)
1268static __inline__ void 1273static __inline__ void
1269free_pages(void *ptr, u_long index, struct pginfo *info) 1274free_pages(void *ptr, u_long index, struct pginfo *info)
1270{ 1275{
1271 u_long i, l; 1276 u_long i, l, cachesize = 0;
1272 struct pginfo **pd; 1277 struct pginfo **pd;
1273 struct pdinfo *pi, *spi; 1278 struct pdinfo *pi, *spi;
1274 u_long pidx, lidx; 1279 u_long pidx, lidx;
@@ -1281,7 +1286,7 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1281 } 1286 }
1282 1287
1283 if (info != MALLOC_FIRST) { 1288 if (info != MALLOC_FIRST) {
1284 wrtwarning("pointer to wrong page\n"); 1289 wrtwarning("free_pages: pointer to wrong page\n");
1285 return; 1290 return;
1286 } 1291 }
1287 1292
@@ -1330,6 +1335,7 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1330 memset(ptr, SOME_JUNK, l); 1335 memset(ptr, SOME_JUNK, l);
1331 1336
1332 malloc_used -= l; 1337 malloc_used -= l;
1338 malloc_guarded -= malloc_guard;
1333 if (malloc_guard) { 1339 if (malloc_guard) {
1334#ifdef MALLOC_EXTRA_SANITY 1340#ifdef MALLOC_EXTRA_SANITY
1335 if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index+i)) { 1341 if (pi == NULL || PD_IDX(pi->dirnum) != PI_IDX(index+i)) {
@@ -1371,9 +1377,18 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1371 1377
1372 /* Find the right spot, leave pf pointing to the modified entry. */ 1378 /* Find the right spot, leave pf pointing to the modified entry. */
1373 1379
1374 for(pf = free_list.next; (pf->page+pf->size) < ptr && pf->next != NULL; 1380 /* Race ahead here, while calculating cache size. */
1375 pf = pf->next) 1381 for (pf = free_list.next;
1376 ; /* Race ahead here. */ 1382 (pf->page + pf->size) < ptr && pf->next != NULL;
1383 pf = pf->next)
1384 cachesize += pf->size;
1385
1386 /* Finish cache size calculation. */
1387 pt = pf;
1388 while (pt) {
1389 cachesize += pt->size;
1390 pt = pt->next;
1391 }
1377 1392
1378 if (pf->page > tail) { 1393 if (pf->page > tail) {
1379 /* Insert before entry */ 1394 /* Insert before entry */
@@ -1385,6 +1400,7 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1385 px = NULL; 1400 px = NULL;
1386 } else if ((pf->page + pf->size) == ptr ) { 1401 } else if ((pf->page + pf->size) == ptr ) {
1387 /* Append to the previous entry. */ 1402 /* Append to the previous entry. */
1403 cachesize -= pf->size;
1388 pf->size += l; 1404 pf->size += l;
1389 if (pf->next != NULL && (pf->page + pf->size) == pf->next->page ) { 1405 if (pf->next != NULL && (pf->page + pf->size) == pf->next->page ) {
1390 /* And collapse the next too. */ 1406 /* And collapse the next too. */
@@ -1396,6 +1412,7 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1396 } 1412 }
1397 } else if (pf->page == tail) { 1413 } else if (pf->page == tail) {
1398 /* Prepend to entry. */ 1414 /* Prepend to entry. */
1415 cachesize -= pf->size;
1399 pf->size += l; 1416 pf->size += l;
1400 pf->page = ptr; 1417 pf->page = ptr;
1401 pf->pdir = spi; 1418 pf->pdir = spi;
@@ -1419,34 +1436,32 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1419 } 1436 }
1420 1437
1421 /* Return something to OS ? */ 1438 /* Return something to OS ? */
1422 if (pf->next == NULL && /* If we're the last one, */ 1439 if (pf->size > (malloc_cache - cachesize)) {
1423 pf->size > malloc_cache && /* ..and the cache is full, */
1424 (pf->page + pf->size) == malloc_brk) { /* ..and none behind us, */
1425 1440
1426 /* 1441 /*
1427 * Keep the cache intact. Notice that the '>' above guarantees that 1442 * Keep the cache intact. Notice that the '>' above guarantees that
1428 * the pf will always have at least one page afterwards. 1443 * the pf will always have at least one page afterwards.
1429 */ 1444 */
1430 if (munmap((char *)pf->page + malloc_cache, pf->size - malloc_cache)!=0) 1445 if (munmap((char *)pf->page + (malloc_cache - cachesize),
1446 pf->size - (malloc_cache - cachesize)) != 0)
1431 goto not_return; 1447 goto not_return;
1432 tail = pf->page + pf->size; 1448 tail = pf->page + pf->size;
1433 lidx = ptr2index(tail) - 1; 1449 lidx = ptr2index(tail) - 1;
1434 pf->size = malloc_cache; 1450 pf->size = malloc_cache - cachesize;
1435 1451
1436 malloc_brk = pf->page + malloc_cache; 1452 index = ptr2index(pf->page + pf->size);
1437
1438 index = ptr2index(malloc_brk);
1439 1453
1440 pidx = PI_IDX(index); 1454 pidx = PI_IDX(index);
1441 if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) >= pidx) 1455 if (prev_dir != NULL && PD_IDX(prev_dir->dirnum) >= pidx)
1442 prev_dir = NULL; /* Will be wiped out below ! */ 1456 prev_dir = NULL; /* Will be wiped out below ! */
1443 1457
1444 for (pi=pf->pdir; pi!=NULL && PD_IDX(pi->dirnum)<pidx; pi=pi->next); 1458 for (pi=pf->pdir; pi!=NULL && PD_IDX(pi->dirnum)<pidx; pi=pi->next);
1445 1459
1460 spi = pi;
1446 if (pi != NULL && PD_IDX(pi->dirnum) == pidx) { 1461 if (pi != NULL && PD_IDX(pi->dirnum) == pidx) {
1447 pd = pi->base; 1462 pd = pi->base;
1448 1463
1449 for(i=index;i <= last_index;) { 1464 for(i=index;i <= lidx;) {
1450 if (pd[PI_OFF(i)] != MALLOC_NOT_MINE) { 1465 if (pd[PI_OFF(i)] != MALLOC_NOT_MINE) {
1451 pd[PI_OFF(i)] = MALLOC_NOT_MINE; 1466 pd[PI_OFF(i)] = MALLOC_NOT_MINE;
1452#ifdef MALLOC_EXTRA_SANITY 1467#ifdef MALLOC_EXTRA_SANITY
@@ -1458,12 +1473,19 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1458#endif /* MALLOC_EXTRA_SANITY */ 1473#endif /* MALLOC_EXTRA_SANITY */
1459 pi->dirnum--; 1474 pi->dirnum--;
1460 } 1475 }
1476#ifdef MALLOC_EXTRA_SANITY
1477 else
1478 wrtwarning("(ES): page already unmapped\n");
1479#endif /* MALLOC_EXTRA_SANITY */
1461 i++; 1480 i++;
1462 if (!PI_OFF(i)) { 1481 if (!PI_OFF(i)) {
1463 /* If no page in that dir, free directory page. */ 1482 /* If no page in that dir, free directory page. */
1464 if (!PD_OFF(pi->dirnum)) { 1483 if (!PD_OFF(pi->dirnum)) {
1465 /* Remove from list. */ 1484 /* Remove from list. */
1466 pi->prev->next = pi->next; 1485 if (spi == pi) /* Update spi only if first. */
1486 spi = pi->prev;
1487 if (pi->prev != NULL)
1488 pi->prev->next = pi->next;
1467 if (pi->next != NULL) 1489 if (pi->next != NULL)
1468 pi->next->prev = pi->prev; 1490 pi->next->prev = pi->prev;
1469 pi = pi->next; 1491 pi = pi->next;
@@ -1475,11 +1497,65 @@ free_pages(void *ptr, u_long index, struct pginfo *info)
1475 pd = pi->base; 1497 pd = pi->base;
1476 } 1498 }
1477 } 1499 }
1500 if (pi && !PD_OFF(pi->dirnum)) {
1501 /* Resulting page dir is now empty. */
1502 /* Remove from list. */
1503 if (spi == pi) /* Update spi only if first. */
1504 spi = pi->prev;
1505 if (pi->prev != NULL)
1506 pi->prev->next = pi->next;
1507 if (pi->next != NULL)
1508 pi->next->prev = pi->prev;
1509 pi = pi->next;
1510 munmap(pd, malloc_pagesize);
1511 }
1478 } 1512 }
1479 1513
1480 last_index = index - 1; 1514 if (pi == NULL && malloc_brk == tail) {
1515 /* Resize down the malloc upper boundary. */
1516 last_index = index - 1;
1517 malloc_brk = index2ptr(index);
1518 }
1481 1519
1482 /* XXX: We could realloc/shrink the pagedir here I guess. */ 1520 /* XXX: We could realloc/shrink the pagedir here I guess. */
1521 if (pf->size == 0) { /* Remove from free-list as well. */
1522 if (px)
1523 ifree(px);
1524 if ((px = pf->prev) != &free_list) {
1525 if (pi == NULL && last_index == (index - 1)) {
1526 if (spi == NULL) {
1527 malloc_brk = NULL;
1528 i = 11;
1529 } else {
1530 pd = spi->base;
1531 if (PD_IDX(spi->dirnum) < pidx)
1532 index = ((PD_IDX(spi->dirnum) + 1) * pdi_mod) - 1;
1533 for (pi=spi,i=index;pd[PI_OFF(i)]==MALLOC_NOT_MINE;i--)
1534#ifdef MALLOC_EXTRA_SANITY
1535 if (!PI_OFF(i)) { /* Should never enter here. */
1536 pi = pi->prev;
1537 if (pi == NULL || i == 0)
1538 break;
1539 pd = pi->base;
1540 i = (PD_IDX(pi->dirnum) + 1) * pdi_mod;
1541 }
1542#else /* !MALLOC_EXTRA_SANITY */
1543 { }
1544#endif /* MALLOC_EXTRA_SANITY */
1545 malloc_brk = index2ptr(i + 1);
1546 }
1547 last_index = i;
1548 }
1549 if ((px->next = pf->next) != NULL)
1550 px->next->prev = px;
1551 } else {
1552 if ((free_list.next = pf->next) != NULL)
1553 free_list.next->prev = &free_list;
1554 }
1555 px = pf;
1556 last_dir = prev_dir;
1557 prev_dir = NULL;
1558 }
1483 } 1559 }
1484not_return: 1560not_return:
1485 if (pt != NULL) 1561 if (pt != NULL)
@@ -1607,17 +1683,19 @@ ifree(void *ptr)
1607 return; 1683 return;
1608 1684
1609 if (malloc_ptrguard && PTR_ALIGNED(ptr)) 1685 if (malloc_ptrguard && PTR_ALIGNED(ptr))
1610 ptr = (char *)ptr - PTR_GAP; 1686 ptr = (char *)ptr - PTR_GAP;
1611 1687
1612 index = ptr2index(ptr); 1688 index = ptr2index(ptr);
1613 1689
1614 if (index < malloc_pageshift) { 1690 if (index < malloc_pageshift) {
1615 wrtwarning("junk pointer, too low to make sense\n"); 1691 warnx("(%p)", ptr);
1692 wrtwarning("ifree: junk pointer, too low to make sense\n");
1616 return; 1693 return;
1617 } 1694 }
1618 1695
1619 if (index > last_index) { 1696 if (index > last_index) {
1620 wrtwarning("junk pointer, too high to make sense\n"); 1697 warnx("(%p)", ptr);
1698 wrtwarning("ifree: junk pointer, too high to make sense\n");
1621 return; 1699 return;
1622 } 1700 }
1623 1701