/* * Copyright (c) 2004-2005 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/hptmv/hptproc.c,v 1.8 2009/04/07 16:38:25 delphij Exp $ */ /* * hptproc.c sysctl support */ #include #include #include #include #include #include #ifndef __KERNEL__ #define __KERNEL__ #endif #include #include #include #include int hpt_rescan_all(void); /***************************************************************************/ static char hptproc_buffer[256]; extern char DRIVER_VERSION[]; #define FORMAL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, int arg2, \ struct sysctl_req *req #define REAL_HANDLER_ARGS oidp, arg1, arg2, req typedef struct sysctl_req HPT_GET_INFO; static int hpt_set_asc_info(IAL_ADAPTER_T *pAdapter, char *buffer,int length) { int orig_length = length+4; PVBus _vbus_p = &pAdapter->VBus; PVDevice pArray; PVDevice pSubArray, pVDev; UINT i, iarray, ichan; struct cam_periph *periph = NULL; #ifdef SUPPORT_ARRAY if (length>=8 && strncmp(buffer, "rebuild ", 8)==0) { buffer+=8; length-=8; if (length>=5 && strncmp(buffer, "start", 5)==0) { lock_driver(); for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) if ((pArray=ArrayTables(i))->u.array.dArStamp==0) continue; else{ if (pArray->u.array.rf_need_rebuild && !pArray->u.array.rf_rebuilding) hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, (UCHAR)((pArray->u.array.CriticalMembers || pArray->VDeviceType == VD_RAID_1)? DUPLICATE : REBUILD_PARITY)); } unlock_driver(); return orig_length; } else if (length>=4 && strncmp(buffer, "stop", 4)==0) { lock_driver(); for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) if ((pArray=ArrayTables(i))->u.array.dArStamp==0) continue; else{ if (pArray->u.array.rf_rebuilding) pArray->u.array.rf_abort_rebuild = 1; } unlock_driver(); return orig_length; } else if (length>=3 && buffer[1]==','&& buffer[0]>='1'&& buffer[2]>='1') { iarray = buffer[0]-'1'; ichan = buffer[2]-'1'; if(iarray >= MAX_VDEVICE_PER_VBUS || ichan >= MV_SATA_CHANNELS_NUM) return -EINVAL; pArray = _vbus_p->pVDevice[iarray]; if (!pArray || (pArray->vf_online == 0)) return -EINVAL; for (i=0;iVDevices[ichan]; if(!pVDev->u.disk.df_on_line || pVDev->pParent) return -EINVAL; /* Not allow to use a mounted disk ??? test*/ for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++) if(pVDev == _vbus_p->pVDevice[i]) { periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId,i); if (periph != NULL && periph->refcount >= 1) { hpt_printk(("Can not use disk used by OS!\n")); return -EINVAL; } /* the Mounted Disk isn't delete */ } switch(pArray->VDeviceType) { case VD_RAID_1: case VD_RAID_5: { pSubArray = pArray; loop: lock_driver(); if(hpt_add_disk_to_array(_VBUS_P VDEV_TO_ID(pSubArray), VDEV_TO_ID(pVDev)) == -1) { unlock_driver(); return -EINVAL; } pSubArray->u.array.rf_auto_rebuild = 0; pSubArray->u.array.rf_abort_rebuild = 0; hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pSubArray, DUPLICATE); unlock_driver(); break; } case VD_RAID_0: for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) if(pArray->u.array.pMember[i] && mIsArray(pArray->u.array.pMember[i]) && (pArray->u.array.pMember[i]->u.array.rf_broken == 1)) { pSubArray = pArray->u.array.pMember[i]; goto loop; } default: return -EINVAL; } return orig_length; } } else if (length>=7 && strncmp(buffer, "verify ", 7)==0) { buffer+=7; length-=7; if (length>=6 && strncmp(buffer, "start ", 6)==0) { buffer+=6; length-=6; if (length>=1 && *buffer>='1') { iarray = *buffer-'1'; if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL; pArray = _vbus_p->pVDevice[iarray]; if (!pArray || (pArray->vf_online == 0)) return -EINVAL; if(pArray->VDeviceType != VD_RAID_1 && pArray->VDeviceType != VD_RAID_5) return -EINVAL; if (!(pArray->u.array.rf_need_rebuild || pArray->u.array.rf_rebuilding || pArray->u.array.rf_verifying || pArray->u.array.rf_initializing)) { lock_driver(); pArray->u.array.RebuildSectors = 0; hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, VERIFY); unlock_driver(); } return orig_length; } } else if (length>=5 && strncmp(buffer, "stop ", 5)==0) { buffer+=5; length-=5; if (length>=1 && *buffer>='1') { iarray = *buffer-'1'; if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL; pArray = _vbus_p->pVDevice[iarray]; if (!pArray || (pArray->vf_online == 0)) return -EINVAL; if(pArray->u.array.rf_verifying) { lock_driver(); pArray->u.array.rf_abort_rebuild = 1; unlock_driver(); } return orig_length; } } } else #ifdef _RAID5N_ if (length>=10 && strncmp(buffer, "writeback ", 10)==0) { buffer+=10; length-=10; if (length>=1 && *buffer>='0' && *buffer<='1') { _vbus_(r5.enable_write_back) = *buffer-'0'; if (_vbus_(r5.enable_write_back)) hpt_printk(("RAID5 write back enabled")); return orig_length; } } else #endif #endif if (0) {} /* just to compile */ #ifdef DEBUG else if (length>=9 && strncmp(buffer, "dbglevel ", 9)==0) { buffer+=9; length-=9; if (length>=1 && *buffer>='0' && *buffer<='3') { hpt_dbg_level = *buffer-'0'; return orig_length; } } else if (length>=8 && strncmp(buffer, "disable ", 8)==0) { /* TO DO */ } #endif return -EINVAL; } /* * Since we have only one sysctl node, add adapter ID in the command * line string: e.g. "hpt 0 rebuild start" */ static int hpt_set_info(int length) { int retval; #ifdef SUPPORT_IOCTL PUCHAR ke_area; int err; DWORD dwRet; PHPT_IOCTL_PARAM piop; #endif char *buffer = hptproc_buffer; if (length >= 6) { if (strncmp(buffer,"hpt ",4) == 0) { IAL_ADAPTER_T *pAdapter; retval = buffer[4]-'0'; for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) { if (pAdapter->mvSataAdapter.adapterId==retval) return (retval = hpt_set_asc_info(pAdapter, buffer+6, length-6)) >= 0? retval : -EINVAL; } return -EINVAL; } #ifdef SUPPORT_IOCTL piop = (PHPT_IOCTL_PARAM)buffer; if (piop->Magic == HPT_IOCTL_MAGIC || piop->Magic == HPT_IOCTL_MAGIC32) { KdPrintE(("ioctl=%d in=%p len=%d out=%p len=%d\n", piop->dwIoControlCode, piop->lpInBuffer, piop->nInBufferSize, piop->lpOutBuffer, piop->nOutBufferSize)); /* * map buffer to kernel. */ if (piop->nInBufferSize+piop->nOutBufferSize > PAGE_SIZE) { KdPrintE(("User buffer too large\n")); return -EINVAL; } ke_area = kmalloc(piop->nInBufferSize+piop->nOutBufferSize, M_DEVBUF, M_NOWAIT); if (ke_area == NULL) { KdPrintE(("Couldn't allocate kernel mem.\n")); return -EINVAL; } if (piop->nInBufferSize) copyin((void*)(ULONG_PTR)piop->lpInBuffer, ke_area, piop->nInBufferSize); /* * call kernel handler. */ err = Kernel_DeviceIoControl(&gIal_Adapter->VBus, piop->dwIoControlCode, ke_area, piop->nInBufferSize, ke_area + piop->nInBufferSize, piop->nOutBufferSize, &dwRet); if (err==0) { if (piop->nOutBufferSize) copyout(ke_area + piop->nInBufferSize, (void*)(ULONG_PTR)piop->lpOutBuffer, piop->nOutBufferSize); if (piop->lpBytesReturned) copyout(&dwRet, (void*)(ULONG_PTR)piop->lpBytesReturned, sizeof(DWORD)); kfree(ke_area, M_DEVBUF); return length; } else KdPrintW(("Kernel_ioctl(): return %d\n", err)); kfree(ke_area, M_DEVBUF); return -EINVAL; } else { KdPrintW(("Wrong signature: %x\n", piop->Magic)); return -EINVAL; } #endif } return -EINVAL; } #define shortswap(w) ((WORD)((w)>>8 | ((w) & 0xFF)<<8)) static void get_disk_name(char *name, PDevice pDev) { int i; MV_SATA_CHANNEL *pMvSataChannel = pDev->mv; IDENTIFY_DATA2 *pIdentifyData = (IDENTIFY_DATA2 *)pMvSataChannel->identifyDevice; for (i = 0; i < 10; i++) ((WORD*)name)[i] = shortswap(pIdentifyData->ModelNumber[i]); name[20] = '\0'; } static int hpt_copy_info(HPT_GET_INFO *, char *, ...) __printf0like(2, 3); static int hpt_copy_info(HPT_GET_INFO *pinfo, char *fmt, ...) { int printfretval; __va_list ap; if(fmt == NULL) { *hptproc_buffer = 0; return (SYSCTL_OUT(pinfo, hptproc_buffer, 1)); } else { __va_start(ap, fmt); printfretval = kvsnprintf(hptproc_buffer, sizeof(hptproc_buffer), fmt, ap); __va_end(ap); return(SYSCTL_OUT(pinfo, hptproc_buffer, strlen(hptproc_buffer))); } } static void hpt_copy_disk_info(HPT_GET_INFO *pinfo, PVDevice pVDev, UINT iChan) { char name[32], arrayname[16], *status; get_disk_name(name, &pVDev->u.disk); if (!pVDev->u.disk.df_on_line) status = "Disabled"; else if (pVDev->VDeviceType==VD_SPARE) status = "Spare "; else status = "Normal "; #ifdef SUPPORT_ARRAY if(pVDev->pParent) { memcpy(arrayname, pVDev->pParent->u.array.ArrayName, MAX_ARRAY_NAME); if (pVDev->pParent->u.array.CriticalMembers & (1<bSerialNumber)) status = "Degraded"; } else #endif arrayname[0]=0; hpt_copy_info(pinfo, "Channel %d %s %5lluMB %s %s\n", iChan+1, name, pVDev->VDeviceCapacity>>11, status, arrayname); } #ifdef SUPPORT_ARRAY static void hpt_copy_array_info(HPT_GET_INFO *pinfo, int nld, PVDevice pArray) { int i; char *sType=NULL, *sStatus=NULL; char buf[32]; PVDevice pTmpArray; switch (pArray->VDeviceType) { case VD_RAID_0: for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) if(pArray->u.array.pMember[i]) { if(mIsArray(pArray->u.array.pMember[i])) sType = "RAID 1/0 "; /* TO DO */ else sType = "RAID 0 "; break; } break; case VD_RAID_1: sType = "RAID 1 "; break; case VD_JBOD: sType = "JBOD "; break; case VD_RAID_5: sType = "RAID 5 "; break; default: sType = "N/A "; break; } if (pArray->vf_online == 0) sStatus = "Disabled"; else if (pArray->u.array.rf_broken) sStatus = "Critical"; for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) { if (!sStatus) { if(mIsArray(pArray->u.array.pMember[i])) pTmpArray = pArray->u.array.pMember[i]; else pTmpArray = pArray; if (pTmpArray->u.array.rf_rebuilding) { #ifdef DEBUG ksprintf(buf, "Rebuilding %lldMB", (pTmpArray->u.array.RebuildSectors>>11)); #else ksprintf(buf, "Rebuilding %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11))); #endif sStatus = buf; } else if (pTmpArray->u.array.rf_verifying) { ksprintf(buf, "Verifying %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11))); sStatus = buf; } else if (pTmpArray->u.array.rf_need_rebuild) sStatus = "Critical"; else if (pTmpArray->u.array.rf_broken) sStatus = "Critical"; if(pTmpArray == pArray) goto out; } else goto out; } out: if (!sStatus) sStatus = "Normal"; hpt_copy_info(pinfo, "%2d %11s %-20s %5lldMB %-16s", nld, sType, pArray->u.array.ArrayName, pArray->VDeviceCapacity>>11, sStatus); } #endif static int hpt_get_info(IAL_ADAPTER_T *pAdapter, HPT_GET_INFO *pinfo) { PVBus _vbus_p = &pAdapter->VBus; struct cam_periph *periph = NULL; UINT channel,j,i; PVDevice pVDev; #ifndef FOR_DEMO if (pAdapter->beeping) { lock_driver(); pAdapter->beeping = 0; BeepOff(pAdapter->mvSataAdapter.adapterIoBaseAddress); unlock_driver(); } #endif hpt_copy_info(pinfo, "Controller #%d:\n\n", pAdapter->mvSataAdapter.adapterId); hpt_copy_info(pinfo, "Physical device list\n"); hpt_copy_info(pinfo, "Channel Model Capacity Status Array\n"); hpt_copy_info(pinfo, "-------------------------------------------------------------------\n"); for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) { pVDev = &(pAdapter->VDevices[channel]); if(pVDev->u.disk.df_on_line) hpt_copy_disk_info(pinfo, pVDev, channel); } hpt_copy_info(pinfo, "\nLogical device list\n"); hpt_copy_info(pinfo, "No. Type Name Capacity Status OsDisk\n"); hpt_copy_info(pinfo, "--------------------------------------------------------------------------\n"); j=1; for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++){ pVDev = _vbus_p->pVDevice[i]; if(pVDev){ j=i+1; #ifdef SUPPORT_ARRAY if (mIsArray(pVDev)) { is_array: hpt_copy_array_info(pinfo, j, pVDev); } else #endif { char name[32]; /* it may be add to an array after driver loaded, check it */ #ifdef SUPPORT_ARRAY if (pVDev->pParent) /* in this case, pVDev can only be a RAID 1 source disk. */ if (pVDev->pParent->VDeviceType==VD_RAID_1 && pVDev==pVDev->pParent->u.array.pMember[0]) goto is_array; #endif get_disk_name(name, &pVDev->u.disk); hpt_copy_info(pinfo, "%2d %s %s %5lluMB %-16s", j, "Single disk", name, pVDev->VDeviceCapacity>>11, /* gmm 2001-6-19: Check if pDev has been added to an array. */ ((pVDev->pParent) ? "Unavailable" : "Normal")); } periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId, i); if (periph == NULL) hpt_copy_info(pinfo," %s\n","not registered"); else hpt_copy_info(pinfo," %s%d\n", periph->periph_name, periph->unit_number); } } return 0; } static __inline int hpt_proc_in(FORMAL_HANDLER_ARGS, int *len) { int i, error=0; *len = 0; if ((req->newlen - req->newidx) >= sizeof(hptproc_buffer)) { error = EINVAL; } else { i = (req->newlen - req->newidx); error = SYSCTL_IN(req, hptproc_buffer, i); if (!error) *len = i; (hptproc_buffer)[i] = '\0'; } return (error); } static int hpt_status(FORMAL_HANDLER_ARGS) { int length, error=0, retval=0; IAL_ADAPTER_T *pAdapter; error = hpt_proc_in(REAL_HANDLER_ARGS, &length); if (req->newptr != NULL) { if (error || length == 0) { KdPrint(("error!\n")); retval = EINVAL; goto out; } if (hpt_set_info(length) >= 0) retval = 0; else retval = EINVAL; goto out; } hpt_copy_info(req, "%s Version %s\n", DRIVER_NAME, DRIVER_VERSION); for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) { if (hpt_get_info(pAdapter, req) < 0) { retval = EINVAL; break; } } hpt_copy_info(req, NULL); goto out; out: return (retval); } #define xhptregister_node(name) hptregister_node(name) #define hptregister_node(name) \ SYSCTL_NODE(, OID_AUTO, name, CTLFLAG_RW, 0, "Get/Set " #name " state root node"); \ SYSCTL_OID(_ ## name, OID_AUTO, status, CTLTYPE_STRING|CTLFLAG_RW, \ NULL, 0, hpt_status, "A", "Get/Set " #name " state"); xhptregister_node(PROC_DIR_NAME);