xref: /openbsd/usr.sbin/ldomctl/mdstore.c (revision 72b890ef)
1 /*	$OpenBSD: mdstore.c,v 1.14 2021/02/01 16:27:06 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Mark Kettenis
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <assert.h>
20 #include <err.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include "ds.h"
27 #include "mdesc.h"
28 #include "mdstore.h"
29 #include "ldom_util.h"
30 #include "ldomctl.h"
31 
32 void	mdstore_start(struct ldc_conn *, uint64_t);
33 void	mdstore_start_v2(struct ldc_conn *, uint64_t);
34 void	mdstore_start_v3(struct ldc_conn *, uint64_t);
35 void	mdstore_rx_data(struct ldc_conn *, uint64_t, void *, size_t);
36 
37 struct ds_service mdstore_service = {
38 	"mdstore", 1, 0, mdstore_start, mdstore_rx_data
39 };
40 
41 struct ds_service mdstore_service_v2 = {
42 	"mdstore", 2, 0, mdstore_start_v2, mdstore_rx_data
43 };
44 
45 struct ds_service mdstore_service_v3 = {
46 	"mdstore", 3, 0, mdstore_start_v3, mdstore_rx_data
47 };
48 
49 #define MDSET_BEGIN_REQUEST	0x0001
50 #define MDSET_END_REQUEST	0x0002
51 #define MD_TRANSFER_REQUEST	0x0003
52 #define MDSET_LIST_REQUEST	0x0004
53 #define MDSET_SELECT_REQUEST	0x0005
54 #define MDSET_DELETE_REQUEST	0x0006
55 #define MDSET_RETREIVE_REQUEST	0x0007
56 
57 struct mdstore_msg {
58 	uint32_t	msg_type;
59 	uint32_t	payload_len;
60 	uint64_t	svc_handle;
61 	uint64_t	reqnum;
62 	uint16_t	command;
63 	uint8_t		reserved[6];
64 } __packed;
65 
66 struct mdstore_begin_end_req {
67 	uint32_t	msg_type;
68 	uint32_t	payload_len;
69 	uint64_t	svc_handle;
70 	uint64_t	reqnum;
71 	uint16_t	command;
72 	uint16_t	nmds;
73 	uint32_t	namelen;
74 	char		name[1];
75 } __packed;
76 
77 struct mdstore_begin_req_v2 {
78 	uint32_t	msg_type;
79 	uint32_t	payload_len;
80 	uint64_t	svc_handle;
81 	uint64_t	reqnum;
82 	uint16_t	command;
83 	uint16_t	nmds;
84 	uint32_t	config_size;
85   	uint64_t	timestamp;
86 	uint32_t	namelen;
87 	char		name[1];
88 } __packed;
89 
90 struct mdstore_begin_req_v3 {
91 	uint32_t	msg_type;
92 	uint32_t	payload_len;
93 	uint64_t	svc_handle;
94 	uint64_t	reqnum;
95 	uint16_t	command;
96 	uint16_t	nmds;
97 	uint32_t	config_size;
98   	uint64_t	timestamp;
99 	uint8_t		degraded;
100 	uint8_t		active_config;
101 	uint8_t		reserved[2];
102 	uint32_t	namelen;
103 	char		name[1];
104 } __packed;
105 
106 #define CONFIG_NORMAL		0x00
107 #define CONFIG_DEGRADED		0x01
108 
109 #define CONFIG_EXISTING		0x00
110 #define CONFIG_ACTIVE		0x01
111 
112 struct mdstore_transfer_req {
113 	uint32_t	msg_type;
114 	uint32_t	payload_len;
115 	uint64_t	svc_handle;
116 	uint64_t	reqnum;
117 	uint16_t	command;
118 	uint16_t	type;
119 	uint32_t	size;
120 	uint64_t	offset;
121 	char		md[];
122 } __packed;
123 
124 #define MDSTORE_PRI_TYPE	0x01
125 #define MDSTORE_HV_MD_TYPE	0x02
126 #define MDSTORE_CTL_DOM_MD_TYPE	0x04
127 #define MDSTORE_SVC_DOM_MD_TYPE	0x08
128 
129 struct mdstore_sel_del_req {
130 	uint32_t	msg_type;
131 	uint32_t	payload_len;
132 	uint64_t	svc_handle;
133 	uint64_t	reqnum;
134 	uint16_t	command;
135 	uint8_t		reserved[2];
136 	uint32_t	namelen;
137 	char		name[1];
138 } __packed;
139 
140 #define MDSET_LIST_REPLY	0x0104
141 
142 struct mdstore_list_resp {
143 	uint32_t	msg_type;
144 	uint32_t	payload_len;
145 	uint64_t	svc_handle;
146 	uint64_t	reqnum;
147 	uint32_t	result;
148 	uint16_t	booted_set;
149 	uint16_t	boot_set;
150 	char		sets[1];
151 } __packed;
152 
153 #define MDST_SUCCESS		0x0
154 #define MDST_FAILURE		0x1
155 #define MDST_INVALID_MSG	0x2
156 #define MDST_MAX_MDS_ERR	0x3
157 #define MDST_BAD_NAME_ERR	0x4
158 #define MDST_SET_EXISTS_ERR	0x5
159 #define MDST_ALLOC_SET_ERR	0x6
160 #define MDST_ALLOC_MD_ERR	0x7
161 #define MDST_MD_COUNT_ERR	0x8
162 #define MDST_MD_SIZE_ERR	0x9
163 #define MDST_MD_TYPE_ERR	0xa
164 #define MDST_NOT_EXIST_ERR	0xb
165 
166 struct mdstore_set_head mdstore_sets = TAILQ_HEAD_INITIALIZER(mdstore_sets);
167 uint64_t mdstore_reqnum;
168 uint64_t mdstore_command;
169 uint16_t mdstore_major;
170 
171 void
mdstore_register(struct ds_conn * dc)172 mdstore_register(struct ds_conn *dc)
173 {
174 	ds_conn_register_service(dc, &mdstore_service);
175 	ds_conn_register_service(dc, &mdstore_service_v2);
176 	ds_conn_register_service(dc, &mdstore_service_v3);
177 }
178 
179 void
mdstore_start(struct ldc_conn * lc,uint64_t svc_handle)180 mdstore_start(struct ldc_conn *lc, uint64_t svc_handle)
181 {
182 	struct mdstore_msg mm;
183 
184 	bzero(&mm, sizeof(mm));
185 	mm.msg_type = DS_DATA;
186 	mm.payload_len = sizeof(mm) - 8;
187 	mm.svc_handle = svc_handle;
188 	mm.reqnum = mdstore_reqnum++;
189 	mm.command = mdstore_command = MDSET_LIST_REQUEST;
190 	ds_send_msg(lc, &mm, sizeof(mm));
191 }
192 
193 void
mdstore_start_v2(struct ldc_conn * lc,uint64_t svc_handle)194 mdstore_start_v2(struct ldc_conn *lc, uint64_t svc_handle)
195 {
196 	mdstore_major = 2;
197 	mdstore_start(lc, svc_handle);
198 }
199 
200 void
mdstore_start_v3(struct ldc_conn * lc,uint64_t svc_handle)201 mdstore_start_v3(struct ldc_conn *lc, uint64_t svc_handle)
202 {
203 	mdstore_major = 3;
204 	mdstore_start(lc, svc_handle);
205 }
206 
207 void
mdstore_rx_data(struct ldc_conn * lc,uint64_t svc_handle,void * data,size_t len)208 mdstore_rx_data(struct ldc_conn *lc, uint64_t svc_handle, void *data,
209     size_t len)
210 {
211 	struct mdstore_list_resp *mr = data;
212 	struct mdstore_set *set;
213 	int idx;
214 
215 	if (mr->result != MDST_SUCCESS) {
216 		switch (mr->result) {
217 		case MDST_SET_EXISTS_ERR:
218 			errx(1, "Configuration already exists");
219 			break;
220 		case MDST_NOT_EXIST_ERR:
221 			errx(1, "No such configuration");
222 			break;
223 		default:
224 			errx(1, "Unexpected result 0x%x\n", mr->result);
225 			break;
226 		}
227 	}
228 
229 	switch (mdstore_command) {
230 	case MDSET_LIST_REQUEST:
231 		for (idx = 0, len = 0; len < mr->payload_len - 24; idx++) {
232 			set = xmalloc(sizeof(*set));
233 			set->name = xstrdup(&mr->sets[len]);
234 			set->booted_set = (idx == mr->booted_set);
235 			set->boot_set = (idx == mr->boot_set);
236 			TAILQ_INSERT_TAIL(&mdstore_sets, set, link);
237 			len += strlen(&mr->sets[len]) + 1;
238 			if (mdstore_major >= 2)
239 				len += sizeof(uint64_t); /* skip timestamp */
240 			if (mdstore_major >= 3)
241 				len += sizeof(uint8_t);	/* skip has_degraded */
242 		}
243 		break;
244 	}
245 
246 	mdstore_command = 0;
247 }
248 
249 void
mdstore_begin_v1(struct ds_conn * dc,uint64_t svc_handle,const char * name,int nmds)250 mdstore_begin_v1(struct ds_conn *dc, uint64_t svc_handle, const char *name,
251     int nmds)
252 {
253 	struct mdstore_begin_end_req *mr;
254 	size_t len = sizeof(*mr) + strlen(name);
255 
256 	mr = xzalloc(len);
257 	mr->msg_type = DS_DATA;
258 	mr->payload_len = len - 8;
259 	mr->svc_handle = svc_handle;
260 	mr->reqnum = mdstore_reqnum++;
261 	mr->command = mdstore_command = MDSET_BEGIN_REQUEST;
262 	mr->nmds = nmds;
263 	mr->namelen = strlen(name);
264 	memcpy(mr->name, name, strlen(name));
265 
266 	ds_send_msg(&dc->lc, mr, len);
267 	free(mr);
268 
269 	while (mdstore_command == MDSET_BEGIN_REQUEST)
270 		ds_conn_handle(dc);
271 }
272 
273 void
mdstore_begin_v2(struct ds_conn * dc,uint64_t svc_handle,const char * name,int nmds,uint32_t config_size)274 mdstore_begin_v2(struct ds_conn *dc, uint64_t svc_handle, const char *name,
275     int nmds, uint32_t config_size)
276 {
277 	struct mdstore_begin_req_v2 *mr;
278 	size_t len = sizeof(*mr) + strlen(name);
279 
280 	mr = xzalloc(len);
281 	mr->msg_type = DS_DATA;
282 	mr->payload_len = len - 8;
283 	mr->svc_handle = svc_handle;
284 	mr->reqnum = mdstore_reqnum++;
285 	mr->command = mdstore_command = MDSET_BEGIN_REQUEST;
286 	mr->config_size = config_size;
287 	mr->timestamp = time(NULL);
288 	mr->nmds = nmds;
289 	mr->namelen = strlen(name);
290 	memcpy(mr->name, name, strlen(name));
291 
292 	ds_send_msg(&dc->lc, mr, len);
293 	free(mr);
294 
295 	while (mdstore_command == MDSET_BEGIN_REQUEST)
296 		ds_conn_handle(dc);
297 }
298 
299 void
mdstore_begin_v3(struct ds_conn * dc,uint64_t svc_handle,const char * name,int nmds,uint32_t config_size)300 mdstore_begin_v3(struct ds_conn *dc, uint64_t svc_handle, const char *name,
301     int nmds, uint32_t config_size)
302 {
303 	struct mdstore_begin_req_v3 *mr;
304 	size_t len = sizeof(*mr) + strlen(name);
305 
306 	mr = xzalloc(len);
307 	mr->msg_type = DS_DATA;
308 	mr->payload_len = len - 8;
309 	mr->svc_handle = svc_handle;
310 	mr->reqnum = mdstore_reqnum++;
311 	mr->command = mdstore_command = MDSET_BEGIN_REQUEST;
312 	mr->config_size = config_size;
313 	mr->timestamp = time(NULL);
314 	mr->degraded = CONFIG_NORMAL;
315 	mr->active_config = CONFIG_EXISTING;
316 	mr->nmds = nmds;
317 	mr->namelen = strlen(name);
318 	memcpy(mr->name, name, strlen(name));
319 
320 	ds_send_msg(&dc->lc, mr, len);
321 	free(mr);
322 
323 	while (mdstore_command == MDSET_BEGIN_REQUEST)
324 		ds_conn_handle(dc);
325 }
326 
327 void
mdstore_begin(struct ds_conn * dc,uint64_t svc_handle,const char * name,int nmds,uint32_t config_size)328 mdstore_begin(struct ds_conn *dc, uint64_t svc_handle, const char *name,
329     int nmds, uint32_t config_size)
330 {
331 	if (mdstore_major == 3)
332 		mdstore_begin_v3(dc, svc_handle, name, nmds, config_size);
333 	else if (mdstore_major == 2)
334 		mdstore_begin_v2(dc, svc_handle, name, nmds, config_size);
335 	else
336 		mdstore_begin_v1(dc, svc_handle, name, nmds);
337 }
338 
339 void
mdstore_transfer(struct ds_conn * dc,uint64_t svc_handle,const char * path,uint16_t type,uint64_t offset)340 mdstore_transfer(struct ds_conn *dc, uint64_t svc_handle, const char *path,
341     uint16_t type, uint64_t offset)
342 {
343 	struct mdstore_transfer_req *mr;
344 	uint32_t size;
345 	size_t len;
346 	FILE *fp;
347 
348 	fp = fopen(path, "r");
349 	if (fp == NULL)
350 		err(1, "fopen");
351 
352 	fseek(fp, 0, SEEK_END);
353 	size = ftell(fp);
354 	fseek(fp, 0, SEEK_SET);
355 
356 	len = sizeof(*mr) + size;
357 	mr = xzalloc(len);
358 
359 	mr->msg_type = DS_DATA;
360 	mr->payload_len = len - 8;
361 	mr->svc_handle = svc_handle;
362 	mr->reqnum = mdstore_reqnum++;
363 	mr->command = mdstore_command = MD_TRANSFER_REQUEST;
364 	mr->type = type;
365 	mr->size = size;
366 	mr->offset = offset;
367 	if (fread(&mr->md, size, 1, fp) != 1)
368 		err(1, "fread");
369 	ds_send_msg(&dc->lc, mr, len);
370 	free(mr);
371 
372 	fclose(fp);
373 
374 	while (mdstore_command == MD_TRANSFER_REQUEST)
375 		ds_conn_handle(dc);
376 }
377 
378 void
mdstore_end(struct ds_conn * dc,uint64_t svc_handle,const char * name,int nmds)379 mdstore_end(struct ds_conn *dc, uint64_t svc_handle, const char *name,
380     int nmds)
381 {
382 	struct mdstore_begin_end_req *mr;
383 	size_t len = sizeof(*mr) + strlen(name);
384 
385 	mr = xzalloc(len);
386 	mr->msg_type = DS_DATA;
387 	mr->payload_len = len - 8;
388 	mr->svc_handle = svc_handle;
389 	mr->reqnum = mdstore_reqnum++;
390 	mr->command = mdstore_command = MDSET_END_REQUEST;
391 	mr->nmds = nmds;
392 	mr->namelen = strlen(name);
393 	memcpy(mr->name, name, strlen(name));
394 
395 	ds_send_msg(&dc->lc, mr, len);
396 	free(mr);
397 
398 	while (mdstore_command == MDSET_END_REQUEST)
399 		ds_conn_handle(dc);
400 }
401 
402 void
mdstore_select(struct ds_conn * dc,const char * name)403 mdstore_select(struct ds_conn *dc, const char *name)
404 {
405 	struct ds_conn_svc *dcs;
406 	struct mdstore_sel_del_req *mr;
407 	size_t len = sizeof(*mr) + strlen(name);
408 
409 	TAILQ_FOREACH(dcs, &dc->services, link)
410 		if (strcmp(dcs->service->ds_svc_id, "mdstore") == 0 &&
411 		    dcs->svc_handle != 0)
412 			break;
413 	assert(dcs != NULL);
414 
415 	mr = xzalloc(len);
416 	mr->msg_type = DS_DATA;
417 	mr->payload_len = len - 8;
418 	mr->svc_handle = dcs->svc_handle;
419 	mr->reqnum = mdstore_reqnum++;
420 	mr->command = mdstore_command = MDSET_SELECT_REQUEST;
421 	mr->namelen = strlen(name);
422 	memcpy(mr->name, name, strlen(name));
423 
424 	ds_send_msg(&dc->lc, mr, len);
425 	free(mr);
426 
427 	while (mdstore_command == MDSET_SELECT_REQUEST)
428 		ds_conn_handle(dc);
429 }
430 
431 void
mdstore_delete(struct ds_conn * dc,const char * name)432 mdstore_delete(struct ds_conn *dc, const char *name)
433 {
434 	struct ds_conn_svc *dcs;
435 	struct mdstore_sel_del_req *mr;
436 	size_t len = sizeof(*mr) + strlen(name);
437 
438 	TAILQ_FOREACH(dcs, &dc->services, link)
439 		if (strcmp(dcs->service->ds_svc_id, "mdstore") == 0 &&
440 		    dcs->svc_handle != 0)
441 			break;
442 	assert(dcs != NULL);
443 
444 	mr = xzalloc(len);
445 	mr->msg_type = DS_DATA;
446 	mr->payload_len = len - 8;
447 	mr->svc_handle = dcs->svc_handle;
448 	mr->reqnum = mdstore_reqnum++;
449 	mr->command = mdstore_command = MDSET_DELETE_REQUEST;
450 	mr->namelen = strlen(name);
451 	memcpy(mr->name, name, strlen(name));
452 
453 	ds_send_msg(&dc->lc, mr, len);
454 	free(mr);
455 
456 	while (mdstore_command == MDSET_DELETE_REQUEST)
457 		ds_conn_handle(dc);
458 }
459 
460 void frag_init(void);
461 void add_frag_mblock(struct md_node *);
462 void add_frag(uint64_t);
463 void delete_frag(uint64_t);
464 uint64_t alloc_frag(void);
465 
466 void
mdstore_download(struct ds_conn * dc,const char * name)467 mdstore_download(struct ds_conn *dc, const char *name)
468 {
469 	struct ds_conn_svc *dcs;
470 	struct md_node *node;
471 	struct md_prop *prop;
472 	struct guest *guest;
473 	int nmds = 2;
474 	char *path;
475 	uint32_t total_size = 0;
476 	uint16_t type;
477 
478 	TAILQ_FOREACH(dcs, &dc->services, link)
479 		if (strcmp(dcs->service->ds_svc_id, "mdstore") == 0 &&
480 		    dcs->svc_handle != 0)
481 			break;
482 	assert(dcs != NULL);
483 
484 	if (asprintf(&path, "%s/hv.md", name) == -1)
485 		err(1, "asprintf");
486 	hvmd = md_read(path);
487 	free(path);
488 
489 	if (hvmd == NULL)
490 		err(1, "%s", name);
491 
492 	node = md_find_node(hvmd, "guests");
493 	TAILQ_INIT(&guest_list);
494 	TAILQ_FOREACH(prop, &node->prop_list, link) {
495 		if (prop->tag == MD_PROP_ARC &&
496 		    strcmp(prop->name->str, "fwd") == 0) {
497 			add_guest(prop->d.arc.node);
498 			nmds++;
499 		}
500 	}
501 
502 	frag_init();
503 	hv_mdpa = alloc_frag();
504 
505 	TAILQ_FOREACH(guest, &guest_list, link) {
506 		if (asprintf(&path, "%s/%s.md", name, guest->name) == -1)
507 			err(1, "asprintf");
508 		total_size += md_size(path);
509 	}
510 	if (asprintf(&path, "%s/hv.md", name) == -1)
511 		err(1, "asprintf");
512 	total_size += md_size(path);
513 	if (asprintf(&path, "%s/pri", name) == -1)
514 		err(1, "asprintf");
515 	total_size += md_size(path);
516 
517 	mdstore_begin(dc, dcs->svc_handle, name, nmds, total_size);
518 	TAILQ_FOREACH(guest, &guest_list, link) {
519 		if (asprintf(&path, "%s/%s.md", name, guest->name) == -1)
520 			err(1, "asprintf");
521 		type = 0;
522 		if (strcmp(guest->name, "primary") == 0)
523 			type = MDSTORE_CTL_DOM_MD_TYPE;
524 		mdstore_transfer(dc, dcs->svc_handle, path, type, guest->mdpa);
525 		free(path);
526 	}
527 	if (asprintf(&path, "%s/hv.md", name) == -1)
528 		err(1, "asprintf");
529 	mdstore_transfer(dc, dcs->svc_handle, path,
530 	    MDSTORE_HV_MD_TYPE, hv_mdpa);
531 	free(path);
532 	if (asprintf(&path, "%s/pri", name) == -1)
533 		err(1, "asprintf");
534 	mdstore_transfer(dc, dcs->svc_handle, path,
535 	    MDSTORE_PRI_TYPE, 0);
536 	free(path);
537 	mdstore_end(dc, dcs->svc_handle, name, nmds);
538 }
539 
540 struct frag {
541 	TAILQ_ENTRY(frag) link;
542 	uint64_t base;
543 };
544 
545 TAILQ_HEAD(frag_head, frag) mdstore_frags;
546 
547 uint64_t mdstore_fragsize;
548 
549 void
frag_init(void)550 frag_init(void)
551 {
552 	struct md_node *node;
553 	struct md_prop *prop;
554 
555 	node = md_find_node(hvmd, "frag_space");
556 	md_get_prop_val(hvmd, node, "fragsize", &mdstore_fragsize);
557 	TAILQ_INIT(&mdstore_frags);
558 	TAILQ_FOREACH(prop, &node->prop_list, link) {
559 		if (prop->tag == MD_PROP_ARC &&
560 		    strcmp(prop->name->str, "fwd") == 0)
561 			add_frag_mblock(prop->d.arc.node);
562 	}
563 }
564 
565 void
add_frag_mblock(struct md_node * node)566 add_frag_mblock(struct md_node *node)
567 {
568 	uint64_t base, size;
569 	struct guest *guest;
570 
571 	md_get_prop_val(hvmd, node, "base", &base);
572 	md_get_prop_val(hvmd, node, "size", &size);
573 	while (size > mdstore_fragsize) {
574 		add_frag(base);
575 		size -= mdstore_fragsize;
576 		base += mdstore_fragsize;
577 	}
578 
579 	delete_frag(hv_mdpa);
580 	TAILQ_FOREACH(guest, &guest_list, link)
581 		delete_frag(guest->mdpa);
582 }
583 
584 void
add_frag(uint64_t base)585 add_frag(uint64_t base)
586 {
587 	struct frag *frag;
588 
589 	frag = xmalloc(sizeof(*frag));
590 	frag->base = base;
591 	TAILQ_INSERT_TAIL(&mdstore_frags, frag, link);
592 }
593 
594 void
delete_frag(uint64_t base)595 delete_frag(uint64_t base)
596 {
597 	struct frag *frag;
598 	struct frag *tmp;
599 
600 	TAILQ_FOREACH_SAFE(frag, &mdstore_frags, link, tmp) {
601 		if (frag->base == base) {
602 			TAILQ_REMOVE(&mdstore_frags, frag, link);
603 			free(frag);
604 		}
605 	}
606 }
607 
608 uint64_t
alloc_frag(void)609 alloc_frag(void)
610 {
611 	struct frag *frag;
612 	uint64_t base;
613 
614 	frag = TAILQ_FIRST(&mdstore_frags);
615 	if (frag == NULL)
616 		return -1;
617 
618 	TAILQ_REMOVE(&mdstore_frags, frag, link);
619 	base = frag->base;
620 	free(frag);
621 
622 	return base;
623 }
624