Ignore:
Timestamp:
Oct 8, 2011, 10:28:48 PM (14 years ago)
Author:
cjm
Message:

Version 1.21


  • Triggered by reports of performance loss with NCQ
  • New command line flag "/F" to force using write buffers even when upstream I/O requested non-buffered I/O; the primary purpose of this flag is to debug the NCQ performance drop but the flag may or may not remain.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/os2ahci/ioctl.c

    r113 r125  
    2727#include "os2ahci.h"
    2828#include "atapi.h"
     29#include "ata.h"
    2930#include "ioctl.h"
    3031
     
    4243 * we'll need to define a union to cover all IORB types in question.
    4344 */
     45#define CMDLEN sizeof(((OS2AHCI_PASSTHROUGH *) 0)->cmd)
    4446typedef struct {
    4547  IORB_ADAPTER_PASSTHRU iorb;                  /* IORB */
    4648  SCSI_STATUS_BLOCK   ssb;                     /* SCSI status block */
     49  UCHAR               cmd[CMDLEN];             /* copy of passthrough cmd
     50                                                * (need to fill ATA return
     51                                                * registers at interrupt time)
     52                                                */
    4753  UCHAR               sense[ATAPI_SENSE_LEN];  /* sense buffer */
    4854  SCATGATENTRY        sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
     
    5359/* -------------------------- function prototypes -------------------------- */
    5460
    55 static LIN lin(void _far *p);
     61static USHORT do_smart  (BYTE unit, BYTE sub_func, BYTE parm, void _far *buf);
     62static int    map_unit  (BYTE unit, USHORT _far *a, USHORT _far *p,
     63                         USHORT _far *d);
     64static LIN    lin       (void _far *p);
    5665
    5766IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
     
    171180    return(STDON | STERR | ERROR_I24_BAD_UNIT);
    172181  }
    173   if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2) {
     182  if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
     183      req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd)) {
    174184    return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
    175185  }
     
    206216  ic->iorb.ppSGLIST         = virt_to_phys(ic->sg_lst);
    207217
     218  memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
    208219  ic->iorb.ControllerCmdLen = req->cmdlen;
    209   ic->iorb.pControllerCmd   = req->cmd.cdb;
     220  ic->iorb.pControllerCmd   = ic->cmd;
    210221  ic->iorb.Flags            = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
    211222
     
    236247  spin_lock(drv_lock);
    237248  while (!(ic->iorb.iorbh.Status & IORB_DONE)) {
     249
    238250#   ifndef OS2AHCI_SMP
    239251      drv_lock = 0;
    240252#   endif
     253
    241254    DevHelp_ProcBlock((ULONG) (void _far *) &ic->iorb.iorbh, 30000, 1);
    242255    spin_lock(drv_lock);
     
    306319             min(ic->ssb.ReqSenseLen, req->sense_len));
    307320    }
     321
     322  } else if ((req->flags & PT_ATAPI) == 0) {
     323    /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
     324     * registers which are sometimes used to indicate return conditions,
     325     * e.g. when requesting the smart status)
     326     */
     327    memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
    308328  }
    309329
     
    316336
    317337/******************************************************************************
     338 * Generic disk IOCTL handler; this IOCTL category has originally been defined
     339 * in IBM1S506; the code has been more or less copied from DANIS506.
     340 *
     341 * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
     342 *       point, basically those calls required to get HDMON working.
     343 */
     344USHORT ioctl_gen_dsk(RP_GENIOCTL _far *ioctl)
     345{
     346  DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
     347  UnitInformationData _far *ui;
     348  OS2AHCI_PASSTHROUGH pt;
     349  RP_GENIOCTL tmp_ioctl;
     350  USHORT size = 0;
     351  USHORT ret;
     352  USHORT a;
     353  USHORT p;
     354  USHORT d;
     355  UCHAR unit;
     356 
     357  /* verify addressability of parm buffer (DSKSP_CommandParameters) */
     358  if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
     359                            sizeof(DSKSP_CommandParameters),
     360                            (USHORT) (ULONG) cp,
     361                            VERIFY_READONLY) != 0) {
     362    return(STDON | STERR | 0x05);
     363  }
     364  unit = cp->byPhysicalUnit;
     365
     366  /* verify addressability of data buffer (depends on function code) */
     367  switch (ioctl->Function) {
     368
     369  case DSKSP_GEN_GET_COUNTERS:
     370    size = sizeof(DeviceCountersData);
     371    break;
     372
     373  case DSKSP_GET_UNIT_INFORMATION:
     374    size = sizeof(UnitInformationData);
     375    break;
     376
     377  case DSKSP_GET_INQUIRY_DATA:
     378    size = ATA_ID_WORDS * sizeof(u16);
     379    break;
     380  }
     381
     382  if (size > 0) {
     383    if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
     384                             size, (USHORT) (ULONG) ioctl->DataPacket,
     385                             VERIFY_READWRITE) != 0) {
     386      return(STDON | STERR | 0x05);
     387    }
     388  }
     389
     390  if (map_unit(unit, &a, &p, &d)) {
     391    return(STDON | STERR | ERROR_I24_BAD_UNIT);
     392  }
     393
     394  /* execute generic disk request */
     395  switch (ioctl->Function) {
     396
     397  case DSKSP_GEN_GET_COUNTERS:
     398    /* Not supported, yet; we would need dynamically allocated device
     399     * structures to cope with the memory requirements of the corresponding
     400     * statistics buffer. For the time being, we'll return an empty buffer.
     401     */
     402    memset(ioctl->DataPacket, 0x00, sizeof(DeviceCountersData));
     403    ret = STDON;
     404    break;
     405
     406  case DSKSP_GET_UNIT_INFORMATION:
     407    /* get unit information; things like port addresses won't fit so we don't
     408     * even bother returning those.
     409     */
     410    ui = (UnitInformationData _far *) ioctl->DataPacket;
     411    memset(ui, 0x00, sizeof(*ui));
     412
     413    ui->wRevisionNumber = 1;
     414    ui->wIRQ            = ad_infos[a].irq;
     415    ui->wFlags          = UIF_VALID;
     416    ui->wFlags         |= UIF_RUNNING_BMDMA;
     417    ui->wFlags         |= (unit & 0x0001) ? UIF_SLAVE : 0;
     418    ui->wFlags         |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
     419    ui->wFlags         |= UIF_SATA;
     420
     421    ret = STDON;
     422    break;
     423
     424  case DSKSP_GET_INQUIRY_DATA:
     425    /* return ATA ID buffer */
     426    memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
     427    tmp_ioctl.Category   = OS2AHCI_IOCTL_CATEGORY;
     428    tmp_ioctl.Function   = OS2AHCI_IOCTL_PASSTHROUGH;
     429    tmp_ioctl.ParmPacket = (void _far *) &pt;
     430
     431    memset(&pt, 0x00, sizeof(pt));
     432    pt.adapter          = a;
     433    pt.port             = p;
     434    pt.device           = d;
     435    pt.cmdlen           = sizeof(pt.cmd.ata);
     436    pt.cmd.ata.cmd      = (ad_infos[a].ports[p].devs[d].atapi) ?
     437                           ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
     438    pt.buflen           = size;
     439    pt.buf              = lin(ioctl->DataPacket);
     440
     441    ret = gen_ioctl(&tmp_ioctl);
     442    break;
     443
     444  default:
     445    ret = STDON | STATUS_ERR_UNKCMD;
     446    break;
     447  }
     448
     449  return(ret);
     450}
     451
     452/******************************************************************************
     453 * SMART IOCTL handler; this IOCTL category has originally been defined in
     454 * IBM1S506; the code has been more or less copied from DANIS506.
     455 */
     456USHORT ioctl_smart(RP_GENIOCTL _far *ioctl)
     457{
     458  DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
     459  USHORT size = 0;
     460  USHORT ret;
     461  UCHAR unit;
     462  UCHAR parm;
     463
     464  /* verify addressability of parm buffer (DSKSP_CommandParameters) */
     465  if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
     466                            sizeof(DSKSP_CommandParameters),
     467                            (USHORT) (ULONG) cp,
     468                            VERIFY_READONLY) != 0) {
     469    return(STDON | STERR | 0x05);
     470  }
     471  unit = cp->byPhysicalUnit;
     472
     473  /* verify addressability of data buffer (depends on SMART function) */
     474  switch (ioctl->Function) {
     475
     476  case DSKSP_SMART_GETSTATUS:
     477    size = sizeof(ULONG);
     478    break;
     479
     480  case DSKSP_SMART_GET_ATTRIBUTES:
     481  case DSKSP_SMART_GET_THRESHOLDS:
     482  case DSKSP_SMART_GET_LOG:
     483    size = 512;
     484    break;
     485  }
     486
     487  if (size > 0) {
     488    if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
     489                             size, (USHORT) (ULONG) ioctl->DataPacket,
     490                             VERIFY_READWRITE) != 0) {
     491      return(STDON | STERR | 0x05);
     492    }
     493    parm = ioctl->DataPacket[0];
     494  }
     495
     496  /* execute SMART request */
     497  switch (ioctl->Function) {
     498
     499  case DSKSP_SMART_ONOFF:
     500    ret = do_smart(unit,
     501                   (BYTE) ((parm) ? ATA_SMART_ENABLE
     502                                  : ATA_SMART_DISABLE),
     503                   0, NULL);
     504    break;
     505
     506  case DSKSP_SMART_AUTOSAVE_ONOFF:
     507    ret = do_smart(unit, ATA_SMART_AUTOSAVE,
     508                   (BYTE) ((parm) ? (BYTE) 0xf1 : 0),
     509                   NULL);
     510    break;
     511
     512  case DSKSP_SMART_AUTO_OFFLINE:
     513    ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, NULL);
     514    break;
     515
     516  case DSKSP_SMART_EXEC_OFFLINE:
     517    ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, parm, NULL);
     518    break;
     519
     520  case DSKSP_SMART_SAVE:
     521    ret = do_smart(unit, ATA_SMART_SAVE, 0, NULL);
     522    break;
     523
     524  case DSKSP_SMART_GETSTATUS:
     525    ret = do_smart(unit, ATA_SMART_STATUS, 0, ioctl->DataPacket);
     526    break;
     527
     528  case DSKSP_SMART_GET_ATTRIBUTES:
     529    ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, ioctl->DataPacket);
     530    break;
     531
     532  case DSKSP_SMART_GET_THRESHOLDS:
     533    ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, ioctl->DataPacket);
     534    break;
     535
     536  case DSKSP_SMART_GET_LOG:
     537    ret = do_smart(unit, ATA_SMART_READ_LOG, parm, ioctl->DataPacket);
     538    break;
     539
     540  default:
     541    ret = STDON | STATUS_ERR_UNKCMD;
     542  }
     543
     544  return(ret);
     545}
     546
     547/******************************************************************************
     548 * Perform SMART request. The code has been more or less copied from DANIS506.
     549 */
     550static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE parm, void _far *buf)
     551{
     552  OS2AHCI_PASSTHROUGH pt;
     553  RP_GENIOCTL ioctl;
     554  USHORT ret;
     555  USHORT a;
     556  USHORT p;
     557  USHORT d;
     558
     559  if (map_unit(unit, &a, &p, &d)) {
     560    return(STDON | STERR | ERROR_I24_BAD_UNIT);
     561  }
     562
     563  /* Perform SMART request using the existing OS2AHCI_IOTCL_PASSTHROUGH IOCTL
     564   * interface which already takes care of allocating an IORB, s/g lists, etc.
     565   */
     566  memset(&ioctl, 0x00, sizeof(ioctl));
     567  ioctl.Category   = OS2AHCI_IOCTL_CATEGORY;
     568  ioctl.Function   = OS2AHCI_IOCTL_PASSTHROUGH;
     569  ioctl.ParmPacket = (void _far *) &pt;
     570
     571  memset(&pt, 0x00, sizeof(pt));
     572  pt.adapter          = a;
     573  pt.port             = p;
     574  pt.device           = d;
     575  pt.cmdlen           = sizeof(pt.cmd.ata);
     576  pt.cmd.ata.features = sub_func;
     577  pt.cmd.ata.count    = parm;
     578  pt.cmd.ata.lba_l    = (0xc24fL << 8) | parm;
     579  pt.cmd.ata.cmd      = ATA_CMD_SMART;
     580
     581  if (buf != NULL && sub_func != ATA_SMART_STATUS) {
     582    pt.buflen         = 512;
     583    pt.buf            = lin(buf);
     584  }
     585
     586  if (((ret = gen_ioctl(&ioctl)) & STERR) == 0 &&
     587      sub_func == ATA_SMART_STATUS) {
     588
     589    /* ATA_SMART_STATUS doesn't transfer anything but instead relies on the
     590     * returned D2H FIS, mapped to the ATA CMD, to have a certain value
     591     * (0xf42c); the IOCTL result is expected to be returned as a ULONG in
     592     * the data buffer.
     593     */
     594    if (((pt.cmd.ata.lba_l >> 8) & 0xffff) == 0xf42c) {
     595      *((ULONG _far *) buf) = 1;
     596    } else {
     597      *((ULONG _far *) buf) = 0;
     598    }
     599  }
     600
     601  return(ret);
     602}
     603
     604/******************************************************************************
     605 * Map DSKSP unit number to corresponding adapter/port/device number. Units
     606 * are identified by an 8-bit adapter/device number with the lowest bit
     607 * selecting between master (0) and slave (1). This number is mapped to our
     608 * ATA/ATAPI units sequentially.
     609 */
     610static int map_unit(BYTE unit, USHORT _far *a, USHORT _far *p, USHORT _far *d)
     611{
     612  USHORT _a;
     613  USHORT _p;
     614  USHORT _d;
     615
     616  /* map unit to adapter/port/device */
     617  for (_a = 0; _a < ad_info_cnt; _a++) {
     618    AD_INFO *ai = ad_infos + _a;
     619
     620    for (_p = 0; _p <= ai->port_max; _p++) {
     621      P_INFO *pi = ai->ports + _p;
     622
     623      for (_d = 0; _d <= pi->dev_max; _d++) {
     624        if (pi->devs[_d].present) {
     625          if (--unit == 0) {
     626            /* found the device */
     627            *a = _a;
     628            *p = _p;
     629            *d = _d;
     630            return(0);
     631          }
     632        }
     633      }
     634    }
     635  }
     636
     637  /* unit not found */
     638  return(-1);
     639}
     640
     641/******************************************************************************
    318642 * Get linear address for specified virtual address.
    319643 */
Note: See TracChangeset for help on using the changeset viewer.