[PATCH] Direct I/O support v1

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This change makes vdr write the video stream directly to disk, bypassing
OS caches. O_DIRECT is used if it is supported, falling back to "normal"
I/O and fadvise() if not. Only recording is affected, playback remains
unchanged.

I did this to see if using direct i/o made sense for vdr, wasn't convinced
before. Avoiding all the fadvise hacks and especially the fdatasyncs on
close seems to be worth it though.
Only lightly tested, but seems to work, i tried a few recordings on both
ext3 and NFS and didn't notice any missing data yet ;) Upgraded all vdrs
and will keep testing for some time, but in case anyone else would like
to try (or even better improve) it, patch is below.

This patch is based on top of the one I posted in the "Problems playing
ongoing recordings?" thread, so you'll need to apply that one before
applying this one. Should work on both 1.4 and 1.6.

artur
diff --git a/tools.c b/tools.c
index a14f799..b7281b0 100644
--- a/tools.c
+++ b/tools.c
@@ -1054,6 +1054,11 @@ bool cSafeFile::Close(void)
 // --- cUnbufferedFile -------------------------------------------------------
 
 #define USE_FADVISE
+#define USE_DIRECTIO
+
+// O_DIRECT can have various alignment restrictions, usually at most
+// the block size of the filesystem. 4096 bytes should be enough.
+#define ALIGN_DIO 4096
 
 //#define dfsyslog dsyslog			// uncomment to turn on fadvise related logging
 #define dfsyslog(a...) do {} while (0)
@@ -1071,7 +1076,17 @@ cUnbufferedFile::~cUnbufferedFile()
 int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
 {
   Close();
-  fd = open(FileName, Flags, Mode);
+#ifdef USE_DIRECTIO
+  if (Flags&(O_WRONLY|O_RDWR)) {
+     fd = open(FileName, Flags|O_DIRECT, Mode);
+     directio = 1;
+  }
+#endif
+  if (fd==-1) {
+     directio = 0;
+     fd = open(FileName, Flags, Mode);
+  }
+  dsyslog("Using %s IO to access %s", directio?"DIRECT":"normal", FileName);
   curpos = 0;
 #ifdef USE_FADVISE
   lastpos = 0;
@@ -1101,7 +1116,7 @@ int cUnbufferedFile::Close(void)
      free(wbuf);
      }
 #ifdef USE_FADVISE
-  if (fd >= 0) {
+  if (fd >= 0 || !directio) {
      if (totwritten)    // if we wrote anything make sure the data has hit the disk before
         fdatasync(fd);  // calling fadvise, as this is our last chance to un-cache it.
      posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
@@ -1243,7 +1258,36 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
 ssize_t cUnbufferedFile::WriteBuf(const void *Data, size_t Size)
 {
   if (fd >=0) {
-     ssize_t bytesWritten = safe_write(fd, Data, Size);
+     ssize_t bytesWritten;
+     
+#ifdef USE_DIRECTIO
+     if (directio) {
+        // write properly sized buffers directly.
+        if ((Size & (ALIGN_DIO-1)) == 0) {
+           bytesWritten = safe_write(fd, Data, Size);
+           curpos += bytesWritten;
+           return bytesWritten;
+        }
+        // in the unlikely case of a short write (inevitable when closing)
+        // pad the data with zeros, write it, then truncate the file.
+        int padding = ALIGN_DIO - (Size & (ALIGN_DIO-1));
+        memset((char *)Data+Size, 0, padding);
+        bytesWritten = safe_write(fd, Data, Size+padding);
+	
+        padding = bytesWritten - Size;
+        if (padding<0)
+           padding = 0;
+        bytesWritten -= padding;
+        curpos += bytesWritten;
+	
+        lseek(fd, -padding, SEEK_CUR);
+        ftruncate(fd, curpos);
+        // note: past this point the file offset is likely unaligned
+        // so further directio shouldn't happen.
+        return min(bytesWritten, (ssize_t)Size);
+     }
+#endif
+     bytesWritten = safe_write(fd, Data, Size);
      //dsyslog("WRIT: fd:%3d %9zd .. %9zd SIZE: %6zd", fd, curpos, curpos+Size, Size);
 #ifdef USE_FADVISE
      if (bytesWritten > 0) {
@@ -1296,18 +1340,23 @@ ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
 {
   if (!wbuf) {
      wbuf_chunk = cutting?MEGABYTE(8):MEGABYTE(4);
-     wbuf = MALLOC(uchar,wbuf_chunk);
-     if (!wbuf)
+     if (posix_memalign(&wbuf, ALIGN_DIO, wbuf_chunk)) {
+        directio = 0;
         return WriteBuf(Data, Size);
+     }
      wbuf_len = 0;
   }
   if (Size <= wbuf_chunk-wbuf_len) {
      memcpy(wbuf+wbuf_len, Data, Size);
      wbuf_len += Size;
   } else {
-     WriteBuf(wbuf, wbuf_len);
-     memcpy(wbuf, Data, Size);
-     wbuf_len = Size;
+     unsigned l = wbuf_chunk-wbuf_len;
+     if (l)
+        memcpy(wbuf+wbuf_len, Data, l);
+     WriteBuf(wbuf, wbuf_chunk);
+     
+     memcpy(wbuf, (char *)Data+l, Size-l);
+     wbuf_len = Size-l;
   }
   return Size;
 }
diff --git a/tools.h b/tools.h
index ce7283c..c2ff493 100644
--- a/tools.h
+++ b/tools.h
@@ -255,6 +255,7 @@ private:
   size_t written;
   size_t totwritten;
   int cutting;
+  int directio;
   size_t writebuffer;
   int FadviseDrop(off_t Offset, off_t Len);
   int FadviseRead(off_t Offset, off_t Len);
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux