summaryrefslogtreecommitdiffstats
path: root/sys/dev/ips
diff options
context:
space:
mode:
authormbr <mbr@FreeBSD.org>2003-11-27 08:37:36 +0000
committermbr <mbr@FreeBSD.org>2003-11-27 08:37:36 +0000
commit03480fac227a174c542c25b116c230e275646e0c (patch)
treec03060ca9d975d935275b77d71f5efdda413dafb /sys/dev/ips
parentf893489f6f4ccaa804d9f9f440a9da1d01ef7ea4 (diff)
downloadFreeBSD-src-03480fac227a174c542c25b116c230e275646e0c.zip
FreeBSD-src-03480fac227a174c542c25b116c230e275646e0c.tar.gz
ServeRaid (at least 5i) didn't initialize correctly. To get
them working (cache, automatic rebuild and hotswap) the FFDC info (First Failure Data Capture) on the adapter must be initialised. Logical drives in critical/degraded states weren't added to the drive list. FreeBSD was not able to see a degraded array after a reboot. Degraded drives are now also added to the drivelist and the state of the logical drive is given at boottime. The adapter type is detected from informations in nvram page 5 and displayed at boottime. Change IPS_OS_FREEBSD definition from 10 to 8 according to IBM specs. Submitted by: <Patrick Guelat> pgfb@imp.ch Reviewed by: mbr, scottl Approved by: re
Diffstat (limited to 'sys/dev/ips')
-rw-r--r--sys/dev/ips/ips.c66
-rw-r--r--sys/dev/ips/ips.h59
-rw-r--r--sys/dev/ips/ips_commands.c90
3 files changed, 211 insertions, 4 deletions
diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c
index 0cbcf6b..165ebb0 100644
--- a/sys/dev/ips/ips.c
+++ b/sys/dev/ips/ips.c
@@ -46,6 +46,25 @@ static struct cdevsw ips_cdevsw = {
.d_maj = IPS_CDEV_MAJOR,
};
+static const char* ips_adapter_name[] = {
+ "N/A",
+ "ServeRAID (copperhead)",
+ "ServeRAID II (copperhead refresh)",
+ "ServeRAID onboard (copperhead)",
+ "ServeRAID onboard (copperhead)",
+ "ServeRAID 3H (clarinet)",
+ "ServeRAID 3L (clarinet lite)",
+ "ServeRAID 4H (trombone)",
+ "ServeRAID 4M (morpheus)",
+ "ServeRAID 4L (morpheus lite)",
+ "ServeRAID 4Mx (neo)",
+ "ServeRAID 4Lx (neo lite)",
+ "ServeRAID 5i II (sarasota)",
+ "ServeRAID 5i (sarasota)",
+ "ServeRAID 6M (marco)",
+ "ServeRAID 6i (sebring)"
+};
+
static int ips_open(dev_t dev, int flags, int fmt, struct thread *td)
{
@@ -240,12 +259,44 @@ void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
if(!(sc->state & IPS_TIMEOUT))
ips_run_waiting_command(sc);
}
+static const char* ips_diskdev_statename(u_int8_t state)
+{
+ static char statebuf[20];
+ switch(state){
+ case IPS_LD_OFFLINE:
+ return("OFFLINE");
+ break;
+ case IPS_LD_OKAY:
+ return("OK");
+ break;
+ case IPS_LD_DEGRADED:
+ return("DEGRADED");
+ break;
+ case IPS_LD_FREE:
+ return("FREE");
+ break;
+ case IPS_LD_SYS:
+ return("SYS");
+ break;
+ case IPS_LD_CRS:
+ return("CRS");
+ break;
+ }
+ sprintf(statebuf,"UNKNOWN(0x%02x)", state);
+ return(statebuf);
+}
static int ips_diskdev_init(ips_softc_t *sc)
{
int i;
for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
- if(sc->drives[i].state & IPS_LD_OKAY){
+ if(sc->drives[i].state == IPS_LD_FREE) continue;
+ device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
+ i, sc->drives[i].raid_lvl,
+ sc->drives[i].sector_count,
+ ips_diskdev_statename(sc->drives[i].state));
+ if(sc->drives[i].state == IPS_LD_OKAY ||
+ sc->drives[i].state == IPS_LD_DEGRADED){
sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
}
@@ -369,15 +420,26 @@ int ips_adapter_init(ips_softc_t *sc)
goto error;
mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF);
+
+ /* initialize ffdc values */
+ microtime(&sc->ffdc_resettime);
+ sc->ffdc_resetcount = 1;
+ if ((i = ips_ffdc_reset(sc)) != 0) {
+ device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
+ goto error;
+ }
if ((i = ips_get_adapter_info(sc)) != 0) {
device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
goto error;
}
+ ips_update_nvram(sc); /* no error check as failure doesn't matter */
+ if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
+ device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
+ }
if ((i = ips_get_drive_info(sc)) != 0) {
device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
goto error;
}
- ips_update_nvram(sc); /* no error check as failure doesn't matter */
ips_cmdqueue_free(sc);
if(sc->adapter_info.max_concurrent_cmds)
diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h
index 3d0f19a..f644df6 100644
--- a/sys/dev/ips/ips.h
+++ b/sys/dev/ips/ips.h
@@ -39,6 +39,7 @@
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
+#include <sys/time.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
@@ -82,6 +83,7 @@
#define IPS_LD_OFFLINE 0x02
#define IPS_LD_OKAY 0x03
+#define IPS_LD_DEGRADED 0x04
#define IPS_LD_FREE 0x00
#define IPS_LD_SYS 0x06
#define IPS_LD_CRS 0x24
@@ -140,14 +142,47 @@
#define IPS_SG_READ_CMD 0x82
#define IPS_SG_WRITE_CMD 0x83
#define IPS_RW_NVRAM_CMD 0xBC
+#define IPS_FFDC_CMD 0xD7
/* error information returned by the adapter */
#define IPS_MIN_ERROR 0x02
#define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */
-#define IPS_OS_FREEBSD 10
+#define IPS_OS_FREEBSD 8
#define IPS_VERSION_MAJOR "0.90"
-#define IPS_VERSION_MINOR ".00"
+#define IPS_VERSION_MINOR ".10"
+
+/* Adapter Types */
+#define IPS_ADAPTER_COPPERHEAD 0x01
+#define IPS_ADAPTER_COPPERHEAD2 0x02
+#define IPS_ADAPTER_COPPERHEADOB1 0x03
+#define IPS_ADAPTER_COPPERHEADOB2 0x04
+#define IPS_ADAPTER_CLARINET 0x05
+#define IPS_ADAPTER_CLARINETLITE 0x06
+#define IPS_ADAPTER_TROMBONE 0x07
+#define IPS_ADAPTER_MORPHEUS 0x08
+#define IPS_ADAPTER_MORPHEUSLITE 0x09
+#define IPS_ADAPTER_NEO 0x0A
+#define IPS_ADAPTER_NEOLITE 0x0B
+#define IPS_ADAPTER_SARASOTA2 0x0C
+#define IPS_ADAPTER_SARASOTA1 0x0D
+#define IPS_ADAPTER_MARCO 0x0E
+#define IPS_ADAPTER_SEBRING 0x0F
+#define IPS_ADAPTER_MAX_T IPS_ADAPTER_SEBRING
+
+/* values for ffdc_settime (from gmtime) */
+#define IPS_SECSPERMIN 60
+#define IPS_MINSPERHOUR 60
+#define IPS_HOURSPERDAY 24
+#define IPS_DAYSPERWEEK 7
+#define IPS_DAYSPERNYEAR 365
+#define IPS_DAYSPERLYEAR 366
+#define IPS_SECSPERHOUR (IPS_SECSPERMIN * IPS_MINSPERHOUR)
+#define IPS_SECSPERDAY ((long) IPS_SECSPERHOUR * IPS_HOURSPERDAY)
+#define IPS_MONSPERYEAR 12
+#define IPS_EPOCH_YEAR 1970
+#define IPS_LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+#define ips_isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
* IPS MACROS
@@ -232,6 +267,22 @@ typedef struct{
u_int32_t reserve3;
} __attribute__((packed)) ips_adapter_info_cmd;
+typedef struct{
+ u_int8_t command;
+ u_int8_t id;
+ u_int8_t reset_count;
+ u_int8_t reset_type;
+ u_int8_t second;
+ u_int8_t minute;
+ u_int8_t hour;
+ u_int8_t day;
+ u_int8_t reserve1[4];
+ u_int8_t month;
+ u_int8_t yearH;
+ u_int8_t yearL;
+ u_int8_t reserve2;
+} __attribute__((packed)) ips_adapter_ffdc_cmd;
+
typedef union{
ips_generic_cmd generic_cmd;
ips_drive_cmd drive_cmd;
@@ -358,10 +409,13 @@ typedef struct ips_softc{
device_t dev;
dev_t device_file;
struct callout_handle timer;
+ u_int16_t adapter_type;
ips_adapter_info_t adapter_info;
device_t diskdev[IPS_MAX_NUM_DRIVES];
ips_drive_t drives[IPS_MAX_NUM_DRIVES];
u_int8_t drivecount;
+ u_int16_t ffdc_resetcount;
+ struct timeval ffdc_resettime;
u_int8_t next_drive;
u_int8_t max_cmds;
volatile u_int8_t used_commands;
@@ -387,6 +441,7 @@ extern int ips_flush_cache(ips_softc_t *sc);
extern void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf);
extern int ips_get_drive_info(ips_softc_t *sc);
extern int ips_get_adapter_info(ips_softc_t *sc);
+extern int ips_ffdc_reset(ips_softc_t *sc);
extern int ips_update_nvram(ips_softc_t *sc);
extern int ips_clear_adapter(ips_softc_t *sc);
diff --git a/sys/dev/ips/ips_commands.c b/sys/dev/ips/ips_commands.c
index 67f1655..cb0dd76 100644
--- a/sys/dev/ips/ips_commands.c
+++ b/sys/dev/ips/ips_commands.c
@@ -434,6 +434,93 @@ int ips_flush_cache(ips_softc_t *sc)
return 0;
}
+/* Simplified localtime to provide timevalues for ffdc.
+ * Taken from libc/stdtime/localtime.c
+ */
+void static ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
+{
+ long days, rem, y;
+ int yleap, *ip, month;
+ int year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
+ int mon_lengths[2][IPS_MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+ };
+
+ days = sctime / IPS_SECSPERDAY;
+ rem = sctime % IPS_SECSPERDAY;
+
+ command->hour = rem / IPS_SECSPERHOUR;
+ rem = rem % IPS_SECSPERHOUR;
+
+ command->minute = rem / IPS_SECSPERMIN;
+ command->second = rem % IPS_SECSPERMIN;
+
+ y = IPS_EPOCH_YEAR;
+ while (days < 0 || days >= (long) year_lengths[yleap = ips_isleap(y)]) {
+ long newy;
+
+ newy = y + days / IPS_DAYSPERNYEAR;
+ if (days < 0)
+ --newy;
+ days -= (newy - y) * IPS_DAYSPERNYEAR +
+ IPS_LEAPS_THRU_END_OF(newy - 1) -
+ IPS_LEAPS_THRU_END_OF(y - 1);
+ y = newy;
+ }
+ command->yearH = y / 100;
+ command->yearL = y % 100;
+ ip = mon_lengths[yleap];
+ for(month = 0; days >= (long) ip[month]; ++month)
+ days = days - (long) ip[month];
+ command->month = month + 1;
+ command->day = days + 1;
+}
+
+static int ips_send_ffdc_reset_cmd(ips_command_t *command)
+{
+ ips_softc_t *sc = command->sc;
+ ips_cmd_status_t *status = command->arg;
+ ips_adapter_ffdc_cmd *command_struct;
+
+ PRINTF(10,"ips test: got a command, building ffdc reset command\n");
+ command->callback = ips_wakeup_callback;
+ command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
+ command_struct->command = IPS_FFDC_CMD;
+ command_struct->id = command->id;
+ command_struct->reset_count = sc->ffdc_resetcount;
+ command_struct->reset_type = 0x80;
+ ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
+
+ bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+ BUS_DMASYNC_PREWRITE);
+ mtx_lock(&sc->cmd_mtx);
+ sc->ips_issue_cmd(command);
+ if (status->value != IPS_ERROR_STATUS)
+ msleep(status, &sc->cmd_mtx, 0, "ffdc", 0);
+ mtx_unlock(&sc->cmd_mtx);
+ ips_insert_free_cmd(sc, command);
+ return 0;
+}
+
+int ips_ffdc_reset(ips_softc_t *sc)
+{
+ ips_cmd_status_t *status;
+ status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT|M_ZERO);
+ if(!status)
+ return ENOMEM;
+ if(ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status,
+ IPS_NOWAIT_FLAG)){
+ free(status, M_DEVBUF);
+ device_printf(sc->dev, "ERROR: unable to get a command! can't send ffdc reset!\n");
+ }
+ if(COMMAND_ERROR(status)){
+ device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
+ }
+ free(status, M_DEVBUF);
+ return 0;
+}
+
static void ips_write_nvram(ips_command_t *command){
ips_softc_t *sc = command->sc;
ips_rw_nvram_cmd *command_struct;
@@ -449,6 +536,9 @@ static void ips_write_nvram(ips_command_t *command){
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
nvram = command->data_buffer;
+ /* retrieve adapter info and save in sc */
+ sc->adapter_type = nvram->adapter_type;
+
strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
nvram->operating_system = IPS_OS_FREEBSD;
OpenPOWER on IntegriCloud