diff --git a/sys/sys/disk.h b/sys/sys/disk.h
index 6a3a6da..78887e7 100644
--- a/sys/sys/disk.h
+++ b/sys/sys/disk.h
@@ -92,6 +92,7 @@ struct disk_info {
 	u_int			d_ncylinders;
 	u_int			d_secpertrack;
 	u_int			d_secpercyl;
+	u_int			d_trimflag;
 	char			*d_serialno;
 };
 
diff --git a/sys/sys/ioctl_compat.h b/sys/sys/ioctl_compat.h
index 7ed17fa..ea99056 100644
--- a/sys/sys/ioctl_compat.h
+++ b/sys/sys/ioctl_compat.h
@@ -166,5 +166,6 @@ struct sgttyb {
 #define	OTTYDISC	0
 #define	NETLDISC	1
 #define	NTTYDISC	2
+#define IOCTLTRIM	_IOW('t', 128, off_t[2])
 
 #endif /* !_SYS_IOCTL_COMPAT_H_ */
diff --git a/sys/bus/cam/scsi/scsi_all.h b/sys/bus/cam/scsi/scsi_all.h
index 27bc17c..c722f31 100644
--- a/sys/bus/cam/scsi/scsi_all.h
+++ b/sys/bus/cam/scsi/scsi_all.h
@@ -600,6 +600,7 @@ struct ata_pass_16 {
 #define	WRITE_BUFFER            0x3b
 #define	READ_BUFFER             0x3c
 #define	CHANGE_DEFINITION	0x40
+#define	TRIM			0x42
 #define	LOG_SELECT		0x4c
 #define	LOG_SENSE		0x4d
 #define	MODE_SELECT_10		0x55
diff --git a/sys/bus/cam/scsi/scsi_da.c b/sys/bus/cam/scsi/scsi_da.c
index 48e3c73..2cf60d4 100644
--- a/sys/bus/cam/scsi/scsi_da.c
+++ b/sys/bus/cam/scsi/scsi_da.c
@@ -48,6 +48,7 @@
 #include <sys/malloc.h>
 #include <sys/cons.h>
 #include <sys/proc.h>
+#include <sys/ioctl_compat.h>
 
 #include <sys/buf2.h>
 #include <sys/thread2.h>
@@ -97,7 +98,8 @@ typedef enum {
 	DA_FLAG_OPEN		= 0x100,
 	DA_FLAG_SCTX_INIT	= 0x200,
 	DA_FLAG_RD_LIMIT	= 0x400,
-	DA_FLAG_WR_LIMIT	= 0x800
+	DA_FLAG_WR_LIMIT	= 0x800,
+	DA_FLAG_CAN_TRIM	= 0x1000
 } da_flags;
 
 typedef enum {
@@ -113,6 +115,7 @@ typedef enum {
 	DA_CCB_BUFFER_IO	= 0x03,
 	DA_CCB_WAITING		= 0x04,
 	DA_CCB_DUMP		= 0x05,
+	DA_CCB_TRIM		= 0x06,
 	DA_CCB_TYPE_MASK	= 0x0F,
 	DA_CCB_RETRY_UA		= 0x10
 } da_ccb_state;
@@ -129,9 +132,17 @@ struct disk_params {
 	u_int64_t sectors;	/* total number sectors */
 };
 
+#define TRIM_MAX_BLOCKS 8
+#define TRIM_MAX_RANGES TRIM_MAX_BLOCKS * 64
+struct trim_request {
+        uint8_t         data[TRIM_MAX_RANGES * 8];
+        struct bio      *bios[TRIM_MAX_RANGES];
+};
+
 struct da_softc {
 	struct	 bio_queue_head bio_queue_rd;
 	struct	 bio_queue_head bio_queue_wr;
+	struct	 bio_queue_head bio_queue_trim;
 	struct	 devstat device_stats;
 	SLIST_ENTRY(da_softc) links;
 	LIST_HEAD(, ccb_hdr) pending_ccbs;
@@ -142,6 +153,9 @@ struct da_softc {
 	int	 ordered_tag_count;
 	int	 outstanding_cmds_rd;
 	int	 outstanding_cmds_wr;
+	int      trim_max_ranges;
+	int      trim_running;
+	int      trim_enabled;
 	struct	 disk_params params;
 	struct	 disk disk;
 	union	 ccb saved_ccb;
@@ -149,6 +163,7 @@ struct da_softc {
 	struct sysctl_ctx_list	sysctl_ctx;
 	struct sysctl_oid	*sysctl_tree;
 	struct callout		sendordered_c;
+	struct trim_request     trim_req;
 };
 
 struct da_quirk_entry {
@@ -317,6 +332,7 @@ static	d_open_t	daopen;
 static	d_close_t	daclose;
 static	d_strategy_t	dastrategy;
 static	d_dump_t	dadump;
+static	d_ioctl_t	daioctl;
 static	periph_init_t	dainit;
 static	void		daasync(void *callback_arg, u_int32_t code,
 				struct cam_path *path, void *arg);
@@ -398,7 +414,8 @@ static struct dev_ops da_ops = {
 	.d_read =	physread,
 	.d_write =	physwrite,
 	.d_strategy =	dastrategy,
-	.d_dump =	dadump
+	.d_dump =	dadump,
+	.d_ioctl =	daioctl
 };
 
 static struct extend_array *daperiphs;
@@ -406,6 +423,84 @@ static struct extend_array *daperiphs;
 MALLOC_DEFINE(M_SCSIDA, "scsi_da", "scsi_da buffers");
 
 static int
+daioctl(struct dev_ioctl_args *ap)
+{	
+	int unit;
+	int error = 1;	
+	struct buf *bp;		
+	struct cam_periph *periph;
+	int byte_count;
+	struct da_softc * softc;
+
+	off_t *del_num = (off_t*)ap->a_data;
+	off_t bytes_left;
+	off_t bytes_start;		
+
+	cdev_t dev = ap->a_head.a_dev;
+
+
+	unit = dkunit(dev);
+	periph = cam_extend_get(daperiphs, unit);
+	if (periph == NULL)
+		return(ENXIO);
+	softc = (struct da_softc *)periph->softc;
+	
+	switch (ap->a_cmd) {
+	case IOCTLTRIM:
+	{
+
+		bytes_left = del_num[1];
+		bytes_start = del_num[0];
+
+		/* TRIM occurs on 512-byte sectors. */
+		KKASSERT((bytes_left % 512) == 0);
+		KKASSERT((bytes_start% 512) == 0);
+
+		
+		/* Break TRIM up into int-sized commands because of b_bcount */
+		while(bytes_left) {
+
+			/* 
+			 * Rather than than squezing out more blocks in b_bcount 
+			 * and having to break up the TRIM request in da_start(),
+			 * we ensure we can always TRIM this many bytes with one 
+			 * TRIM command (this happens if the device only 
+			 * supports one TRIM block). 
+			 *  
+			 * With min TRIM blksize of 1, TRIM command free
+			 * 4194240 blks(64*65535): each LBA range can address 
+			 * 65535 blks and there 64 such ranges in a 512-byte 
+			 * block. And, 4194240 * 512 = 0x7FFF8000
+			 * 
+			 */
+			byte_count = MIN(bytes_left,0x7FFF8000);
+			bp = getnewbuf(0,0,0,1);
+		
+			bp->b_cmd = BUF_CMD_FREEBLKS;
+			bp->b_bio1.bio_offset =bytes_start;
+			bp->b_bcount = byte_count;
+			bp->b_bio1.bio_flags |= BIO_SYNC;
+			bp->b_bio1.bio_done = biodone_sync;
+		
+			dev_dstrategy(ap->a_head.a_dev, &bp->b_bio1);
+		
+			if (biowait(&bp->b_bio1, "TRIM")) {
+				kprintf("Error:%d\n", bp->b_error);
+				return(bp->b_error ? bp->b_error : EIO);
+			}
+			brelse(bp);
+			bytes_left -= byte_count;
+			bytes_start += byte_count;	
+		}
+	}
+	default:
+		return(EINVAL);
+	}	
+	
+	return(error);
+}
+
+static int
 daopen(struct dev_open_args *ap)
 {
 	cdev_t dev = ap->a_head.a_dev;
@@ -637,6 +732,8 @@ dastrategy(struct dev_strategy_args *ap)
 	 */
 	if (bp->b_cmd == BUF_CMD_WRITE || bp->b_cmd == BUF_CMD_FLUSH)
 		bioqdisksort(&softc->bio_queue_wr, bio);
+	else if (bp->b_cmd == BUF_CMD_FREEBLKS) 
+		bioqdisksort(&softc->bio_queue_trim, bio);
 	else
 		bioqdisksort(&softc->bio_queue_rd, bio);
 	
@@ -813,6 +910,7 @@ daoninvalidate(struct cam_periph *periph)
 	 * XXX Handle any transactions queued to the card
 	 *     with XPT_ABORT_CCB.
 	 */
+	daflushbioq(&softc->bio_queue_trim, ENXIO);
 	daflushbioq(&softc->bio_queue_wr, ENXIO);
 	daflushbioq(&softc->bio_queue_rd, ENXIO);
 	xpt_print(periph->path, "lost device\n");
@@ -972,6 +1070,17 @@ dasysctlinit(void *context, int pending)
 		&softc->minimum_cmd_size, 0, dacmdsizesysctl, "I",
 		"Minimum CDB size");
 
+	/* Only create the option if the device supports TRIM */
+	if(softc->disk.d_info.d_trimflag) { 
+		SYSCTL_ADD_INT(&softc->sysctl_ctx, 
+			SYSCTL_CHILDREN(softc->sysctl_tree),
+			OID_AUTO,
+			"trim_enabled",
+			CTLFLAG_RW,
+			&softc->trim_enabled, 0,
+			"Enable TRIM for this device (SSD))");
+	}
+
 	cam_periph_release(periph);
 	rel_mplock();
 }
@@ -1031,6 +1140,7 @@ daregister(struct cam_periph *periph, void *arg)
 	softc = kmalloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
 	LIST_INIT(&softc->pending_ccbs);
 	softc->state = DA_STATE_PROBE;
+	bioq_init(&softc->bio_queue_trim);
 	bioq_init(&softc->bio_queue_rd);
 	bioq_init(&softc->bio_queue_wr);
 	if (SID_IS_REMOVABLE(&cgd->inq_data))
@@ -1038,6 +1148,18 @@ daregister(struct cam_periph *periph, void *arg)
 	if ((cgd->inq_data.flags & SID_CmdQue) != 0)
 		softc->flags |= DA_FLAG_TAGGED_QUEUING;
 
+	/* Used to get TRIM status from AHCI driver */
+	if (cgd->inq_data.vendor_specific1[0] == 1) {
+		/*
+		 * max number of lba ranges an SSD can handle in a single
+		 * TRIM command. vendor_specific1[1] is the num of 512-byte 
+		 * blocks the SSD reports that can be passed in a TRIM cmd. 
+		 */
+		softc->trim_max_ranges = 
+				min(cgd->inq_data.vendor_specific1[1] * 64,
+				    TRIM_MAX_RANGES);
+	}
+
 	periph->softc = softc;
 	
 	cam_extend_set(daperiphs, periph->unit_number, periph);
@@ -1153,6 +1275,8 @@ daregister(struct cam_periph *periph, void *arg)
 	    (DA_DEFAULT_TIMEOUT * hz) / DA_ORDEREDTAG_INTERVAL,
 	    dasendorderedtag, softc);
 
+	
+
 	return(CAM_REQ_CMP);
 }
 
@@ -1198,6 +1322,77 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
 			break;
 		}
 
+		/* Run the trim command if not already running */
+		if(!softc->trim_running &&
+		   (bio = bioq_first(&softc->bio_queue_trim)) != 0) {
+                        struct trim_request *req = &softc->trim_req;
+                        struct bio *bio1;
+                        int bps = 0, ranges = 0;
+
+                        softc->trim_running = 1;
+                        bzero(req, sizeof(*req));
+                        bio1 = bio;
+                        do {
+				uint64_t lba;
+				int count;
+
+				bp = bio1->bio_buf;
+                                count = bp->b_bcount / softc->params.secsize;
+                                lba = bio1->bio_offset/softc->params.secsize;
+
+				kprintf("trim lba:%llu boff:%llu count:%d\n",(unsigned long long)
+					lba,(unsigned long long) bio1->bio_offset,
+					count);
+                                bioq_remove(&softc->bio_queue_trim, bio1);
+                                while (count > 0) {
+                                        int c = min(count, 0xffff);
+                                        int off = ranges * 8;
+
+                                        req->data[off + 0] = lba & 0xff;
+                                        req->data[off + 1] = (lba >> 8) & 0xff;
+                                        req->data[off + 2] = (lba >> 16) & 0xff;
+                                        req->data[off + 3] = (lba >> 24) & 0xff;
+                                        req->data[off + 4] = (lba >> 32) & 0xff;
+                                        req->data[off + 5] = (lba >> 40) & 0xff;
+                                        req->data[off + 6] = c & 0xff;
+                                        req->data[off + 7] = (c >> 8) & 0xff;
+                                        lba += c;
+                                        count -= c;
+                                        ranges++;
+                                }
+				
+				/* Try to merge multiple TRIM requests */
+                                req->bios[bps++] = bio1;
+                                bio1 = bioq_first(&softc->bio_queue_trim);
+                                if (bio1 == NULL ||
+                                    bio1->bio_buf->b_bcount / softc->params.secsize >
+                                    (softc->trim_max_ranges - ranges) * 0xffff)
+                                        break;
+                        } while (1);
+
+
+			cam_fill_csio(&start_ccb->csio,
+			      1/*retries*/,
+			      dadone,
+			      CAM_DIR_OUT,
+			      MSG_SIMPLE_Q_TAG,
+			      req->data,
+			      ((ranges +63)/64)*512,
+			      SSD_FULL_SIZE,
+			      sizeof(struct scsi_rw_6),
+			      da_default_timeout*2);
+
+			start_ccb->ccb_h.ccb_state = DA_CCB_TRIM;
+			LIST_INSERT_HEAD(&softc->pending_ccbs,
+					 &start_ccb->ccb_h, periph_links.le);
+			start_ccb->csio.ccb_h.func_code = XPT_TRIM;
+			start_ccb->ccb_h.ccb_bio = bio;
+			devstat_start_transaction(&softc->device_stats);
+			xpt_action(start_ccb);
+			xpt_schedule(periph, 1);
+                        break;
+		}
+
 		/*
 		 * Select a read or write buffer to queue.  Limit the number
 		 * of tags dedicated to reading or writing, giving reads
@@ -1308,6 +1503,11 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
 				);
 			}
 			break;
+		case BUF_CMD_FREEBLKS:
+			if(softc->disk.d_info.d_trimflag & DA_FLAG_CAN_TRIM){
+				start_ccb->csio.ccb_h.func_code = XPT_TRIM;
+				break;
+			}
 		default:
 			xpt_release_ccb(start_ccb);
 			start_ccb = NULL;
@@ -1455,6 +1655,7 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
 	csio = &done_ccb->csio;
 	switch (csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK) {
 	case DA_CCB_BUFFER_IO:
+	case DA_CCB_TRIM:
 	{
 		struct buf *bp;
 		struct bio *bio;
@@ -1549,7 +1750,29 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
 		}
 
 		devstat_end_transaction_buf(&softc->device_stats, bp);
-		biodone(bio);
+		if ((csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK) ==
+		    DA_CCB_TRIM) {
+			struct trim_request *req = 
+				(struct trim_request *)csio->data_ptr;
+			int i;
+
+			for (i = 1; i < softc->trim_max_ranges &&
+			    req->bios[i]; i++) {
+				struct bio *bp1 = req->bios[i];
+
+				bp1->bio_buf->b_resid = bp->b_resid;
+				bp1->bio_buf->b_error = bp->b_error;
+				if (bp->b_flags & B_ERROR)
+					bp1->bio_buf->b_flags |= B_ERROR;
+				biodone(bp1);
+			}
+			kprintf("Processed %d coalesced TRIM req\n", i-1);
+			softc->trim_running = 0;
+			biodone(bio);
+			xpt_schedule(periph,1);
+		} else
+			biodone(bio);
+
 
 		if (mustsched)
 			xpt_schedule(periph, /*priority*/1);
@@ -1612,6 +1835,7 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
 				(uintmax_t)dp->sectors,
 				dp->secsize, dp->heads, dp->secs_per_track,
 				dp->cylinders);
+			
 			CAM_SIM_UNLOCK(periph->sim);
 			info.d_media_blksize = softc->params.secsize;
 			info.d_media_blocks = softc->params.sectors;
@@ -1731,6 +1955,13 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
 			taskqueue_enqueue(taskqueue_thread[mycpuid],
 			    &softc->sysctl_task);
 		}
+
+		if(softc->trim_max_ranges) {
+			softc->disk.d_info.d_trimflag |= DA_FLAG_CAN_TRIM;
+			kprintf("%s%d: supports TRIM\n",
+				periph->periph_name,
+				periph->unit_number);
+		}
 		softc->state = DA_STATE_NORMAL;
 		/*
 		 * Since our peripheral may be invalidated by an error
diff --git a/sys/bus/cam/scsi/scsi_da.h b/sys/bus/cam/scsi/scsi_da.h
index 665c676..31968de 100644
--- a/sys/bus/cam/scsi/scsi_da.h
+++ b/sys/bus/cam/scsi/scsi_da.h
@@ -322,6 +322,8 @@ struct scsi_read_defect_data_hdr_12
 	u_int8_t length[4];
 };
 
+//#define IOCATADELETE            _IOW('a', 104, off_t[2])
+
 union	disk_pages /* this is the structure copied from osf */
 {
 	struct format_device_page {
diff --git a/sys/dev/disk/ahci/ahci_cam.c b/sys/dev/disk/ahci/ahci_cam.c
index 8db775d..85ac37a 100644
--- a/sys/dev/disk/ahci/ahci_cam.c
+++ b/sys/dev/disk/ahci/ahci_cam.c
@@ -958,6 +958,17 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
 			break;
 		}
 		break;
+	case XPT_TRIM:
+	{
+		scsi_cdb_t cdb;
+		struct ccb_scsiio *csio;
+		csio = &ccb->csio;
+		cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
+			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
+		cdb->generic.opcode = TRIM;
+		ahci_xpt_scsi_disk_io(ap, atx, ccb);
+		break;
+	}
 	default:
 		ccbh->status = CAM_REQ_INVALID;
 		xpt_done(ccb);
@@ -1067,6 +1078,17 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
 			      sizeof(rdata->inquiry_data.revision));
 			ccbh->status = CAM_REQ_CMP;
 		}
+		
+		/*
+		 * Use the vendor specific area to set the TRIM status
+		 * for scsi_da
+		 */
+		if (at->at_identify.support_dsm) {
+			rdata->inquiry_data.vendor_specific1[0] =
+			    at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM;
+			rdata->inquiry_data.vendor_specific1[1] = 
+			    at->at_identify.max_dsm_blocks;
+		}
 		break;
 	case READ_CAPACITY_16:
 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
@@ -1119,6 +1141,36 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
 		xa->flags = 0;
 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
 		break;
+	case TRIM:
+		fis = xa->fis;
+		fis->command = ATA_C_DATA_SET_MANAGEMENT;
+		fis->features = (u_int8_t)ATA_SF_DSM_TRIM;
+		fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8);
+
+		xa->flags = ATA_F_WRITE;
+		fis->flags = ATA_H2D_FLAGS_CMD;
+
+		xa->data = csio->data_ptr;
+		xa->datalen = csio->dxfer_len;
+		xa->timeout = ccbh->timeout*50;	/* milliseconds */
+
+		fis->sector_count =(u_int8_t)(xa->datalen/512);
+		fis->sector_count_exp =(u_int8_t)((xa->datalen/512)>>8);
+
+		lba = 0;
+		fis->lba_low = (u_int8_t)lba;
+		fis->lba_mid = (u_int8_t)(lba >> 8);
+		fis->lba_high = (u_int8_t)(lba >> 16);
+		fis->lba_low_exp = (u_int8_t)(lba >> 24);
+		fis->lba_mid_exp = (u_int8_t)(lba >> 32);
+		fis->lba_high_exp = (u_int8_t)(lba >> 40);
+		   
+		fis->device = ATA_H2D_DEVICE_LBA;
+		xa->data = csio->data_ptr;
+
+		xa->complete = ahci_ata_complete_disk_rw;
+		ccbh->status = CAM_REQ_INPROG;
+		break;
 	case TEST_UNIT_READY:
 	case START_STOP_UNIT:
 	case PREVENT_ALLOW:
diff --git a/sys/dev/disk/ahci/atascsi.h b/sys/dev/disk/ahci/atascsi.h
index d23a8fc..1157401 100644
--- a/sys/dev/disk/ahci/atascsi.h
+++ b/sys/dev/disk/ahci/atascsi.h
@@ -23,6 +23,7 @@ struct scsi_link;
  * ATA commands
  */
 
+#define ATA_C_DATA_SET_MANAGEMENT 0x06 /* Data Set Management command */
 #define ATA_C_SATA_FEATURE_ENA	0x10
 #define ATA_C_READDMA_EXT	0x25
 #define ATA_C_READ_LOG_EXT	0x2f
@@ -54,6 +55,7 @@ struct scsi_link;
 /*
  * ATA SET FEATURES subcommands
  */
+#define ATA_SF_DSM_TRIM          0x01 /* TRIM DSM feature */
 #define ATA_SF_WRITECACHE_EN	0x02
 #define ATA_SF_SETXFER		0x03
 #define ATA_SF_LOOKAHEAD_EN	0xaa
@@ -93,7 +95,10 @@ struct ata_identify {
 	u_int16_t	recmwdma;	/*  66 */
 	u_int16_t	minpio;		/*  67 */
 	u_int16_t	minpioflow;	/*  68 */
-	u_int16_t	reserved4[2];	/*  69 */
+	u_int16_t       support3;	/*  69 */
+#define ATA_SUPPORT_RZAT                0x0020
+#define ATA_SUPPORT_DRAT                0x4000
+	u_int16_t	reserved4;	/*  70 */
 	u_int16_t	typtime[2];	/*  71 */
 	u_int16_t	reserved5[2];	/*  73 */
 	u_int16_t	qdepth;		/*  75 */
@@ -123,7 +128,7 @@ struct ata_identify {
 	u_int16_t	streamperf[2];	/*  98 */
 	u_int16_t	addrsecxt[4];	/* 100 */
 	u_int16_t	stream_xfer_p;	/* 104 */
-	u_int16_t	padding1;	/* 105 */
+	u_int16_t	max_dsm_blocks;	/* 105 */
 	u_int16_t	phys_sect_sz;	/* 106 */
 	u_int16_t	seek_delay;	/* 107 */
 	u_int16_t	naa_ieee_oui;	/* 108 */
@@ -141,7 +146,10 @@ struct ata_identify {
 #define ATA_SECURE_LOCKED		(1<<2)
 #define ATA_SECURE_FROZEN		(1<<3)
 	u_int16_t	vendor[31];	/* 129 */
-	u_int16_t	padding3[16];	/* 160 */
+	u_int16_t	padding3[9];	/* 160 */
+	u_int16_t	support_dsm;	/* 169 */	
+#define ATA_SUPPORT_DSM_TRIM            0x0001
+	u_int16_t	padding5[6];	/* 170 */
 	u_int16_t	curmedser[30];	/* 176 */
 	u_int16_t	sctsupport;	/* 206 */
 	u_int16_t	padding4[48];	/* 207 */
diff --git a/sys/bus/cam/cam_ccb.h b/sys/bus/cam/cam_ccb.h
index 71e1b3d..c19d5ca 100644
--- a/sys/bus/cam/cam_ccb.h
+++ b/sys/bus/cam/cam_ccb.h
@@ -188,6 +188,8 @@ typedef enum {
 				/* Notify Host Target driver of event */
 	XPT_NOTIFY_ACK		= 0x35,
 				/* Acknowledgement of event */
+	XPT_TRIM		= 0x36 | XPT_FC_DEV_QUEUED,
+				/* TRIM */
 
 /* Vendor Unique codes: 0x80->0x8F */
 	XPT_VUNIQUE		= 0x80
diff --git a/sys/bus/cam/cam_xpt.c b/sys/bus/cam/cam_xpt.c
index 92b4e37..14813ce 100644
--- a/sys/bus/cam/cam_xpt.c
+++ b/sys/bus/cam/cam_xpt.c
@@ -2962,6 +2962,7 @@ xpt_action(union ccb *start_ccb)
 
 	switch (start_ccb->ccb_h.func_code) {
 	case XPT_SCSI_IO:
+	case XPT_TRIM:
 	{
 		struct cam_ed *device;
 #ifdef CAMDEBUG
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index 9a30ccb..2a2cb9a 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -2012,7 +2012,7 @@ vfs_bio_awrite(struct buf *bp)
  *
  * MPALMOSTSAFE
  */
-static struct buf *
+struct buf *
 getnewbuf(int blkflags, int slptimeo, int size, int maxsize)
 {
 	struct buf *bp;
diff --git a/sys/sys/buf.h b/sys/sys/buf.h
index ff28772..de422da 100644
--- a/sys/sys/buf.h
+++ b/sys/sys/buf.h
@@ -426,6 +426,7 @@ struct buf *findblk (struct vnode *, off_t, int);
 struct buf *getblk (struct vnode *, off_t, int, int, int);
 struct buf *getcacheblk (struct vnode *, off_t, int);
 struct buf *geteblk (int);
+struct buf * getnewbuf(int, int, int, int);
 void	bqhold(struct buf *bp);
 void	bqdrop(struct buf *bp);
 void	regetblk(struct buf *bp);
