From patchwork Thu Mar 27 21:07:38 2008 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Artur Skawina X-Patchwork-Id: 12637 Received: from mx10.go2.pl ([193.17.41.74] helo=poczta.o2.pl) by www.linuxtv.org with esmtp (Exim 4.63) (envelope-from ) id 1JezKF-0005cy-Ur for vdr@linuxtv.org; Thu, 27 Mar 2008 22:07:59 +0100 Received: from poczta.o2.pl (mx10.go2.pl [127.0.0.1]) by poczta.o2.pl (Postfix) with ESMTP id 654AA58020 for ; Thu, 27 Mar 2008 22:07:40 +0100 (CET) Received: from [172.19.43.221] (ip-89-174-120-215.multimo.gtsenergis.pl [89.174.120.215]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by poczta.o2.pl (Postfix) with ESMTP for ; Thu, 27 Mar 2008 22:07:39 +0100 (CET) Message-ID: <47EC0C9A.2070208@o2.pl> Date: Thu, 27 Mar 2008 22:07:38 +0100 From: Artur Skawina User-Agent: Thunderbird 2.0.0.14pre (X11/20080323) MIME-Version: 1.0 To: VDR Mailing List X-LSpam-Score: -2.6 (--) X-LSpam-Report: No, score=-2.6 required=5.0 tests=BAYES_00=-2.599 autolearn=ham Subject: [vdr] [PATCH] Direct I/O support v1 X-BeenThere: vdr@linuxtv.org X-Mailman-Version: 2.1.9 Precedence: list Reply-To: VDR Mailing List List-Id: VDR Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 27 Mar 2008 21:07:59 -0000 Status: O X-Status: X-Keywords: X-UID: 16287 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);