#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: rf_pq.c,v 1.18 2023/10/15 18:15:20 oster Exp $");
#include "rf_archs.h"
#if (RF_INCLUDE_DECL_PQ > 0) || (RF_INCLUDE_RAID6 > 0) || (RF_INCLUDE_EVENODD > 0)
#include <dev/raidframe/raidframevar.h>
#include "rf_raid.h"
#include "rf_dag.h"
#include "rf_dagffrd.h"
#include "rf_dagffwr.h"
#include "rf_dagdegrd.h"
#include "rf_dagdegwr.h"
#include "rf_dagutils.h"
#include "rf_dagfuncs.h"
#include "rf_etimer.h"
#include "rf_pqdeg.h"
#include "rf_general.h"
#include "rf_map.h"
#include "rf_pq.h"
RF_RedFuncs_t rf_pFuncs = {rf_RegularONPFunc, "Regular Old-New P", rf_SimpleONPFunc, "Simple Old-New P"};
RF_RedFuncs_t rf_pRecoveryFuncs = {rf_RecoveryPFunc, "Recovery P Func", rf_RecoveryPFunc, "Recovery P Func"};
void
rf_RegularONPFunc(RF_DagNode_t *node)
{
rf_RegularXorFunc(node);
}
void
rf_SimpleONPFunc(RF_DagNode_t *node)
{
rf_SimpleXorFunc(node);
}
void
rf_RecoveryPFunc(RF_DagNode_t *node)
{
rf_RecoveryXorFunc(node);
}
void
rf_RegularPFunc(RF_DagNode_t *node)
{
rf_RegularXorFunc(node);
}
#endif
#if (RF_INCLUDE_DECL_PQ > 0) || (RF_INCLUDE_RAID6 > 0)
static void
QDelta(char *dest, char *obuf, char *nbuf, unsigned length,
unsigned char coeff);
static void
rf_InvertQ(unsigned long *qbuf, unsigned long *abuf,
unsigned length, unsigned coeff);
RF_RedFuncs_t rf_qFuncs = {rf_RegularONQFunc, "Regular Old-New Q", rf_SimpleONQFunc, "Simple Old-New Q"};
RF_RedFuncs_t rf_qRecoveryFuncs = {rf_RecoveryQFunc, "Recovery Q Func", rf_RecoveryQFunc, "Recovery Q Func"};
RF_RedFuncs_t rf_pqRecoveryFuncs = {rf_RecoveryPQFunc, "Recovery PQ Func", rf_RecoveryPQFunc, "Recovery PQ Func"};
void
rf_PQDagSelect(
RF_Raid_t * raidPtr,
RF_IoType_t type,
RF_AccessStripeMap_t * asmap,
RF_VoidFuncPtr * createFunc)
{
RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
unsigned ndfail = asmap->numDataFailed;
unsigned npfail = asmap->numParityFailed;
unsigned ntfail = npfail + ndfail;
RF_ASSERT(RF_IO_IS_R_OR_W(type));
if (ntfail > 2) {
RF_ERRORMSG("more than two disks failed in a single group! Aborting I/O operation.\n");
*createFunc = NULL;
return;
}
if (type == RF_IO_TYPE_READ) {
switch (ndfail) {
case 0:
*createFunc = (RF_VoidFuncPtr) rf_CreateFaultFreeReadDAG;
break;
case 1:
if (ntfail == 2) {
if (asmap->failedPDAs[1]->type == RF_PDA_TYPE_PARITY)
*createFunc = (RF_VoidFuncPtr) rf_PQ_110_CreateReadDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_101_CreateReadDAG;
} else {
if (rf_NumFailedDataUnitsInStripe(raidPtr, asmap) == 2)
*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateReadDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_100_CreateReadDAG;
}
break;
case 2:
*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateReadDAG;
break;
}
return;
}
switch (ntfail) {
case 0:
if (rf_suppressLocksAndLargeWrites ||
(((asmap->numStripeUnitsAccessed <= (layoutPtr->numDataCol / 2)) && (layoutPtr->numDataCol != 1)) ||
(asmap->parityInfo->next != NULL) || (asmap->qInfo->next != NULL) || rf_CheckStripeForFailures(raidPtr, asmap))) {
*createFunc = (RF_VoidFuncPtr) rf_PQCreateSmallWriteDAG;
} else {
*createFunc = (RF_VoidFuncPtr) rf_PQCreateLargeWriteDAG;
}
break;
case 1:
if (npfail == 1) {
RF_ASSERT((asmap->failedPDAs[0]->type == RF_PDA_TYPE_PARITY) || (asmap->failedPDAs[0]->type == RF_PDA_TYPE_Q));
if (asmap->failedPDAs[0]->type == RF_PDA_TYPE_Q) {
if (((asmap->numStripeUnitsAccessed <= (layoutPtr->numDataCol / 2)) || (asmap->numStripeUnitsAccessed == 1))
|| rf_NumFailedDataUnitsInStripe(raidPtr, asmap))
*createFunc = (RF_VoidFuncPtr) rf_PQ_001_CreateSmallWriteDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_001_CreateLargeWriteDAG;
} else {
if (((asmap->numStripeUnitsAccessed <= (layoutPtr->numDataCol / 2)) || (asmap->numStripeUnitsAccessed == 1))
|| rf_NumFailedDataUnitsInStripe(raidPtr, asmap))
*createFunc = (RF_VoidFuncPtr) rf_PQ_010_CreateSmallWriteDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_010_CreateLargeWriteDAG;
}
} else {
if (rf_NumFailedDataUnitsInStripe(raidPtr, asmap) == 2)
*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateWriteDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_100_CreateWriteDAG;
}
break;
case 2:
switch (npfail) {
case 2:
*createFunc = (RF_VoidFuncPtr) rf_PQ_011_CreateWriteDAG;
break;
case 1:
RF_ASSERT(asmap->failedPDAs[0]->type == RF_PDA_TYPE_DATA);
RF_ASSERT((asmap->failedPDAs[1]->type == RF_PDA_TYPE_PARITY) || (asmap->failedPDAs[1]->type == RF_PDA_TYPE_Q));
if (asmap->failedPDAs[1]->type == RF_PDA_TYPE_Q)
*createFunc = (RF_VoidFuncPtr) rf_PQ_101_CreateWriteDAG;
else
*createFunc = (RF_VoidFuncPtr) rf_PQ_110_CreateWriteDAG;
break;
case 0:
*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateWriteDAG;
break;
}
break;
default:
*createFunc = NULL;
RF_PANIC();
}
return;
}
#if 0
static void
PQOne(RF_Raid_t *raidPtr, int *nSucc, int *nAnte, RF_AccessStripeMap_t *asmap)
{
*nSucc = *nAnte = 1;
}
static void
PQOneTwo(RF_Raid_t *raidPtr, int *nSucc, int *nAnte, RF_AccessStripeMap_t *asmap)
{
*nSucc = 1;
*nAnte = 2;
}
#endif
RF_CREATE_DAG_FUNC_DECL(rf_PQCreateLargeWriteDAG)
{
rf_CommonCreateLargeWriteDAG(raidPtr, asmap, dag_h, bp, flags, allocList, 2,
rf_RegularPQFunc, RF_FALSE);
}
void
rf_RegularONQFunc(RF_DagNode_t *node)
{
int np = node->numParams;
int d;
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
int i;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
char *qbuf, *qpbuf;
char *obuf, *nbuf;
RF_PhysDiskAddr_t *old, *new;
unsigned long coeff;
unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
RF_ETIMER_START(timer);
d = (np - 3) / 4;
RF_ASSERT(4 * d + 3 == np);
qbuf = (char *) node->params[2 * d + 1].p;
for (i = 0; i < d; i++) {
old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
obuf = (char *) node->params[2 * i + 1].p;
new = (RF_PhysDiskAddr_t *) node->params[2 * (d + 1 + i)].p;
nbuf = (char *) node->params[2 * (d + 1 + i) + 1].p;
RF_ASSERT(new->numSector == old->numSector);
RF_ASSERT(new->raidAddress == old->raidAddress);
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), new->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, old->startSector % secPerSU);
QDelta(qpbuf, obuf, nbuf, rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
}
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
rf_GenericWakeupFunc(node, 0);
}
void
rf_SimpleONQFunc(RF_DagNode_t *node)
{
int np = node->numParams;
int d;
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
int i;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
char *qbuf;
char *obuf, *nbuf;
RF_PhysDiskAddr_t *old, *new;
unsigned long coeff;
RF_ETIMER_START(timer);
d = (np - 3) / 4;
RF_ASSERT(4 * d + 3 == np);
qbuf = (char *) node->params[2 * d + 1].p;
for (i = 0; i < d; i++) {
old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
obuf = (char *) node->params[2 * i + 1].p;
new = (RF_PhysDiskAddr_t *) node->params[2 * (d + 1 + i)].p;
nbuf = (char *) node->params[2 * (d + 1 + i) + 1].p;
RF_ASSERT(new->numSector == old->numSector);
RF_ASSERT(new->raidAddress == old->raidAddress);
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), new->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
QDelta(qbuf, obuf, nbuf, rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
}
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
rf_GenericWakeupFunc(node, 0);
}
RF_CREATE_DAG_FUNC_DECL(rf_PQCreateSmallWriteDAG)
{
rf_CommonCreateSmallWriteDAG(raidPtr, asmap, dag_h, bp, flags, allocList, &rf_pFuncs, &rf_qFuncs);
}
static void RegularQSubr(RF_DagNode_t *node, char *qbuf);
static void
RegularQSubr(RF_DagNode_t *node, char *qbuf)
{
int np = node->numParams;
int d;
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
int i;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
char *obuf, *qpbuf;
RF_PhysDiskAddr_t *old;
unsigned long coeff;
RF_ETIMER_START(timer);
d = (np - 1) / 2;
RF_ASSERT(2 * d + 1 == np);
for (i = 0; i < d; i++) {
old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
obuf = (char *) node->params[2 * i + 1].p;
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), old->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, old->startSector % secPerSU);
rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf, rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
}
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
}
static void DegrQSubr(RF_DagNode_t *node);
static void
DegrQSubr(RF_DagNode_t *node)
{
int np = node->numParams;
int d;
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
int i;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
char *qbuf = node->results[1];
char *obuf, *qpbuf;
RF_PhysDiskAddr_t *old;
unsigned long coeff;
unsigned fail_start;
int j;
old = (RF_PhysDiskAddr_t *) node->params[np - 2].p;
fail_start = old->startSector % secPerSU;
RF_ETIMER_START(timer);
d = (np - 2) / 2;
RF_ASSERT(2 * d + 2 == np);
for (i = 0; i < d; i++) {
old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
obuf = (char *) node->params[2 * i + 1].p;
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), old->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
j = old->startSector % secPerSU;
RF_ASSERT(j >= fail_start);
qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, j - fail_start);
rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf, rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
}
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
}
void
rf_RegularPQFunc(RF_DagNode_t *node)
{
RegularQSubr(node, node->results[1]);
rf_RegularXorFunc(node);
}
void
rf_RegularQFunc(RF_DagNode_t *node)
{
RegularQSubr(node, node->results[0]);
rf_GenericWakeupFunc(node, 0);
}
void
rf_Degraded_100_PQFunc(RF_DagNode_t *node)
{
int np = node->numParams;
RF_ASSERT(np >= 2);
DegrQSubr(node);
rf_RecoveryXorFunc(node);
}
void
rf_RecoveryQFunc(RF_DagNode_t *node)
{
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
RF_PhysDiskAddr_t *failedPDA = (RF_PhysDiskAddr_t *) node->params[node->numParams - 2].p;
int i;
RF_PhysDiskAddr_t *pda = NULL;
RF_RaidAddr_t suoffset, failedSUOffset = rf_StripeUnitOffset(layoutPtr, failedPDA->startSector);
char *srcbuf, *destbuf;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
unsigned long coeff;
RF_ETIMER_START(timer);
memcpy(node->results[0], node->params[node->numParams - 3].p,
rf_RaidAddressToByte(raidPtr, failedPDA->numSector));
for (i = 0; i < node->numParams - 4; i += 2) {
RF_ASSERT(node->params[i + 1].p != node->results[0]);
pda = (RF_PhysDiskAddr_t *) node->params[i].p;
srcbuf = (char *) node->params[i + 1].p;
suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
destbuf = ((char *) node->results[0]) + rf_RaidAddressToByte(raidPtr, suoffset - failedSUOffset);
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), pda->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
rf_IncQ((unsigned long *) destbuf, (unsigned long *) srcbuf, rf_RaidAddressToByte(raidPtr, pda->numSector), coeff);
}
coeff = (rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), failedPDA->startSector) % raidPtr->Layout.numDataCol);
rf_InvertQ(node->results[0], node->results[0], rf_RaidAddressToByte(raidPtr, pda->numSector), coeff);
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
rf_GenericWakeupFunc(node, 0);
}
void
rf_RecoveryPQFunc(RF_DagNode_t *node)
{
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
printf("raid%d: Recovery from PQ not implemented.\n",raidPtr->raidid);
}
void
rf_PQ_DegradedWriteQFunc(RF_DagNode_t *node)
{
int np = node->numParams;
int d;
RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
int i;
RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
RF_Etimer_t timer;
char *qbuf = node->results[0];
char *obuf, *qpbuf;
RF_PhysDiskAddr_t *old;
unsigned long coeff;
int fail_start, j;
old = (RF_PhysDiskAddr_t *) node->params[np - 2].p;
fail_start = old->startSector % secPerSU;
RF_ETIMER_START(timer);
d = (np - 2) / 2;
RF_ASSERT(2 * d + 2 == np);
for (i = 0; i < d; i++) {
old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
obuf = (char *) node->params[2 * i + 1].p;
coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout), old->raidAddress);
coeff = (coeff % raidPtr->Layout.numDataCol);
j = old->startSector % secPerSU;
RF_ASSERT(j >= fail_start);
qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, j - fail_start);
rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf, rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
}
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->q_us += RF_ETIMER_VAL_US(timer);
rf_GenericWakeupFunc(node, 0);
}
void
rf_IncQ(unsigned long *dest, unsigned long *buf, unsigned length, unsigned coeff)
{
unsigned long a, d, new;
unsigned long a1, a2;
unsigned int *q = &(rf_qfor[28 - coeff][0]);
unsigned r = rf_rn[coeff + 1];
#define EXTRACT(a,i) ((a >> (5L*i)) & 0x1f)
#define INSERT(a,i) (a << (5L*i))
length /= 8;
while (length) {
a = *buf++;
d = *dest;
a1 = EXTRACT(a, 0) ^ r;
a2 = EXTRACT(a, 1) ^ r;
new = INSERT(a2, 1) | a1;
a1 = EXTRACT(a, 2) ^ r;
a2 = EXTRACT(a, 3) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 2) | INSERT(a2, 3);
a1 = EXTRACT(a, 4) ^ r;
a2 = EXTRACT(a, 5) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 4) | INSERT(a2, 5);
a1 = EXTRACT(a, 5) ^ r;
a2 = EXTRACT(a, 6) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 5) | INSERT(a2, 6);
#if RF_LONGSHIFT > 2
a1 = EXTRACT(a, 7) ^ r;
a2 = EXTRACT(a, 8) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 7) | INSERT(a2, 8);
a1 = EXTRACT(a, 9) ^ r;
a2 = EXTRACT(a, 10) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 9) | INSERT(a2, 10);
a1 = EXTRACT(a, 11) ^ r;
a2 = EXTRACT(a, 12) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 11) | INSERT(a2, 12);
#endif
d ^= new;
*dest++ = d;
length--;
}
}
static void
QDelta(
char *dest,
char *obuf,
char *nbuf,
unsigned length,
unsigned char coeff)
{
#ifndef _KERNEL
unsigned long a, d, new;
unsigned long a1, a2;
unsigned int *q = &(rf_qfor[28 - coeff][0]);
unsigned int r = rf_rn[coeff + 1];
r = a1 = a2 = new = d = a = 0;
q = NULL;
#endif
#ifdef _KERNEL
memset(dest, 0, length);
#else
length /= 8;
while (length) {
a = *obuf++;
a ^= *nbuf++;
d = *dest;
a1 = EXTRACT(a, 0) ^ r;
a2 = EXTRACT(a, 1) ^ r;
a1 = q[a1];
a2 = q[a2];
new = INSERT(a2, 1) | a1;
a1 = EXTRACT(a, 2) ^ r;
a2 = EXTRACT(a, 3) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 2) | INSERT(a2, 3);
a1 = EXTRACT(a, 4) ^ r;
a2 = EXTRACT(a, 5) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 4) | INSERT(a2, 5);
a1 = EXTRACT(a, 5) ^ r;
a2 = EXTRACT(a, 6) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 5) | INSERT(a2, 6);
#if RF_LONGSHIFT > 2
a1 = EXTRACT(a, 7) ^ r;
a2 = EXTRACT(a, 8) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 7) | INSERT(a2, 8);
a1 = EXTRACT(a, 9) ^ r;
a2 = EXTRACT(a, 10) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 9) | INSERT(a2, 10);
a1 = EXTRACT(a, 11) ^ r;
a2 = EXTRACT(a, 12) ^ r;
a1 = q[a1];
a2 = q[a2];
new = new | INSERT(a1, 11) | INSERT(a2, 12);
#endif
d ^= new;
*dest++ = d;
length--;
}
#endif
}
void
rf_PQ_recover(unsigned long *pbuf, unsigned long *qbuf, unsigned long *abuf, unsigned long *bbuf, unsigned length, unsigned coeff_a, unsigned coeff_b)
{
unsigned long p, q, a, a0, a1;
int col = (29 * coeff_a) + coeff_b;
unsigned char *q0 = &(rf_qinv[col][0]);
length /= 8;
while (length) {
p = *pbuf++;
q = *qbuf++;
a0 = EXTRACT(p, 0);
a1 = EXTRACT(q, 0);
a = q0[a0 << 5 | a1];
#define MF(i) \
a0 = EXTRACT(p,i); \
a1 = EXTRACT(q,i); \
a = a | INSERT(q0[a0<<5 | a1],i)
MF(1);
MF(2);
MF(3);
MF(4);
MF(5);
MF(6);
#if 0
MF(7);
MF(8);
MF(9);
MF(10);
MF(11);
MF(12);
#endif
*abuf++ = a;
*bbuf++ = a ^ p;
length--;
}
}
static void
rf_InvertQ(
unsigned long *qbuf,
unsigned long *abuf,
unsigned length,
unsigned coeff)
{
unsigned long a, new;
unsigned long a1, a2;
unsigned int *q = &(rf_qfor[3 + coeff][0]);
unsigned r = rf_rn[coeff + 1];
length /= 8;
while (length) {
a = *qbuf++;
a1 = EXTRACT(a, 0);
a2 = EXTRACT(a, 1);
a1 = r ^ q[a1];
a2 = r ^ q[a2];
new = INSERT(a2, 1) | a1;
#define M(i,j) \
a1 = EXTRACT(a,i); \
a2 = EXTRACT(a,j); \
a1 = r ^ q[a1]; \
a2 = r ^ q[a2]; \
new = new | INSERT(a1,i) | INSERT(a2,j)
M(2, 3);
M(4, 5);
M(5, 6);
#if RF_LONGSHIFT > 2
M(7, 8);
M(9, 10);
M(11, 12);
#endif
*abuf++ = new;
length--;
}
}
#endif