📄 tffs.c
字号:
tw(tr(TR_FUNC, TrPowerfail, "powerfail_test_begin(0x%x, 0x%x, 0x%x) \n",
mode, offset, mask));
memset(&powerfail, 0 ,sizeof(powerfail));
powerfail.mode = mode;
powerfail.offset = offset;
powerfail.bytemask = mask;
}
// NOTEME change to macro?
void powerfail_test_end(void)
{
tw(tr(TR_FUNC, TrPowerfail, "powerfail_test_end() \n"));
powerfail.mode = 0;
powerfail.enable = 0;
// NOTEME: Display stats?
}
// NOTEME change to macro?
// In the start of the journal code and in other critical places (start of
// each domain), the below function is called. The function enables and
// disables powerfail. It is important that it called before any driver
// write in the current domain.
void powerfail_domain_begin(int domain)
{
if (powerfail.mode == 0)
return;
tw(tr(TR_FUNC, TrPowerfail, "powerfail_begin_domain(0x%x)\n", domain));
powerfail.enable = powerfail.mode & domain;
}
// NOTEME change to macro?
// This function is called at the end of each domain.
void powerfail_domain_end(void)
{
if (powerfail.enable == 0)
return;
tw(tr(TR_FUNC, TrPowerfail, "powerfail_end_domain()\n"));
powerfail.enable = 0;
}
int powerfail_suspend_test(void)
{
int mode = powerfail.mode;
powerfail.mode = 0;
powerfail.enable = 0;
return mode;
}
void powerfail_resume_test(int mode)
{
powerfail.mode = mode;
}
// The below function sets the absolute address used for powerfail. It is
// important that the function powerfail_set_domain() is called before this
// function as that function determine if the powerfail is enabled or not
// for this domain. Typical 'addr' will be the address of a inode or block
// header.
void powerfail_set_addr(int addr)
{
if (POWERFAIL_ENABLED) {
tw(tr(TR_FUNC, TrPowerfail, "powerfail_set_addr(0x%x)\n", addr));
if (powerfail.mode & PFM_RANDOM)
return; // Skip because this test do not use addr
else if (powerfail.mode & PFM_NEXTINODE)
powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.i);
else if (powerfail.mode & PFM_DIRI)
powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.diri);
else if (powerfail.mode & PFM_OLDI)
powerfail.addr = powerfail.offset + (int)inode_addr(fs.journal.oldi);
else if (powerfail.mode & PFM_JOURNAL) {
struct inode_s *ip = inode_addr(fs.ijournal);
powerfail.addr = (int)offset2addr(location2offset(ip->location) +
fs.journal_pos);
}
else if (powerfail.mode & PFM_BLOCKHEADER)
powerfail.addr = powerfail.offset + addr;
else {
tw(tr(TR_FUNC, TrAll, "unknown powerfail mode\n"));
// NOTEME use other error code?
powerfail_fatal_error(-1);
}
// Aligned to halfword?
if (powerfail.addr & 1) {
// Not aligned, aligne addr and fix mask
powerfail.addr--;
powerfail.halfwordmask = powerfail.bytemask << 8;
}
else
powerfail.halfwordmask = powerfail.bytemask;
tw(tr(TR_FUNC, TrPowerfail, "pf addr 0x%x, mask 0x%x \n", powerfail.addr,
powerfail.halfwordmask));
}
}
// The following function is called conditionally from within the driver's
// write function. The condition gating the call is (powerfail.mode > 0 and
// powerfail.enable > 0).
void powerfail_write(volatile uint16 *addr, uint16 value)
{
int mode;
tw(tr(TR_FUNC, TrPowerfail, "powerfail_write(0x%x, 0x%x)\n", addr, value));
if (powerfail.mode & PFM_RANDOM) {
// Not all these calls trigger a powerfail..
if ((rand() % powerfail.offset) > 0)
return; // Skip powerfail
powerfail_simulate_random_write_failures(addr, value);
}
else if (powerfail.mode & (PFM_INODES | PFM_JOURNAL | PFM_BLOCKHEADER)) {
// Is this the addr and is the mask bits changed?
if ((int)addr != powerfail.addr || ((*addr ^ value) & powerfail.halfwordmask) == 0)
return; // Skip powerfail
powerfail_simulate_write_failures(addr, value);
}
else {
tw(tr(TR_FUNC, TrAll, "Unknown powerfail_mode\n"));
// NOTEME use other error code
powerfail_fatal_error(-2);
}
tmp_fix_trace_indent();
powerfail.step++;
if (!(powerfail.mode & PFM_NOINIT)) {
mode = powerfail_suspend_test();
// Now re-initialise FFS. Then resume at the point we were before.
// We should ensure that all static variables of ffs are zeroed during
// re-initialization!
tw(tr(TR_FUNC, TrPowerfail,"re-initialise FFS (step %d)\n", powerfail.step));
memset(&fs, 0, sizeof(struct fs_s));
ffs_initialize();
powerfail_resume_test(mode);
}
tw(tr(TR_FUNC, TrPowerfail, "jump..\n"));
longjmp(powerfail.env, 0);
}
// This function is used to simulate the Intel Strata Flash. It only
// corrupts the cells that are modified by the write and it only corrupts
// the cells in the way it can happen in a real device e.g. if the existing
// cell level is 0 (11b) and a write of level 2 (01b) is corrupted, the
// function will "corrupt" the cell by setting the state to either 0, 1 or 2
// and not to 3 (00b) as this is not how the Strata flash work.
int powerfail_simulate_random_write_failures(volatile uint16 *addr, uint16 src)
{
int i;
uint16 out_cell, flash_cell, src_cell;
uint16 mask, out;
uint16 diff;
out = *addr;
tw(tr(TR_FUNC, TrPowerfail, "pf_sim_random_wr_fail(0x%x, 0x%x)\n",
addr, src));
tw(tr(TR_FUNC, TrTest,
"POWER FAIL AT ADDR: 0x%x. Before 0x%x\n"
"\t\t\t\t Wanted 0x%x\n", addr, *addr, src));
if (~*addr & src) {
ffsdrv_write_error(*addr, src);
// NOTEME use other error code?
powerfail_fatal_error(-3);
}
// Isolate and corrupt cell per cell
for (i = 0; i < 16; i++) {
// Move flash and src down to lowest 'cell'
flash_cell = *addr >> (i * 2);
src_cell = src >> (i * 2);
// Maskout all bits except from the 2 lowest bits
flash_cell &= 0x03;
src_cell &= 0x03;
diff = flash_cell - src_cell;
if (diff == 0)
continue; // Skip, not possible to corrupt the cell
out_cell = flash_cell - (rand() % (diff + 1));
if (out_cell < src_cell) {
tw(tr(TR_NULL, TrTest,"Bad out_cell, src 0x%x, out 0x%x\n",
src_cell, out_cell));
// NOTEME use other error code?
powerfail_fatal_error(-4);
}
// Move the cell back to the original place
out_cell = out_cell << (i * 2);
// Set all bits except from the current cell
mask = 0x3 << (i * 2);
out_cell |= ~mask;
// Added the result to the output data
out = out_cell & out;
}
tw(tr(TR_NULL, TrTest,"\t\t\t\t Result 0x%x\n", out));
if (~*addr & out) {
ffsdrv_write_error(*addr, out);
// NOTEME use other error code?
powerfail_fatal_error(-5);
}
*addr = out;
return EFFS_OK;
}
// Simulate write failures if the powerfail.mask bits are changed by src. If
// the mask bits are modified by src, 'write' the mask bits and return 0. If
// the mask bits not are valid, trace and return an error. The mask bits are
// invalid if the mask 'cell' level is higher than src.
int powerfail_simulate_write_failures(volatile uint16 *addr, uint16 src)
{
int i;
uint16 out_cell, flash_cell, src_cell, mask_cell;
uint16 out;
out = *addr;
tw(tr(TR_FUNC, TrPowerfail, "pf_sim_wr_fail(0x%x, 0x%x)\n", addr, src));
tw(tr(TR_FUNC, TrTest, "POWER FAIL AT ADDR: 0x%x. Before 0x%x\n"
"\t\t\t\t Wanted 0x%x\n", addr, *addr, src));
// Isolate and corrupt cell per cell
for (i = 0; i < 16; i++) {
// Move flash and src down to lowest 'cell'
flash_cell = *addr >> (i * 2);
src_cell = src >> (i * 2);
mask_cell = ~(powerfail.halfwordmask >> (i * 2));
// Maskout all bits except from the 2 lowest bits
flash_cell &= 0x03;
src_cell &= 0x03;
mask_cell &= 0x03;
// The cell have changed and is marked by mask so check if the mask
// is valid. e.g. a write of '10b' cannot be set to '00b' by mask
if (mask_cell < src_cell) {
tw(tr(TR_FUNC, TrAll, "FATAL invalid mask: 0x%x\n", powerfail.halfwordmask));
// NOTEME use other error code?
powerfail_fatal_error(-6);
}
// Would the cell have been changed by src and is it marked by mask?
if ((flash_cell == src_cell) || (mask_cell == 3))
continue; // Skip, the cell will not be changed by src or the
// cell is not marked by mask
// Move the cell back to the original place
out_cell = mask_cell << (i * 2);
// Set all bits except from the current cell
out_cell |= ~(0x3 << (i * 2));
// Added the result to the output data
out &= out_cell;
}
tw(tr(TR_NULL, TrTest,"\t\t\t\t Result 0x%x\n", out));
*addr = out;
return EFFS_OK;
}
// PFM_ERASE: makes powerfail while erasing the block specified in pf
// variable 'offset'. Instead of erasing the flash it is filled with the
// pattern applied by the pf 'bytemask' (e.g. 0xFF, 0x0, 0xAA etc).
// PFM_NEXTERASE: makes powerfail at the next block erase. The 'bytemask 'is
// used to fill the block.
// PFM_RANDOM: the 'offset' value determine how often it triggers a
// powerfail. Difference patterns are used to fill the block */
void powerfail_erase(uint8 block)
{
int i, mode;
uint8 *addr;
tw(tr(TR_FUNC, TrPowerfail, "powerfail_erase(%d)\n", block));
if (powerfail.mode & PFM_RANDOM) {
if ((rand() % powerfail.offset) > 0)
return; // Skip powerfail
powerfail.bytemask = rand() % 0xFF;
}
else if (powerfail.mode & PFM_ERASE) {
if (powerfail.offset != block)
return; // Skip powerfail
}
else if (powerfail.mode & PFM_NEXTERASE) {
// offset is not used, bytemask is set in powerfail_test_begin()
}
tw(tr(TR_FUNC, TrTest, "POWER FAIL AT BLOCK ERASE: %d \n", block));
// Fill the block with the bytemask
addr = block2addr(block);
for (i = 0; i < 1 << dev.binfo[block].size_ld; i++) {
*addr++ = powerfail.bytemask;
}
powerfail.step++;
if (!(powerfail.mode & PFM_NOINIT)) {
mode = powerfail_suspend_test();
// Now re-initialise FFS. Then resume at the point we were before.
// We should ensure that all static variables of ffs are zeroed during
// re-initialization!
tw(tr(TR_FUNC, TrPowerfail,"re-initialise FFS (step %d)\n", powerfail.step));
memset(&fs, 0, sizeof(struct fs_s));
ffs_initialize();
powerfail_resume_test(mode);
}
tw(tr(TR_FUNC, TrPowerfail, "jump..\n"));
longjmp(powerfail.env, 0);
}
void powerfail_fatal_error(int error)
{
tw(tr(TR_FUNC, TrAll, "FATAL pf error %d\n", error));
ttw(ttr(TTrFatal, "FATAL pf error %d" NL, error));
powerfail_display_stats();
exit(1);
}
void powerfail_display_stats(void)
{
tw(tr(TR_FUNC, TrTest, "powerfail: \n"));
tw(tr(TR_FUNC, TrTest, " mode: 0x%x\n", powerfail.mode));
tw(tr(TR_FUNC, TrTest, " boundary: 0x%x\n", powerfail.boundary));
tw(tr(TR_FUNC, TrTest, " step: 0x%x\n", powerfail.step));
ttw(ttr(TTrAll, "powerfail:" NL));
ttw(ttr(TTrAll, "mode: 0x%x" NL, powerfail.mode));
ttw(ttr(TTrAll, "boundary: 0x%x" NL, powerfail.boundary));
ttw(ttr(TTrAll, "step: 0x%x" NL, powerfail.step));
}
void tmp_fix_trace_indent(void)
{
if (powerfail.boundary == JOURNAL_TEST_COMMITTING)
tw(tr(TR_END, TrJournal, "} dummy \n"));
if (powerfail.boundary == JOURNAL_TEST_READY)
tw(tr(TR_END, TrJournal, "} dummy \n"));
tw(tr(TR_END, TrJournal, "} dummy \n"));
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -