Annotation of sys/dev/raidframe/rf_layout.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: rf_layout.c,v 1.7 2007/04/10 17:47:55 miod Exp $ */
! 2: /* $NetBSD: rf_layout.c,v 1.6 2000/04/17 19:35:12 oster Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1995 Carnegie-Mellon University.
! 6: * All rights reserved.
! 7: *
! 8: * Author: Mark Holland
! 9: *
! 10: * Permission to use, copy, modify and distribute this software and
! 11: * its documentation is hereby granted, provided that both the copyright
! 12: * notice and this permission notice appear in all copies of the
! 13: * software, derivative works or modified versions, and any portions
! 14: * thereof, and that both notices appear in supporting documentation.
! 15: *
! 16: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
! 17: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
! 18: * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
! 19: *
! 20: * Carnegie Mellon requests users of this software to return to
! 21: *
! 22: * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
! 23: * School of Computer Science
! 24: * Carnegie Mellon University
! 25: * Pittsburgh PA 15213-3890
! 26: *
! 27: * any improvements or extensions that they make and grant Carnegie the
! 28: * rights to redistribute these changes.
! 29: */
! 30:
! 31: /*
! 32: * rf_layout.c -- Driver code dealing with layout and mapping issues.
! 33: */
! 34:
! 35: #include "rf_types.h"
! 36: #include "rf_archs.h"
! 37: #include "rf_raid.h"
! 38: #include "rf_configure.h"
! 39: #include "rf_dag.h"
! 40: #include "rf_desc.h"
! 41: #include "rf_decluster.h"
! 42: #include "rf_pq.h"
! 43: #include "rf_declusterPQ.h"
! 44: #include "rf_raid0.h"
! 45: #include "rf_raid1.h"
! 46: #include "rf_raid4.h"
! 47: #include "rf_raid5.h"
! 48: #include "rf_states.h"
! 49: #if RF_INCLUDE_RAID5_RS > 0
! 50: #include "rf_raid5_rotatedspare.h"
! 51: #endif /* RF_INCLUDE_RAID5_RS > 0 */
! 52: #if RF_INCLUDE_CHAINDECLUSTER > 0
! 53: #include "rf_chaindecluster.h"
! 54: #endif /* RF_INCLUDE_CHAINDECLUSTER > 0 */
! 55: #if RF_INCLUDE_INTERDECLUSTER > 0
! 56: #include "rf_interdecluster.h"
! 57: #endif /* RF_INCLUDE_INTERDECLUSTER > 0 */
! 58: #if RF_INCLUDE_PARITYLOGGING > 0
! 59: #include "rf_paritylogging.h"
! 60: #endif /* RF_INCLUDE_PARITYLOGGING > 0 */
! 61: #if RF_INCLUDE_EVENODD > 0
! 62: #include "rf_evenodd.h"
! 63: #endif /* RF_INCLUDE_EVENODD > 0 */
! 64: #include "rf_general.h"
! 65: #include "rf_driver.h"
! 66: #include "rf_parityscan.h"
! 67: #include "rf_reconbuffer.h"
! 68: #include "rf_reconutil.h"
! 69:
! 70: /*****************************************************************************
! 71: *
! 72: * The layout switch defines all the layouts that are supported.
! 73: * Fields are: layout ID, init routine, shutdown routine, map sector,
! 74: * map parity, identify stripe, dag selection, map stripeid
! 75: * to parity stripe id (optional), num faults tolerated,
! 76: * special flags.
! 77: *
! 78: *****************************************************************************/
! 79:
! 80: static RF_AccessState_t DefaultStates[] = {
! 81: rf_QuiesceState, rf_IncrAccessesCountState, rf_MapState,
! 82: rf_LockState, rf_CreateDAGState, rf_ExecuteDAGState,
! 83: rf_ProcessDAGState, rf_DecrAccessesCountState,
! 84: rf_CleanupState, rf_LastState
! 85: };
! 86:
! 87: #if (defined(__NetBSD__) || defined(__OpenBSD__)) && !defined(_KERNEL)
! 88: /*
! 89: * XXX Gross hack to shutup gcc -- It complains that DefaultStates is not
! 90: * used when compiling this in userland... I hate to burst its bubble, but
! 91: * DefaultStates is used all over the place here in the initialization of
! 92: * lots of data structures. GO
! 93: */
! 94: RF_AccessState_t *NothingAtAll = DefaultStates;
! 95: #endif
! 96:
! 97: #if (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(_KERNEL)
! 98: /* XXX Remove static so GCC doesn't complain about these being unused ! */
! 99: int distSpareYes = 1;
! 100: int distSpareNo = 0;
! 101: #else
! 102: static int distSpareYes = 1;
! 103: static int distSpareNo = 0;
! 104: #endif
! 105:
! 106: #ifdef _KERNEL
! 107: #define RF_NK2(a,b)
! 108: #else /* _KERNEL */
! 109: #define RF_NK2(a,b) a,b,
! 110: #endif /* !_KERNEL */
! 111:
! 112: #if RF_UTILITY > 0
! 113: #define RF_NU(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)
! 114: #else /* RF_UTILITY > 0 */
! 115: #define RF_NU(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p
! 116: #endif /* RF_UTILITY > 0 */
! 117:
! 118: static RF_LayoutSW_t mapsw[] = {
! 119: /* Parity declustering. */
! 120: {'T', "Parity declustering",
! 121: RF_NK2(rf_MakeLayoutSpecificDeclustered, &distSpareNo)
! 122: RF_NU(
! 123: rf_ConfigureDeclustered,
! 124: rf_MapSectorDeclustered, rf_MapParityDeclustered, NULL,
! 125: rf_IdentifyStripeDeclustered,
! 126: rf_RaidFiveDagSelect,
! 127: rf_MapSIDToPSIDDeclustered,
! 128: rf_GetDefaultHeadSepLimitDeclustered,
! 129: rf_GetDefaultNumFloatingReconBuffersDeclustered,
! 130: NULL, NULL,
! 131: rf_SubmitReconBufferBasic,
! 132: rf_VerifyParityBasic,
! 133: 1,
! 134: DefaultStates,
! 135: 0)
! 136: },
! 137:
! 138: /* Parity declustering with distributed sparing. */
! 139: {'D', "Distributed sparing parity declustering",
! 140: RF_NK2(rf_MakeLayoutSpecificDeclustered, &distSpareYes)
! 141: RF_NU(
! 142: rf_ConfigureDeclusteredDS,
! 143: rf_MapSectorDeclustered, rf_MapParityDeclustered, NULL,
! 144: rf_IdentifyStripeDeclustered,
! 145: rf_RaidFiveDagSelect,
! 146: rf_MapSIDToPSIDDeclustered,
! 147: rf_GetDefaultHeadSepLimitDeclustered,
! 148: rf_GetDefaultNumFloatingReconBuffersDeclustered,
! 149: rf_GetNumSpareRUsDeclustered, rf_InstallSpareTable,
! 150: rf_SubmitReconBufferBasic,
! 151: rf_VerifyParityBasic,
! 152: 1,
! 153: DefaultStates,
! 154: RF_DISTRIBUTE_SPARE | RF_BD_DECLUSTERED)
! 155: },
! 156:
! 157: #if RF_INCLUDE_DECL_PQ > 0
! 158: /* Declustered P+Q. */
! 159: {'Q', "Declustered P+Q",
! 160: RF_NK2(rf_MakeLayoutSpecificDeclustered, &distSpareNo)
! 161: RF_NU(
! 162: rf_ConfigureDeclusteredPQ,
! 163: rf_MapSectorDeclusteredPQ, rf_MapParityDeclusteredPQ,
! 164: rf_MapQDeclusteredPQ,
! 165: rf_IdentifyStripeDeclusteredPQ,
! 166: rf_PQDagSelect,
! 167: rf_MapSIDToPSIDDeclustered,
! 168: rf_GetDefaultHeadSepLimitDeclustered,
! 169: rf_GetDefaultNumFloatingReconBuffersPQ,
! 170: NULL, NULL,
! 171: NULL,
! 172: rf_VerifyParityBasic,
! 173: 2,
! 174: DefaultStates,
! 175: 0)
! 176: },
! 177: #endif /* RF_INCLUDE_DECL_PQ > 0 */
! 178:
! 179: #if RF_INCLUDE_RAID5_RS > 0
! 180: /* RAID 5 with rotated sparing. */
! 181: {'R', "RAID Level 5 rotated sparing",
! 182: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 183: RF_NU(
! 184: rf_ConfigureRAID5_RS,
! 185: rf_MapSectorRAID5_RS, rf_MapParityRAID5_RS, NULL,
! 186: rf_IdentifyStripeRAID5_RS,
! 187: rf_RaidFiveDagSelect,
! 188: rf_MapSIDToPSIDRAID5_RS,
! 189: rf_GetDefaultHeadSepLimitRAID5,
! 190: rf_GetDefaultNumFloatingReconBuffersRAID5,
! 191: rf_GetNumSpareRUsRAID5_RS, NULL,
! 192: rf_SubmitReconBufferBasic,
! 193: rf_VerifyParityBasic,
! 194: 1,
! 195: DefaultStates,
! 196: RF_DISTRIBUTE_SPARE)
! 197: },
! 198: #endif /* RF_INCLUDE_RAID5_RS > 0 */
! 199:
! 200: #if RF_INCLUDE_CHAINDECLUSTER > 0
! 201: /* Chained Declustering. */
! 202: {'C', "Chained Declustering",
! 203: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 204: RF_NU(
! 205: rf_ConfigureChainDecluster,
! 206: rf_MapSectorChainDecluster, rf_MapParityChainDecluster,
! 207: NULL,
! 208: rf_IdentifyStripeChainDecluster,
! 209: rf_RAIDCDagSelect,
! 210: rf_MapSIDToPSIDChainDecluster,
! 211: NULL,
! 212: NULL,
! 213: rf_GetNumSpareRUsChainDecluster, NULL,
! 214: rf_SubmitReconBufferBasic,
! 215: rf_VerifyParityBasic,
! 216: 1,
! 217: DefaultStates,
! 218: 0)
! 219: },
! 220: #endif /* RF_INCLUDE_CHAINDECLUSTER > 0 */
! 221:
! 222: #if RF_INCLUDE_INTERDECLUSTER > 0
! 223: /* Interleaved Declustering. */
! 224: {'I', "Interleaved Declustering",
! 225: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 226: RF_NU(
! 227: rf_ConfigureInterDecluster,
! 228: rf_MapSectorInterDecluster, rf_MapParityInterDecluster,
! 229: NULL,
! 230: rf_IdentifyStripeInterDecluster,
! 231: rf_RAIDIDagSelect,
! 232: rf_MapSIDToPSIDInterDecluster,
! 233: rf_GetDefaultHeadSepLimitInterDecluster,
! 234: rf_GetDefaultNumFloatingReconBuffersInterDecluster,
! 235: rf_GetNumSpareRUsInterDecluster, NULL,
! 236: rf_SubmitReconBufferBasic,
! 237: rf_VerifyParityBasic,
! 238: 1,
! 239: DefaultStates,
! 240: RF_DISTRIBUTE_SPARE)
! 241: },
! 242: #endif /* RF_INCLUDE_INTERDECLUSTER > 0 */
! 243:
! 244: #if RF_INCLUDE_RAID0 > 0
! 245: /* RAID level 0. */
! 246: {'0', "RAID Level 0",
! 247: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 248: RF_NU(
! 249: rf_ConfigureRAID0,
! 250: rf_MapSectorRAID0, rf_MapParityRAID0, NULL,
! 251: rf_IdentifyStripeRAID0,
! 252: rf_RAID0DagSelect,
! 253: rf_MapSIDToPSIDRAID0,
! 254: NULL,
! 255: NULL,
! 256: NULL, NULL,
! 257: NULL,
! 258: rf_VerifyParityRAID0,
! 259: 0,
! 260: DefaultStates,
! 261: 0)
! 262: },
! 263: #endif /* RF_INCLUDE_RAID0 > 0 */
! 264:
! 265: #if RF_INCLUDE_RAID1 > 0
! 266: /* RAID level 1. */
! 267: {'1', "RAID Level 1",
! 268: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 269: RF_NU(
! 270: rf_ConfigureRAID1,
! 271: rf_MapSectorRAID1, rf_MapParityRAID1, NULL,
! 272: rf_IdentifyStripeRAID1,
! 273: rf_RAID1DagSelect,
! 274: rf_MapSIDToPSIDRAID1,
! 275: NULL,
! 276: NULL,
! 277: NULL, NULL,
! 278: rf_SubmitReconBufferRAID1,
! 279: rf_VerifyParityRAID1,
! 280: 1,
! 281: DefaultStates,
! 282: 0)
! 283: },
! 284: #endif /* RF_INCLUDE_RAID1 > 0 */
! 285:
! 286: #if RF_INCLUDE_RAID4 > 0
! 287: /* RAID level 4. */
! 288: {'4', "RAID Level 4",
! 289: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 290: RF_NU(
! 291: rf_ConfigureRAID4,
! 292: rf_MapSectorRAID4, rf_MapParityRAID4, NULL,
! 293: rf_IdentifyStripeRAID4,
! 294: rf_RaidFiveDagSelect,
! 295: rf_MapSIDToPSIDRAID4,
! 296: rf_GetDefaultHeadSepLimitRAID4,
! 297: rf_GetDefaultNumFloatingReconBuffersRAID4,
! 298: NULL, NULL,
! 299: rf_SubmitReconBufferBasic,
! 300: rf_VerifyParityBasic,
! 301: 1,
! 302: DefaultStates,
! 303: 0)
! 304: },
! 305: #endif /* RF_INCLUDE_RAID4 > 0 */
! 306:
! 307: #if RF_INCLUDE_RAID5 > 0
! 308: /* RAID level 5. */
! 309: {'5', "RAID Level 5",
! 310: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 311: RF_NU(
! 312: rf_ConfigureRAID5,
! 313: rf_MapSectorRAID5, rf_MapParityRAID5, NULL,
! 314: rf_IdentifyStripeRAID5,
! 315: rf_RaidFiveDagSelect,
! 316: rf_MapSIDToPSIDRAID5,
! 317: rf_GetDefaultHeadSepLimitRAID5,
! 318: rf_GetDefaultNumFloatingReconBuffersRAID5,
! 319: NULL, NULL,
! 320: rf_SubmitReconBufferBasic,
! 321: rf_VerifyParityBasic,
! 322: 1,
! 323: DefaultStates,
! 324: 0)
! 325: },
! 326: #endif /* RF_INCLUDE_RAID5 > 0 */
! 327:
! 328: #if RF_INCLUDE_EVENODD > 0
! 329: /* Evenodd. */
! 330: {'E', "EvenOdd",
! 331: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 332: RF_NU(
! 333: rf_ConfigureEvenOdd,
! 334: rf_MapSectorRAID5, rf_MapParityEvenOdd, rf_MapEEvenOdd,
! 335: rf_IdentifyStripeEvenOdd,
! 336: rf_EODagSelect,
! 337: rf_MapSIDToPSIDRAID5,
! 338: NULL,
! 339: NULL,
! 340: NULL, NULL,
! 341: NULL, /* No reconstruction, yet. */
! 342: rf_VerifyParityEvenOdd,
! 343: 2,
! 344: DefaultStates,
! 345: 0)
! 346: },
! 347: #endif /* RF_INCLUDE_EVENODD > 0 */
! 348:
! 349: #if RF_INCLUDE_EVENODD > 0
! 350: /* Declustered Evenodd. */
! 351: {'e', "Declustered EvenOdd",
! 352: RF_NK2(rf_MakeLayoutSpecificDeclustered, &distSpareNo)
! 353: RF_NU(
! 354: rf_ConfigureDeclusteredPQ,
! 355: rf_MapSectorDeclusteredPQ, rf_MapParityDeclusteredPQ,
! 356: rf_MapQDeclusteredPQ,
! 357: rf_IdentifyStripeDeclusteredPQ,
! 358: rf_EODagSelect,
! 359: rf_MapSIDToPSIDRAID5,
! 360: rf_GetDefaultHeadSepLimitDeclustered,
! 361: rf_GetDefaultNumFloatingReconBuffersPQ,
! 362: NULL, NULL,
! 363: NULL, /* No reconstruction, yet. */
! 364: rf_VerifyParityEvenOdd,
! 365: 2,
! 366: DefaultStates,
! 367: 0)
! 368: },
! 369: #endif /* RF_INCLUDE_EVENODD > 0 */
! 370:
! 371: #if RF_INCLUDE_PARITYLOGGING > 0
! 372: /* Parity logging. */
! 373: {'L', "Parity logging",
! 374: RF_NK2(rf_MakeLayoutSpecificNULL, NULL)
! 375: RF_NU(
! 376: rf_ConfigureParityLogging,
! 377: rf_MapSectorParityLogging, rf_MapParityParityLogging, NULL,
! 378: rf_IdentifyStripeParityLogging,
! 379: rf_ParityLoggingDagSelect,
! 380: rf_MapSIDToPSIDParityLogging,
! 381: rf_GetDefaultHeadSepLimitParityLogging,
! 382: rf_GetDefaultNumFloatingReconBuffersParityLogging,
! 383: NULL, NULL,
! 384: rf_SubmitReconBufferBasic,
! 385: NULL,
! 386: 1,
! 387: DefaultStates,
! 388: 0)
! 389: },
! 390: #endif /* RF_INCLUDE_PARITYLOGGING > 0 */
! 391:
! 392: /* End-of-list marker. */
! 393: {'\0', NULL,
! 394: RF_NK2(NULL, NULL)
! 395: RF_NU(
! 396: NULL,
! 397: NULL, NULL, NULL,
! 398: NULL,
! 399: NULL,
! 400: NULL,
! 401: NULL,
! 402: NULL,
! 403: NULL, NULL,
! 404: NULL,
! 405: NULL,
! 406: 0,
! 407: NULL,
! 408: 0)
! 409: }
! 410: };
! 411:
! 412: RF_LayoutSW_t *
! 413: rf_GetLayout(RF_ParityConfig_t parityConfig)
! 414: {
! 415: RF_LayoutSW_t *p;
! 416:
! 417: /* Look up the specific layout. */
! 418: for (p = &mapsw[0]; p->parityConfig; p++)
! 419: if (p->parityConfig == parityConfig)
! 420: break;
! 421: if (!p->parityConfig)
! 422: return (NULL);
! 423: RF_ASSERT(p->parityConfig == parityConfig);
! 424: return (p);
! 425: }
! 426:
! 427: #if RF_UTILITY == 0
! 428: /*****************************************************************************
! 429: *
! 430: * ConfigureLayout
! 431: *
! 432: * Read the configuration file and set up the RAID layout parameters.
! 433: * After reading common params, invokes the layout-specific configuration
! 434: * routine to finish the configuration.
! 435: *
! 436: *****************************************************************************/
! 437: int
! 438: rf_ConfigureLayout(RF_ShutdownList_t **listp, RF_Raid_t *raidPtr,
! 439: RF_Config_t *cfgPtr)
! 440: {
! 441: RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
! 442: RF_ParityConfig_t parityConfig;
! 443: RF_LayoutSW_t *p;
! 444: int retval;
! 445:
! 446: layoutPtr->sectorsPerStripeUnit = cfgPtr->sectPerSU;
! 447: layoutPtr->SUsPerPU = cfgPtr->SUsPerPU;
! 448: layoutPtr->SUsPerRU = cfgPtr->SUsPerRU;
! 449: parityConfig = cfgPtr->parityConfig;
! 450:
! 451: if (layoutPtr->sectorsPerStripeUnit <= 0) {
! 452: RF_ERRORMSG2("raid%d: Invalid sectorsPerStripeUnit: %d.\n",
! 453: raidPtr->raidid, (int)layoutPtr->sectorsPerStripeUnit);
! 454: return (EINVAL);
! 455: }
! 456:
! 457: layoutPtr->stripeUnitsPerDisk = raidPtr->sectorsPerDisk /
! 458: layoutPtr->sectorsPerStripeUnit;
! 459:
! 460: p = rf_GetLayout(parityConfig);
! 461: if (p == NULL) {
! 462: RF_ERRORMSG1("Unknown parity configuration '%c'", parityConfig);
! 463: return (EINVAL);
! 464: }
! 465: RF_ASSERT(p->parityConfig == parityConfig);
! 466: layoutPtr->map = p;
! 467:
! 468: /* Initialize the specific layout. */
! 469:
! 470: retval = (p->Configure) (listp, raidPtr, cfgPtr);
! 471:
! 472: if (retval)
! 473: return (retval);
! 474:
! 475: layoutPtr->dataBytesPerStripe = layoutPtr->dataSectorsPerStripe <<
! 476: raidPtr->logBytesPerSector;
! 477: raidPtr->sectorsPerDisk = layoutPtr->stripeUnitsPerDisk *
! 478: layoutPtr->sectorsPerStripeUnit;
! 479:
! 480: if (rf_forceNumFloatingReconBufs >= 0) {
! 481: raidPtr->numFloatingReconBufs = rf_forceNumFloatingReconBufs;
! 482: } else {
! 483: raidPtr->numFloatingReconBufs =
! 484: rf_GetDefaultNumFloatingReconBuffers(raidPtr);
! 485: }
! 486:
! 487: if (rf_forceHeadSepLimit >= 0) {
! 488: raidPtr->headSepLimit = rf_forceHeadSepLimit;
! 489: } else {
! 490: raidPtr->headSepLimit = rf_GetDefaultHeadSepLimit(raidPtr);
! 491: }
! 492:
! 493: #ifdef RAIDDEBUG
! 494: if (raidPtr->headSepLimit >= 0) {
! 495: printf("RAIDFRAME(%s): Using %ld floating recon bufs"
! 496: " with head sep limit %ld.\n", layoutPtr->map->configName,
! 497: (long) raidPtr->numFloatingReconBufs,
! 498: (long) raidPtr->headSepLimit);
! 499: } else {
! 500: printf("RAIDFRAME(%s): Using %ld floating recon bufs"
! 501: " with no head sep limit.\n", layoutPtr->map->configName,
! 502: (long) raidPtr->numFloatingReconBufs);
! 503: }
! 504: #endif /* RAIDDEBUG */
! 505:
! 506: return (0);
! 507: }
! 508:
! 509: /*
! 510: * Typically there is a 1-1 mapping between stripes and parity stripes.
! 511: * However, the declustering code supports packing multiple stripes into
! 512: * a single parity stripe, so as to increase the size of the reconstruction
! 513: * unit without affecting the size of the stripe unit. This routine finds
! 514: * the parity stripe identifier associated with a stripe ID. There is also
! 515: * a RaidAddressToParityStripeID macro in layout.h
! 516: */
! 517: RF_StripeNum_t
! 518: rf_MapStripeIDToParityStripeID(RF_RaidLayout_t *layoutPtr,
! 519: RF_StripeNum_t stripeID, RF_ReconUnitNum_t *which_ru)
! 520: {
! 521: RF_StripeNum_t parityStripeID;
! 522:
! 523: /* Quick exit in the common case of SUsPerPU == 1. */
! 524: if ((layoutPtr->SUsPerPU == 1) || !layoutPtr->map->MapSIDToPSID) {
! 525: *which_ru = 0;
! 526: return (stripeID);
! 527: } else {
! 528: (layoutPtr->map->MapSIDToPSID) (layoutPtr, stripeID,
! 529: &parityStripeID, which_ru);
! 530: }
! 531: return (parityStripeID);
! 532: }
! 533: #endif /* RF_UTILITY == 0 */
CVSweb