Skip to contentSkip to conversation

fat: Batched discard support for fat

PATCH Superseded Kyungmin Park 4 participants 12 messages Feb 24, 2011

Subsystems (2)

v2 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 fat: Batched discard support for fat

From: Kyungmin Park <[email protected]>

FAT supports batched discard as ext4.

Cited from Lukas words.
"The current solution is not ideal because of its bad performance impact. So basic idea to improve things is to avoid discarding every time some blocks are freed. and instead batching is together into bigger trims, which tends to be more effective."

You can find an information in detail at following URLs.
http://lwn.net/Articles/397538/
http://lwn.net/Articles/383933/

---
Changelog V2:
Use the given start and len as Lukas comments Check the queue supports discard feature
---

fs/fat/fat.h +1 -0
index f504089..08b53e1 100644
@@ -299,6 +299,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
 			      int nr_cluster);
 extern int fat_free_clusters(struct inode *inode, int cluster);
 extern int fat_count_free_clusters(struct super_block *sb);
+extern int fat_trim_fs(struct super_block *sb, struct fstrim_range *range);
 
 /* fat/file.c */
 extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
fs/fat/fatent.c +98 -5

124 lines not on this page · raw message

fs/fat/file.c +28 -0
index 7257752..05e6545 100644
@@ -125,6 +125,34 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return fat_ioctl_get_attributes(inode, user_attr);
 	case FAT_IOCTL_SET_ATTRIBUTES:
 		return fat_ioctl_set_attributes(filp, user_attr);
+	case FITRIM:
+	{
+		struct super_block *sb = inode->i_sb;
+		struct request_queue *q = bdev_get_queue(sb->s_bdev);
+		struct fstrim_range range;
+		int ret = 0;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!blk_queue_discard(q))
+			return -EOPNOTSUPP;
+
+		if (copy_from_user(&range, (struct fstrim_range *)arg,
+					sizeof(range)))
+			return -EFAULT;
+
+		ret = fat_trim_fs(sb, &range);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user((struct fstrim_range *)arg, &range,
+					sizeof(range)))
+			return -EFAULT;
+
+		return 0;
+	}
+
 	default:
 		return -ENOTTY;	/* Inappropriate ioctl for device */
 	}
#1 lore raw

Need fat64 info

Hello Kyungmin Park

Do you have any info on fat64 (ExFAT) implementation on FAT?

Regards
Keshava Munegowda

#6 lore raw
In reply to Kyungmin Park
Quoted 10 lines · Kyungmin Park

From: Kyungmin Park <[email protected]>

FAT supports batched discard as ext4.

Cited from Lukas words.
"The current solution is not ideal because of its bad performance impact. So basic idea to improve things is to avoid discarding every time some blocks are freed. and instead batching is together into bigger trims, which tends to be more effective."

Quoted 5 lines

You can find an information in detail at following URLs.
http://lwn.net/Articles/397538/
http://lwn.net/Articles/383933/

Hi Kyungmin,
thanks to second version. How did you test it ? You can try xtestest 251 for simple verification.
I can not really comment on FAT specific code, however I have couple of comments bellow.

Thanks!
-Lukas

Quoted 32 lines

---
Changelog V2:
Use the given start and len as Lukas comments Check the queue supports discard feature
---

fs/fat/fat.h +1 -0
index f504089..08b53e1 100644
@@ -299,6 +299,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
 			      int nr_cluster);
 extern int fat_free_clusters(struct inode *inode, int cluster);
 extern int fat_count_free_clusters(struct super_block *sb);
+extern int fat_trim_fs(struct super_block *sb, struct fstrim_range *range);
 
 /* fat/file.c */
 extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
fs/fat/fatent.c +2 -0
index b47d2c9..ea31ee4 100644
@@ -1,6 +1,8 @@
 /*
  * Copyright (C) 2004, OGAWA Hirofumi
  * Released under GPL v2.
+ *
+ * Batched discard support by Kyungmin Park <[email protected]>
  */
 
 #include <linux/module.h>
@@ -541,6 +543,16 @@ out:
 	return err;

fs/fat/fatent.c out: · line 553

Quoted 10 lines
fs/fat/fatent.c +8 -0
 }
 
+static int fat_issue_discard(struct super_block *sb, int cluster, int nr_clus)
+{
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	sector_t block, nr_blocks;
+
+        block = fat_clus_to_blknr(sbi, cluster);
+        nr_blocks = nr_clus * sbi->sec_per_clus;
+        return sb_issue_discard(sb, block, nr_blocks, GFP_NOFS, 0);

^^^^^^^^
This patch does not pass checkpatch.pl script. Use tabs for indention.

Quoted 28 lines
fs/fat/fatent.c +9 -5
+}
+
 int fat_free_clusters(struct inode *inode, int cluster)
 {
 	struct super_block *sb = inode->i_sb;
@@ -575,11 +587,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
 			if (cluster != fatent.entry + 1) {
 				int nr_clus = fatent.entry - first_cl + 1;
 
-				sb_issue_discard(sb,
-					fat_clus_to_blknr(sbi, first_cl),
-					nr_clus * sbi->sec_per_clus,
-					GFP_NOFS, 0);
-
+				fat_issue_discard(sb, first_cl, nr_clus);
 				first_cl = cluster;
 			}
 		}
@@ -683,3 +691,88 @@ out:
 	unlock_fat(sbi);
 	return err;
 }
+
+int fat_trim_fs(struct super_block *sb, struct fstrim_range *range)
+{
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	struct fatent_operations *ops = sbi->fatent_ops;
+	struct fat_entry fatent;

fs/fat/fatent.c out: · line 709

Quoted 10 lines
fs/fat/fatent.c +10 -0
+	unsigned long reada_blocks, reada_mask, cur_block;
+	int err = 0, free, count, entry;
+	int start, len, minlen, trimmed;
+
+	start = range->start >> sb->s_blocksize_bits;
+	start = start / sbi->sec_per_clus;
+	len = range->len >> sb->s_blocksize_bits;
+	len = len / sbi->sec_per_clus;
+	minlen = range->minlen >> sb->s_blocksize_bits;
+	minlen = minlen / sbi->sec_per_clus;

I should have mention that earlier, but you can also adjust mineln according to the discard_granularity, because extents smaller than that would not be discarded anyway.

Quoted 7 lines
fs/fat/fatent.c +7 -0
+	trimmed = 0;
+	count = 0;
+
+	lock_fat(sbi);
+	if (sbi->free_clusters != -1 && sbi->free_clus_valid)
+		goto out;
+

fs/fat/fatent.c out: · line 726

Quoted 10 lines
fs/fat/fatent.c +10 -0
+	reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
+	reada_mask = reada_blocks - 1;
+	cur_block = 0;
+
+	entry = 0;
+	free = 0;
+	fatent_init(&fatent);
+
+	if (start < FAT_START_ENT)
+		start = FAT_START_ENT;

You're not using start anywhere.

Quoted 13 lines
fs/fat/fatent.c +13 -0
+
+	fatent_set_entry(&fatent, FAT_START_ENT);
+
+	while (count < sbi->max_cluster) {
+		if (fatent.entry >= sbi->max_cluster)
+			fatent.entry = FAT_START_ENT;
+		/* readahead of fat blocks */
+		if ((cur_block & reada_mask) == 0) {
+			unsigned long rest = sbi->fat_length - cur_block;
+			fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+		}
+		cur_block++;
+

fs/fat/fatent.c out: · line 749

Quoted 10 lines
fs/fat/fatent.c +10 -0
+		err = fat_ent_read_block(sb, &fatent);
+		if (err)
+			goto out;
+
+		do {
+			if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+				free++;
+				if (!entry)
+					entry = fatent.entry;
+				if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

Quoted 69 lines
fs/fat/fatent.c +29 -0
+					fat_issue_discard(sb, entry, free);
+					trimmed += free;
+				}
+				if (trimmed >= len)
+					goto done;
+			} else if (entry) {
+				if (free >= minlen) {
+					fat_issue_discard(sb, entry, free);
+					trimmed += free;
+				}
+				if (trimmed >= len)
+					goto done;
+				free = 0;
+				entry = 0;
+			}
+			count++;
+		} while (fat_ent_next(sbi, &fatent));
+	}
+	if (free >= minlen) {
+		fat_issue_discard(sb, entry, free);
+		trimmed += free;
+	}
+done:
+	range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+	fatent_brelse(&fatent);
+out:
+	unlock_fat(sbi);
+	return err;
+}
fs/fat/file.c +28 -0
index 7257752..05e6545 100644
@@ -125,6 +125,34 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return fat_ioctl_get_attributes(inode, user_attr);
 	case FAT_IOCTL_SET_ATTRIBUTES:
 		return fat_ioctl_set_attributes(filp, user_attr);
+	case FITRIM:
+	{
+		struct super_block *sb = inode->i_sb;
+		struct request_queue *q = bdev_get_queue(sb->s_bdev);
+		struct fstrim_range range;
+		int ret = 0;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!blk_queue_discard(q))
+			return -EOPNOTSUPP;
+
+		if (copy_from_user(&range, (struct fstrim_range *)arg,
+					sizeof(range)))
+			return -EFAULT;
+
+		ret = fat_trim_fs(sb, &range);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user((struct fstrim_range *)arg, &range,
+					sizeof(range)))
+			return -EFAULT;
+
+		return 0;
+	}
+
 	default:
 		return -ENOTTY;	/* Inappropriate ioctl for device */
 	}
#7 lore raw
Quoted 61 lines · Lukas Czerner
Quoted 10 lines · Kyungmin Park

From: Kyungmin Park <[email protected]>

FAT supports batched discard as ext4.

Cited from Lukas words.
"The current solution is not ideal because of its bad performance impact. So basic idea to improve things is to avoid discarding every time some blocks are freed. and instead batching is together into bigger trims, which tends to be more effective."

Quoted 5 lines

You can find an information in detail at following URLs.
http://lwn.net/Articles/397538/
http://lwn.net/Articles/383933/

Hi Kyungmin,
thanks to second version. How did you test it ? You can try xtestest 251 for simple verification.
I can not really comment on FAT specific code, however I have couple of comments bellow.

Thanks!
-Lukas

Quoted 34 lines

---
Changelog V2:
      Use the given start and len as Lukas comments Check the queue supports discard feature
---

fs/fat/fat.h +1 -0
index f504089..08b53e1 100644
@@ -299,6 +299,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
                             int nr_cluster);
  extern int fat_free_clusters(struct inode *inode, int cluster);
  extern int fat_count_free_clusters(struct super_block *sb);
+extern int fat_trim_fs(struct super_block *sb, struct fstrim_range *range);
 
  /* fat/file.c */
  extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
fs/fat/fatent.c +2 -0
index b47d2c9..ea31ee4 100644
@@ -1,6 +1,8 @@
  /*
   * Copyright (C) 2004, OGAWA Hirofumi
   * Released under GPL v2.
+ *
+ * Batched discard support by Kyungmin Park <[email protected]>
   */
 
  #include <linux/module.h>
@@ -541,6 +543,16 @@ out:
       return err;
  }
 
Quoted 10 lines

fs/fat/fatent.c out: · line 553

Quoted 8 lines
fs/fat/fatent.c +8 -0
+static int fat_issue_discard(struct super_block *sb, int cluster, int nr_clus)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     sector_t block, nr_blocks;
+
+        block = fat_clus_to_blknr(sbi, cluster);
+        nr_blocks = nr_clus * sbi->sec_per_clus;
+        return sb_issue_discard(sb, block, nr_blocks, GFP_NOFS, 0);

  ^^^^^^^^
This patch does not pass checkpatch.pl script. Use tabs for indention.

right, I will fix it. now passed the checkpatch.pl.

Quoted 35 lines
Quoted 35 lines
fs/fat/fatent.c +15 -5
+}
+
  int fat_free_clusters(struct inode *inode, int cluster)
  {
       struct super_block *sb = inode->i_sb;
@@ -575,11 +587,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
                       if (cluster != fatent.entry + 1) {
                               int nr_clus = fatent.entry - first_cl + 1;
 
-                             sb_issue_discard(sb,
-                                     fat_clus_to_blknr(sbi, first_cl),
-                                     nr_clus * sbi->sec_per_clus,
-                                     GFP_NOFS, 0);
-
+                             fat_issue_discard(sb, first_cl, nr_clus);
                               first_cl = cluster;
                       }
               }
@@ -683,3 +691,88 @@ out:
       unlock_fat(sbi);
       return err;
  }
+
+int fat_trim_fs(struct super_block *sb, struct fstrim_range *range)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     struct fatent_operations *ops = sbi->fatent_ops;
+     struct fat_entry fatent;
+     unsigned long reada_blocks, reada_mask, cur_block;
+     int err = 0, free, count, entry;
+     int start, len, minlen, trimmed;
+
+     start = range->start >> sb->s_blocksize_bits;
+     start = start / sbi->sec_per_clus;
Quoted 8 lines

fs/fat/fatent.c out: · line 709

Quoted 4 lines
fs/fat/fatent.c +4 -0
+     len = range->len >> sb->s_blocksize_bits;
+     len = len / sbi->sec_per_clus;
+     minlen = range->minlen >> sb->s_blocksize_bits;
+     minlen = minlen / sbi->sec_per_clus;

I should have mention that earlier, but you can also adjust mineln according to the discard_granularity, because extents smaller than that would not be discarded anyway.

I adjust the minlen granularity with fat cluster unit. and FAT don't know what's the effected block size. it's role of user side.

Quoted 12 lines
Quoted 12 lines
fs/fat/fatent.c +11 -0
+     trimmed = 0;
+     count = 0;
+
+     lock_fat(sbi);
+     if (sbi->free_clusters != -1 && sbi->free_clus_valid)
+             goto out;
+
+     reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
+     reada_mask = reada_blocks - 1;
+     cur_block = 0;
+
Quoted 11 lines

fs/fat/fatent.c out: · line 726

Quoted 6 lines
fs/fat/fatent.c +6 -0
+     entry = 0;
+     free = 0;
+     fatent_init(&fatent);
+
+     if (start < FAT_START_ENT)
+             start = FAT_START_ENT;

You're not using start anywhere.

Quoted 2 lines
fs/fat/fatent.c +2 -0
+
+     fatent_set_entry(&fatent, FAT_START_ENT);

It should be fatent_set_entry(&fatent, start);

Quoted 20 lines
Quoted 20 lines
fs/fat/fatent.c +20 -0
+
+     while (count < sbi->max_cluster) {
+             if (fatent.entry >= sbi->max_cluster)
+                     fatent.entry = FAT_START_ENT;
+             /* readahead of fat blocks */
+             if ((cur_block & reada_mask) == 0) {
+                     unsigned long rest = sbi->fat_length - cur_block;
+                     fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+             }
+             cur_block++;
+
+             err = fat_ent_read_block(sb, &fatent);
+             if (err)
+                     goto out;
+
+             do {
+                     if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                             free++;
+                             if (!entry)
+                                     entry = fatent.entry;
Quoted 8 lines

fs/fat/fatent.c out: · line 749

Quoted 1 line
fs/fat/fatent.c +1 -0
+                             if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

No. len is adjusted at fat cluster number. it's not used byte unit. I think it's easy to compare with fat internal units.

I hope fat peoples comment this one.

Thank you,
Kyungmin Park

Quoted 64 lines
Quoted 64 lines
fs/fat/fatent.c +29 -0
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                     } else if (entry) {
+                             if (free >= minlen) {
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                             free = 0;
+                             entry = 0;
+                     }
+                     count++;
+             } while (fat_ent_next(sbi, &fatent));
+     }
+     if (free >= minlen) {
+             fat_issue_discard(sb, entry, free);
+             trimmed += free;
+     }
+done:
+     range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+     fatent_brelse(&fatent);
+out:
+     unlock_fat(sbi);
+     return err;
+}
fs/fat/file.c +0 -0
index 7257752..05e6545 100644
@@ -125,6 +125,34 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
               return fat_ioctl_get_attributes(inode, user_attr);

      case FAT_IOCTL_SET_ATTRIBUTES:
              return fat_ioctl_set_attributes(filp, user_attr);
+     case FITRIM:
+     {
+             struct super_block *sb = inode->i_sb;
+             struct request_queue *q = bdev_get_queue(sb->s_bdev);
+             struct fstrim_range range;
+             int ret = 0;

fs/fat/file.c +20 -0
+
+             if (!capable(CAP_SYS_ADMIN))
+                     return -EPERM;
+
+             if (!blk_queue_discard(q))
+                     return -EOPNOTSUPP;
+
+             if (copy_from_user(&range, (struct fstrim_range *)arg,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             ret = fat_trim_fs(sb, &range);
+             if (ret < 0)
+                     return ret;
+
+             if (copy_to_user((struct fstrim_range *)arg, &range,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             return 0;
Quoted 9 lines
Quoted 6 lines

+     }
+
      default:
              return -ENOTTY; /* Inappropriate ioctl for device */ }

--

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#2 lore raw

Re: Need fat64 info

In reply to Keshava Munegowda

Hi,

Sorry I don't have any information of exFAT.

Thank you,
Kyungmin Park

Quoted 11 lines · Keshava Munegowda

Hello Kyungmin Park

 Do you have any info on fat64 (ExFAT) implementation on FAT?

Regards
Keshava Munegowda
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#3 lore raw

RE: Need fat64 info

Quoted 2 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of

Kyungmin Park

Quoted 8 lines

Sent: Thursday, February 24, 2011 3:50 PM
To: Keshava Munegowda
Cc: [email protected]
Subject: Re: Need fat64 info

Hi,

Sorry I don't have any information of exFAT.

Thank you for your reply.
Basically, I am trying to know are there anybody in the community working on fat64?

Keshava Munegowda

Quoted 14 lines

Thank you,
Kyungmin Park

Quoted 8 lines · Keshava Munegowda

Hello Kyungmin Park

 Do you have any info on fat64 (ExFAT) implementation on FAT?

Regards
Keshava Munegowda
--
To unsubscribe from this list: send the line "unsubscribe

linux-fsdevel" in

Quoted 3 lines
Quoted 3 lines

the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw
In reply to Kyungmin Park
Quoted 117 lines · Kyungmin Park
Quoted 61 lines · Lukas Czerner
Quoted 10 lines · Kyungmin Park

From: Kyungmin Park <[email protected]>

FAT supports batched discard as ext4.

Cited from Lukas words.
"The current solution is not ideal because of its bad performance impact. So basic idea to improve things is to avoid discarding every time some blocks are freed. and instead batching is together into bigger trims, which tends to be more effective."

Quoted 5 lines

You can find an information in detail at following URLs.
http://lwn.net/Articles/397538/
http://lwn.net/Articles/383933/

Hi Kyungmin,
thanks to second version. How did you test it ? You can try xtestest 251 for simple verification.
I can not really comment on FAT specific code, however I have couple of comments bellow.

Thanks!
-Lukas

Quoted 34 lines

---
Changelog V2:
      Use the given start and len as Lukas comments Check the queue supports discard feature
---

fs/fat/fat.h +1 -0
index f504089..08b53e1 100644
@@ -299,6 +299,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
                             int nr_cluster);
  extern int fat_free_clusters(struct inode *inode, int cluster);
  extern int fat_count_free_clusters(struct super_block *sb);
+extern int fat_trim_fs(struct super_block *sb, struct fstrim_range *range);
 
  /* fat/file.c */
  extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
fs/fat/fatent.c +2 -0
index b47d2c9..ea31ee4 100644
@@ -1,6 +1,8 @@
  /*
   * Copyright (C) 2004, OGAWA Hirofumi
   * Released under GPL v2.
+ *
+ * Batched discard support by Kyungmin Park <[email protected]>
   */
 
  #include <linux/module.h>
@@ -541,6 +543,16 @@ out:
       return err;
  }
 
Quoted 10 lines

fs/fat/fatent.c out: · line 553

Quoted 8 lines
fs/fat/fatent.c +8 -0
+static int fat_issue_discard(struct super_block *sb, int cluster, int nr_clus)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     sector_t block, nr_blocks;
+
+        block = fat_clus_to_blknr(sbi, cluster);
+        nr_blocks = nr_clus * sbi->sec_per_clus;
+        return sb_issue_discard(sb, block, nr_blocks, GFP_NOFS, 0);

  ^^^^^^^^
This patch does not pass checkpatch.pl script. Use tabs for indention.

right, I will fix it. now passed the checkpatch.pl.

Quoted 35 lines
Quoted 35 lines
fs/fat/fatent.c +15 -5
+}
+
  int fat_free_clusters(struct inode *inode, int cluster)
  {
       struct super_block *sb = inode->i_sb;
@@ -575,11 +587,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
                       if (cluster != fatent.entry + 1) {
                               int nr_clus = fatent.entry - first_cl + 1;
 
-                             sb_issue_discard(sb,
-                                     fat_clus_to_blknr(sbi, first_cl),
-                                     nr_clus * sbi->sec_per_clus,
-                                     GFP_NOFS, 0);
-
+                             fat_issue_discard(sb, first_cl, nr_clus);
                               first_cl = cluster;
                       }
               }
@@ -683,3 +691,88 @@ out:
       unlock_fat(sbi);
       return err;
  }
+
+int fat_trim_fs(struct super_block *sb, struct fstrim_range *range)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     struct fatent_operations *ops = sbi->fatent_ops;
+     struct fat_entry fatent;
+     unsigned long reada_blocks, reada_mask, cur_block;
+     int err = 0, free, count, entry;
+     int start, len, minlen, trimmed;
+
+     start = range->start >> sb->s_blocksize_bits;
+     start = start / sbi->sec_per_clus;
Quoted 8 lines

fs/fat/fatent.c out: · line 709

Quoted 4 lines
fs/fat/fatent.c +4 -0
+     len = range->len >> sb->s_blocksize_bits;
+     len = len / sbi->sec_per_clus;
+     minlen = range->minlen >> sb->s_blocksize_bits;
+     minlen = minlen / sbi->sec_per_clus;

I should have mention that earlier, but you can also adjust mineln according to the discard_granularity, because extents smaller than that would not be discarded anyway.

Quoted 2 lines

I adjust the minlen granularity with fat cluster unit. and FAT don't know what's the effected block size. it's role of user side.

Actually it is not and you should check
request_queue->limits.discard_granularity

see: http://www.spinics.net/lists/linux-ext4/msg23145.html

Quoted 52 lines
Quoted 12 lines
Quoted 12 lines
fs/fat/fatent.c +11 -0
+     trimmed = 0;
+     count = 0;
+
+     lock_fat(sbi);
+     if (sbi->free_clusters != -1 && sbi->free_clus_valid)
+             goto out;
+
+     reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
+     reada_mask = reada_blocks - 1;
+     cur_block = 0;
+
Quoted 11 lines

fs/fat/fatent.c out: · line 726

Quoted 6 lines
fs/fat/fatent.c +6 -0
+     entry = 0;
+     free = 0;
+     fatent_init(&fatent);
+
+     if (start < FAT_START_ENT)
+             start = FAT_START_ENT;

You're not using start anywhere.

Quoted 2 lines
fs/fat/fatent.c +2 -0
+
+     fatent_set_entry(&fatent, FAT_START_ENT);

It should be fatent_set_entry(&fatent, start);

Quoted 20 lines
Quoted 20 lines
fs/fat/fatent.c +20 -0
+
+     while (count < sbi->max_cluster) {
+             if (fatent.entry >= sbi->max_cluster)
+                     fatent.entry = FAT_START_ENT;
+             /* readahead of fat blocks */
+             if ((cur_block & reada_mask) == 0) {
+                     unsigned long rest = sbi->fat_length - cur_block;
+                     fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+             }
+             cur_block++;
+
+             err = fat_ent_read_block(sb, &fatent);
+             if (err)
+                     goto out;
+
+             do {
+                     if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                             free++;
+                             if (!entry)
+                                     entry = fatent.entry;
Quoted 8 lines

fs/fat/fatent.c out: · line 749

Quoted 1 line
fs/fat/fatent.c +1 -0
+                             if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

Quoted 2 lines

No. len is adjusted at fat cluster number. it's not used byte unit. I think it's easy to compare with fat internal units.

Does not matter what units are you using for len. I it just that you are checking for (free >= (len - trimmed)) which is wrong because len is not meant to be "overall length of trimmed data" but rather "overall length of data to walk through and check for free extents" see ext4 implementation for reference.

Thanks!
-Lukas

Quoted 79 lines

I hope fat peoples comment this one.

Thank you,
Kyungmin Park

Quoted 73 lines
Quoted 66 lines
fs/fat/fatent.c +29 -0
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                     } else if (entry) {
+                             if (free >= minlen) {
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                             free = 0;
+                             entry = 0;
+                     }
+                     count++;
+             } while (fat_ent_next(sbi, &fatent));
+     }
+     if (free >= minlen) {
+             fat_issue_discard(sb, entry, free);
+             trimmed += free;
+     }
+done:
+     range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+     fatent_brelse(&fatent);
+out:
+     unlock_fat(sbi);
+     return err;
+}
fs/fat/file.c +0 -0
index 7257752..05e6545 100644
@@ -125,6 +125,34 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
               return fat_ioctl_get_attributes(inode, user_attr);

      case FAT_IOCTL_SET_ATTRIBUTES:
              return fat_ioctl_set_attributes(filp, user_attr);
+     case FITRIM:
+     {
+             struct super_block *sb = inode->i_sb;
+             struct request_queue *q = bdev_get_queue(sb->s_bdev);
+             struct fstrim_range range;
+             int ret = 0;

fs/fat/file.c +22 -0
+
+             if (!capable(CAP_SYS_ADMIN))
+                     return -EPERM;
+
+             if (!blk_queue_discard(q))
+                     return -EOPNOTSUPP;
+
+             if (copy_from_user(&range, (struct fstrim_range *)arg,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             ret = fat_trim_fs(sb, &range);
+             if (ret < 0)
+                     return ret;
+
+             if (copy_to_user((struct fstrim_range *)arg, &range,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             return 0;
+     }
+
Quoted 4 lines

      default:
              return -ENOTTY; /* Inappropriate ioctl for device */ }

--

#9 lore raw
Quoted 119 lines · Lukas Czerner
Quoted 117 lines · Kyungmin Park
Quoted 61 lines · Lukas Czerner
Quoted 10 lines · Kyungmin Park

From: Kyungmin Park <[email protected]>

FAT supports batched discard as ext4.

Cited from Lukas words.
"The current solution is not ideal because of its bad performance impact. So basic idea to improve things is to avoid discarding every time some blocks are freed. and instead batching is together into bigger trims, which tends to be more effective."

Quoted 5 lines

You can find an information in detail at following URLs.
http://lwn.net/Articles/397538/
http://lwn.net/Articles/383933/

Hi Kyungmin,
thanks to second version. How did you test it ? You can try xtestest 251 for simple verification.
I can not really comment on FAT specific code, however I have couple of comments bellow.

Thanks!
-Lukas

Quoted 34 lines

---
Changelog V2:
      Use the given start and len as Lukas comments Check the queue supports discard feature
---

fs/fat/fat.h +1 -0
index f504089..08b53e1 100644
@@ -299,6 +299,7 @@ extern int fat_alloc_clusters(struct inode *inode, int *cluster,
                             int nr_cluster);
  extern int fat_free_clusters(struct inode *inode, int cluster);
  extern int fat_count_free_clusters(struct super_block *sb);
+extern int fat_trim_fs(struct super_block *sb, struct fstrim_range *range);
 
  /* fat/file.c */
  extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
fs/fat/fatent.c +2 -0
index b47d2c9..ea31ee4 100644
@@ -1,6 +1,8 @@
  /*
   * Copyright (C) 2004, OGAWA Hirofumi
   * Released under GPL v2.
+ *
+ * Batched discard support by Kyungmin Park <[email protected]>
   */
 
  #include <linux/module.h>
@@ -541,6 +543,16 @@ out:
       return err;
  }
 
Quoted 10 lines

fs/fat/fatent.c out: · line 553

Quoted 8 lines
fs/fat/fatent.c +8 -0
+static int fat_issue_discard(struct super_block *sb, int cluster, int nr_clus)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     sector_t block, nr_blocks;
+
+        block = fat_clus_to_blknr(sbi, cluster);
+        nr_blocks = nr_clus * sbi->sec_per_clus;
+        return sb_issue_discard(sb, block, nr_blocks, GFP_NOFS, 0);

  ^^^^^^^^
This patch does not pass checkpatch.pl script. Use tabs for indention.

right, I will fix it. now passed the checkpatch.pl.

Quoted 43 lines
Quoted 29 lines
fs/fat/fatent.c +9 -5
+}
+
  int fat_free_clusters(struct inode *inode, int cluster)
  {
       struct super_block *sb = inode->i_sb;
@@ -575,11 +587,7 @@ int fat_free_clusters(struct inode *inode, int cluster)
                       if (cluster != fatent.entry + 1) {
                               int nr_clus = fatent.entry - first_cl + 1;
 
-                             sb_issue_discard(sb,
-                                     fat_clus_to_blknr(sbi, first_cl),
-                                     nr_clus * sbi->sec_per_clus,
-                                     GFP_NOFS, 0);
-
+                             fat_issue_discard(sb, first_cl, nr_clus);
                               first_cl = cluster;
                       }
               }
@@ -683,3 +691,88 @@ out:
       unlock_fat(sbi);
       return err;
  }
+
+int fat_trim_fs(struct super_block *sb, struct fstrim_range *range)
+{
+     struct msdos_sb_info *sbi = MSDOS_SB(sb);
+     struct fatent_operations *ops = sbi->fatent_ops;
+     struct fat_entry fatent;

fs/fat/fatent.c out: · line 709

Quoted 10 lines
fs/fat/fatent.c +10 -0
+     unsigned long reada_blocks, reada_mask, cur_block;
+     int err = 0, free, count, entry;
+     int start, len, minlen, trimmed;
+
+     start = range->start >> sb->s_blocksize_bits;
+     start = start / sbi->sec_per_clus;
+     len = range->len >> sb->s_blocksize_bits;
+     len = len / sbi->sec_per_clus;
+     minlen = range->minlen >> sb->s_blocksize_bits;
+     minlen = minlen / sbi->sec_per_clus;

I should have mention that earlier, but you can also adjust mineln according to the discard_granularity, because extents smaller than that would not be discarded anyway.

Quoted 5 lines
Quoted 2 lines

I adjust the minlen granularity with fat cluster unit. and FAT don't know what's the effected  block size. it's role of user side.

Actually it is not and you should check
request_queue->limits.discard_granularity

Okay, it's simple one, I will add it.

Quoted 55 lines

see: http://www.spinics.net/lists/linux-ext4/msg23145.html

Quoted 52 lines
Quoted 12 lines
Quoted 12 lines
fs/fat/fatent.c +11 -0
+     trimmed = 0;
+     count = 0;
+
+     lock_fat(sbi);
+     if (sbi->free_clusters != -1 && sbi->free_clus_valid)
+             goto out;
+
+     reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
+     reada_mask = reada_blocks - 1;
+     cur_block = 0;
+
Quoted 11 lines

fs/fat/fatent.c out: · line 726

Quoted 6 lines
fs/fat/fatent.c +6 -0
+     entry = 0;
+     free = 0;
+     fatent_init(&fatent);
+
+     if (start < FAT_START_ENT)
+             start = FAT_START_ENT;

You're not using start anywhere.

Quoted 2 lines
fs/fat/fatent.c +2 -0
+
+     fatent_set_entry(&fatent, FAT_START_ENT);

It should be fatent_set_entry(&fatent, start);

Quoted 28 lines
Quoted 11 lines
fs/fat/fatent.c +11 -0
+
+     while (count < sbi->max_cluster) {
+             if (fatent.entry >= sbi->max_cluster)
+                     fatent.entry = FAT_START_ENT;
+             /* readahead of fat blocks */
+             if ((cur_block & reada_mask) == 0) {
+                     unsigned long rest = sbi->fat_length - cur_block;
+                     fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+             }
+             cur_block++;
+

fs/fat/fatent.c out: · line 749

Quoted 10 lines
fs/fat/fatent.c +10 -0
+             err = fat_ent_read_block(sb, &fatent);
+             if (err)
+                     goto out;
+
+             do {
+                     if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                             free++;
+                             if (!entry)
+                                     entry = fatent.entry;
+                             if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

Quoted 8 lines
Quoted 2 lines

No. len is adjusted at fat cluster number. it's not used byte unit. I think it's easy to compare with fat internal units.

Does not matter what units are you using for len. I it just that you are checking for (free >= (len - trimmed)) which is wrong because len is not meant to be "overall length of trimmed data" but rather "overall length of data to walk through and check for free extents" see ext4 implementation for reference.

I think I used it as you said. e.g., I want to trim 256 (* minimum discard granularity),
First time, I can find 10 entries. and trimmed has 10 and len has still 256. next time, I found the 246 free entries then trim remaining 246 one.

do you want to trim it more than given len?

Thank you,
Kyungmin Park

Quoted 85 lines

Thanks!
-Lukas

Quoted 79 lines

I hope fat peoples comment this one.

Thank you,
Kyungmin Park

Quoted 73 lines
Quoted 66 lines
fs/fat/fatent.c +29 -0
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                     } else if (entry) {
+                             if (free >= minlen) {
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                             free = 0;
+                             entry = 0;
+                     }
+                     count++;
+             } while (fat_ent_next(sbi, &fatent));
+     }
+     if (free >= minlen) {
+             fat_issue_discard(sb, entry, free);
+             trimmed += free;
+     }
+done:
+     range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+     fatent_brelse(&fatent);
+out:
+     unlock_fat(sbi);
+     return err;
+}
fs/fat/file.c +0 -0
index 7257752..05e6545 100644
@@ -125,6 +125,34 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
               return fat_ioctl_get_attributes(inode, user_attr);

      case FAT_IOCTL_SET_ATTRIBUTES:
              return fat_ioctl_set_attributes(filp, user_attr);
+     case FITRIM:
+     {
+             struct super_block *sb = inode->i_sb;
+             struct request_queue *q = bdev_get_queue(sb->s_bdev);
+             struct fstrim_range range;
+             int ret = 0;

fs/fat/file.c +22 -0
+
+             if (!capable(CAP_SYS_ADMIN))
+                     return -EPERM;
+
+             if (!blk_queue_discard(q))
+                     return -EOPNOTSUPP;
+
+             if (copy_from_user(&range, (struct fstrim_range *)arg,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             ret = fat_trim_fs(sb, &range);
+             if (ret < 0)
+                     return ret;
+
+             if (copy_to_user((struct fstrim_range *)arg, &range,
+                                     sizeof(range)))
+                     return -EFAULT;
+
+             return 0;
+     }
+
Quoted 4 lines

      default:
              return -ENOTTY; /* Inappropriate ioctl for device */ }

--

--

#10 lore raw

On Thu, 24 Feb 2011, Kyungmin Park wrote:

...snip...

Quoted 35 lines
Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines

+     while (count < sbi->max_cluster) {
+             if (fatent.entry >= sbi->max_cluster)
+                     fatent.entry = FAT_START_ENT;
+             /* readahead of fat blocks */
+             if ((cur_block & reada_mask) == 0) {
+                     unsigned long rest = sbi->fat_length - cur_block;
+                     fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+             }
+             cur_block++;

fs/fat/fatent.c +1 -0
+

fs/fat/fatent.c out: · line 749

Quoted 10 lines
fs/fat/fatent.c +10 -0
+             err = fat_ent_read_block(sb, &fatent);
+             if (err)
+                     goto out;
+
+             do {
+                     if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                             free++;
+                             if (!entry)
+                                     entry = fatent.entry;
+                             if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

Quoted 8 lines
Quoted 2 lines

No. len is adjusted at fat cluster number. it's not used byte unit. I think it's easy to compare with fat internal units.

Does not matter what units are you using for len. I it just that you are checking for (free >= (len - trimmed)) which is wrong because len is not meant to be "overall length of trimmed data" but rather "overall length of data to walk through and check for free extents" see ext4 implementation for reference.

Quoted 6 lines

I think I used it as you said. e.g., I want to trim 256 (* minimum discard granularity),
First time, I can find 10 entries. and trimmed has 10 and len has still 256. next time, I found the 246 free entries then trim remaining 246 one.

do you want to trim it more than given len?

Let the "O" be free (bytes, blocks, whatever), and "=" be used. Now, we have a filesystem like this.

OOOO==O===OO===OOOOO==O===O===OOOOOOO===
^ ^
0 40

This is how it supposed to wotk if you have called FITIRM with parameters:

start = 0
minlen = 2
len = 20

So you will go through (blocks, bytes...) 0 -> 20

OOOO==O===OO===OOOOO==O===O===OOOOOOO===
^ ^
0 20

So, you will call discard on extents:

0-3
You'll skip 6 because is smaller than minlen
10-11
15-19

But in your implementation you will trim extents:
0-3
10-11
15-19
30-36

Hope it is clear now.

Quoted 42 lines

Thank you,
Kyungmin Park

Quoted 39 lines

Thanks!
-Lukas

Quoted 35 lines

I hope fat peoples comment this one.

Thank you,
Kyungmin Park

Quoted 30 lines
Quoted 30 lines

+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                     } else if (entry) {
+                             if (free >= minlen) {
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                             free = 0;
+                             entry = 0;
+                     }
+                     count++;
+             } while (fat_ent_next(sbi, &fatent));
+     }
+     if (free >= minlen) {
+             fat_issue_discard(sb, entry, free);
+             trimmed += free;
+     }

fs/fat/fatent.c +7 -0
+done:
+     range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+     fatent_brelse(&fatent);
+out:
+     unlock_fat(sbi);
+     return err;
+}

...snip...

#11 lore raw
Quoted 72 lines · Lukas Czerner

On Thu, 24 Feb 2011, Kyungmin Park wrote:

...snip...

Quoted 35 lines
Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines

+     while (count < sbi->max_cluster) {
+             if (fatent.entry >= sbi->max_cluster)
+                     fatent.entry = FAT_START_ENT;
+             /* readahead of fat blocks */
+             if ((cur_block & reada_mask) == 0) {
+                     unsigned long rest = sbi->fat_length - cur_block;
+                     fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
+             }
+             cur_block++;

fs/fat/fatent.c +1 -0
+

fs/fat/fatent.c out: · line 749

Quoted 10 lines
fs/fat/fatent.c +10 -0
+             err = fat_ent_read_block(sb, &fatent);
+             if (err)
+                     goto out;
+
+             do {
+                     if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                             free++;
+                             if (!entry)
+                                     entry = fatent.entry;
+                             if (free >= (len - trimmed) && free >= minlen) {

It seems to me that you are using len as number of bytes to trim. This is not right and I am sorry for not explaining it correctly. "len" is supposed to be a number of bytes you want to "investigate" from the start. So it means that you will trim every single free extent bigger than minlen between "start" byte and "start + len" byte of the underlying device, or partition.

Quoted 8 lines
Quoted 2 lines

No. len is adjusted at fat cluster number. it's not used byte unit. I think it's easy to compare with fat internal units.

Does not matter what units are you using for len. I it just that you are checking for (free >= (len - trimmed)) which is wrong because len is not meant to be "overall length of trimmed data" but rather "overall length of data to walk through and check for free extents" see ext4 implementation for reference.

Quoted 6 lines

I think I used it as you said. e.g., I want to trim 256 (* minimum discard granularity),
First time, I can find 10 entries. and trimmed has 10 and len has still 256. next time, I found the 246 free entries then trim remaining 246 one.

do you want to trim it more than given len?

Let the "O" be free (bytes, blocks, whatever), and "=" be used. Now, we have a filesystem like this.

  OOOO==O===OO===OOOOO==O===O===OOOOOOO===
  ^                                      ^
  0                                      40

This is how it supposed to wotk if you have called FITIRM with parameters:

start = 0
minlen = 2
len = 20

So you will go through (blocks, bytes...) 0 -> 20

  OOOO==O===OO===OOOOO==O===O===OOOOOOO===
  ^                   ^
  0                   20

So, you will call discard on extents:

0-3
You'll skip 6 because is smaller than minlen
10-11
15-19

Quoted 7 lines

But in your implementation you will trim extents:
0-3
10-11
15-19
30-36

Hope it is clear now.

Okay you mean it's end address instead of total amount of trim size. It will be helpful if you add these diagram or comments on linux/include/fs.h

Thank you,
Kyungmin Park

Quoted 37 lines
Quoted 37 lines

Thank you,
Kyungmin Park

Quoted 33 lines

Thanks!
-Lukas

Quoted 29 lines

I hope fat peoples comment this one.

Thank you,
Kyungmin Park

Quoted 24 lines
Quoted 24 lines

+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                     } else if (entry) {
+                             if (free >= minlen) {
+                                     fat_issue_discard(sb, entry, free);
+                                     trimmed += free;
+                             }
+                             if (trimmed >= len)
+                                     goto done;
+                             free = 0;
+                             entry = 0;
+                     }
+                     count++;
+             } while (fat_ent_next(sbi, &fatent));
+     }
+     if (free >= minlen) {
+             fat_issue_discard(sb, entry, free);
+             trimmed += free;
+     }
+done:

Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

+     range->len = (trimmed * sbi->sec_per_clus) << sb->s_blocksize_bits;
+     fatent_brelse(&fatent);

fs/fat/fatent.c +4 -0
+out:
+     unlock_fat(sbi);
+     return err;
+}

...snip...

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#4 lore raw

Re: Need fat64 info

In reply to Keshava Munegowda

Hi,

Quoted 8 lines · Keshava Munegowda
Quoted 2 lines

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of

Kyungmin Park

Quoted 5 lines

Sent: Thursday, February 24, 2011 3:50 PM
To: Keshava Munegowda
Cc: [email protected]
Subject: Re: Need fat64 info

Quoted 7 lines
Quoted 3 lines

Hi,

Sorry I don't have any information of exFAT.

Thank you for your reply.
Basically, I am trying to know are there anybody in the community working on fat64?

No one in the community is allowed to work on exFAT. You must have an exFAT license from Microsoft to use exFAT.

There are plenty of commercial drivers though. For example Tuxera (http://www.tuxera.com/) has both a FUSE based and a kernel based exFAT driver...

Best regards,

Anton

Quoted 20 lines

Keshava Munegowda

Quoted 14 lines

Thank you,
Kyungmin Park

Quoted 8 lines · Keshava Munegowda

Hello Kyungmin Park

Do you have any info on fat64 (ExFAT) implementation on FAT?

Regards
Keshava Munegowda
--
To unsubscribe from this list: send the line "unsubscribe

linux-fsdevel" in

Quoted 2 lines
Quoted 2 lines

the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 3 lines
Anton Altaparmakov <anton at tuxera.com> (replace at with @)
Senior Kernel Developer, Tuxera Inc., http://www.tuxera.com/
Linux NTFS maintainer, http://www.linux-ntfs.org/
#5 lore raw

RE: Need fat64 info

Thanks Anton

This is really a very good info;

Regards
Keshava Munegowda

Quoted 2 lines

No one in the community is allowed to work on exFAT. You must have an

exFAT license from Microsoft

Quoted 3 lines

to use exFAT.

There are plenty of commercial drivers though. For example Tuxera

(http://www.tuxera.com/) has both

Quoted 32 lines

a FUSE based and a kernel based exFAT driver...

Best regards,

Anton

Quoted 11 lines

Keshava Munegowda

Quoted 8 lines

Thank you,
Kyungmin Park

Quoted 2 lines · Keshava Munegowda

Hello Kyungmin Park

Quoted 9 lines
Quoted 6 lines
Quoted 6 lines

Do you have any info on fat64 (ExFAT) implementation on FAT?

Regards
Keshava Munegowda
--
To unsubscribe from this list: send the line "unsubscribe

linux-fsdevel" in

Quoted 2 lines
Quoted 2 lines

the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Anton Altaparmakov <anton at tuxera.com> (replace at with @) Senior Kernel Developer, Tuxera Inc., http://www.tuxera.com/
Linux NTFS maintainer, http://www.linux-ntfs.org/