Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Littlefs only syncing with fsync, but not fflush #15840

Open
1 task done
linguini1 opened this issue Feb 14, 2025 · 3 comments
Open
1 task done

[BUG] Littlefs only syncing with fsync, but not fflush #15840

linguini1 opened this issue Feb 14, 2025 · 3 comments
Labels
Arch: arm Issues related to ARM (32-bit) architecture Area: File System File System issues OS: Linux Issues related to Linux (building system, etc) Type: Bug Something isn't working

Comments

@linguini1
Copy link
Contributor

Description / Steps to reproduce the issue

This may not be considered a bug, but the behaviour of fflush on files in a littlefs filesystem does not match what I would expect to see.

My setup is an RP2040 based MCU connected to an SD card over SPI1. The SD card has two partitions, the first one is a FAT filesystem and the second is a littlefs file system. The littlefs file system is mounted at /pwrfs

When I run the following code with a littlefs file system and interrupt its execution by removing power to my board, none of logs that were written in the main loop are visible in the log file. The file was created, but that's it:

  /* Open power safe file system */

  pwrfs = fopen("/pwrfs/somefile.bin", "w");
  if (pwrfs < 0)
    {
      err = errno;
      fprintf(stderr, "Couldn't open power safe log file: %d\n", err);
      pthread_exit((void *)(long)err);
    }

  pthread_cleanup_push(close_fd, pwrfs);

  /* Log sensor data continuously */

  char data[] = "Some data\n";
  for (;;)
    {
      printf("Logging...\n");
      b_written = fwrite(data, 1, sizeof(data), pwrfs);
      if (b_written <= 0)
        {
          err = errno;
          fprintf(stderr, "Couldn't write data to logfile: %d\n", err);
          continue;
        }

      err = fflush(pwrfs);
      if (err == EOF)
        {
          err = errno;
          fprintf(stderr, "Couldn't flush logfile: %d\n", err);
        }

      printf("Logged\n");
      usleep(10000);
    }

  pthread_cleanup_pop(1); /* Close pwrfs */

I have also tried limiting the loop to 10 iterations, then closing the file with fclose after the loop and then cutting power. The file still remains empty.

After some searching through file system debug logs, I saw the echo command makes calls to lfs_file_sync. This appears only to be accessible through littlefs_sync and littlefs_open from my grepping in the source code. I decided to try operating on my log file using file descriptors instead of the FILE API. The following code performs exactly how I would expect:

  /* Open power safe file system */

  pwrfs = open("/pwrfs/somefile.bin", O_WRONLY | O_CREAT);
  if (pwrfs < 0)
    {
      err = errno;
      fprintf(stderr, "Couldn't open power safe log file: %d\n", err);
      pthread_exit((void *)(long)err);
    }

  pthread_cleanup_push(close_fd, pwrfs);

  /* Log sensor data continuously */

  char data[] = "Some data\n";
  for (;;)
    {
      printf("Logging...\n");
      b_written = write(pwrfs, data, sizeof(data));
      if (b_written <= 0)
        {
          err = errno;
          fprintf(stderr, "Couldn't write data to logfile: %d\n", err);
          continue;
        }

      err = fsync(pwrfs);
      if (err < 0)
        {
          err = errno;
          fprintf(stderr, "Couldn't sync logfile: %d\n", err);
        }

      printf("Logged\n");
      usleep(10000);
    }

  pthread_cleanup_pop(1); /* Close pwrfs */

No matter when I pull the power on my board, logs are preserved in the log file and I can access the contents on the next boot, instead of being greeted by an empty file.

I don't know if there would be any other similar/equivalent FILE API function to fsync besides fflush, so I would intuitively expect that this function would sync the file system to "disk" underneath the hood. Maybe that's an incorrect/naive assumption. If so, could anyone tell me an alternative option so I can use the FILE API but still have my log file be synced? I've let the program run for quite a few iterations and I still observe an empty file. Does the file ever get synced when modified through the FILE API? And when?

On which OS does this issue occur?

[OS: Linux]

What is the version of your OS?

Linux 6.13.2-arch1-1 #1 SMP PREEMPT_DYNAMIC Sat, 08 Feb 2025 18:54:55 +0000 x86_64 GNU/Linux

NuttX Version

e538855

Issue Architecture

[Arch: arm]

Issue Area

[Area: File System]

Host information

No response

Verification

  • I have verified before submitting the report.
@linguini1 linguini1 added the Type: Bug Something isn't working label Feb 14, 2025
@github-actions github-actions bot added Arch: arm Issues related to ARM (32-bit) architecture Area: File System File System issues OS: Linux Issues related to Linux (building system, etc) labels Feb 14, 2025
@yamt
Copy link
Contributor

yamt commented Feb 14, 2025

This may not be considered a bug, but the behaviour of fflush on files in a littlefs filesystem does not match what I would expect to see.

iirc, fflush only ought to flush the stream buffer.
ie. i don't consider it a bug.

could anyone tell me an alternative option so I can use the FILE API but still have my log file be synced?

how about fsync(fileno(fp))?

@linguini1
Copy link
Contributor Author

how about fsync(fileno(fp))?

Thank you! I think this should solve my issue!

However, my other question I am still wondering about: when during normal file operations can I expect the file to be synced to the "disk" (in this case SD card)? Regular fopen, fwrite, fclose sequence still leaves a completely blank log file. Surely fclose should trigger a synchronization to make sure no information is lost. How come after running fwrite for many iterations there is still no file contents actually written to the card?

@yamt
Copy link
Contributor

yamt commented Feb 17, 2025

how about fsync(fileno(fp))?

Thank you! I think this should solve my issue!

However, my other question I am still wondering about: when during normal file operations can I expect the file to be synced to the "disk" (in this case SD card)? Regular fopen, fwrite, fclose sequence still leaves a completely blank log file. Surely fclose should trigger a synchronization to make sure no information is lost. How come after running fwrite for many iterations there is still no file contents actually written to the card?

api-wise, none of these operations guarantee to preserve data on ungraceful shutdown events like power loss.
implementation-wise, usually these operations at least sometimes flush data to the underlying device.

re-reading your description,

I have also tried limiting the loop to 10 iterations, then closing the file with fclose after the loop and then cutting power. The file still remains empty.

this part looks weird to me because, iirc, littlefs close() commits the modifications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Arch: arm Issues related to ARM (32-bit) architecture Area: File System File System issues OS: Linux Issues related to Linux (building system, etc) Type: Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants