1 /* 2 * Copyright (c) 2004-2005 HighPoint Technologies, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 /* 29 * hptproc.c sysctl support 30 */ 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <sys/sysctl.h> 36 #include <machine/stdarg.h> 37 38 #ifndef __KERNEL__ 39 #define __KERNEL__ 40 #endif 41 42 #include <dev/hptmv/global.h> 43 #include <dev/hptmv/hptintf.h> 44 #include <dev/hptmv/osbsd.h> 45 #include <dev/hptmv/access601.h> 46 47 int hpt_rescan_all(void); 48 49 /***************************************************************************/ 50 51 static char hptproc_buffer[256]; 52 extern char DRIVER_VERSION[]; 53 54 #define FORMAL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \ 55 intptr_t arg2, struct sysctl_req *req 56 #define REAL_HANDLER_ARGS oidp, arg1, arg2, req 57 typedef struct sysctl_req HPT_GET_INFO; 58 59 static int 60 hpt_set_asc_info(IAL_ADAPTER_T *pAdapter, char *buffer,int length) 61 { 62 int orig_length = length+4; 63 PVBus _vbus_p = &pAdapter->VBus; 64 PVDevice pArray; 65 PVDevice pSubArray, pVDev; 66 UINT i, iarray, ichan; 67 struct cam_periph *periph = NULL; 68 intrmask_t oldspl; 69 70 #ifdef SUPPORT_ARRAY 71 if (length>=8 && strncmp(buffer, "rebuild ", 8)==0) 72 { 73 buffer+=8; 74 length-=8; 75 if (length>=5 && strncmp(buffer, "start", 5)==0) 76 { 77 oldspl = lock_driver(); 78 for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) 79 if ((pArray=ArrayTables(i))->u.array.dArStamp==0) 80 continue; 81 else{ 82 if (pArray->u.array.rf_need_rebuild && !pArray->u.array.rf_rebuilding) 83 hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, 84 (UCHAR)((pArray->u.array.CriticalMembers || pArray->VDeviceType == VD_RAID_1)? DUPLICATE : REBUILD_PARITY)); 85 } 86 unlock_driver(oldspl); 87 return orig_length; 88 } 89 else if (length>=4 && strncmp(buffer, "stop", 4)==0) 90 { 91 oldspl = lock_driver(); 92 for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) 93 if ((pArray=ArrayTables(i))->u.array.dArStamp==0) 94 continue; 95 else{ 96 if (pArray->u.array.rf_rebuilding) 97 pArray->u.array.rf_abort_rebuild = 1; 98 } 99 unlock_driver(oldspl); 100 return orig_length; 101 } 102 else if (length>=3 && buffer[1]==','&& buffer[0]>='1'&& buffer[2]>='1') 103 { 104 iarray = buffer[0]-'1'; 105 ichan = buffer[2]-'1'; 106 107 if(iarray >= MAX_VDEVICE_PER_VBUS || ichan >= MV_SATA_CHANNELS_NUM) return -EINVAL; 108 109 pArray = _vbus_p->pVDevice[iarray]; 110 if (!pArray || (pArray->vf_online == 0)) return -EINVAL; 111 112 for (i=0;i<MV_SATA_CHANNELS_NUM;i++) 113 if(i == ichan) 114 goto rebuild; 115 116 return -EINVAL; 117 118 rebuild: 119 pVDev = &pAdapter->VDevices[ichan]; 120 if(!pVDev->u.disk.df_on_line || pVDev->pParent) return -EINVAL; 121 122 /* Not allow to use a mounted disk ??? test*/ 123 for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++) 124 if(pVDev == _vbus_p->pVDevice[i]) 125 { 126 periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId,i); 127 if (periph != NULL && periph->refcount >= 1) 128 { 129 hpt_printk(("Can not use disk used by OS!\n")); 130 return -EINVAL; 131 } 132 /* the Mounted Disk isn't delete */ 133 } 134 135 switch(pArray->VDeviceType) 136 { 137 case VD_RAID_1: 138 case VD_RAID_5: 139 { 140 pSubArray = pArray; 141 loop: 142 oldspl = lock_driver(); 143 if(hpt_add_disk_to_array(_VBUS_P VDEV_TO_ID(pSubArray), VDEV_TO_ID(pVDev)) == -1) { 144 unlock_driver(oldspl); 145 return -EINVAL; 146 } 147 pSubArray->u.array.rf_auto_rebuild = 0; 148 pSubArray->u.array.rf_abort_rebuild = 0; 149 hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pSubArray, DUPLICATE); 150 unlock_driver(oldspl); 151 break; 152 } 153 case VD_RAID_0: 154 for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) 155 if(pArray->u.array.pMember[i] && mIsArray(pArray->u.array.pMember[i]) && 156 (pArray->u.array.pMember[i]->u.array.rf_broken == 1)) 157 { 158 pSubArray = pArray->u.array.pMember[i]; 159 goto loop; 160 } 161 default: 162 return -EINVAL; 163 } 164 return orig_length; 165 } 166 } 167 else if (length>=7 && strncmp(buffer, "verify ", 7)==0) 168 { 169 buffer+=7; 170 length-=7; 171 if (length>=6 && strncmp(buffer, "start ", 6)==0) 172 { 173 buffer+=6; 174 length-=6; 175 if (length>=1 && *buffer>='1') 176 { 177 iarray = *buffer-'1'; 178 if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL; 179 180 pArray = _vbus_p->pVDevice[iarray]; 181 if (!pArray || (pArray->vf_online == 0)) return -EINVAL; 182 183 if(pArray->VDeviceType != VD_RAID_1 && pArray->VDeviceType != VD_RAID_5) 184 return -EINVAL; 185 186 if (!(pArray->u.array.rf_need_rebuild || 187 pArray->u.array.rf_rebuilding || 188 pArray->u.array.rf_verifying || 189 pArray->u.array.rf_initializing)) 190 { 191 oldspl = lock_driver(); 192 pArray->u.array.RebuildSectors = 0; 193 hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, VERIFY); 194 unlock_driver(oldspl); 195 } 196 return orig_length; 197 } 198 } 199 else if (length>=5 && strncmp(buffer, "stop ", 5)==0) 200 { 201 buffer+=5; 202 length-=5; 203 if (length>=1 && *buffer>='1') 204 { 205 iarray = *buffer-'1'; 206 if(iarray >= MAX_VDEVICE_PER_VBUS) return -EINVAL; 207 208 pArray = _vbus_p->pVDevice[iarray]; 209 if (!pArray || (pArray->vf_online == 0)) return -EINVAL; 210 if(pArray->u.array.rf_verifying) 211 { 212 oldspl = lock_driver(); 213 pArray->u.array.rf_abort_rebuild = 1; 214 unlock_driver(oldspl); 215 } 216 return orig_length; 217 } 218 } 219 } 220 else 221 #ifdef _RAID5N_ 222 if (length>=10 && strncmp(buffer, "writeback ", 10)==0) { 223 buffer+=10; 224 length-=10; 225 if (length>=1 && *buffer>='0' && *buffer<='1') { 226 _vbus_(r5.enable_write_back) = *buffer-'0'; 227 if (_vbus_(r5.enable_write_back)) 228 hpt_printk(("RAID5 write back enabled")); 229 return orig_length; 230 } 231 } 232 else 233 #endif 234 #endif 235 if (0) {} /* just to compile */ 236 #ifdef DEBUG 237 else if (length>=9 && strncmp(buffer, "dbglevel ", 9)==0) { 238 buffer+=9; 239 length-=9; 240 if (length>=1 && *buffer>='0' && *buffer<='3') { 241 hpt_dbg_level = *buffer-'0'; 242 return orig_length; 243 } 244 } 245 else if (length>=8 && strncmp(buffer, "disable ", 8)==0) { 246 /* TO DO */ 247 } 248 #endif 249 250 return -EINVAL; 251 } 252 253 /* 254 * Since we have only one sysctl node, add adapter ID in the command 255 * line string: e.g. "hpt 0 rebuild start" 256 */ 257 static int 258 hpt_set_info(int length) 259 { 260 int retval; 261 262 #ifdef SUPPORT_IOCTL 263 PUCHAR ke_area; 264 int err; 265 DWORD dwRet; 266 PHPT_IOCTL_PARAM piop; 267 #endif 268 char *buffer = hptproc_buffer; 269 if (length >= 6) { 270 if (strncmp(buffer,"hpt ",4) == 0) { 271 IAL_ADAPTER_T *pAdapter; 272 retval = buffer[4]-'0'; 273 for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) { 274 if (pAdapter->mvSataAdapter.adapterId==retval) 275 return (retval = hpt_set_asc_info(pAdapter, buffer+6, length-6)) >= 0? retval : -EINVAL; 276 } 277 return -EINVAL; 278 } 279 #ifdef SUPPORT_IOCTL 280 piop = (PHPT_IOCTL_PARAM)buffer; 281 if (piop->Magic == HPT_IOCTL_MAGIC || 282 piop->Magic == HPT_IOCTL_MAGIC32) { 283 KdPrintE(("ioctl=%d in=%p len=%d out=%p len=%d\n", 284 piop->dwIoControlCode, 285 piop->lpInBuffer, 286 piop->nInBufferSize, 287 piop->lpOutBuffer, 288 piop->nOutBufferSize)); 289 290 /* 291 * map buffer to kernel. 292 */ 293 if (piop->nInBufferSize+piop->nOutBufferSize > PAGE_SIZE) { 294 KdPrintE(("User buffer too large\n")); 295 return -EINVAL; 296 } 297 298 ke_area = malloc(piop->nInBufferSize+piop->nOutBufferSize, M_DEVBUF, M_NOWAIT); 299 if (ke_area == NULL) { 300 KdPrintE(("Couldn't allocate kernel mem.\n")); 301 return -EINVAL; 302 } 303 304 if (piop->nInBufferSize) 305 copyin((void*)(ULONG_PTR)piop->lpInBuffer, ke_area, piop->nInBufferSize); 306 307 /* 308 * call kernel handler. 309 */ 310 err = Kernel_DeviceIoControl(&gIal_Adapter->VBus, 311 piop->dwIoControlCode, ke_area, piop->nInBufferSize, 312 ke_area + piop->nInBufferSize, piop->nOutBufferSize, &dwRet); 313 314 if (err==0) { 315 if (piop->nOutBufferSize) 316 copyout(ke_area + piop->nInBufferSize, (void*)(ULONG_PTR)piop->lpOutBuffer, piop->nOutBufferSize); 317 318 if (piop->lpBytesReturned) 319 copyout(&dwRet, (void*)(ULONG_PTR)piop->lpBytesReturned, sizeof(DWORD)); 320 321 free(ke_area, M_DEVBUF); 322 return length; 323 } 324 else KdPrintW(("Kernel_ioctl(): return %d\n", err)); 325 326 free(ke_area, M_DEVBUF); 327 return -EINVAL; 328 } else { 329 KdPrintW(("Wrong signature: %x\n", piop->Magic)); 330 return -EINVAL; 331 } 332 #endif 333 } 334 335 return -EINVAL; 336 } 337 338 #define shortswap(w) ((WORD)((w)>>8 | ((w) & 0xFF)<<8)) 339 340 static void 341 get_disk_name(char *name, PDevice pDev) 342 { 343 int i; 344 MV_SATA_CHANNEL *pMvSataChannel = pDev->mv; 345 IDENTIFY_DATA2 *pIdentifyData = (IDENTIFY_DATA2 *)pMvSataChannel->identifyDevice; 346 347 for (i = 0; i < 10; i++) 348 ((WORD*)name)[i] = shortswap(pIdentifyData->ModelNumber[i]); 349 name[20] = '\0'; 350 } 351 352 static int 353 hpt_copy_info(HPT_GET_INFO *pinfo, char *fmt, ...) 354 { 355 int printfretval; 356 va_list ap; 357 358 if(fmt == NULL) { 359 *hptproc_buffer = 0; 360 return (SYSCTL_OUT(pinfo, hptproc_buffer, 1)); 361 } 362 else 363 { 364 va_start(ap, fmt); 365 printfretval = vsnprintf(hptproc_buffer, sizeof(hptproc_buffer), fmt, ap); 366 va_end(ap); 367 return(SYSCTL_OUT(pinfo, hptproc_buffer, strlen(hptproc_buffer))); 368 } 369 } 370 371 static void 372 hpt_copy_disk_info(HPT_GET_INFO *pinfo, PVDevice pVDev, UINT iChan) 373 { 374 char name[32], arrayname[16], *status; 375 376 get_disk_name(name, &pVDev->u.disk); 377 378 if (!pVDev->u.disk.df_on_line) 379 status = "Disabled"; 380 else if (pVDev->VDeviceType==VD_SPARE) 381 status = "Spare "; 382 else 383 status = "Normal "; 384 385 #ifdef SUPPORT_ARRAY 386 if(pVDev->pParent) { 387 memcpy(arrayname, pVDev->pParent->u.array.ArrayName, MAX_ARRAY_NAME); 388 if (pVDev->pParent->u.array.CriticalMembers & (1<<pVDev->bSerialNumber)) 389 status = "Degraded"; 390 } 391 else 392 #endif 393 arrayname[0]=0; 394 395 hpt_copy_info(pinfo, "Channel %d %s %5dMB %s %s\n", 396 iChan+1, 397 name, pVDev->VDeviceCapacity>>11, status, arrayname); 398 } 399 400 #ifdef SUPPORT_ARRAY 401 static void 402 hpt_copy_array_info(HPT_GET_INFO *pinfo, int nld, PVDevice pArray) 403 { 404 int i; 405 char *sType=0, *sStatus=0; 406 char buf[32]; 407 PVDevice pTmpArray; 408 409 switch (pArray->VDeviceType) { 410 case VD_RAID_0: 411 for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) 412 if(pArray->u.array.pMember[i]) { 413 if(mIsArray(pArray->u.array.pMember[i])) 414 sType = "RAID 1/0 "; 415 /* TO DO */ 416 else 417 sType = "RAID 0 "; 418 break; 419 } 420 break; 421 422 case VD_RAID_1: 423 sType = "RAID 1 "; 424 break; 425 426 case VD_JBOD: 427 sType = "JBOD "; 428 break; 429 430 case VD_RAID_5: 431 sType = "RAID 5 "; 432 break; 433 434 default: 435 sType = "N/A "; 436 break; 437 } 438 439 if (pArray->vf_online == 0) 440 sStatus = "Disabled"; 441 else if (pArray->u.array.rf_broken) 442 sStatus = "Critical"; 443 for (i = 0; (UCHAR)i < pArray->u.array.bArnMember; i++) 444 { 445 if (!sStatus) 446 { 447 if(mIsArray(pArray->u.array.pMember[i])) 448 pTmpArray = pArray->u.array.pMember[i]; 449 else 450 pTmpArray = pArray; 451 452 if (pTmpArray->u.array.rf_rebuilding) { 453 #ifdef DEBUG 454 sprintf(buf, "Rebuilding %lldMB", (pTmpArray->u.array.RebuildSectors>>11)); 455 #else 456 sprintf(buf, "Rebuilding %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11))); 457 #endif 458 sStatus = buf; 459 } 460 else if (pTmpArray->u.array.rf_verifying) { 461 sprintf(buf, "Verifying %d%%", (UINT)((pTmpArray->u.array.RebuildSectors>>11)*100/((pTmpArray->VDeviceCapacity/(pTmpArray->u.array.bArnMember-1))>>11))); 462 sStatus = buf; 463 } 464 else if (pTmpArray->u.array.rf_need_rebuild) 465 sStatus = "Critical"; 466 else if (pTmpArray->u.array.rf_broken) 467 sStatus = "Critical"; 468 469 if(pTmpArray == pArray) goto out; 470 } 471 else 472 goto out; 473 } 474 out: 475 if (!sStatus) sStatus = "Normal"; 476 hpt_copy_info(pinfo, "%2d %11s %-20s %5lldMB %-16s", nld, sType, pArray->u.array.ArrayName, pArray->VDeviceCapacity>>11, sStatus); 477 } 478 #endif 479 480 static int 481 hpt_get_info(IAL_ADAPTER_T *pAdapter, HPT_GET_INFO *pinfo) 482 { 483 PVBus _vbus_p = &pAdapter->VBus; 484 struct cam_periph *periph = NULL; 485 UINT channel,j,i; 486 PVDevice pVDev; 487 488 #ifndef FOR_DEMO 489 if (pAdapter->beeping) { 490 intrmask_t oldspl = lock_driver(); 491 pAdapter->beeping = 0; 492 BeepOff(pAdapter->mvSataAdapter.adapterIoBaseAddress); 493 unlock_driver(oldspl); 494 } 495 #endif 496 497 hpt_copy_info(pinfo, "Controller #%d:\n\n", pAdapter->mvSataAdapter.adapterId); 498 499 hpt_copy_info(pinfo, "Physical device list\n"); 500 hpt_copy_info(pinfo, "Channel Model Capacity Status Array\n"); 501 hpt_copy_info(pinfo, "-------------------------------------------------------------------\n"); 502 503 for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) 504 { 505 pVDev = &(pAdapter->VDevices[channel]); 506 if(pVDev->u.disk.df_on_line) 507 hpt_copy_disk_info(pinfo, pVDev, channel); 508 } 509 510 hpt_copy_info(pinfo, "\nLogical device list\n"); 511 hpt_copy_info(pinfo, "No. Type Name Capacity Status OsDisk\n"); 512 hpt_copy_info(pinfo, "--------------------------------------------------------------------------\n"); 513 514 j=1; 515 for(i = 0; i < MAX_VDEVICE_PER_VBUS; i++){ 516 pVDev = _vbus_p->pVDevice[i]; 517 if(pVDev){ 518 j=i+1; 519 #ifdef SUPPORT_ARRAY 520 if (mIsArray(pVDev)) 521 { 522 is_array: 523 hpt_copy_array_info(pinfo, j, pVDev); 524 } 525 else 526 #endif 527 { 528 char name[32]; 529 /* it may be add to an array after driver loaded, check it */ 530 #ifdef SUPPORT_ARRAY 531 if (pVDev->pParent) 532 /* in this case, pVDev can only be a RAID 1 source disk. */ 533 if (pVDev->pParent->VDeviceType==VD_RAID_1 && pVDev==pVDev->pParent->u.array.pMember[0]) 534 goto is_array; 535 #endif 536 get_disk_name(name, &pVDev->u.disk); 537 538 hpt_copy_info(pinfo, "%2d %s %s %5dMB %-16s", 539 j, "Single disk", name, pVDev->VDeviceCapacity>>11, 540 /* gmm 2001-6-19: Check if pDev has been added to an array. */ 541 ((pVDev->pParent) ? "Unavailable" : "Normal")); 542 } 543 periph = hpt_get_periph(pAdapter->mvSataAdapter.adapterId, i); 544 if (periph == NULL) 545 hpt_copy_info(pinfo," %s\n","not registered"); 546 else 547 hpt_copy_info(pinfo," %s%d\n", periph->periph_name, periph->unit_number); 548 } 549 } 550 return 0; 551 } 552 553 static __inline int 554 hpt_proc_in(FORMAL_HANDLER_ARGS, int *len) 555 { 556 int i, error=0; 557 558 *len = 0; 559 if ((req->newlen - req->newidx) >= sizeof(hptproc_buffer)) { 560 error = EINVAL; 561 } else { 562 i = (req->newlen - req->newidx); 563 error = SYSCTL_IN(req, hptproc_buffer, i); 564 if (!error) 565 *len = i; 566 (hptproc_buffer)[i] = '\0'; 567 } 568 return (error); 569 } 570 571 static int 572 hpt_status(FORMAL_HANDLER_ARGS) 573 { 574 int length, error=0, retval=0; 575 IAL_ADAPTER_T *pAdapter; 576 577 error = hpt_proc_in(REAL_HANDLER_ARGS, &length); 578 579 if (req->newptr != NULL) 580 { 581 if (error || length == 0) 582 { 583 KdPrint(("error!\n")); 584 retval = EINVAL; 585 goto out; 586 } 587 588 if (hpt_set_info(length) >= 0) 589 retval = 0; 590 else 591 retval = EINVAL; 592 goto out; 593 } 594 595 hpt_copy_info(req, "%s Version %s\n", DRIVER_NAME, DRIVER_VERSION); 596 for (pAdapter=gIal_Adapter; pAdapter; pAdapter=pAdapter->next) { 597 if (hpt_get_info(pAdapter, req) < 0) { 598 retval = EINVAL; 599 break; 600 } 601 } 602 603 hpt_copy_info(req, NULL); 604 goto out; 605 606 out: 607 return (retval); 608 } 609 610 611 #define xhptregister_node(name) hptregister_node(name) 612 613 #if (__FreeBSD_version < 500043) 614 #define hptregister_node(name) \ 615 SYSCTL_NODE(, OID_AUTO, name, CTLFLAG_RW, 0, "Get/Set " #name " state root node") \ 616 SYSCTL_OID(_ ## name, OID_AUTO, status, CTLTYPE_STRING|CTLFLAG_RW, \ 617 NULL, 0, hpt_status, "A", "Get/Set " #name " state") 618 #else 619 #define hptregister_node(name) \ 620 SYSCTL_NODE(, OID_AUTO, name, CTLFLAG_RW, 0, "Get/Set " #name " state root node"); \ 621 SYSCTL_OID(_ ## name, OID_AUTO, status, CTLTYPE_STRING|CTLFLAG_RW, \ 622 NULL, 0, hpt_status, "A", "Get/Set " #name " state"); 623 #endif 624 625 xhptregister_node(PROC_DIR_NAME); 626