| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: rf_driver.c,v 1.126 2011/04/30 01:44:36 mrg Exp $ */ | | 1 | /* $NetBSD: rf_driver.c,v 1.127 2011/05/05 07:12:58 mrg Exp $ */ |
2 | /*- | | 2 | /*- |
3 | * Copyright (c) 1999 The NetBSD Foundation, Inc. | | 3 | * Copyright (c) 1999 The NetBSD Foundation, Inc. |
4 | * All rights reserved. | | 4 | * All rights reserved. |
5 | * | | 5 | * |
6 | * This code is derived from software contributed to The NetBSD Foundation | | 6 | * This code is derived from software contributed to The NetBSD Foundation |
7 | * by Greg Oster | | 7 | * by Greg Oster |
8 | * | | 8 | * |
9 | * Redistribution and use in source and binary forms, with or without | | 9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions | | 10 | * modification, are permitted provided that the following conditions |
11 | * are met: | | 11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright | | 12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. | | 13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright | | 14 | * 2. Redistributions in binary form must reproduce the above copyright |
| @@ -56,27 +56,27 @@ | | | @@ -56,27 +56,27 @@ |
56 | * rights to redistribute these changes. | | 56 | * rights to redistribute these changes. |
57 | */ | | 57 | */ |
58 | | | 58 | |
59 | /****************************************************************************** | | 59 | /****************************************************************************** |
60 | * | | 60 | * |
61 | * rf_driver.c -- main setup, teardown, and access routines for the RAID driver | | 61 | * rf_driver.c -- main setup, teardown, and access routines for the RAID driver |
62 | * | | 62 | * |
63 | * all routines are prefixed with rf_ (raidframe), to avoid conficts. | | 63 | * all routines are prefixed with rf_ (raidframe), to avoid conficts. |
64 | * | | 64 | * |
65 | ******************************************************************************/ | | 65 | ******************************************************************************/ |
66 | | | 66 | |
67 | | | 67 | |
68 | #include <sys/cdefs.h> | | 68 | #include <sys/cdefs.h> |
69 | __KERNEL_RCSID(0, "$NetBSD: rf_driver.c,v 1.126 2011/04/30 01:44:36 mrg Exp $"); | | 69 | __KERNEL_RCSID(0, "$NetBSD: rf_driver.c,v 1.127 2011/05/05 07:12:58 mrg Exp $"); |
70 | | | 70 | |
71 | #ifdef _KERNEL_OPT | | 71 | #ifdef _KERNEL_OPT |
72 | #include "opt_raid_diagnostic.h" | | 72 | #include "opt_raid_diagnostic.h" |
73 | #endif | | 73 | #endif |
74 | | | 74 | |
75 | #include <sys/param.h> | | 75 | #include <sys/param.h> |
76 | #include <sys/systm.h> | | 76 | #include <sys/systm.h> |
77 | #include <sys/ioctl.h> | | 77 | #include <sys/ioctl.h> |
78 | #include <sys/fcntl.h> | | 78 | #include <sys/fcntl.h> |
79 | #include <sys/vnode.h> | | 79 | #include <sys/vnode.h> |
80 | | | 80 | |
81 | | | 81 | |
82 | #include "rf_archs.h" | | 82 | #include "rf_archs.h" |
| @@ -126,30 +126,31 @@ char rf_panicbuf[2048]; /* a buffer t | | | @@ -126,30 +126,31 @@ char rf_panicbuf[2048]; /* a buffer t |
126 | | | 126 | |
127 | /* main configuration routines */ | | 127 | /* main configuration routines */ |
128 | static int raidframe_booted = 0; | | 128 | static int raidframe_booted = 0; |
129 | | | 129 | |
130 | static void rf_ConfigureDebug(RF_Config_t * cfgPtr); | | 130 | static void rf_ConfigureDebug(RF_Config_t * cfgPtr); |
131 | static void set_debug_option(char *name, long val); | | 131 | static void set_debug_option(char *name, long val); |
132 | static void rf_UnconfigureArray(void); | | 132 | static void rf_UnconfigureArray(void); |
133 | static void rf_ShutdownRDFreeList(void *); | | 133 | static void rf_ShutdownRDFreeList(void *); |
134 | static int rf_ConfigureRDFreeList(RF_ShutdownList_t **); | | 134 | static int rf_ConfigureRDFreeList(RF_ShutdownList_t **); |
135 | | | 135 | |
136 | rf_declare_mutex2(rf_printf_mutex); /* debug only: avoids interleaved | | 136 | rf_declare_mutex2(rf_printf_mutex); /* debug only: avoids interleaved |
137 | * printfs by different stripes */ | | 137 | * printfs by different stripes */ |
138 | | | 138 | |
139 | #define SIGNAL_QUIESCENT_COND(_raid_) wakeup(&((_raid_)->accesses_suspended)) | | 139 | #define SIGNAL_QUIESCENT_COND(_raid_) \ |
| | | 140 | rf_broadcast_cond2((_raid_)->access_suspend_cv) |
140 | #define WAIT_FOR_QUIESCENCE(_raid_) \ | | 141 | #define WAIT_FOR_QUIESCENCE(_raid_) \ |
141 | ltsleep(&((_raid_)->accesses_suspended), PRIBIO, \ | | 142 | rf_wait_cond2((_raid_)->access_suspend_cv, \ |
142 | "raidframe quiesce", 0, &((_raid_)->access_suspend_mutex)) | | 143 | (_raid_)->access_suspend_mutex) |
143 | | | 144 | |
144 | static int configureCount = 0; /* number of active configurations */ | | 145 | static int configureCount = 0; /* number of active configurations */ |
145 | static int isconfigged = 0; /* is basic raidframe (non per-array) | | 146 | static int isconfigged = 0; /* is basic raidframe (non per-array) |
146 | * stuff configured */ | | 147 | * stuff configured */ |
147 | static rf_declare_mutex2(configureMutex); /* used to lock the configuration | | 148 | static rf_declare_mutex2(configureMutex); /* used to lock the configuration |
148 | * stuff */ | | 149 | * stuff */ |
149 | static RF_ShutdownList_t *globalShutdown; /* non array-specific | | 150 | static RF_ShutdownList_t *globalShutdown; /* non array-specific |
150 | * stuff */ | | 151 | * stuff */ |
151 | | | 152 | |
152 | static int rf_ConfigureRDFreeList(RF_ShutdownList_t ** listp); | | 153 | static int rf_ConfigureRDFreeList(RF_ShutdownList_t ** listp); |
153 | static int rf_AllocEmergBuffers(RF_Raid_t *); | | 154 | static int rf_AllocEmergBuffers(RF_Raid_t *); |
154 | static void rf_FreeEmergBuffers(RF_Raid_t *); | | 155 | static void rf_FreeEmergBuffers(RF_Raid_t *); |
155 | | | 156 | |
| @@ -242,26 +243,29 @@ rf_Shutdown(RF_Raid_t *raidPtr) | | | @@ -242,26 +243,29 @@ rf_Shutdown(RF_Raid_t *raidPtr) |
242 | raidPtr->valid = 0; | | 243 | raidPtr->valid = 0; |
243 | | | 244 | |
244 | if (raidPtr->parity_map != NULL) | | 245 | if (raidPtr->parity_map != NULL) |
245 | rf_paritymap_detach(raidPtr); | | 246 | rf_paritymap_detach(raidPtr); |
246 | | | 247 | |
247 | rf_update_component_labels(raidPtr, RF_FINAL_COMPONENT_UPDATE); | | 248 | rf_update_component_labels(raidPtr, RF_FINAL_COMPONENT_UPDATE); |
248 | | | 249 | |
249 | rf_UnconfigureVnodes(raidPtr); | | 250 | rf_UnconfigureVnodes(raidPtr); |
250 | | | 251 | |
251 | rf_FreeEmergBuffers(raidPtr); | | 252 | rf_FreeEmergBuffers(raidPtr); |
252 | | | 253 | |
253 | rf_ShutdownList(&raidPtr->shutdownList); | | 254 | rf_ShutdownList(&raidPtr->shutdownList); |
254 | | | 255 | |
| | | 256 | rf_destroy_mutex2(raidPtr->access_suspend_mutex); |
| | | 257 | rf_destroy_cond2(raidPtr->access_suspend_cv); |
| | | 258 | |
255 | rf_destroy_cond2(raidPtr->outstandingCond); | | 259 | rf_destroy_cond2(raidPtr->outstandingCond); |
256 | rf_destroy_mutex2(raidPtr->rad_lock); | | 260 | rf_destroy_mutex2(raidPtr->rad_lock); |
257 | | | 261 | |
258 | rf_UnconfigureArray(); | | 262 | rf_UnconfigureArray(); |
259 | | | 263 | |
260 | return (0); | | 264 | return (0); |
261 | } | | 265 | } |
262 | | | 266 | |
263 | | | 267 | |
264 | #define DO_INIT_CONFIGURE(f) { \ | | 268 | #define DO_INIT_CONFIGURE(f) { \ |
265 | rc = f (&globalShutdown); \ | | 269 | rc = f (&globalShutdown); \ |
266 | if (rc) { \ | | 270 | if (rc) { \ |
267 | RF_ERRORMSG2("RAIDFRAME: failed %s with %d\n", RF_STRING(f), rc); \ | | 271 | RF_ERRORMSG2("RAIDFRAME: failed %s with %d\n", RF_STRING(f), rc); \ |
| @@ -278,30 +282,26 @@ rf_Shutdown(RF_Raid_t *raidPtr) | | | @@ -278,30 +282,26 @@ rf_Shutdown(RF_Raid_t *raidPtr) |
278 | rf_ShutdownList(&raidPtr->shutdownList); \ | | 282 | rf_ShutdownList(&raidPtr->shutdownList); \ |
279 | rf_UnconfigureArray(); \ | | 283 | rf_UnconfigureArray(); \ |
280 | } | | 284 | } |
281 | | | 285 | |
282 | #define DO_RAID_INIT_CONFIGURE(f) { \ | | 286 | #define DO_RAID_INIT_CONFIGURE(f) { \ |
283 | rc = f (&raidPtr->shutdownList, raidPtr, cfgPtr); \ | | 287 | rc = f (&raidPtr->shutdownList, raidPtr, cfgPtr); \ |
284 | if (rc) { \ | | 288 | if (rc) { \ |
285 | RF_ERRORMSG2("RAIDFRAME: failed %s with %d\n", RF_STRING(f), rc); \ | | 289 | RF_ERRORMSG2("RAIDFRAME: failed %s with %d\n", RF_STRING(f), rc); \ |
286 | DO_RAID_FAIL(); \ | | 290 | DO_RAID_FAIL(); \ |
287 | return(rc); \ | | 291 | return(rc); \ |
288 | } \ | | 292 | } \ |
289 | } | | 293 | } |
290 | | | 294 | |
291 | #define DO_RAID_MUTEX(_m_) { \ | | | |
292 | rf_mutex_init((_m_)); \ | | | |
293 | } | | | |
294 | | | | |
295 | int | | 295 | int |
296 | rf_Configure(RF_Raid_t *raidPtr, RF_Config_t *cfgPtr, RF_AutoConfig_t *ac) | | 296 | rf_Configure(RF_Raid_t *raidPtr, RF_Config_t *cfgPtr, RF_AutoConfig_t *ac) |
297 | { | | 297 | { |
298 | RF_RowCol_t col; | | 298 | RF_RowCol_t col; |
299 | int rc; | | 299 | int rc; |
300 | | | 300 | |
301 | rf_lock_mutex2(configureMutex); | | 301 | rf_lock_mutex2(configureMutex); |
302 | configureCount++; | | 302 | configureCount++; |
303 | if (isconfigged == 0) { | | 303 | if (isconfigged == 0) { |
304 | rf_init_mutex2(rf_printf_mutex, IPL_VM); | | 304 | rf_init_mutex2(rf_printf_mutex, IPL_VM); |
305 | | | 305 | |
306 | /* initialize globals */ | | 306 | /* initialize globals */ |
307 | | | 307 | |
| @@ -323,27 +323,27 @@ rf_Configure(RF_Raid_t *raidPtr, RF_Conf | | | @@ -323,27 +323,27 @@ rf_Configure(RF_Raid_t *raidPtr, RF_Conf |
323 | DO_INIT_CONFIGURE(rf_ConfigureNWayXor); | | 323 | DO_INIT_CONFIGURE(rf_ConfigureNWayXor); |
324 | DO_INIT_CONFIGURE(rf_ConfigureStripeLockFreeList); | | 324 | DO_INIT_CONFIGURE(rf_ConfigureStripeLockFreeList); |
325 | DO_INIT_CONFIGURE(rf_ConfigureMCPair); | | 325 | DO_INIT_CONFIGURE(rf_ConfigureMCPair); |
326 | DO_INIT_CONFIGURE(rf_ConfigureDAGs); | | 326 | DO_INIT_CONFIGURE(rf_ConfigureDAGs); |
327 | DO_INIT_CONFIGURE(rf_ConfigureDAGFuncs); | | 327 | DO_INIT_CONFIGURE(rf_ConfigureDAGFuncs); |
328 | DO_INIT_CONFIGURE(rf_ConfigureReconstruction); | | 328 | DO_INIT_CONFIGURE(rf_ConfigureReconstruction); |
329 | DO_INIT_CONFIGURE(rf_ConfigureCopyback); | | 329 | DO_INIT_CONFIGURE(rf_ConfigureCopyback); |
330 | DO_INIT_CONFIGURE(rf_ConfigureDiskQueueSystem); | | 330 | DO_INIT_CONFIGURE(rf_ConfigureDiskQueueSystem); |
331 | DO_INIT_CONFIGURE(rf_ConfigurePSStatus); | | 331 | DO_INIT_CONFIGURE(rf_ConfigurePSStatus); |
332 | isconfigged = 1; | | 332 | isconfigged = 1; |
333 | } | | 333 | } |
334 | rf_unlock_mutex2(configureMutex); | | 334 | rf_unlock_mutex2(configureMutex); |
335 | | | 335 | |
336 | DO_RAID_MUTEX(&raidPtr->mutex); | | 336 | rf_mutex_init(&raidPtr->mutex); |
337 | /* set up the cleanup list. Do this after ConfigureDebug so that | | 337 | /* set up the cleanup list. Do this after ConfigureDebug so that |
338 | * value of memDebug will be set */ | | 338 | * value of memDebug will be set */ |
339 | | | 339 | |
340 | rf_MakeAllocList(raidPtr->cleanupList); | | 340 | rf_MakeAllocList(raidPtr->cleanupList); |
341 | if (raidPtr->cleanupList == NULL) { | | 341 | if (raidPtr->cleanupList == NULL) { |
342 | DO_RAID_FAIL(); | | 342 | DO_RAID_FAIL(); |
343 | return (ENOMEM); | | 343 | return (ENOMEM); |
344 | } | | 344 | } |
345 | rf_ShutdownCreate(&raidPtr->shutdownList, | | 345 | rf_ShutdownCreate(&raidPtr->shutdownList, |
346 | (void (*) (void *)) rf_FreeAllocList, | | 346 | (void (*) (void *)) rf_FreeAllocList, |
347 | raidPtr->cleanupList); | | 347 | raidPtr->cleanupList); |
348 | | | 348 | |
349 | raidPtr->numCol = cfgPtr->numCol; | | 349 | raidPtr->numCol = cfgPtr->numCol; |
| @@ -351,27 +351,28 @@ rf_Configure(RF_Raid_t *raidPtr, RF_Conf | | | @@ -351,27 +351,28 @@ rf_Configure(RF_Raid_t *raidPtr, RF_Conf |
351 | | | 351 | |
352 | raidPtr->status = rf_rs_optimal; | | 352 | raidPtr->status = rf_rs_optimal; |
353 | raidPtr->reconControl = NULL; | | 353 | raidPtr->reconControl = NULL; |
354 | | | 354 | |
355 | DO_RAID_INIT_CONFIGURE(rf_ConfigureEngine); | | 355 | DO_RAID_INIT_CONFIGURE(rf_ConfigureEngine); |
356 | DO_RAID_INIT_CONFIGURE(rf_ConfigureStripeLocks); | | 356 | DO_RAID_INIT_CONFIGURE(rf_ConfigureStripeLocks); |
357 | | | 357 | |
358 | rf_init_cond2(raidPtr->outstandingCond, "rfocond"); | | 358 | rf_init_cond2(raidPtr->outstandingCond, "rfocond"); |
359 | rf_init_mutex2(raidPtr->rad_lock, IPL_VM); | | 359 | rf_init_mutex2(raidPtr->rad_lock, IPL_VM); |
360 | | | 360 | |
361 | raidPtr->nAccOutstanding = 0; | | 361 | raidPtr->nAccOutstanding = 0; |
362 | raidPtr->waitShutdown = 0; | | 362 | raidPtr->waitShutdown = 0; |
363 | | | 363 | |
364 | DO_RAID_MUTEX(&raidPtr->access_suspend_mutex); | | 364 | rf_init_mutex2(raidPtr->access_suspend_mutex, IPL_VM); |
| | | 365 | rf_init_cond2(raidPtr->access_suspend_cv, "rfquiesce"); |
365 | | | 366 | |
366 | raidPtr->waitForReconCond = 0; | | 367 | raidPtr->waitForReconCond = 0; |
367 | | | 368 | |
368 | if (ac!=NULL) { | | 369 | if (ac!=NULL) { |
369 | /* We have an AutoConfig structure.. Don't do the | | 370 | /* We have an AutoConfig structure.. Don't do the |
370 | normal disk configuration... call the auto config | | 371 | normal disk configuration... call the auto config |
371 | stuff */ | | 372 | stuff */ |
372 | rf_AutoConfigureDisks(raidPtr, cfgPtr, ac); | | 373 | rf_AutoConfigureDisks(raidPtr, cfgPtr, ac); |
373 | } else { | | 374 | } else { |
374 | DO_RAID_INIT_CONFIGURE(rf_ConfigureDisks); | | 375 | DO_RAID_INIT_CONFIGURE(rf_ConfigureDisks); |
375 | DO_RAID_INIT_CONFIGURE(rf_ConfigureSpareDisks); | | 376 | DO_RAID_INIT_CONFIGURE(rf_ConfigureSpareDisks); |
376 | } | | 377 | } |
377 | /* do this after ConfigureDisks & ConfigureSpareDisks to be sure dev | | 378 | /* do this after ConfigureDisks & ConfigureSpareDisks to be sure dev |
| @@ -778,67 +779,67 @@ rf_SignalQuiescenceLock(RF_Raid_t *raidP | | | @@ -778,67 +779,67 @@ rf_SignalQuiescenceLock(RF_Raid_t *raidP |
778 | | | 779 | |
779 | if (raidPtr->waiting_for_quiescence) { | | 780 | if (raidPtr->waiting_for_quiescence) { |
780 | SIGNAL_QUIESCENT_COND(raidPtr); | | 781 | SIGNAL_QUIESCENT_COND(raidPtr); |
781 | } | | 782 | } |
782 | } | | 783 | } |
783 | /* suspends all new requests to the array. No effect on accesses that are in flight. */ | | 784 | /* suspends all new requests to the array. No effect on accesses that are in flight. */ |
784 | int | | 785 | int |
785 | rf_SuspendNewRequestsAndWait(RF_Raid_t *raidPtr) | | 786 | rf_SuspendNewRequestsAndWait(RF_Raid_t *raidPtr) |
786 | { | | 787 | { |
787 | #if RF_DEBUG_QUIESCE | | 788 | #if RF_DEBUG_QUIESCE |
788 | if (rf_quiesceDebug) | | 789 | if (rf_quiesceDebug) |
789 | printf("raid%d: Suspending new reqs\n", raidPtr->raidid); | | 790 | printf("raid%d: Suspending new reqs\n", raidPtr->raidid); |
790 | #endif | | 791 | #endif |
791 | RF_LOCK_MUTEX(raidPtr->access_suspend_mutex); | | 792 | rf_lock_mutex2(raidPtr->access_suspend_mutex); |
792 | raidPtr->accesses_suspended++; | | 793 | raidPtr->accesses_suspended++; |
793 | raidPtr->waiting_for_quiescence = (raidPtr->accs_in_flight == 0) ? 0 : 1; | | 794 | raidPtr->waiting_for_quiescence = (raidPtr->accs_in_flight == 0) ? 0 : 1; |
794 | | | 795 | |
795 | if (raidPtr->waiting_for_quiescence) { | | 796 | if (raidPtr->waiting_for_quiescence) { |
796 | raidPtr->access_suspend_release = 0; | | 797 | raidPtr->access_suspend_release = 0; |
797 | while (!raidPtr->access_suspend_release) { | | 798 | while (!raidPtr->access_suspend_release) { |
798 | #if RF_DEBUG_QUIESCE | | 799 | #if RF_DEBUG_QUIESCE |
799 | printf("raid%d: Suspending: Waiting for Quiescence\n", | | 800 | printf("raid%d: Suspending: Waiting for Quiescence\n", |
800 | raidPtr->raidid); | | 801 | raidPtr->raidid); |
801 | #endif | | 802 | #endif |
802 | WAIT_FOR_QUIESCENCE(raidPtr); | | 803 | WAIT_FOR_QUIESCENCE(raidPtr); |
803 | raidPtr->waiting_for_quiescence = 0; | | 804 | raidPtr->waiting_for_quiescence = 0; |
804 | } | | 805 | } |
805 | } | | 806 | } |
806 | #if RF_DEBUG_QUIESCE | | 807 | #if RF_DEBUG_QUIESCE |
807 | printf("raid%d: Quiescence reached..\n", raidPtr->raidid); | | 808 | printf("raid%d: Quiescence reached..\n", raidPtr->raidid); |
808 | #endif | | 809 | #endif |
809 | | | 810 | |
810 | RF_UNLOCK_MUTEX(raidPtr->access_suspend_mutex); | | 811 | rf_unlock_mutex2(raidPtr->access_suspend_mutex); |
811 | return (raidPtr->waiting_for_quiescence); | | 812 | return (raidPtr->waiting_for_quiescence); |
812 | } | | 813 | } |
813 | /* wake up everyone waiting for quiescence to be released */ | | 814 | /* wake up everyone waiting for quiescence to be released */ |
814 | void | | 815 | void |
815 | rf_ResumeNewRequests(RF_Raid_t *raidPtr) | | 816 | rf_ResumeNewRequests(RF_Raid_t *raidPtr) |
816 | { | | 817 | { |
817 | RF_CallbackDesc_t *t, *cb; | | 818 | RF_CallbackDesc_t *t, *cb; |
818 | | | 819 | |
819 | #if RF_DEBUG_QUIESCE | | 820 | #if RF_DEBUG_QUIESCE |
820 | if (rf_quiesceDebug) | | 821 | if (rf_quiesceDebug) |
821 | printf("raid%d: Resuming new requests\n", raidPtr->raidid); | | 822 | printf("raid%d: Resuming new requests\n", raidPtr->raidid); |
822 | #endif | | 823 | #endif |
823 | | | 824 | |
824 | RF_LOCK_MUTEX(raidPtr->access_suspend_mutex); | | 825 | rf_lock_mutex2(raidPtr->access_suspend_mutex); |
825 | raidPtr->accesses_suspended--; | | 826 | raidPtr->accesses_suspended--; |
826 | if (raidPtr->accesses_suspended == 0) | | 827 | if (raidPtr->accesses_suspended == 0) |
827 | cb = raidPtr->quiesce_wait_list; | | 828 | cb = raidPtr->quiesce_wait_list; |
828 | else | | 829 | else |
829 | cb = NULL; | | 830 | cb = NULL; |
830 | raidPtr->quiesce_wait_list = NULL; | | 831 | raidPtr->quiesce_wait_list = NULL; |
831 | RF_UNLOCK_MUTEX(raidPtr->access_suspend_mutex); | | 832 | rf_unlock_mutex2(raidPtr->access_suspend_mutex); |
832 | | | 833 | |
833 | while (cb) { | | 834 | while (cb) { |
834 | t = cb; | | 835 | t = cb; |
835 | cb = cb->next; | | 836 | cb = cb->next; |
836 | (t->callbackFunc) (t->callbackArg); | | 837 | (t->callbackFunc) (t->callbackArg); |
837 | rf_FreeCallbackDesc(t); | | 838 | rf_FreeCallbackDesc(t); |
838 | } | | 839 | } |
839 | } | | 840 | } |
840 | /***************************************************************************************** | | 841 | /***************************************************************************************** |
841 | * | | 842 | * |
842 | * debug routines | | 843 | * debug routines |
843 | * | | 844 | * |
844 | ****************************************************************************************/ | | 845 | ****************************************************************************************/ |