[PATCH 8/8] dosfs: Cluster write optimization

Ralf Kirchner ralf.kirchner at embedded-brains.de
Thu Nov 29 14:32:46 UTC 2012


Seperate cluster write from sector write for quick file write.

New test fstests/fsdosfswrite01.
---
 cpukit/libfs/src/dosfs/fat.c                       |  240 +++++++++++++++-----
 cpukit/libfs/src/dosfs/fat.h                       |   36 ++-
 cpukit/libfs/src/dosfs/fat_fat_operations.c        |   14 +-
 cpukit/libfs/src/dosfs/fat_file.c                  |  231 +++++++++++++------
 cpukit/libfs/src/dosfs/msdos_misc.c                |   18 +-
 testsuites/fstests/Makefile.am                     |    1 +
 testsuites/fstests/configure.ac                    |    1 +
 testsuites/fstests/fsdosfsformat01/init.c          |   62 +++--
 testsuites/fstests/fsdosfswrite01/Makefile.am      |   19 ++
 .../fstests/fsdosfswrite01/fsdosfswrite01.doc      |   12 +
 testsuites/fstests/fsdosfswrite01/init.c           |  219 ++++++++++++++++++
 11 Dateien geändert, 675 Zeilen hinzugefügt(+), 178 Zeilen entfernt(-)
 create mode 100644 testsuites/fstests/fsdosfswrite01/Makefile.am
 create mode 100644 testsuites/fstests/fsdosfswrite01/fsdosfswrite01.doc
 create mode 100644 testsuites/fstests/fsdosfswrite01/fsdosfswrite01.scn
 create mode 100644 testsuites/fstests/fsdosfswrite01/init.c

diff --git a/cpukit/libfs/src/dosfs/fat.c b/cpukit/libfs/src/dosfs/fat.c
index 033c15d..c82bf5a 100644
--- a/cpukit/libfs/src/dosfs/fat.c
+++ b/cpukit/libfs/src/dosfs/fat.c
@@ -32,10 +32,23 @@ static ssize_t
                   uint32_t                             cln,
                   void                                *buff);
 
-static ssize_t
- fat_cluster_write(fat_fs_info_t                      *fs_info,
-                   uint32_t                            cln,
-                   const void                         *buff);
+static inline uint32_t
+fat_cluster_num_to_block_num (const fat_fs_info_t *fs_info,
+                              uint32_t             cln)
+{
+    uint32_t blk;
+
+    if ( (cln == 0) && (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)) )
+        blk = fat_sector_num_to_block_num(fs_info, fs_info->vol.rdir_loc);
+    else
+    {
+        cln -= FAT_RSRVD_CLN;
+        blk = cln << (fs_info->vol.bpc_log2 - fs_info->vol.bytes_per_block_log2);
+        blk += fat_sector_num_to_block_num(fs_info, fs_info->vol.data_fsec);
+    }
+
+    return blk;
+}
 
 int
 fat_buf_access(fat_fs_info_t   *fs_info,
@@ -188,7 +201,44 @@ _fat_block_read(
     return cmpltd;
 }
 
-/* _fat_block_write --
+static ssize_t
+ fat_block_write(
+    fat_fs_info_t                        *fs_info,
+    const uint32_t                        start_blk,
+    const uint32_t                        offset,
+    const uint32_t                        count,
+    const void                           *buf,
+    const bool                            overwrite_block)
+{
+    int                 rc             = RC_OK;
+    uint32_t            bytes_to_write = MIN(count, (fs_info->vol.bytes_per_block - offset));
+    uint8_t            *blk_buf;
+    uint32_t            sec_num        = fat_block_num_to_sector_num(fs_info, start_blk);
+
+    if (0 < bytes_to_write)
+    {
+        if (   overwrite_block
+            || (bytes_to_write == fs_info->vol.bytes_per_block))
+        {
+            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_GET, &blk_buf);
+        }
+        else
+            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_READ, &blk_buf);
+
+        if (RC_OK == rc)
+        {
+            memcpy(blk_buf + offset, buf, bytes_to_write);
+
+            fat_buf_mark_modified(fs_info);
+        }
+    }
+    if (RC_OK != rc)
+        return rc;
+    else
+        return bytes_to_write;
+}
+
+/* fat_sector_write --
  *     This function write 'count' bytes to device filesystem is mounted on,
  *     starts at 'start+offset' position where 'start' computed in sectors
  *     and 'offset' is offset inside sector (writing may cross sectors
@@ -206,7 +256,7 @@ _fat_block_read(
  *     and errno set appropriately
  */
 ssize_t
-_fat_block_write(
+fat_sector_write(
     fat_fs_info_t                        *fs_info,
     uint32_t                              start,
     uint32_t                              offset,
@@ -243,39 +293,84 @@ _fat_block_write(
     return cmpltd;
 }
 
-int
-_fat_block_zero(
-    fat_fs_info_t                        *fs_info,
-    uint32_t                              start,
-    uint32_t                              offset,
-    uint32_t                              count)
+static ssize_t
+ fat_block_set (
+     fat_fs_info_t                        *fs_info,
+     const uint32_t                        start_blk,
+     const uint32_t                        offset,
+     const uint32_t                        count,
+     const uint8_t                         pattern)
 {
-    int                 rc = RC_OK;
-    uint32_t            sec_num = start;
-    uint32_t            ofs = offset;
-    uint8_t            *sec_buf;
-    uint32_t            c = 0;
+    int                 rc             = RC_OK;
+    uint32_t            bytes_to_write = MIN(count, (fs_info->vol.bytes_per_block - offset));
+    uint8_t            *blk_buf;
+    uint32_t            sec_num        = fat_block_num_to_sector_num(fs_info, start_blk);
 
-    while(count > 0)
+    if (0 < bytes_to_write)
     {
-        c = MIN(count, (fs_info->vol.bps - ofs));
-
-        if (c == fs_info->vol.bps)
-            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_GET, &sec_buf);
+        if (bytes_to_write == fs_info->vol.bytes_per_block)
+        {
+            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_GET, &blk_buf);
+        }
         else
-            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_READ, &sec_buf);
-        if (rc != RC_OK)
-            return -1;
+            rc = fat_buf_access(fs_info, sec_num, FAT_OP_TYPE_READ, &blk_buf);
 
-        memset((block + ofs), 0, c);
+        if (RC_OK == rc)
+        {
+            memset(blk_buf + offset, pattern, bytes_to_write);
 
-        fat_buf_mark_modified(fs_info);
+            fat_buf_mark_modified(fs_info);
+        }
+    }
+    if (RC_OK != rc)
+        return rc;
+    else
+        return bytes_to_write;
+}
 
-        count -= c;
-        sec_num++;
-        ofs = 0;
+ssize_t
+fat_cluster_set(
+     fat_fs_info_t                        *fs_info,
+     const uint32_t                        start_cln,
+     const uint32_t                        offset,
+     const uint32_t                        count,
+     const uint8_t                         pattern)
+{
+  ssize_t             rc               = RC_OK;
+  uint32_t            bytes_to_write   = MIN(count, (fs_info->vol.bpc - offset));
+  uint32_t            cur_blk          = fat_cluster_num_to_block_num(fs_info, start_cln);
+  uint32_t            blocks_in_offset = offset >> fs_info->vol.bytes_per_block_log2;
+  uint32_t            ofs_blk          = offset - (blocks_in_offset << fs_info->vol.bytes_per_block_log2);
+  ssize_t             bytes_written    = 0;
+  ssize_t             ret;
+
+  cur_blk += blocks_in_offset;
+
+  while (   (RC_OK == rc)
+         && (0 < bytes_to_write))
+  {
+    uint32_t c = MIN(bytes_to_write, (fs_info->vol.bytes_per_block - ofs_blk));
+
+    ret = fat_block_set(
+        fs_info,
+        cur_blk,
+        ofs_blk,
+        c,
+        pattern);
+    if (c != ret)
+      rc = -1;
+    else
+    {
+        bytes_to_write -= ret;
+        bytes_written  += ret;
+        ++cur_blk;
     }
-    return 0;
+    ofs_blk = 0;
+  }
+  if (RC_OK != rc)
+    return rc;
+  else
+    return bytes_written;
 }
 
 /* _fat_block_release --
@@ -322,12 +417,18 @@ fat_cluster_read(
 }
 
 /* fat_cluster_write --
- *     wrapper for writting a whole cluster at once
+ *     This function write 'count' bytes to device filesystem is mounted on,
+ *     starts at 'start+offset' position where 'start' computed in clusters
+ *     and 'offset' is offset inside cluster.
+ *     Writing will NOT cross cluster boundaries!
  *
  * PARAMETERS:
- *     fs_info  - FS info
- *     cln      - number of cluster to write
- *     buff     - buffer provided by user
+ *     fs_info            - FS info
+ *     start_cln          - cluster number to start writing to
+ *     offset             - offset inside cluster 'start'
+ *     count              - count of bytes to write
+ *     buff               - buffer provided by user
+ *     overwrite_cluster  - true if cluster can get overwritten, false if cluster content must be kept
  *
  * RETURNS:
  *     bytes written on success, or -1 if error occured
@@ -336,16 +437,50 @@ fat_cluster_read(
 ssize_t
 fat_cluster_write(
     fat_fs_info_t                        *fs_info,
-    uint32_t                              cln,
-    const void                           *buff
-    )
+    const uint32_t                        start_cln,
+    const uint32_t                        offset,
+    const uint32_t                        count,
+    const void                           *buff,
+    const bool                            overwrite_cluster)
 {
-    uint32_t       fsec = 0;
-
-    fsec = fat_cluster_num_to_sector_num(fs_info, cln);
-
-    return _fat_block_write(fs_info, fsec, 0,
-                          fs_info->vol.spc << fs_info->vol.sec_log2, buff);
+    ssize_t             rc               = RC_OK;
+    uint32_t            bytes_to_write   = MIN(count, (fs_info->vol.bpc - offset));
+    uint32_t            cur_blk          = fat_cluster_num_to_block_num(fs_info, start_cln);
+    uint32_t            blocks_in_offset = (offset >> fs_info->vol.bytes_per_block_log2);
+    uint32_t            ofs_blk          = offset - (blocks_in_offset << fs_info->vol.bytes_per_block_log2);
+    ssize_t             bytes_written    = 0;
+    uint8_t             *buffer          = (uint8_t*)buff;
+    ssize_t             ret;
+    uint32_t            c;
+
+    cur_blk += blocks_in_offset;
+
+    while (   (RC_OK == rc)
+           && (0 < bytes_to_write))
+    {
+      c = MIN(bytes_to_write, (fs_info->vol.bytes_per_block - ofs_blk));
+
+      ret = fat_block_write(
+          fs_info,
+          cur_blk,
+          ofs_blk,
+          c,
+          &buffer[bytes_written],
+          overwrite_cluster);
+      if (c != ret)
+        rc = -1;
+      else
+      {
+          bytes_to_write -= ret;
+          bytes_written  += ret;
+          ++cur_blk;
+      }
+      ofs_blk = 0;
+    }
+    if (RC_OK != rc)
+      return rc;
+    else
+      return bytes_written;
 }
 
 static bool is_cluster_aligned(const fat_vol_t *vol, uint32_t sec_num)
@@ -680,7 +815,7 @@ fat_fat32_update_fsinfo_sector(fat_fs_info_t *fs_info)
 
             fs_info->vol.free_cls_in_fs_info = free_count;
 
-            ret1 = _fat_block_write(fs_info,
+            ret1 = fat_sector_write(fs_info,
                                     fs_info->vol.info_sec,
                                     FAT_FSINFO_FREE_CLUSTER_COUNT_OFFSET,
                                     sizeof(le_free_count),
@@ -693,7 +828,7 @@ fat_fat32_update_fsinfo_sector(fat_fs_info_t *fs_info)
 
             fs_info->vol.next_cl_in_fs_info = next_free;
 
-            ret2 = _fat_block_write(fs_info,
+            ret2 = fat_sector_write(fs_info,
                                     fs_info->vol.info_sec,
                                     FAT_FSINFO_NEXT_FREE_CLUSTER_OFFSET,
                                     sizeof(le_next_free),
@@ -794,30 +929,23 @@ fat_init_clusters_chain(
     int                     rc = RC_OK;
     ssize_t                 ret = 0;
     uint32_t                cur_cln = start_cln;
-    char                   *buf;
-
-    buf = calloc(fs_info->vol.bpc, sizeof(char));
-    if ( buf == NULL )
-        rtems_set_errno_and_return_minus_one( EIO );
 
     while ((cur_cln & fs_info->vol.mask) < fs_info->vol.eoc_val)
     {
-        ret = fat_cluster_write(fs_info, cur_cln, buf);
-        if ( ret == -1 )
+        ret = fat_cluster_set(fs_info, cur_cln, 0, fs_info->vol.bpc, 0);
+        if ( ret != fs_info->vol.bpc )
         {
-            free(buf);
             return -1;
         }
 
         rc  = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
         if ( rc != RC_OK )
         {
-            free(buf);
             return rc;
         }
 
     }
-    free(buf);
+
     return rc;
 }
 
diff --git a/cpukit/libfs/src/dosfs/fat.h b/cpukit/libfs/src/dosfs/fat.h
index 48b1491..5d36fb4 100644
--- a/cpukit/libfs/src/dosfs/fat.h
+++ b/cpukit/libfs/src/dosfs/fat.h
@@ -440,6 +440,20 @@ fat_cluster_num_to_sector512_num(
 }
 
 static inline uint32_t
+ fat_block_num_to_cluster_num (const fat_fs_info_t *fs_info,
+                               const uint32_t block_number)
+{
+  return block_number >> (fs_info->vol.bpc_log2 - fs_info->vol.bytes_per_block_log2);
+}
+
+static inline uint32_t
+ fat_block_num_to_sector_num (const fat_fs_info_t *fs_info,
+                              const uint32_t block_number)
+{
+  return block_number << (fs_info->vol.bytes_per_block_log2 - fs_info->vol.sec_log2);
+}
+
+static inline uint32_t
  fat_sector_num_to_block_num (const fat_fs_info_t *fs_info,
                               const uint32_t sector_number)
 {
@@ -481,18 +495,26 @@ _fat_block_read(fat_fs_info_t                        *fs_info,
                 void                                 *buff);
 
 ssize_t
-_fat_block_write(fat_fs_info_t                        *fs_info,
+fat_cluster_write(fat_fs_info_t                    *fs_info,
+                    uint32_t                          start_cln,
+                    uint32_t                          offset,
+                    uint32_t                          count,
+                    const void                       *buff,
+                    bool                              overwrite_cluster);
+
+ssize_t
+fat_sector_write(fat_fs_info_t                        *fs_info,
                  uint32_t                              start,
                  uint32_t                              offset,
                  uint32_t                              count,
                  const void                           *buff);
 
-int
-_fat_block_zero(fat_fs_info_t                         *fs_info,
-                 uint32_t                              start,
-                 uint32_t                              offset,
-                 uint32_t                              count);
-
+ssize_t
+fat_cluster_set(fat_fs_info_t                        *fs_info,
+                  uint32_t                              start,
+                  uint32_t                              offset,
+                  uint32_t                              count,
+                  uint8_t                               pattern);
 
 
 int
diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.c b/cpukit/libfs/src/dosfs/fat_fat_operations.c
index 291819d..7496c09 100644
--- a/cpukit/libfs/src/dosfs/fat_fat_operations.c
+++ b/cpukit/libfs/src/dosfs/fat_fat_operations.c
@@ -54,6 +54,7 @@ fat_scan_fat_for_free_clusters(
     uint32_t       save_cln = 0;
     uint32_t       data_cls_val = fs_info->vol.data_cls + 2;
     uint32_t       i = 2;
+    ssize_t        bytes_written;
 
     *cls_added = 0;
 
@@ -114,13 +115,14 @@ fat_scan_fat_for_free_clusters(
                     goto cleanup;
             }
 
-            if (zero_fill) {
-                uint32_t sec = fat_cluster_num_to_sector_num(fs_info,
-                                                             cl4find);
-
-                rc = _fat_block_zero(fs_info, sec, 0, fs_info->vol.bpc);
-                if ( rc != RC_OK )
+            if (zero_fill)
+            {
+                bytes_written = fat_cluster_set (fs_info, cl4find, 0, fs_info->vol.bpc, 0);
+                if (fs_info->vol.bpc != bytes_written)
+                {
+                    rc = -1;
                     goto cleanup;
+                }
             }
 
             save_cln = cl4find;
diff --git a/cpukit/libfs/src/dosfs/fat_file.c b/cpukit/libfs/src/dosfs/fat_file.c
index ddbe02c..43826c9 100644
--- a/cpukit/libfs/src/dosfs/fat_file.c
+++ b/cpukit/libfs/src/dosfs/fat_file.c
@@ -339,6 +339,110 @@ fat_file_read(
     return cmpltd;
 }
 
+/* fat_is_fat12_or_fat16_root_dir --
+ *     Returns true for FAT12 root directories respectively FAT16
+ *     root directories. Returns false for everything else.
+ *
+ *  PARAMETERS:
+ *      fat_fd        - fat-file descriptor
+ *      volume_type   - type of fat volume: FAT_FAT12 or FAT_FAT16 or FAT_FAT32
+ *
+ *  RETURNS:
+ *      true if conditions for FAT12 root directory or FAT16 root directory
+ *      match, false if not
+ */
+static bool
+ fat_is_fat12_or_fat16_root_dir (const fat_file_fd_t *fat_fd,
+                                 const uint8_t volume_type)
+{
+    return (FAT_FD_OF_ROOT_DIR(fat_fd)) && (volume_type & (FAT_FAT12 | FAT_FAT16));
+}
+
+/* fat_file_write_fat32_or_non_root_dir --
+ *     Execute fat file write for FAT32 respectively for non-root
+ *     directories of FAT12 or FAT16
+ *
+ * PARAMETERS:
+ *     fs_info          - FS info
+ *     fat_fd           - fat-file descriptor
+ *     start            - offset(in bytes) to write from
+ *     count            - count
+ *     buf              - buffer provided by user
+ *     file_cln_initial - initial current cluster number of the file
+ *
+ * RETURNS:
+ *     number of bytes actually written to the file on success, or -1 if
+ *     error occured (errno set appropriately)
+ */
+static ssize_t
+ fat_file_write_fat32_or_non_root_dir(
+     fat_fs_info_t                        *fs_info,
+     fat_file_fd_t                        *fat_fd,
+     const uint32_t                        start,
+     const uint32_t                        count,
+     const uint8_t                        *buf,
+     const uint32_t                        file_cln_initial)
+{
+    int            rc = RC_OK;
+    uint32_t       cmpltd = 0;
+    uint32_t       cur_cln = 0;
+    uint32_t       save_cln = 0; /* FIXME: This might be incorrect, cf. below */
+    uint32_t       start_cln = start >> fs_info->vol.bpc_log2;
+    uint32_t       ofs_cln = start - (start_cln << fs_info->vol.bpc_log2);
+    uint32_t       ofs_cln_save = ofs_cln;
+    uint32_t       bytes_to_write = count;
+    uint32_t       file_cln_cnt;
+    ssize_t        ret;
+    uint32_t       c;
+    bool           overwrite_cluster = false;
+
+    rc = fat_file_lseek(fs_info, fat_fd, start_cln, &cur_cln);
+    if (RC_OK == rc)
+    {
+        file_cln_cnt = cur_cln - fat_fd->cln;
+        while (   (RC_OK == rc)
+               && (bytes_to_write > 0))
+        {
+            c = MIN(bytes_to_write, (fs_info->vol.bpc - ofs_cln));
+
+            if (file_cln_initial < file_cln_cnt)
+                overwrite_cluster = true;
+
+            ret = fat_cluster_write(fs_info,
+                                      cur_cln,
+                                      ofs_cln,
+                                      c,
+                                      &buf[cmpltd],
+                                      overwrite_cluster);
+            if (0 > ret)
+              rc = -1;
+
+            if (RC_OK == rc)
+            {
+                ++file_cln_cnt;
+                bytes_to_write -= ret;
+                cmpltd += ret;
+                save_cln = cur_cln;
+                if (0 < bytes_to_write)
+                  rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
+
+                ofs_cln = 0;
+            }
+        }
+
+        /* update cache */
+        /* XXX: check this - I'm not sure :( */
+        fat_fd->map.file_cln = start_cln +
+                               ((ofs_cln_save + cmpltd - 1) >> fs_info->vol.bpc_log2);
+        fat_fd->map.disk_cln = save_cln;
+    }
+
+    if (RC_OK != rc)
+      return rc;
+    else
+      return cmpltd;
+}
+
 /* fat_file_write --
  *     Write 'count' bytes of data from user supplied buffer to fat-file
  *     starting at offset 'start'. This interface hides the architecture
@@ -364,18 +468,15 @@ fat_file_write(
     const uint8_t                        *buf
     )
 {
-    int            rc = 0;
-    ssize_t        ret = 0;
+    int            rc = RC_OK;
+    ssize_t        ret;
     uint32_t       cmpltd = 0;
-    uint32_t       cur_cln = 0;
-    uint32_t       save_cln = 0; /* FIXME: This might be incorrect, cf. below */
-    uint32_t       cl_start = 0;
-    uint32_t       ofs = 0;
-    uint32_t       save_ofs;
-    uint32_t       sec = 0;
-    uint32_t       byte = 0;
+    uint32_t       byte;
     uint32_t       c = 0;
     bool           zero_fill = start > fat_fd->fat_file_size;
+    uint32_t       file_cln_initial = fat_fd->map.file_cln;
+    uint32_t       cln;
+
 
     if ( count == 0 )
         return cmpltd;
@@ -387,66 +488,51 @@ fat_file_write(
         count = fat_fd->size_limit - start;
 
     rc = fat_file_extend(fs_info, fat_fd, zero_fill, start + count, &c);
-    if (rc != RC_OK)
-        return rc;
-
-    /*
-     * check whether there was enough room on device to locate
-     * file of 'start + count' bytes
-     */
-    if (c != (start + count))
-        count = c - start;
-
-    if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
-        (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
+    if (RC_OK == rc)
     {
-        sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->cln);
-        sec += (start >> fs_info->vol.sec_log2);
-        byte = start & (fs_info->vol.bps - 1);
-
-        ret = _fat_block_write(fs_info, sec, byte, count, buf);
-        if ( ret < 0 )
-            return -1;
-
-        return ret;
+        /*
+         * check whether there was enough room on device to locate
+         * file of 'start + count' bytes
+         */
+        if (c != (start + count))
+            count = c - start;
+
+        /* for the root directory of FAT12 and FAT16 we need this special handling */
+        if (fat_is_fat12_or_fat16_root_dir(fat_fd, fs_info->vol.type))
+        {
+            cln = fat_fd->cln;
+            cln += (start >> fs_info->vol.bpc_log2);
+            byte = start & (fs_info->vol.bpc -1);
+
+            ret = fat_cluster_write(fs_info,
+                                      cln,
+                                      byte,
+                                      count,
+                                      buf,
+                                      false);
+            if (0 > ret)
+              rc = -1;
+            else
+              cmpltd = ret;
+        }
+        else
+        {
+            ret = fat_file_write_fat32_or_non_root_dir(fs_info,
+                                                       fat_fd,
+                                                       start,
+                                                       count,
+                                                       buf,
+                                                       file_cln_initial);
+            if (0 > ret)
+              rc = -1;
+            else
+              cmpltd = ret;
+        }
     }
-
-    cl_start = start >> fs_info->vol.bpc_log2;
-    save_ofs = ofs = start & (fs_info->vol.bpc - 1);
-
-    rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
-    if (rc != RC_OK)
+    if (RC_OK != rc)
         return rc;
-
-    while (count > 0)
-    {
-        c = MIN(count, (fs_info->vol.bpc - ofs));
-
-        sec = fat_cluster_num_to_sector_num(fs_info, cur_cln);
-        sec += (ofs >> fs_info->vol.sec_log2);
-        byte = ofs & (fs_info->vol.bps - 1);
-
-        ret = _fat_block_write(fs_info, sec, byte, c, buf + cmpltd);
-        if ( ret < 0 )
-            return -1;
-
-        count -= c;
-        cmpltd += c;
-        save_cln = cur_cln;
-        rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
-        if ( rc != RC_OK )
-            return rc;
-
-        ofs = 0;
-    }
-
-    /* update cache */
-    /* XXX: check this - I'm not sure :( */
-    fat_fd->map.file_cln = cl_start +
-                           ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2);
-    fat_fd->map.disk_cln = save_cln;
-
-    return cmpltd;
+    else
+        return cmpltd;
 }
 
 /* fat_file_extend --
@@ -482,6 +568,7 @@ fat_file_extend(
     uint32_t       last_cl = 0;
     uint32_t       bytes_remain = 0;
     uint32_t       cls_added;
+    ssize_t        bytes_written;
 
     *a_length = new_length;
 
@@ -508,20 +595,14 @@ fat_file_extend(
         uint32_t cl_start = start >> fs_info->vol.bpc_log2;
         uint32_t ofs = start & (fs_info->vol.bpc - 1);
         uint32_t cur_cln;
-        uint32_t sec;
-        uint32_t byte;
 
         rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
         if (rc != RC_OK)
             return rc;
 
-        sec = fat_cluster_num_to_sector_num(fs_info, cur_cln);
-        sec += ofs >> fs_info->vol.sec_log2;
-        byte = ofs & (fs_info->vol.bps - 1);
-
-        rc = _fat_block_zero(fs_info, sec, byte, bytes_remain);
-        if (rc != RC_OK)
-            return rc;
+        bytes_written = fat_cluster_set (fs_info, cur_cln, ofs, bytes_remain, 0);
+        if (bytes_remain != bytes_written)
+            return -1;
     }
 
     /*
diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c
index f4de0d5..5ebf257 100644
--- a/cpukit/libfs/src/dosfs/msdos_misc.c
+++ b/cpukit/libfs/src/dosfs/msdos_misc.c
@@ -641,7 +641,7 @@ msdos_set_dir_wrt_time_and_date(
     msdos_date_unix2dos(fat_fd->mtime, &date, &time_val);
 
     /*
-     * calculate input for _fat_block_write: convert (cluster num, offset) to
+     * calculate input for fat_sector_write: convert (cluster num, offset) to
      * (sector num, new offset)
      */
     sec = fat_cluster_num_to_sector_num(&fs_info->fat, fat_fd->dir_pos.sname.cln);
@@ -650,12 +650,12 @@ msdos_set_dir_wrt_time_and_date(
     byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
 
     time_val = CT_LE_W(time_val);
-    ret1 = _fat_block_write(&fs_info->fat, sec, byte + MSDOS_FILE_WTIME_OFFSET,
+    ret1 = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_WTIME_OFFSET,
                             2, (char *)(&time_val));
     date = CT_LE_W(date);
-    ret2 = _fat_block_write(&fs_info->fat, sec, byte + MSDOS_FILE_WDATE_OFFSET,
+    ret2 = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_WDATE_OFFSET,
                             2, (char *)(&date));
-    ret3 = _fat_block_write(&fs_info->fat, sec, byte + MSDOS_FILE_ADATE_OFFSET,
+    ret3 = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_ADATE_OFFSET,
                             2, (char *)(&date));
 
     if ( (ret1 < 0) || (ret2 < 0) || (ret3 < 0) )
@@ -691,7 +691,7 @@ msdos_set_first_cluster_num(
     uint32_t         byte = 0;
 
     /*
-     * calculate input for _fat_block_write: convert (cluster num, offset) to
+     * calculate input for fat_sector_write: convert (cluster num, offset) to
      * (sector num, new offset)
      */
     sec = fat_cluster_num_to_sector_num(&fs_info->fat, fat_fd->dir_pos.sname.cln);
@@ -700,11 +700,11 @@ msdos_set_first_cluster_num(
     byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
 
     le_cl_low = CT_LE_W((uint16_t  )(new_cln & 0x0000FFFF));
-    ret1 = _fat_block_write(&fs_info->fat, sec,
+    ret1 = fat_sector_write(&fs_info->fat, sec,
                             byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2,
                             (char *)(&le_cl_low));
     le_cl_hi = CT_LE_W((uint16_t  )((new_cln & 0xFFFF0000) >> 16));
-    ret2 = _fat_block_write(&fs_info->fat, sec,
+    ret2 = fat_sector_write(&fs_info->fat, sec,
                             byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2,
                             (char *)(&le_cl_hi));
     if ( (ret1 < 0) || (ret2 < 0) )
@@ -742,7 +742,7 @@ msdos_set_file_size(
     byte = (fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1));
 
     le_new_length = CT_LE_L((fat_fd->fat_file_size));
-    ret = _fat_block_write(&fs_info->fat, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
+    ret = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
                            (char *)(&le_new_length));
     if ( ret < 0 )
         return -1;
@@ -802,7 +802,7 @@ msdos_set_first_char4file_name(
                       (start.ofs >> fs_info->fat.vol.sec_log2));
       uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));
 
-      ret = _fat_block_write(&fs_info->fat, sec, byte + MSDOS_FILE_NAME_OFFSET,
+      ret = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_NAME_OFFSET,
                              1, &fchar);
       if (ret < 0)
         return -1;
diff --git a/testsuites/fstests/Makefile.am b/testsuites/fstests/Makefile.am
index 7cf6571..5161380 100644
--- a/testsuites/fstests/Makefile.am
+++ b/testsuites/fstests/Makefile.am
@@ -1,6 +1,7 @@
 ACLOCAL_AMFLAGS = -I ../aclocal
 
 SUBDIRS = 
+SUBDIRS += fsdosfswrite01
 SUBDIRS += fsdosfsformat01
 SUBDIRS += fsfseeko01
 SUBDIRS += fsdosfssync01
diff --git a/testsuites/fstests/configure.ac b/testsuites/fstests/configure.ac
index 3496de4..4993beb 100644
--- a/testsuites/fstests/configure.ac
+++ b/testsuites/fstests/configure.ac
@@ -77,6 +77,7 @@ AC_CHECK_SIZEOF([blkcnt_t])
 
 # Explicitly list all Makefiles here
 AC_CONFIG_FILES([Makefile
+fsdosfswrite01/Makefile
 fsdosfsformat01/Makefile
 fsfseeko01/Makefile
 fsdosfssync01/Makefile
diff --git a/testsuites/fstests/fsdosfsformat01/init.c b/testsuites/fstests/fsdosfsformat01/init.c
index 14de46f..ca5e28f 100644
--- a/testsuites/fstests/fsdosfsformat01/init.c
+++ b/testsuites/fstests/fsdosfsformat01/init.c
@@ -19,12 +19,15 @@
 #include "tmacros.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <sys/statvfs.h>
 #include <rtems/libio.h>
 #include <rtems/blkdev.h>
 #include <rtems/dosfs.h>
 #include <rtems/sparse-disk.h>
 
+#include <bsp.h>
+
 #define MAX_PATH_LENGTH                        100  /* Maximum number of characters per path */
 #define SECTOR_SIZE                            512  /* sector size (bytes) */
 #define FAT12_MAX_CLN                         4085  /* maximum + 1 number of clusters for FAT12 */
@@ -79,47 +82,56 @@ test_disk_params (
   rtems_test_assert(0 == rv);
 }
 
+static void test_create_file(
+  const char *mount_dir,
+  uint32_t file_idx,
+  bool expect_ok
+)
+{
+  char file_name[MAX_PATH_LENGTH +1];
+  int fd;
+
+  snprintf (file_name, MAX_PATH_LENGTH, "%s/file%" PRIu32 ".txt", mount_dir, file_idx);
+  fd = open (file_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+  if (expect_ok) {
+    int rv;
+
+    rtems_test_assert(fd >= 0);
+
+    rv = close(fd);
+    rtems_test_assert(rv == 0);
+  } else {
+    rtems_test_assert(fd == -1);
+  }
+}
+
 static void
  test_file_creation (
   const char* dev_name,
   const char* mount_dir,
-  const unsigned int number_of_files
+  const uint32_t number_of_files
 )
 {
   int rv;
-  int file_idx = 0;
-  int fildes[number_of_files + 1];
+  uint32_t file_idx;
   char file_name[MAX_PATH_LENGTH +1];
-  unsigned int files_created;
 
   rv = mount (dev_name, mount_dir, RTEMS_FILESYSTEM_TYPE_DOSFS, RTEMS_FILESYSTEM_READ_WRITE, NULL);
   rtems_test_assert(0 == rv);
 
-  /* Create as many files as should be supported */
-  do {
-    snprintf (file_name, MAX_PATH_LENGTH, "%s/file%u.txt", mount_dir, file_idx);
-    fildes[file_idx] = open (file_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-    rtems_test_assert(-1 != fildes[file_idx]);
-
-    ++file_idx;
+  for (file_idx = 0; file_idx < number_of_files; ++file_idx) {
+    test_create_file(mount_dir, file_idx, true);
   }
-  while (number_of_files > file_idx);
-
-  /* Try to create another file which should fail */
-  snprintf (file_name, MAX_PATH_LENGTH, "%s/file%u.txt", mount_dir, file_idx);
-  fildes[file_idx] = open (file_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-  rtems_test_assert(-1 == fildes[file_idx]);
-
-  /* Tidy up again by closing and deleting all files */
-  files_created = file_idx;
-  for (file_idx = 0; file_idx < files_created; file_idx++)
-  {
-    snprintf (file_name, MAX_PATH_LENGTH, "%s/file%u.txt", mount_dir, file_idx);
-    rv = close(fildes[file_idx]);
-    rtems_test_assert(0 == rv);
+
+  test_create_file(mount_dir, file_idx, false);
+
+  for (file_idx = 0; file_idx < number_of_files; ++file_idx) {
+    snprintf (file_name, MAX_PATH_LENGTH, "%s/file%" PRIu32 ".txt", mount_dir, file_idx);
     rv = unlink (file_name);
     rtems_test_assert(0 == rv);
   }
+
   rv = unmount (mount_dir);
   rtems_test_assert(0 == rv);
 }
diff --git a/testsuites/fstests/fsdosfswrite01/Makefile.am b/testsuites/fstests/fsdosfswrite01/Makefile.am
new file mode 100644
index 0000000..2d21751
--- /dev/null
+++ b/testsuites/fstests/fsdosfswrite01/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = fsdosfswrite01
+fsdosfswrite01_SOURCES = init.c
+
+dist_rtems_tests_DATA = fsdosfswrite01.scn fsdosfswrite01.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP at .cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(fsdosfswrite01_OBJECTS)
+LINK_LIBS = $(fsdosfswrite01_LDLIBS)
+
+fsdosfswrite01$(EXEEXT): $(fsdosfswrite01_OBJECTS) $(fsdosfswrite01_DEPENDENCIES)
+	@rm -f fsdosfswrite01$(EXEEXT)
+	$(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/fstests/fsdosfswrite01/fsdosfswrite01.doc b/testsuites/fstests/fsdosfswrite01/fsdosfswrite01.doc
new file mode 100644
index 0000000..269f65d
--- /dev/null
+++ b/testsuites/fstests/fsdosfswrite01/fsdosfswrite01.doc
@@ -0,0 +1,12 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: fsdosfswrite01
+
+directives:
+Verify that a write access does not result in uneccessary reads of the device:
+- Verify that appending data to a file does not result in reading the new clusters from device
+- Verify writing a whole cluster does not result in reading the cluster from device
+
+concepts:
+- Avoiding uneccessary device reads is to make sure that writing to the device does not get slowed
+  down unneccesarily
diff --git a/testsuites/fstests/fsdosfswrite01/fsdosfswrite01.scn b/testsuites/fstests/fsdosfswrite01/fsdosfswrite01.scn
new file mode 100644
index 0000000..e69de29
diff --git a/testsuites/fstests/fsdosfswrite01/init.c b/testsuites/fstests/fsdosfswrite01/init.c
new file mode 100644
index 0000000..d500f7f
--- /dev/null
+++ b/testsuites/fstests/fsdosfswrite01/init.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2012 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include "tmacros.h"
+#include <fcntl.h>
+#include <rtems/dosfs.h>
+#include <rtems/sparse-disk.h>
+#include <rtems/blkdev.h>
+#include <bsp.h>
+
+#define MAX_PATH_LENGTH                        100  /* Maximum number of characters per path */
+#define SECTOR_SIZE                            512  /* sector size (bytes) */
+#define FAT16_MAX_CLN                        65525  /* maximum + 1 number of clusters for FAT16 */
+#define FAT16_DEFAULT_SECTORS_PER_CLUSTER       32  /* Default number of sectors per cluster for FAT16 */
+
+static void
+ test_file_write (
+  const char* dev_name,
+  const char* mount_dir,
+  uint8_t *blk_buf,
+  uint32_t block_size
+)
+{
+  int rv;
+  int fildes;
+  int devdes;
+  char file_name[MAX_PATH_LENGTH +1];
+  ssize_t num_bytes;
+  unsigned int value;
+  rtems_blkdev_stats blkdev_stats[2];
+  rtems_blkdev_stats *blkdev_stats_tmp    = NULL;
+  rtems_blkdev_stats *blkdev_stats_before = &blkdev_stats[0];
+  rtems_blkdev_stats *blkdev_stats_after  = &blkdev_stats[1];
+
+  memset(&blkdev_stats, 0, sizeof(blkdev_stats));
+  memset(blk_buf, 0xFE, block_size);
+
+  snprintf (file_name, MAX_PATH_LENGTH, "%s/file1.txt", mount_dir);
+
+  devdes = open(dev_name, O_RDONLY);
+  rtems_test_assert(devdes >= 0);
+
+  rv = mount (dev_name, mount_dir, RTEMS_FILESYSTEM_TYPE_DOSFS, RTEMS_FILESYSTEM_READ_WRITE, NULL);
+  rtems_test_assert(0 == rv);
+
+  fildes = open (file_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+  rtems_test_assert(-1 != fildes);
+
+  rv = ioctl(devdes, RTEMS_BLKIO_GETDEVSTATS, blkdev_stats_before);
+  rtems_test_assert(0 == rv);
+
+  /* Write blocks of the size of bdufblocks correctly aligned. This should avoid reading from the device */
+  for (value = 0; value < 3; ++value) {
+    num_bytes = write (fildes, blk_buf, block_size);
+    rtems_test_assert((ssize_t) block_size == num_bytes);
+  }
+
+  rv = fsync(fildes);
+  rtems_test_assert(0 == rv);
+
+  rv = ioctl(devdes, RTEMS_BLKIO_GETDEVSTATS, blkdev_stats_after);
+  rtems_test_assert(0 == rv);
+  rtems_test_assert(  (     blkdev_stats_after->write_blocks - blkdev_stats_before->write_blocks)
+                    > (2 * (blkdev_stats_after->read_blocks  - blkdev_stats_before->read_blocks)));
+
+  blkdev_stats_tmp = blkdev_stats_before;
+  blkdev_stats_before = blkdev_stats_after;
+  blkdev_stats_after = blkdev_stats_tmp;
+  memset(blkdev_stats_after, 0, sizeof(*blkdev_stats_after));
+
+  /* Append further bytes to the file. Appending should not require reading from the device */
+  for (value = 0; value < (block_size * 2 / 3); ++value) {
+    num_bytes = write (fildes, &value, sizeof(value));
+    rtems_test_assert(sizeof(value) == num_bytes);
+  }
+
+  rv = fsync(fildes);
+  rtems_test_assert(0 == rv);
+
+  rv = ioctl(devdes, RTEMS_BLKIO_GETDEVSTATS, blkdev_stats_after);
+  rtems_test_assert(0 == rv);
+  rtems_test_assert(  (     blkdev_stats_after->write_blocks - blkdev_stats_before->write_blocks)
+                    > (2 * (blkdev_stats_after->read_blocks  - blkdev_stats_before->read_blocks)));
+
+  rv = close (fildes);
+  rtems_test_assert(0 == rv);
+
+  rv = unmount (mount_dir);
+  rtems_test_assert(0 == rv);
+
+  rv = close (devdes);
+  rtems_test_assert(0 == rv);
+
+}
+
+
+static void test(void)
+{
+  rtems_status_code sc;
+  int rv;
+  const char dev_name[] = "/dev/rda";
+  const char mount_dir[] = "/mnt";
+  rtems_sparse_disk *sparse_disk = NULL;
+  uint8_t blk_buf[SECTOR_SIZE * 2];
+
+  sc = rtems_disk_io_initialize();
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  rv = mkdir(mount_dir, S_IRWXU | S_IRWXG | S_IRWXO);
+  rtems_test_assert(0 == rv);
+
+
+
+  /* FAT12 */
+  /* For 1.44 MB disks */
+  sc = rtems_sparse_disk_create(
+    SECTOR_SIZE,
+    64,
+    &sparse_disk
+  );
+  rtems_test_assert(RTEMS_SUCCESSFUL == sc);
+  sc = rtems_sparse_disk_register(
+    dev_name,
+    sparse_disk,
+    SECTOR_SIZE,
+    2880,
+    64,
+    rtems_sparse_disk_delete,
+    0
+  );
+  rtems_test_assert(RTEMS_SUCCESSFUL == sc);
+
+  rv = msdos_format(dev_name, NULL);
+  rtems_test_assert(rv == 0);
+
+  test_file_write (dev_name, mount_dir, blk_buf, SECTOR_SIZE * 2);
+
+  rv = unlink (dev_name);
+  rtems_test_assert(rv == 0);
+
+
+
+  /* FAT32 */
+
+  sc = rtems_sparse_disk_create(
+    SECTOR_SIZE,
+    1024,
+    &sparse_disk
+  );
+  rtems_test_assert(RTEMS_SUCCESSFUL == sc);
+  sc = rtems_sparse_disk_register(
+    dev_name,
+    sparse_disk,
+    SECTOR_SIZE,
+    (FAT16_MAX_CLN * FAT16_DEFAULT_SECTORS_PER_CLUSTER) + 41,
+    1024,
+    rtems_sparse_disk_delete,
+    0
+  );
+  rtems_test_assert(RTEMS_SUCCESSFUL == sc);
+
+  rv = msdos_format(dev_name, NULL);
+  rtems_test_assert(rv == 0);
+
+  test_file_write (dev_name, mount_dir, blk_buf, SECTOR_SIZE);
+
+  rv = unlink (dev_name);
+  rtems_test_assert(rv == 0);
+}
+
+static void Init(rtems_task_argument arg)
+{
+  puts("\n\n*** TEST fsdosfswrite01 ***");
+
+  test();
+
+  puts("*** END OF TEST fsdosfswrite01 ***");
+
+  rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+
+#define CONFIGURE_FILESYSTEM_DOSFS
+
+/* 1 device file for blkstats + 1 file for writing + 1 mount_dir + stdin + stdout + stderr + device file when mounted */
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 7
+
+#define CONFIGURE_UNLIMITED_OBJECTS
+#define CONFIGURE_UNIFIED_WORK_AREAS
+
+#define CONFIGURE_INIT_TASK_STACK_SIZE (32 * 1024)
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_BDBUF_BUFFER_MAX_SIZE (32 * 1024)
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
-- 
1.7.10.4




More information about the devel mailing list