Module Name: src Committed By: perseant Date: Fri Aug 2 00:16:55 UTC 2024
Modified Files: src/sys/fs/exfatfs [perseant-exfatfs]: TODO exfatfs.h exfatfs_balloc.c exfatfs_balloc.h exfatfs_extern.c exfatfs_extern.h exfatfs_vfsops.c exfatfs_vnops.c Log Message: Use a symbol for the size of the boot code. Deallocate bitmap entries more efficiently for unfragmented files Use TRIM if the disk driver supports it. Share code for locating a valid superblock. Share code for writing, and writing back, the superblock. Correct the sense of the archive bit (it was inverted). To generate a diff of this commit: cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/fs/exfatfs/TODO \ src/sys/fs/exfatfs/exfatfs_balloc.c src/sys/fs/exfatfs/exfatfs_balloc.h \ src/sys/fs/exfatfs/exfatfs_extern.h cvs rdiff -u -r1.1.2.5 -r1.1.2.6 src/sys/fs/exfatfs/exfatfs.h \ src/sys/fs/exfatfs/exfatfs_vfsops.c cvs rdiff -u -r1.1.2.6 -r1.1.2.7 src/sys/fs/exfatfs/exfatfs_extern.c cvs rdiff -u -r1.1.2.8 -r1.1.2.9 src/sys/fs/exfatfs/exfatfs_vnops.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/fs/exfatfs/TODO diff -u src/sys/fs/exfatfs/TODO:1.1.2.3 src/sys/fs/exfatfs/TODO:1.1.2.4 --- src/sys/fs/exfatfs/TODO:1.1.2.3 Fri Jul 19 16:19:15 2024 +++ src/sys/fs/exfatfs/TODO Fri Aug 2 00:16:55 2024 @@ -1 +1,2 @@ * Use unions in directory entries instead of the cumbersome casting. +* Copy extended boot code too, if requested. \ No newline at end of file Index: src/sys/fs/exfatfs/exfatfs_balloc.c diff -u src/sys/fs/exfatfs/exfatfs_balloc.c:1.1.2.3 src/sys/fs/exfatfs/exfatfs_balloc.c:1.1.2.4 --- src/sys/fs/exfatfs/exfatfs_balloc.c:1.1.2.3 Fri Jul 19 16:19:15 2024 +++ src/sys/fs/exfatfs/exfatfs_balloc.c Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_balloc.c,v 1.1.2.3 2024/07/19 16:19:15 perseant Exp $ */ +/* $NetBSD: exfatfs_balloc.c,v 1.1.2.4 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022, 2024 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: exfatfs_balloc.c,v 1.1.2.3 2024/07/19 16:19:15 perseant Exp $"); +__KERNEL_RCSID(0, "$NetBSD: exfatfs_balloc.c,v 1.1.2.4 2024/08/02 00:16:55 perseant Exp $"); #include <sys/types.h> #include <sys/buf.h> @@ -185,24 +185,39 @@ exfatfs_bitmap_alloc(struct exfatfs *fs, } int -exfatfs_bitmap_dealloc(struct exfatfs *fs, uint32_t cno) +exfatfs_bitmap_dealloc(struct exfatfs *fs, uint32_t cno, int len) { daddr_t lbn, blkno; - struct buf *bp; - int off, error; + struct buf *bp = NULL; + int off, error, lbsize; uint8_t *data; - + lbn = BITMAPLBN(fs, cno); off = BITMAPOFF(fs, cno); - exfatfs_bmap_shared(fs->xf_bitmapvp, lbn, NULL, &blkno, NULL); - if ((error = bread(fs->xf_devvp, blkno, EXFATFS_LSIZE(fs), 0, &bp)) != 0) - return error; - data = (uint8_t *)bp->b_data; - DPRINTF(("basic deallocate cluster %u at lbn %u bit %d\n", - (unsigned)cno, (unsigned)lbn, (int)off)); - assert(isset(data, off)); - clrbit(data, off); - bdwrite(bp); + lbsize = EXFATFS_LSIZE(fs) * NBBY; + while (--len >= 0) { + if (off >= lbsize) { + /* Switch to a new block */ + off = 0; + ++lbn; + if (bp != NULL) + bdwrite(bp); + bp = NULL; + } + if (bp == NULL) { + exfatfs_bmap_shared(fs->xf_bitmapvp, lbn, NULL, &blkno, NULL); + if ((error = bread(fs->xf_devvp, blkno, EXFATFS_LSIZE(fs), 0, &bp)) != 0) + return error; + data = (uint8_t *)bp->b_data; + } + DPRINTF(("basic deallocate cluster %u at lbn %u bit %d\n", + (unsigned)cno, (unsigned)lbn, (int)off)); + assert(isset(data, off)); + clrbit(data, off); + ++off; + } + if (bp != NULL) + bdwrite(bp); #ifdef _KERNEL mutex_enter(&fs->xf_lock); #endif /* _KERNEL */ Index: src/sys/fs/exfatfs/exfatfs_balloc.h diff -u src/sys/fs/exfatfs/exfatfs_balloc.h:1.1.2.3 src/sys/fs/exfatfs/exfatfs_balloc.h:1.1.2.4 --- src/sys/fs/exfatfs/exfatfs_balloc.h:1.1.2.3 Fri Jul 19 16:19:15 2024 +++ src/sys/fs/exfatfs/exfatfs_balloc.h Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_balloc.h,v 1.1.2.3 2024/07/19 16:19:15 perseant Exp $ */ +/* $NetBSD: exfatfs_balloc.h,v 1.1.2.4 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022, 2024 The NetBSD Foundation, Inc. @@ -42,6 +42,6 @@ int exfatfs_bitmap_init(struct exfatfs *); void exfatfs_bitmap_destroy(struct exfatfs *); int exfatfs_bitmap_alloc(struct exfatfs *, uint32_t, uint32_t *); -int exfatfs_bitmap_dealloc(struct exfatfs *, uint32_t); +int exfatfs_bitmap_dealloc(struct exfatfs *, uint32_t, int); #endif /* EXFATFS_BALLOC_H_ */ Index: src/sys/fs/exfatfs/exfatfs_extern.h diff -u src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.3 src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.4 --- src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.3 Wed Jul 24 00:38:26 2024 +++ src/sys/fs/exfatfs/exfatfs_extern.h Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_extern.h,v 1.1.2.3 2024/07/24 00:38:26 perseant Exp $ */ +/* $NetBSD: exfatfs_extern.h,v 1.1.2.4 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. @@ -37,6 +37,7 @@ int exfatfs_bmap_shared(struct vnode *, daddr_t, struct vnode **, daddr_t *, int *); +int exfatfs_locate_valid_superblock(struct vnode *, size_t, struct exfatfs **); int exfatfs_mountfs_shared(struct vnode *, struct exfatfs_mount *, unsigned, struct exfatfs **); struct xfinode *exfatfs_newxfinode(struct exfatfs *, uint32_t, uint32_t); @@ -51,6 +52,6 @@ int exfatfs_scandir(struct vnode *, off_ void *arg); #define SCANDIR_STOP 0x00000001 #define SCANDIR_DONTFREE 0x00000002 -int exfatfs_write_sb(struct exfatfs *); +int exfatfs_write_sb(struct exfatfs *, int); #endif /* EXFATFS_EXTERN_H_ */ Index: src/sys/fs/exfatfs/exfatfs.h diff -u src/sys/fs/exfatfs/exfatfs.h:1.1.2.5 src/sys/fs/exfatfs/exfatfs.h:1.1.2.6 --- src/sys/fs/exfatfs/exfatfs.h:1.1.2.5 Fri Jul 19 16:19:15 2024 +++ src/sys/fs/exfatfs/exfatfs.h Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs.h,v 1.1.2.5 2024/07/19 16:19:15 perseant Exp $ */ +/* $NetBSD: exfatfs.h,v 1.1.2.6 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022, 2024 The NetBSD Foundation, Inc. @@ -71,7 +71,8 @@ struct exfatdfs { uint8_t xdf_DriveSelect; uint8_t xdf_PercentInUse; uint8_t xdf_Reserved[7]; - uint8_t xdf_BootCode[390]; +#define EXFATFS_BOOTCODE_SIZE 390 + uint8_t xdf_BootCode[EXFATFS_BOOTCODE_SIZE]; uint16_t xdf_BootSignature; #define EXFAT_BOOT_SIGNATURE 0xAA55 #define EXFAT_EXTENDED_BOOT_SIGNATURE 0xAA550000 Index: src/sys/fs/exfatfs/exfatfs_vfsops.c diff -u src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.5 src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.6 --- src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.5 Wed Jul 24 00:38:26 2024 +++ src/sys/fs/exfatfs/exfatfs_vfsops.c Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_vfsops.c,v 1.1.2.5 2024/07/24 00:38:26 perseant Exp $ */ +/* $NetBSD: exfatfs_vfsops.c,v 1.1.2.6 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: exfatfs_vfsops.c,v 1.1.2.5 2024/07/24 00:38:26 perseant Exp $"); +__KERNEL_RCSID(0, "$NetBSD: exfatfs_vfsops.c,v 1.1.2.6 2024/08/02 00:16:55 perseant Exp $"); struct vm_page; @@ -620,7 +620,7 @@ exfatfs_unmount(struct mount *mp, int mn fs->xf_VolumeFlags &= ~EXFATFS_VOLUME_DIRTY; fs->xf_PercentInUse = (fs->xf_ClusterCount - fs->xf_FreeClusterCount) / fs->xf_ClusterCount; - exfatfs_write_sb(fs); + exfatfs_write_sb(fs, 0); DPRINTF((" lock devvp...\n")); vn_lock(fs->xf_devvp, LK_EXCLUSIVE | LK_RETRY); DPRINTF((" close devvp...\n")); Index: src/sys/fs/exfatfs/exfatfs_extern.c diff -u src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.6 src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.7 --- src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.6 Wed Jul 24 00:38:26 2024 +++ src/sys/fs/exfatfs/exfatfs_extern.c Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_extern.c,v 1.1.2.6 2024/07/24 00:38:26 perseant Exp $ */ +/* $NetBSD: exfatfs_extern.c,v 1.1.2.7 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. @@ -33,10 +33,11 @@ #ifdef _KERNEL # include <sys/buf.h> +# define B_INVAL BC_INVAL # include <sys/malloc.h> # include <sys/rwlock.h> # include <miscfs/specfs/specdev.h> /* For v_rdev */ -#else +#else /* ! _KERNEL */ # include <stdlib.h> # include <string.h> # include <assert.h> @@ -49,7 +50,7 @@ # define buf ubuf typedef struct uvvnode uvnode_t; # define vnode_t uvnode_t -#endif +#endif /* ! _KERNEL */ #include <fs/exfatfs/exfatfs.h> #include <fs/exfatfs/exfatfs_balloc.h> @@ -254,40 +255,21 @@ errout: return error; } -int exfatfs_mountfs_shared(struct vnode *devvp, struct exfatfs_mount *xmp, - unsigned secsize, struct exfatfs **fsp) +int +exfatfs_locate_valid_superblock(struct vnode *devvp, size_t secshift, struct exfatfs **fsp) { struct exfatfs *fs = NULL; struct buf *bp; - uint16_t *uctable; - int error; - unsigned secshift; + int error, last_error; const char *errstr; int boot_offset; uint8_t boot_ignore[3] = { 106, 107, 112 }; int bn; uint32_t sum, badsb; - off_t res, off; + size_t secsize, newshift; - DPRINTF(("exfatfs_mountfs_shared(%p, %u, %p)\n", - devvp, secsize, fsp)); - - if (secsize < DEV_BSIZE) { - DPRINTF(("Invalid block secsize (%d < DEV_BSIZE)\n", secsize)); - return EINVAL; - } - secshift = DEV_BSHIFT; - while ((1U << secshift) < secsize) - ++secshift; - - /* - * Set up our exfat-specific mount structure - */ - if (secsize < sizeof(fs->xf_exfatdfs)) { - DPRINTF(("sector size %u smaller than required %u\n", - (unsigned)secsize, (unsigned)sizeof(fs->xf_exfatdfs))); - return EINVAL; - } + *fsp = NULL; + secsize = (1 << secshift); /* * Check both boot blocks and checksums to find a valid one @@ -307,8 +289,17 @@ int exfatfs_mountfs_shared(struct vnode (unsigned long)((bn + boot_offset) << (secshift - DEV_BSHIFT)), (unsigned long)secsize, error)); + last_error = error; continue; } + + /* If the sectors are of a different size, switch to that size */ + newshift = ((struct exfatfs *)bp->b_data)->xf_BytesPerSectorShift; + if (newshift != secshift && newshift >= 9 && newshift <= 12) { + brelse(bp, B_INVAL); + return exfatfs_locate_valid_superblock(devvp, newshift, fsp); + } + /* * The checksum of the first sector ignores * certain fields, per the specification. @@ -322,26 +313,28 @@ int exfatfs_mountfs_shared(struct vnode * this is our superblock if checksums match. */ if (bn == 0 && fs == NULL) { - fs = malloc(sizeof(*fs) + *fsp = malloc(sizeof(**fsp) #ifdef _KERNEL , M_EXFATFSBOOT, M_WAITOK #endif /* _KERNEL */ ); - memset(fs, 0, sizeof(*fs)); - memcpy(fs, bp->b_data, sizeof(fs->xf_exfatdfs)); + memset(*fsp, 0, sizeof(*fs)); + memcpy(*fsp, bp->b_data, sizeof((*fsp)->xf_exfatdfs)); #ifdef DEBUG_VERBOSE - DPRINTF(("fs = %p\n", fs)); + DPRINTF(("fs = %p\n", *fsp)); #endif } brelse(bp, 0); } - if (fs == NULL) + if (*fsp == NULL) continue; /* Now read the recorded checksum and compare */ if ((error = bread(devvp, (11 + boot_offset) << (secshift - DEV_BSHIFT), - secsize, 0, &bp)) != 0) + secsize, 0, &bp)) != 0) { + last_error = error; continue; + } if (sum != *(u_int32_t *)(bp->b_data)) { DPRINTF(("Checksum mismatch at offset %u\n", boot_offset)); badsb = 1; @@ -350,7 +343,7 @@ int exfatfs_mountfs_shared(struct vnode bp = NULL; /* Check other aspects of the boot block */ - if (!badsb && (errstr = exfatfs_check_bootblock(fs)) != NULL) { + if (!badsb && (errstr = exfatfs_check_bootblock(*fsp)) != NULL) { DPRINTF(("%s\n", errstr)); badsb = 1; } @@ -359,14 +352,52 @@ int exfatfs_mountfs_shared(struct vnode break; /* Not an error on the first one, just try the other */ - free(fs + free(*fsp #ifdef _KERNEL , M_EXFATFSBOOT #endif /* _KERNEL */ ); - fs = NULL; + *fsp = NULL; } + + if (*fsp) + last_error = 0; + return last_error; +} +int exfatfs_mountfs_shared(struct vnode *devvp, struct exfatfs_mount *xmp, + unsigned secsize, struct exfatfs **fsp) +{ + struct exfatfs *fs = NULL; + struct buf *bp; + uint16_t *uctable; + int error; + unsigned secshift; + off_t res, off; + + DPRINTF(("exfatfs_mountfs_shared(%p, %u, %p)\n", + devvp, secsize, fsp)); + + if (secsize < DEV_BSIZE) { + DPRINTF(("Invalid block secsize (%d < DEV_BSIZE)\n", secsize)); + return EINVAL; + } + secshift = DEV_BSHIFT; + while ((1U << secshift) < secsize) + ++secshift; + + /* + * Set up our exfat-specific mount structure + */ + if (secsize < sizeof(fs->xf_exfatdfs)) { + DPRINTF(("sector size %u smaller than required %u\n", + (unsigned)secsize, (unsigned)sizeof(fs->xf_exfatdfs))); + return EINVAL; + } + + if ((error = exfatfs_locate_valid_superblock(devvp, secshift, &fs)) != 0) + return error; + if (fs == NULL) { DPRINTF(("Neither boot block was valid\n")); return EINVAL; @@ -381,7 +412,7 @@ int exfatfs_mountfs_shared(struct vnode /* If mounting for write, mark the fs dirty */ if (!(xmp->xm_flags & EXFATFSMNT_RONLY)) { fs->xf_VolumeFlags |= EXFATFS_VOLUME_DIRTY; - exfatfs_write_sb(fs); + exfatfs_write_sb(fs, 0); } } @@ -584,7 +615,8 @@ done: return 0; } -static const char * exfatfs_check_bootblock(struct exfatfs *fs) +static const char * +exfatfs_check_bootblock(struct exfatfs *fs) { unsigned i; @@ -741,7 +773,8 @@ exfatfs_freedirent(struct exfatfs_dirent } #ifdef EXFATFS_FENCE -static int checkzero(void *p, int len) { +static int +checkzero(void *p, int len) { int i; for (i = 0; i < len; i++) @@ -1028,8 +1061,9 @@ out: * Assemble the file name from its various components and * store it in "name", with its length in "namelenp". */ -int exfatfs_get_file_name(struct xfinode *xip, uint16_t *name, - int *namelenp, int resid) +int +exfatfs_get_file_name(struct xfinode *xip, uint16_t *name, + int *namelenp, int resid) { int entryno, i; struct exfatfs_dfn *dfn; @@ -1055,8 +1089,9 @@ int exfatfs_get_file_name(struct xfinode * to preserve other, non-filename-related, secondary entries that may * follow the File Name entries. */ -int exfatfs_set_file_name(struct xfinode *xip, uint16_t *name, - int namelen) +int +exfatfs_set_file_name(struct xfinode *xip, uint16_t *name, + int namelen) { struct exfatfs_dirent *oldp[EXFATFS_MAXDIRENT]; struct exfatfs_dfn *dfnp; @@ -1104,7 +1139,7 @@ int exfatfs_set_file_name(struct xfinode * Write the boot block to disk, and checksum the boot block set. */ int -exfatfs_write_sb(struct exfatfs *fs) +exfatfs_write_sb(struct exfatfs *fs, int re_checksum) { daddr_t base; int i, error; @@ -1120,12 +1155,16 @@ exfatfs_write_sb(struct exfatfs *fs) return error; memcpy(bp->b_data, &fs->xf_exfatdfs, sizeof(fs->xf_exfatdfs)); - cksum = exfatfs_cksum32(0, - (uint8_t *)bp->b_data, - BSSIZE(fs), boot_ignore, - sizeof(boot_ignore)); + if (re_checksum) + cksum = exfatfs_cksum32(0, + (uint8_t *)bp->b_data, + BSSIZE(fs), boot_ignore, + sizeof(boot_ignore)); bwrite(bp); + if (!re_checksum) + continue; + /* Checksum but do not write other sectors */ for (i = 1; i < 11; i++) { if ((error = bread(fs->xf_devvp, base + i, BSSIZE(fs), Index: src/sys/fs/exfatfs/exfatfs_vnops.c diff -u src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.8 src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.9 --- src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.8 Wed Jul 24 00:38:27 2024 +++ src/sys/fs/exfatfs/exfatfs_vnops.c Fri Aug 2 00:16:55 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_vnops.c,v 1.1.2.8 2024/07/24 00:38:27 perseant Exp $ */ +/* $NetBSD: exfatfs_vnops.c,v 1.1.2.9 2024/08/02 00:16:55 perseant Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: exfatfs_vnops.c,v 1.1.2.8 2024/07/24 00:38:27 perseant Exp $"); +__KERNEL_RCSID(0, "$NetBSD: exfatfs_vnops.c,v 1.1.2.9 2024/08/02 00:16:55 perseant Exp $"); #include <sys/buf.h> #include <sys/dirent.h> @@ -224,7 +224,7 @@ exfatfs_getattr(void *v) GET_DFE_CREATE10MS(xip), fs->xf_gmtoff, &vap->va_ctime); vap->va_flags = 0; - if (ISARCHIVE(xip)) { + if (!ISARCHIVE(xip)) { vap->va_flags |= SF_ARCHIVED; vap->va_mode |= S_ARCH1; } @@ -2336,13 +2336,20 @@ detrunc_fat(struct xfinode *xip, off_t b (unsigned)lcn, (unsigned)EXFATFS_B2C(fs, GET_DSE_DATALENGTH_BLK(xip, fs)))); - exfatfs_bitmap_dealloc(fs, opcn); + exfatfs_bitmap_dealloc(fs, opcn, 1); ((uint32_t *)bp->b_data)[EXFATFS_FATOFF(opcn)] = 0xFFFFFFFF; if (ioflags) bwrite(bp); else bdwrite(bp); + + /* Notify underlying device for TRIM */ + vn_lock(fs->xf_devvp, LK_EXCLUSIVE | LK_RETRY); + VOP_FDISCARD(fs->xf_devvp, + EXFATFS_LC2D(fs, pcn) << DEV_BSHIFT, + EXFATFS_C2B(fs, 1)); + VOP_UNLOCK(fs->xf_devvp); } else brelse(bp, 0); ++lcn; @@ -2368,19 +2375,23 @@ detrunc(struct xfinode *xip, off_t bytes struct exfatfs *fs = xip->xi_fs; uint32_t newcount = howmany(bytes, EXFATFS_CSIZE(fs)); uint32_t oldcount = howmany(GET_DSE_DATALENGTH(xip), EXFATFS_CSIZE(fs)); - uint32_t pcn, lcn; + uint32_t pcn; assert(/* oldcount >= 0 && */ oldcount <= fs->xf_ClusterCount); if (oldcount > newcount) { /* We need to deallocate blocks */ - /* If we are contiguous, we can use dead reckoning */ if (IS_DSE_NOFATCHAIN(xip)) { - /* XXX put this loop in exfatfs_balloc.c where it can be more efficient */ - for (lcn = oldcount; lcn > newcount; --lcn) { - pcn = GET_DSE_FIRSTCLUSTER(xip) + lcn - 1; - exfatfs_bitmap_dealloc(fs, pcn); - } + /* If we are contiguous, we can use dead reckoning */ + pcn = GET_DSE_FIRSTCLUSTER(xip) + newcount; + exfatfs_bitmap_dealloc(fs, pcn, oldcount - newcount); + + /* Notify underlying device for TRIM */ + vn_lock(fs->xf_devvp, LK_EXCLUSIVE | LK_RETRY); + VOP_FDISCARD(fs->xf_devvp, + EXFATFS_LC2D(fs, pcn) << DEV_BSHIFT, + EXFATFS_C2B(fs, oldcount - newcount)); + VOP_UNLOCK(fs->xf_devvp); } else { /* If not we need to walk the FAT */ detrunc_fat(xip, bytes, ioflags, cred);