TRIM support
[dragonfly.git] / sys / dev / disk / ahci / ahci_cam.c
index e4783e1..8e61fbf 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * (MPSAFE)
+ *
  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
  *
  * This code is derived from software contributed to The DragonFly Project
@@ -104,15 +106,23 @@ ahci_cam_attach(struct ahci_port *ap)
        if (devq == NULL) {
                return (ENOMEM);
        }
+
+       /*
+        * Give the devq enough room to run with 32 max_dev_transactions,
+        * but set the overall max tags to 1 until NCQ is negotiated.
+        */
        sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
-                          (void *)ap, unit, &sim_mplock, 1, 1, devq);
+                          (void *)ap, unit, &ap->ap_sim_lock,
+                          32, 1, devq);
        cam_simq_release(devq);
        if (sim == NULL) {
                return (ENOMEM);
        }
        ap->ap_sim = sim;
        ahci_os_unlock_port(ap);
+       lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
        error = xpt_bus_register(ap->ap_sim, ap->ap_num);
+       lockmgr(&ap->ap_sim_lock, LK_RELEASE);
        ahci_os_lock_port(ap);
        if (error != CAM_SUCCESS) {
                ahci_cam_detach(ap);
@@ -190,7 +200,7 @@ ahci_cam_detach(struct ahci_port *ap)
 
        if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
                return;
-       get_mplock();
+       lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
        if (ap->ap_sim) {
                xpt_freeze_simq(ap->ap_sim, 1);
        }
@@ -203,7 +213,7 @@ ahci_cam_detach(struct ahci_port *ap)
                cam_sim_free(ap->ap_sim);
                ap->ap_sim = NULL;
        }
-       rel_mplock();
+       lockmgr(&ap->ap_sim_lock, LK_RELEASE);
        ap->ap_flags &= ~AP_F_CAM_ATTACHED;
 }
 
@@ -368,8 +378,8 @@ ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
                                }
                        }
                        if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
-                               cam_devq_resize(ap->ap_sim->devq,
-                                               at->at_ncqdepth - 1);
+                               cam_sim_set_max_tags(ap->ap_sim,
+                                                    at->at_ncqdepth - 1);
                        }
                }
        } else {
@@ -872,6 +882,9 @@ ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
                        case AHCI_PREG_SSTS_SPD_GEN2:
                                ccb->cpi.base_transfer_speed = 300000;
                                break;
+                       case AHCI_PREG_SSTS_SPD_GEN3:
+                               ccb->cpi.base_transfer_speed = 600000;
+                               break;
                        default:
                                /* unknown */
                                ccb->cpi.base_transfer_speed = 1000;
@@ -945,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);
@@ -1054,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) {
@@ -1106,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:
@@ -1132,7 +1197,7 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                default:
                        xa->flags = 0;
                }
-               xa->flags |= ATA_F_POLL;
+               xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
                xa->data = csio->data_ptr;
                xa->datalen = csio->dxfer_len;
                xa->complete = ahci_ata_complete_disk_rw;
@@ -1174,7 +1239,7 @@ ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
                default:
                        xa->flags = 0;
                }
-               xa->flags |= ATA_F_POLL;
+               xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
                xa->data = csio->data_ptr;
                xa->datalen = csio->dxfer_len;
                xa->complete = ahci_ata_complete_disk_rw;