]> git.proxmox.com Git - mirror_spl.git/blob - module/spl/spl-kstat.c
57aabe2d0290c039b92f32c7fd2452b35f6f3067
[mirror_spl.git] / module / spl / spl-kstat.c
1 /*****************************************************************************\
2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 * Copyright (C) 2007 The Regents of the University of California.
4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
6 * UCRL-CODE-235197
7 *
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://zfsonlinux.org/>.
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting Layer (SPL) Kstat Implementation.
25 \*****************************************************************************/
26
27 #include <linux/seq_file.h>
28 #include <sys/kstat.h>
29 #include <spl-debug.h>
30
31 #ifdef SS_DEBUG_SUBSYS
32 #undef SS_DEBUG_SUBSYS
33 #endif
34
35 #define SS_DEBUG_SUBSYS SS_KSTAT
36 #ifndef HAVE_PDE_DATA
37 #define PDE_DATA(x) (PDE(x)->data)
38 #endif
39
40 static kmutex_t kstat_module_lock;
41 static struct list_head kstat_module_list;
42 static kid_t kstat_id;
43
44 static int
45 kstat_resize_raw(kstat_t *ksp)
46 {
47 if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX)
48 return ENOMEM;
49
50 vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
51 ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX);
52 ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
53
54 return 0;
55 }
56
57 static int
58 kstat_seq_show_headers(struct seq_file *f)
59 {
60 kstat_t *ksp = (kstat_t *)f->private;
61 int rc = 0;
62
63 ASSERT(ksp->ks_magic == KS_MAGIC);
64
65 seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
66 ksp->ks_kid, ksp->ks_type, ksp->ks_flags,
67 ksp->ks_ndata, (int)ksp->ks_data_size,
68 ksp->ks_crtime, ksp->ks_snaptime);
69
70 switch (ksp->ks_type) {
71 case KSTAT_TYPE_RAW:
72 restart:
73 if (ksp->ks_raw_ops.headers) {
74 rc = ksp->ks_raw_ops.headers(
75 ksp->ks_raw_buf, ksp->ks_raw_bufsize);
76 if (rc == ENOMEM && !kstat_resize_raw(ksp))
77 goto restart;
78 if (!rc)
79 seq_puts(f, ksp->ks_raw_buf);
80 } else {
81 seq_printf(f, "raw data\n");
82 }
83 break;
84 case KSTAT_TYPE_NAMED:
85 seq_printf(f, "%-31s %-4s %s\n",
86 "name", "type", "data");
87 break;
88 case KSTAT_TYPE_INTR:
89 seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n",
90 "hard", "soft", "watchdog",
91 "spurious", "multsvc");
92 break;
93 case KSTAT_TYPE_IO:
94 seq_printf(f,
95 "%-8s %-8s %-8s %-8s %-8s %-8s "
96 "%-8s %-8s %-8s %-8s %-8s %-8s\n",
97 "nread", "nwritten", "reads", "writes",
98 "wtime", "wlentime", "wupdate",
99 "rtime", "rlentime", "rupdate",
100 "wcnt", "rcnt");
101 break;
102 case KSTAT_TYPE_TIMER:
103 seq_printf(f,
104 "%-31s %-8s "
105 "%-8s %-8s %-8s %-8s %-8s\n",
106 "name", "events", "elapsed",
107 "min", "max", "start", "stop");
108 break;
109 case KSTAT_TYPE_TXG:
110 seq_printf(f,
111 "%-8s %-5s %-13s %-12s %-12s %-8s %-8s "
112 "%-12s %-12s %-12s\n",
113 "txg", "state", "birth",
114 "nread", "nwritten", "reads", "writes",
115 "otime", "qtime", "stime");
116 break;
117 default:
118 PANIC("Undefined kstat type %d\n", ksp->ks_type);
119 }
120
121 return -rc;
122 }
123
124 static int
125 kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l)
126 {
127 int i, j;
128
129 for (i = 0; ; i++) {
130 seq_printf(f, "%03x:", i);
131
132 for (j = 0; j < 16; j++) {
133 if (i * 16 + j >= l) {
134 seq_printf(f, "\n");
135 goto out;
136 }
137
138 seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]);
139 }
140 seq_printf(f, "\n");
141 }
142 out:
143 return 0;
144 }
145
146 static int
147 kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp)
148 {
149 seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type);
150
151 switch (knp->data_type) {
152 case KSTAT_DATA_CHAR:
153 knp->value.c[15] = '\0'; /* NULL terminate */
154 seq_printf(f, "%-16s", knp->value.c);
155 break;
156 /* XXX - We need to be more careful able what tokens are
157 * used for each arch, for now this is correct for x86_64.
158 */
159 case KSTAT_DATA_INT32:
160 seq_printf(f, "%d", knp->value.i32);
161 break;
162 case KSTAT_DATA_UINT32:
163 seq_printf(f, "%u", knp->value.ui32);
164 break;
165 case KSTAT_DATA_INT64:
166 seq_printf(f, "%lld", (signed long long)knp->value.i64);
167 break;
168 case KSTAT_DATA_UINT64:
169 seq_printf(f, "%llu", (unsigned long long)knp->value.ui64);
170 break;
171 case KSTAT_DATA_LONG:
172 seq_printf(f, "%ld", knp->value.l);
173 break;
174 case KSTAT_DATA_ULONG:
175 seq_printf(f, "%lu", knp->value.ul);
176 break;
177 case KSTAT_DATA_STRING:
178 KSTAT_NAMED_STR_PTR(knp)
179 [KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0';
180 seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp));
181 break;
182 default:
183 PANIC("Undefined kstat data type %d\n", knp->data_type);
184 }
185
186 seq_printf(f, "\n");
187
188 return 0;
189 }
190
191 static int
192 kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip)
193 {
194 seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n",
195 kip->intrs[KSTAT_INTR_HARD],
196 kip->intrs[KSTAT_INTR_SOFT],
197 kip->intrs[KSTAT_INTR_WATCHDOG],
198 kip->intrs[KSTAT_INTR_SPURIOUS],
199 kip->intrs[KSTAT_INTR_MULTSVC]);
200
201 return 0;
202 }
203
204 static int
205 kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip)
206 {
207 seq_printf(f,
208 "%-8llu %-8llu %-8u %-8u %-8lld %-8lld "
209 "%-8lld %-8lld %-8lld %-8lld %-8u %-8u\n",
210 kip->nread, kip->nwritten,
211 kip->reads, kip->writes,
212 kip->wtime, kip->wlentime, kip->wlastupdate,
213 kip->rtime, kip->wlentime, kip->rlastupdate,
214 kip->wcnt, kip->rcnt);
215
216 return 0;
217 }
218
219 static int
220 kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
221 {
222 seq_printf(f,
223 "%-31s %-8llu %-8lld %-8lld %-8lld %-8lld %-8lld\n",
224 ktp->name, ktp->num_events, ktp->elapsed_time,
225 ktp->min_time, ktp->max_time,
226 ktp->start_time, ktp->stop_time);
227
228 return 0;
229 }
230
231 static int
232 kstat_seq_show_txg(struct seq_file *f, kstat_txg_t *ktp)
233 {
234 char state;
235
236 switch (ktp->state) {
237 case TXG_STATE_OPEN: state = 'O'; break;
238 case TXG_STATE_QUIESCING: state = 'Q'; break;
239 case TXG_STATE_SYNCING: state = 'S'; break;
240 case TXG_STATE_COMMITTED: state = 'C'; break;
241 default: state = '?'; break;
242 }
243
244 seq_printf(f,
245 "%-8llu %-5c %-13llu %-12llu %-12llu %-8u %-8u "
246 "%12lld %12lld %12lld\n", ktp->txg, state, ktp->birth,
247 ktp->nread, ktp->nwritten, ktp->reads, ktp->writes,
248 ktp->open_time, ktp->quiesce_time, ktp->sync_time);
249 return 0;
250 }
251
252 static int
253 kstat_seq_show(struct seq_file *f, void *p)
254 {
255 kstat_t *ksp = (kstat_t *)f->private;
256 int rc = 0;
257
258 ASSERT(ksp->ks_magic == KS_MAGIC);
259
260 switch (ksp->ks_type) {
261 case KSTAT_TYPE_RAW:
262 restart:
263 if (ksp->ks_raw_ops.data) {
264 rc = ksp->ks_raw_ops.data(
265 ksp->ks_raw_buf, ksp->ks_raw_bufsize, p);
266 if (rc == ENOMEM && !kstat_resize_raw(ksp))
267 goto restart;
268 if (!rc)
269 seq_puts(f, ksp->ks_raw_buf);
270 } else {
271 ASSERT(ksp->ks_ndata == 1);
272 rc = kstat_seq_show_raw(f, ksp->ks_data,
273 ksp->ks_data_size);
274 }
275 break;
276 case KSTAT_TYPE_NAMED:
277 rc = kstat_seq_show_named(f, (kstat_named_t *)p);
278 break;
279 case KSTAT_TYPE_INTR:
280 rc = kstat_seq_show_intr(f, (kstat_intr_t *)p);
281 break;
282 case KSTAT_TYPE_IO:
283 rc = kstat_seq_show_io(f, (kstat_io_t *)p);
284 break;
285 case KSTAT_TYPE_TIMER:
286 rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
287 break;
288 case KSTAT_TYPE_TXG:
289 rc = kstat_seq_show_txg(f, (kstat_txg_t *)p);
290 break;
291 default:
292 PANIC("Undefined kstat type %d\n", ksp->ks_type);
293 }
294
295 return -rc;
296 }
297
298 int
299 kstat_default_update(kstat_t *ksp, int rw)
300 {
301 ASSERT(ksp != NULL);
302
303 if (rw == KSTAT_WRITE)
304 return (EACCES);
305
306 return 0;
307 }
308
309 static void *
310 kstat_seq_data_addr(kstat_t *ksp, loff_t n)
311 {
312 void *rc = NULL;
313 SENTRY;
314
315 switch (ksp->ks_type) {
316 case KSTAT_TYPE_RAW:
317 if (ksp->ks_raw_ops.addr)
318 rc = ksp->ks_raw_ops.addr(ksp, n);
319 else
320 rc = ksp->ks_data;
321 break;
322 case KSTAT_TYPE_NAMED:
323 rc = ksp->ks_data + n * sizeof(kstat_named_t);
324 break;
325 case KSTAT_TYPE_INTR:
326 rc = ksp->ks_data + n * sizeof(kstat_intr_t);
327 break;
328 case KSTAT_TYPE_IO:
329 rc = ksp->ks_data + n * sizeof(kstat_io_t);
330 break;
331 case KSTAT_TYPE_TIMER:
332 rc = ksp->ks_data + n * sizeof(kstat_timer_t);
333 break;
334 case KSTAT_TYPE_TXG:
335 rc = ksp->ks_data + n * sizeof(kstat_txg_t);
336 break;
337 default:
338 PANIC("Undefined kstat type %d\n", ksp->ks_type);
339 }
340
341 SRETURN(rc);
342 }
343
344 static void *
345 kstat_seq_start(struct seq_file *f, loff_t *pos)
346 {
347 loff_t n = *pos;
348 kstat_t *ksp = (kstat_t *)f->private;
349 ASSERT(ksp->ks_magic == KS_MAGIC);
350 SENTRY;
351
352 mutex_enter(&ksp->ks_lock);
353
354 if (ksp->ks_type == KSTAT_TYPE_RAW) {
355 ksp->ks_raw_bufsize = PAGE_SIZE;
356 ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
357 }
358
359 /* Dynamically update kstat, on error existing kstats are used */
360 (void) ksp->ks_update(ksp, KSTAT_READ);
361
362 ksp->ks_snaptime = gethrtime();
363
364 if (!n && kstat_seq_show_headers(f))
365 SRETURN(NULL);
366
367 if (n >= ksp->ks_ndata)
368 SRETURN(NULL);
369
370 SRETURN(kstat_seq_data_addr(ksp, n));
371 }
372
373 static void *
374 kstat_seq_next(struct seq_file *f, void *p, loff_t *pos)
375 {
376 kstat_t *ksp = (kstat_t *)f->private;
377 ASSERT(ksp->ks_magic == KS_MAGIC);
378 SENTRY;
379
380 ++*pos;
381 if (*pos >= ksp->ks_ndata)
382 SRETURN(NULL);
383
384 SRETURN(kstat_seq_data_addr(ksp, *pos));
385 }
386
387 static void
388 kstat_seq_stop(struct seq_file *f, void *v)
389 {
390 kstat_t *ksp = (kstat_t *)f->private;
391 ASSERT(ksp->ks_magic == KS_MAGIC);
392
393 if (ksp->ks_type == KSTAT_TYPE_RAW)
394 vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
395
396 mutex_exit(&ksp->ks_lock);
397 }
398
399 static struct seq_operations kstat_seq_ops = {
400 .show = kstat_seq_show,
401 .start = kstat_seq_start,
402 .next = kstat_seq_next,
403 .stop = kstat_seq_stop,
404 };
405
406 static kstat_module_t *
407 kstat_find_module(char *name)
408 {
409 kstat_module_t *module;
410
411 list_for_each_entry(module, &kstat_module_list, ksm_module_list)
412 if (strncmp(name, module->ksm_name, KSTAT_STRLEN) == 0)
413 return (module);
414
415 return (NULL);
416 }
417
418 static kstat_module_t *
419 kstat_create_module(char *name)
420 {
421 kstat_module_t *module;
422 struct proc_dir_entry *pde;
423
424 pde = proc_mkdir(name, proc_spl_kstat);
425 if (pde == NULL)
426 return (NULL);
427
428 module = kmem_alloc(sizeof (kstat_module_t), KM_SLEEP);
429 module->ksm_proc = pde;
430 strlcpy(module->ksm_name, name, KSTAT_STRLEN+1);
431 INIT_LIST_HEAD(&module->ksm_kstat_list);
432 list_add_tail(&module->ksm_module_list, &kstat_module_list);
433
434 return (module);
435
436 }
437
438 static void
439 kstat_delete_module(kstat_module_t *module)
440 {
441 ASSERT(list_empty(&module->ksm_kstat_list));
442 remove_proc_entry(module->ksm_name, proc_spl_kstat);
443 list_del(&module->ksm_module_list);
444 kmem_free(module, sizeof(kstat_module_t));
445 }
446
447 static int
448 proc_kstat_open(struct inode *inode, struct file *filp)
449 {
450 struct seq_file *f;
451 int rc;
452
453 rc = seq_open(filp, &kstat_seq_ops);
454 if (rc)
455 return rc;
456
457 f = filp->private_data;
458 f->private = PDE_DATA(inode);
459
460 return rc;
461 }
462
463 static ssize_t
464 proc_kstat_write(struct file *filp, const char __user *buf,
465 size_t len, loff_t *ppos)
466 {
467 struct seq_file *f = filp->private_data;
468 kstat_t *ksp = f->private;
469 int rc;
470
471 ASSERT(ksp->ks_magic == KS_MAGIC);
472
473 mutex_enter(ksp->ks_lock);
474 rc = ksp->ks_update(ksp, KSTAT_WRITE);
475 mutex_exit(ksp->ks_lock);
476
477 if (rc)
478 return (-rc);
479
480 *ppos += len;
481 return (len);
482 }
483
484 static struct file_operations proc_kstat_operations = {
485 .open = proc_kstat_open,
486 .write = proc_kstat_write,
487 .read = seq_read,
488 .llseek = seq_lseek,
489 .release = seq_release,
490 };
491
492 void
493 __kstat_set_raw_ops(kstat_t *ksp,
494 int (*headers)(char *buf, size_t size),
495 int (*data)(char *buf, size_t size, void *data),
496 void *(*addr)(kstat_t *ksp, loff_t index))
497 {
498 ksp->ks_raw_ops.headers = headers;
499 ksp->ks_raw_ops.data = data;
500 ksp->ks_raw_ops.addr = addr;
501 }
502 EXPORT_SYMBOL(__kstat_set_raw_ops);
503
504 kstat_t *
505 __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
506 const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
507 uchar_t ks_flags)
508 {
509 kstat_t *ksp;
510
511 ASSERT(ks_module);
512 ASSERT(ks_instance == 0);
513 ASSERT(ks_name);
514 ASSERT(!(ks_flags & KSTAT_FLAG_UNSUPPORTED));
515
516 if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
517 ASSERT(ks_ndata == 1);
518
519 ksp = kmem_zalloc(sizeof(*ksp), KM_SLEEP);
520 if (ksp == NULL)
521 return ksp;
522
523 mutex_enter(&kstat_module_lock);
524 ksp->ks_kid = kstat_id;
525 kstat_id++;
526 mutex_exit(&kstat_module_lock);
527
528 ksp->ks_magic = KS_MAGIC;
529 mutex_init(&ksp->ks_lock, NULL, MUTEX_DEFAULT, NULL);
530 INIT_LIST_HEAD(&ksp->ks_list);
531
532 ksp->ks_crtime = gethrtime();
533 ksp->ks_snaptime = ksp->ks_crtime;
534 strncpy(ksp->ks_module, ks_module, KSTAT_STRLEN);
535 ksp->ks_instance = ks_instance;
536 strncpy(ksp->ks_name, ks_name, KSTAT_STRLEN);
537 strncpy(ksp->ks_class, ks_class, KSTAT_STRLEN);
538 ksp->ks_type = ks_type;
539 ksp->ks_flags = ks_flags;
540 ksp->ks_update = kstat_default_update;
541 ksp->ks_private = NULL;
542 ksp->ks_raw_ops.headers = NULL;
543 ksp->ks_raw_ops.data = NULL;
544 ksp->ks_raw_ops.addr = NULL;
545 ksp->ks_raw_buf = NULL;
546 ksp->ks_raw_bufsize = 0;
547
548 switch (ksp->ks_type) {
549 case KSTAT_TYPE_RAW:
550 ksp->ks_ndata = 1;
551 ksp->ks_data_size = ks_ndata;
552 break;
553 case KSTAT_TYPE_NAMED:
554 ksp->ks_ndata = ks_ndata;
555 ksp->ks_data_size = ks_ndata * sizeof(kstat_named_t);
556 break;
557 case KSTAT_TYPE_INTR:
558 ksp->ks_ndata = ks_ndata;
559 ksp->ks_data_size = ks_ndata * sizeof(kstat_intr_t);
560 break;
561 case KSTAT_TYPE_IO:
562 ksp->ks_ndata = ks_ndata;
563 ksp->ks_data_size = ks_ndata * sizeof(kstat_io_t);
564 break;
565 case KSTAT_TYPE_TIMER:
566 ksp->ks_ndata = ks_ndata;
567 ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
568 break;
569 case KSTAT_TYPE_TXG:
570 ksp->ks_ndata = ks_ndata;
571 ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
572 break;
573 default:
574 PANIC("Undefined kstat type %d\n", ksp->ks_type);
575 }
576
577 if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
578 ksp->ks_data = NULL;
579 } else {
580 ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_SLEEP);
581 if (ksp->ks_data == NULL) {
582 kmem_free(ksp, sizeof(*ksp));
583 ksp = NULL;
584 }
585 }
586
587 return ksp;
588 }
589 EXPORT_SYMBOL(__kstat_create);
590
591 void
592 __kstat_install(kstat_t *ksp)
593 {
594 kstat_module_t *module;
595 kstat_t *tmp;
596
597 ASSERT(ksp);
598
599 mutex_enter(&kstat_module_lock);
600
601 module = kstat_find_module(ksp->ks_module);
602 if (module == NULL) {
603 module = kstat_create_module(ksp->ks_module);
604 if (module == NULL)
605 goto out;
606 }
607
608 /*
609 * Only one entry by this name per-module, on failure the module
610 * shouldn't be deleted because we know it has at least one entry.
611 */
612 list_for_each_entry(tmp, &module->ksm_kstat_list, ks_list)
613 if (strncmp(tmp->ks_name, ksp->ks_name, KSTAT_STRLEN) == 0)
614 goto out;
615
616 list_add_tail(&ksp->ks_list, &module->ksm_kstat_list);
617
618 mutex_enter(&ksp->ks_lock);
619 ksp->ks_owner = module;
620 ksp->ks_proc = proc_create_data(ksp->ks_name, 0644,
621 module->ksm_proc, &proc_kstat_operations, (void *)ksp);
622 if (ksp->ks_proc == NULL) {
623 list_del_init(&ksp->ks_list);
624 if (list_empty(&module->ksm_kstat_list))
625 kstat_delete_module(module);
626 }
627 mutex_exit(&ksp->ks_lock);
628 out:
629 mutex_exit(&kstat_module_lock);
630 }
631 EXPORT_SYMBOL(__kstat_install);
632
633 void
634 __kstat_delete(kstat_t *ksp)
635 {
636 kstat_module_t *module = ksp->ks_owner;
637
638 mutex_enter(&kstat_module_lock);
639 list_del_init(&ksp->ks_list);
640 mutex_exit(&kstat_module_lock);
641
642 if (ksp->ks_proc) {
643 remove_proc_entry(ksp->ks_name, module->ksm_proc);
644
645 /* Remove top level module directory if it's empty */
646 if (list_empty(&module->ksm_kstat_list))
647 kstat_delete_module(module);
648 }
649
650 if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
651 kmem_free(ksp->ks_data, ksp->ks_data_size);
652
653 mutex_destroy(&ksp->ks_lock);
654 kmem_free(ksp, sizeof(*ksp));
655
656 return;
657 }
658 EXPORT_SYMBOL(__kstat_delete);
659
660 int
661 spl_kstat_init(void)
662 {
663 SENTRY;
664 mutex_init(&kstat_module_lock, NULL, MUTEX_DEFAULT, NULL);
665 INIT_LIST_HEAD(&kstat_module_list);
666 kstat_id = 0;
667 SRETURN(0);
668 }
669
670 void
671 spl_kstat_fini(void)
672 {
673 SENTRY;
674 ASSERT(list_empty(&kstat_module_list));
675 mutex_destroy(&kstat_module_lock);
676 SEXIT;
677 }
678