trim 是ata 的一个指令,对应scsi指令是unmap,那么什么是trim呢?

trim就是OS发送给ssd  or other type controller, 告诉它哪些数据对应的LBA地址是无效的。之后trim就不做什么其他操作了。后续的事情由GC来进行。

Secure erase是一则ATA安全擦除命令,用户清除磁盘上的所有数据。这则命令可以理解为主控的返厂状态命令。

trim对于传统磁盘来讲没有什么意义,因为磁介质是可以覆盖写的。而在flash stroage上,由于nand一个天生的特性,写之前必须擦干净,也就是要释放掉flash里面的电子。

所以trim是flash必须支持的特性。

在用户态如果不发起trim,那么flash设备是不会trim掉相应的数据,只有LBA地址重复时,才会标记数据为脏。那么对于块设备的使用者,可以明确哪些块不使用时,可以直接把数据删除掉,而不必等着LBA重复地址写,标记为脏块。否则只有GC来进行回收脏数据了。

带来的好处是,用户不要的数据,及时被清理掉。这样有效数据块减少,GC的效率也提高了。

这样OP空间及时释放,有利于提高性能。(空间及时释放,OP并不是单独分离的空间)

SSD是以块为单位进行擦除的,大小通常是512KB,这意味着如果某个块上的一个页面发生了变化,整个SSD都需要重写,重写过程需要经历漫长的“读-修改-擦除-写”周期,“读”通常指的是将块上的所有数据读入到缓存中,接着将“修改的”数据和缓存中已有的数据合并,然后“擦除”那个块上的全部数据,最后将缓存中的新数据“回写”到已被擦除的块上。

SSD的擦除速度要比读取速度慢好多倍,导致整个“读-修改-擦除-写”周期花费的总时间变长。更糟糕的是,“读-修改-擦除-写”周期会产生一个所谓的写放大系数,它指的是即使是块上单个页面发生变化或更新,也会导致使用额外的写周期,理想情况下,写放大系数应等于1,即向存储介质写入数据时,不需要额外的写周期,如果写放大系数大于1,则意味着向存储介质写入数据时,不止发生一次写入操作。

一种技术叫做写合并,控制器将多个写操作收集到一起,然后一次性将这些数据写入到SSD的块上。合并的目标是将多个小的写操作合并成一个大的写操作,大多数时候,相邻页面的数据会同时发生变化,这些页面很可能属于同一个文件。通过写合并技术,写放大系数大大减小了,SSD的性能也显著提高了,但这也与发送数据的方式,以及数据块是否同属于同一个文件,或是否在同一时间发生变化有关。

当你在电脑里删除一个文件的时候,操作系统并不会真正的去删除它。操作系统只是把这个文件的地址标记为“空”,让它可以被再次使用,这表示这个文件所占的地址已经“无效”。

对于linux的文件系统,可能仅仅把inode节点释放掉。也可能只是仅仅标记为可回收的inode。

对于块设备的使用者有两种情况,直接操作块设备,另一种是通过文件系统间接来操作。

对于块设备,可以通过ioctl的方式,发送trim。

对于ext4文件系统通过mount时加入discard参数,当用户删除文件时,会自动触发trim操作。

另外文件系统格式化时也会trim。

那么对于trim操作,flash firmware真的就是立即去擦除吗?这个不一定,和其实现的机制有关系。

另外一个问题是,trim一次的大小是什么?

这设计到nand操作的块大小,和擦除块大小的不同。

SSD:一个page为4K或8K或16K,将多个page作为一个block来使用。一个block一般为512K到1M。一些block构成一个plane。一些plane构成die。

读写以page为单位。擦除以block为单位,把所有的cell都变为1.

对于scsi块设备,sg3_utils工具可以发送trim

scsi trim:

dd if=./sg_requests.c of=/dev/sdb //表示写入一些数据

hexdump -C -n 512 /dev/sdb //表示已16进制的方式读取写入的数据,读取512byte

./sg_unmap --lba=0x0 --num=1 /dev/sdb //从lba 0开始擦除一个sector,当然nand是没有扇区的概念的,完全是为传统磁盘里的概念

hexdump -C -n 512 /dev/sdb //查看刚才写入的数据,这里可能为全0,也可能不是,有的实现机制是重启os,flash擦除。

关于hexdump的说明

使用hexdump以16进制查看内容

有时候需要查看一些二进制文件的内容,比如二进制文件中包含的某些字符串。这个时候可以用hexdump工具看查看。
常用参数: hexdump -C -n length -s skip file_name
-C 定义了导出的格式,-s skip 指定了从文件头跳过多少字节,或者说是偏移量,默认是十进制。如果是0x开头,则是十六进制。-n 指定了导出多少长度

那些用户态工具可以发trim: fstrim发送文件系统的trim, blkdiscard发送块设备的trim, sg3_utils可以发送SG_IO。

那么我们来看一下trim在linux内核里是怎么实现的呢?

看一下sg3 sg_unmap是如何发送trim cmd的。

ioctl(fd, SG_IO, &ptp->io_hdr)

Hdparm 可以使用下面的参数发送trim给ssd

--trim-sector-ranges        Tell SSD firmware to discard unneeded data sectors: lba:count ..

--trim-sector-ranges-stdin  Same as above, but reads lba:count pairs from stdin

看一下FIO是如何发送trim的。

通过ioengine发送trim do_io_u_trim(td, io_u)

static inline int os_trim(int fd, unsigned long long start,unsigned long long len)
{
uint64_t range[2];
range[0] = start;
range[1] = len;
if (!ioctl(fd, BLKDISCARD, range))
return 0;
return errno;
}

通过block ioctl发送到dev中。然后调用到blkdev_ioctl中。

case BLKDISCARD:
case BLKSECDISCARD: {
uint64_t range[2];
if (!(mode & FMODE_WRITE))
return -EBADF;
if (copy_from_user(range, (void __user *)arg, sizeof(range)))
return -EFAULT;
return blk_ioctl_discard(bdev, range[0], range[1],cmd == BLKSECDISCARD);
}static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,uint64_t len, int secure)
{unsigned long flags = 0;if (start & 511)return -EINVAL;if (len & 511)return -EINVAL;start >>= 9;len >>= 9;if (start + len > (i_size_read(bdev->bd_inode) >> 9))return -EINVAL;if (secure)flags |= BLKDEV_DISCARD_SECURE;return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
}

文件系统发送Trim的ioctl

#define BMAP_IOCTL 1        /* obsolete - kept for compatibility */
#define FIBMAP     _IO(0x00,1)  /* bmap access */
#define FIGETBSZ   _IO(0x00,2)  /* get the block size used for bmap */
#define FIFREEZE    _IOWR('X', 119, int)  /* Freeze */
#define FITHAW      _IOWR('X', 120, int)  /* Thaw */
#define FITRIM      _IOWR('X', 121, struct fstrim_range)  /* Trim */
FITRIM        

ext4 如何处理trim的

include/uapi/linux/fs.h userspace api 目前放到了uapi下面了struct fstrim_range {__u64 start;__u64 len;__u64 minlen;
};ext4/ioctl.cconst struct file_operations ext4_file_operations = {.llseek     = ext4_llseek,.read        = do_sync_read,.write      = do_sync_write,.aio_read  = generic_file_aio_read,.aio_write = ext4_file_write,.unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl   = ext4_compat_ioctl,
#endif.mmap     = ext4_file_mmap,.open     = ext4_file_open,.release  = ext4_release_file,.fsync     = ext4_sync_file,.splice_read  = generic_file_splice_read,.splice_write   = generic_file_splice_write,.fallocate = ext4_fallocate,
};long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)在这里处理ioctl。case FITRIM:{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 __user *)arg,sizeof(range)))return -EFAULT;range.minlen = max((unsigned int)range.minlen,q->limits.discard_granularity);ret = ext4_trim_fs(sb, &range); //trim ioctl handle functionif (ret < 0)return ret;if (copy_to_user((struct fstrim_range __user *)arg, &range,sizeof(range)))return -EFAULT;return 0;}ext4_trim_fs -->ext4_trim_all_free-->ext4_trim_extent->ext4_issue_discard(sb, group, start, count);-->sb_issue_discard-->//block layer给出API, sb_issue_discard 或 blkdev_issue_discard;ext4_issue_discard->sb_issue_discardstatic inline int sb_issue_discard(struct super_block *sb, sector_t block,sector_t nr_blocks, gfp_t gfp_mask, unsigned long flags)
{return blkdev_issue_discard(sb->s_bdev, block << (sb->s_blocksize_bits - 9),nr_blocks << (sb->s_blocksize_bits - 9),gfp_mask, flags);
}
在blkdev_issue_discard中封装一个bio,send到块设备上。submit_bio(type, bio);这里把discard flag打到bio结构体里。

bio这里加入了type REQ_DISCARD类型,告诉bio这是一个trim的io。

驱动有两种处理方法,一种是处理request,一种是直接处理bio。

struct bio {unsigned long      bi_rw;      /* bottom bits READ/WRITE,* top bits priority*/struct request {union {struct list_head queuelist;struct llist_node ll_list;};union {struct call_single_data csd;struct work_struct mq_flush_work;};struct request_queue *q;struct blk_mq_ctx *mq_ctx;u64 cmd_flags;                  req->cmd_flags & REQ_DISCARD)               
/** Called with local interrupts disabled and the q_lock held.  May not sleep.*/
static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,struct bio *bio)if (bio->bi_rw & REQ_DISCARD) {result = nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);if (result)goto free_cmdid;return result;}/** We reuse the small pool to allocate the 16-byte range here as it is not* worth having a special pool for these or additional cases to handle freeing* the iod.*/
static int nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,struct bio *bio, struct nvme_iod *iod, int cmdid)
{struct nvme_dsm_range *range;struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];range = dma_pool_alloc(nvmeq->dev->prp_small_pool, GFP_ATOMIC,&iod->first_dma);if (!range)return -ENOMEM;iod_list(iod)[0] = (__le64 *)range;iod->npages = 0;range->cattr = cpu_to_le32(0);range->nlb = cpu_to_le32(bio->bi_size >> ns->lba_shift);range->slba = cpu_to_le64(nvme_block_nr(ns, bio->bi_sector));memset(cmnd, 0, sizeof(*cmnd));cmnd->dsm.opcode = nvme_cmd_dsm;cmnd->dsm.command_id = cmdid;cmnd->dsm.nsid = cpu_to_le32(ns->ns_id);cmnd->dsm.prp1 = cpu_to_le64(iod->first_dma);cmnd->dsm.nr = 0;cmnd->dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);if (++nvmeq->sq_tail == nvmeq->q_depth)nvmeq->sq_tail = 0;writel(nvmeq->sq_tail, nvmeq->q_db);return 0;
}/* I/O commands */enum nvme_opcode {nvme_cmd_flush     = 0x00,nvme_cmd_write      = 0x01,nvme_cmd_read       = 0x02,nvme_cmd_write_uncor    = 0x04,nvme_cmd_compare    = 0x05,nvme_cmd_dsm        = 0x09, //  data set management command
};

看一下nvme driver是如何处理这个ioctl的。

转载于:https://blog.51cto.com/xmwang/1678350

Trim or Discard or Unmap相关推荐

  1. 什么是SSD TRIM (by quqi99)

    作者:张华  发表于:2016-03-23 版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 ( http://blog.csdn.net/quqi99 ) 普通 ...

  2. linux qemu 使用方法

    目录 一.QEMU的运行模式 二.QEMU在使用者模式下执行程序 三.QEMU的系统模式使用 一.QEMU的运行模式 直接摘抄自己<揭秘家用路由器0day漏洞挖掘技术>,网上查了一下也没有 ...

  3. Linux 内核调试 四:qemu-system-arm功能选项整理

    参考资料: https://qemu.readthedocs.io/en/latest/about/index.html onlylove@ubuntu:~/My/qemu/qemu-lq$ ./qe ...

  4. qemu网络配置-桥接-IOT固件模拟

    QEMU网络策略 在进行IOT固件模拟的过程中,我们需要进行poc的验证需要能够启动系统级的qemu模拟,这时候需要将固件文件系统传到qemu虚拟机中,我们需要通过qemu网络通信的方法去串通本机和q ...

  5. Android内核的编译和调试

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/70500488 一.Android内核源码的选择 Android手机设备内核源码的调 ...

  6. QEMU的基本使用方法(MIPS)

    一.QEMU的运行模式 直接摘抄自己<揭秘家用路由器0day漏洞挖掘技术>,网上查了一下也没有找到令人满意的QEMU的使用说明,就采用这本书上的介绍.如果后期能够找到比较满意的QEMU的使 ...

  7. sanitize---硬盘数据的防护衣

    转自微信公众号:存储随笔 在今年5月份发布的NVMe Spec 1.3中,对数据安全方面增加了一个"Sanitize"功能,如下图.其实,Sanitize清除功能并不是NVMe ...

  8. nvme sanitize

    在今年5月份发布的NVMe Spec 1.3中,对数据安全方面增加了一个"Sanitize"功能,如下图.其实,Sanitize清除功能并不是NVMe新创,SATA和SAS硬盘早已 ...

  9. qemu-system-aarch64使用和相关参数介绍

    qemu-system-aarch64 1 参数列表查看 2 Standard options: 3 Block device options: 4 Display options: 5 Networ ...

最新文章

  1. Java中Set集合是如何实现添加元素保证不重复的?
  2. javascript html 生成 pdf
  3. [云炬创业基础笔记]第七章创业资源测试7
  4. 关于level_idc和Profile_IDC的解释
  5. . 在第一代计算机时代 编程采用,在第一代计算机时代,编程采用什么语言
  6. 高端物理学名词_物理专业名词
  7. android外接键盘打汉字,安卓手机外接键盘怎么切换输入法?
  8. VMware没有未桥接的主机网络适配器,VMware bridge protocol服务卸载不掉
  9. 计算机mc mr,【科普贴】计算器上的GT、CE、AC、MU、MC、MR、M-、M+都是什么?
  10. 使用NLTK对文档进行分句
  11. Eclipes更改主题及字体
  12. 总结:机器学习中的基本数学知识
  13. kubebuilder 上手体验
  14. Android按键Input KeyEvent
  15. html5引入的新标签canvas,HTML页面中添加Canvas标签示例
  16. 整理了下这三天【面试】遇到的让人心惊胆颤的难题。
  17. 7-3 汉诺塔 (20 分)
  18. android区域和gynoid区域,Roux-en-Y胃肠转流术后2型糖尿病患者体脂分泌和胰岛素抵抗的变化...
  19. SiTime硅晶振在SSD存储中的应用
  20. J2EE Java黑客大曝光:开发安全的Java应用程序

热门文章

  1. 自动化测试框架-pytest框架入门篇
  2. php 二进制 十六进制转换,php 实现进制转换(二进制、八进制、十六进制)互相转换实现代码...
  3. google支付接入PHP语言,PHP语言开发Paypal支付demo的具体实现
  4. 查看数据库开了inodb_Mysql中查看表的类型InnoDB
  5. stack videos in ffmpeg
  6. AT4 more about the sphere
  7. SQLite Tutorial 1 在ubuntu上安装SQLite 3.8.2
  8. 创建一个JFrame,可下拉选择显示字符串和图片
  9. 从零开始刷Leetcode——数组(189.217.219)
  10. 如何啃透周志华的《机器学习》西瓜书?