iprutils/0000755000000000000000000000000012427235652011437 5ustar rootrootiprutils/iprconfig.c0000644000000000000000000151707612427235652013604 0ustar rootroot/** * IBM IPR adapter configuration utility * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. **/ #ifndef iprlib_h #include "iprlib.h" #endif #include #include #include #include #include #include #include #include #include "iprconfig.h" #include #include #include #include #include #include #include char *tool_name = "iprconfig"; struct devs_to_init_t { struct ipr_dev *dev; struct ipr_ioa *ioa; int new_block_size; int cmplt; int do_init; int done; int dev_type; #define IPR_AF_DASD_DEVICE 1 #define IPR_JBOD_DASD_DEVICE 2 struct devs_to_init_t *next; }; #define for_each_dev_to_init(dev) for (dev = dev_init_head; dev; dev = dev->next) struct prot_level { struct ipr_array_cap_entry *array_cap_entry; u8 is_valid:1; u8 reserved:7; }; struct array_cmd_data { u8 array_id; u8 prot_level; u16 stripe_size; int qdepth; u32 do_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_query_data *qac; struct array_cmd_data *next; }; /* not needed once menus incorporated into screen_driver */ struct window_l { WINDOW *win; struct window_l *next; }; struct panel_l { PANEL *panel; struct panel_l *next; }; static struct devs_to_init_t *dev_init_head = NULL; static struct devs_to_init_t *dev_init_tail = NULL; static struct array_cmd_data *raid_cmd_head = NULL; static struct array_cmd_data *raid_cmd_tail = NULL; static i_container *i_con_head = NULL; /* FIXME requires multiple heads */ static char log_root_dir[200]; static char editor[200]; FILE *errpath; static int toggle_field; nl_catd catd; static char **add_args; static int num_add_args; static int use_curses; #define for_each_raid_cmd(cmd) for (cmd = raid_cmd_head; cmd; cmd = cmd->next) #define DEFAULT_LOG_DIR "/var/log" #define DEFAULT_EDITOR "vi -R" #define FAILSAFE_EDITOR "vi -R -" #define IS_CANCEL_KEY(c) ((c == KEY_F(12)) || (c == 'q') || (c == 'Q')) #define CANCEL_KEY_LABEL "q=Cancel " #define IS_EXIT_KEY(c) ((c == KEY_F(3)) || (c == 'e') || (c == 'E')) #define EXIT_KEY_LABEL "e=Exit " #define CONFIRM_KEY_LABEL "c=Confirm " #define CONFIRM_REC_KEY_LABEL "s=Confirm Reclaim " #define ENTER_KEY '\n' #define IS_ENTER_KEY(c) ((c == KEY_ENTER) || (c == ENTER_KEY)) #define IS_REFRESH_KEY(c) ((c == 'r') || (c == 'R')) #define REFRESH_KEY_LABEL "r=Refresh " #define IS_TOGGLE_KEY(c) ((c == 't') || (c == 'T')) #define TOGGLE_KEY_LABEL "t=Toggle " #define IS_PGDN_KEY(c) ((c == KEY_NPAGE) || (c == 'f') || (c == 'F')) #define PAGE_DOWN_KEY_LABEL "f=PageDn " #define IS_PGUP_KEY(c) ((c == KEY_PPAGE) || (c == 'b') || (c == 'B')) #define PAGE_UP_KEY_LABEL "b=PageUp " #define IS_5250_CHAR(c) (c == 0xa) #define is_digit(ch) (((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')?1:0) struct special_status { int index; int num; char *str; } s_status; char *print_device(struct ipr_dev *, char *, char *, int); int display_menu(ITEM **, int, int, int **); char *__print_device(struct ipr_dev *, char *, char *, int, int, int, int, int, int, int, int, int, int, int, int); static char *print_path_details(struct ipr_dev *, char *); static int get_drive_phy_loc(struct ipr_ioa *ioa); #define print_dev(i, dev, buf, fmt, type) \ for (i = 0; i < 2; i++) \ (buf)[i] = print_device(dev, (buf)[i], fmt, type); #define print_dev_conc(i, dev, buf, fmt, type) \ for (i = 0; i < 3; i++) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 5)); #define print_dev_enclosure(i, dev, buf, fmt, type) \ for (i = 0; i < 2; i++) { \ if ( i == 0 ) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 8)); \ if (i == 1) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 8)); \ } static struct sysfs_dev *head_sdev; static struct sysfs_dev *tail_sdev; /** * is_format_allowed - * @dev: ipr dev struct * * Returns: * 1 if format is allowed / 0 otherwise **/ static int is_format_allowed(struct ipr_dev *dev) { int rc; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; struct sense_data_t sense_data; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if (rc == 0 && cmd_status.num_records != 0) { status_record = cmd_status.record; if ((status_record->status != IPR_CMD_STATUS_SUCCESSFUL) && (status_record->status != IPR_CMD_STATUS_FAILED)) return 0; } } else { rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70) { if (sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) return 1; else if ((sense_data.sense_key & 0x0F) == 0x02) return 0; } } return 1; } /** * can_format_for_raid - * @dev: ipr dev struct * * Returns: * 1 if format for raid is allowed / 0 otherwise **/ static int can_format_for_raid(struct ipr_dev *dev) { if (dev->ioa->is_secondary) return 0; if (!ipr_is_gscsi(dev) && !ipr_is_af_dasd_device(dev)) return 0; if (ipr_is_hot_spare(dev) || !device_supported(dev)) return 0; if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) return 0; if (ipr_is_af_dasd_device(dev) && ipr_device_is_zeroed(dev)) return 0; if (!ipr_is_af_dasd_device(dev)) { /* If on a JBOD adapter */ if (!dev->ioa->qac_data->num_records) return 0; if (is_af_blocked(dev, 0)) return 0; } if (!is_format_allowed(dev)) return 0; return 1; } /** * flush_stdscr - * * Returns: * nothing **/ /* not needed after screen_driver can do menus */ static void flush_stdscr() { if (!use_curses) { fprintf(stdout, "\r"); fflush(stdout); return; } nodelay(stdscr, TRUE); while(getch() != ERR) {} nodelay(stdscr, FALSE); } /** * free_i_con - free memory from an i_container list * @i_con: i_container struct * * Returns: * NULL **/ static i_container *free_i_con(i_container *i_con) { i_container *temp_i_con; i_con = i_con_head; if (i_con == NULL) return NULL; do { temp_i_con = i_con->next_item; free(i_con); i_con = temp_i_con; } while (i_con); i_con_head = NULL; return NULL; } /** * add_i_con - create a new i_con list or add an i_container to a list * @i_con: i_container struct * @f: field data * @d: data buffer * * Returns: * i_container pointen **/ static i_container *add_i_con(i_container *i_con, char *f, void *d) { i_container *new_i_con; new_i_con = malloc(sizeof(i_container)); new_i_con->next_item = NULL; /* used to hold data entered into user-entry fields */ strncpy(new_i_con->field_data, f, MAX_FIELD_SIZE+1); new_i_con->field_data[strlen(f)+1] = '\0'; /* a pointen to the device information represented by the i_con */ new_i_con->data = d; if (i_con) i_con->next_item = new_i_con; else i_con_head = new_i_con; return new_i_con; } /** * exit_confirmed - * @i_con: i_container struct * * Returns: * 0 **/ int exit_confirmed(i_container *i_con) { exit_func(); exit(0); return 0; } /** * free_screen - * @panel: * @win: * @fields: * * Returns: * nothing **/ /*not needed after screen_driver can do menus */ static void free_screen(struct panel_l *panel, struct window_l *win, FIELD **fields) { struct panel_l *cur_panel; struct window_l *cur_win; int i; cur_panel = panel; while(cur_panel) { panel = panel->next; del_panel(cur_panel->panel); free(cur_panel); cur_panel = panel; } cur_win = win; while(cur_win) { win = win->next; delwin(cur_win->win); free(cur_win); cur_win = win; } if (fields) { for (i = 0; fields[i] != NULL; i++) free_field(fields[i]); } } /** * add_raid_cmd_tail - * @ioa: ipr ioa struct * @dev: ipr dev struct * @array_id: array ID * * Returns: * nothing **/ static void add_raid_cmd_tail(struct ipr_ioa *ioa, struct ipr_dev *dev, u8 array_id) { if (raid_cmd_head) { raid_cmd_tail->next = malloc(sizeof(struct array_cmd_data)); raid_cmd_tail = raid_cmd_tail->next; } else { raid_cmd_head = raid_cmd_tail = malloc(sizeof(struct array_cmd_data)); } memset(raid_cmd_tail, 0, sizeof(struct array_cmd_data)); raid_cmd_tail->array_id = array_id; raid_cmd_tail->ioa = ioa; raid_cmd_tail->dev = dev; } /** * free_raid_cmds - free the raid_cmd_head list * * Returns: * nothing **/ static void free_raid_cmds() { struct array_cmd_data *cur = raid_cmd_head; while(cur) { free(cur->qac); cur = cur->next; free(raid_cmd_head); raid_cmd_head = cur; } } /** * free_devs_to_init - free the dev_init_head list * * Returns: * nothing **/ static void free_devs_to_init() { struct devs_to_init_t *dev = dev_init_head; while (dev) { dev = dev->next; free(dev_init_head); dev_init_head = dev; } } /** * strip_trailing_whitespace - remove trailing white space from a string * @p_str: string * * Returns: * 0 if success / non-zero on failure **/ static char *strip_trailing_whitespace(char *p_str) { int len; char *p_tmp; len = strlen(p_str); if (len == 0) return p_str; p_tmp = p_str + len - 1; while(*p_tmp == ' ' || *p_tmp == '\n') p_tmp--; p_tmp++; *p_tmp = '\0'; return p_str; } /** * tool_exit_func - clean up curses stuff on exit if needed * * Returns: * nothing **/ static void tool_exit_func() { if (use_curses) { clearenv(); clear(); refresh(); endwin(); } } /** * cmdline_exit_func - * * Returns: * nothing **/ static void cmdline_exit_func() { } /** * ipr_list_opts - * @body: * @key: * @list_str: * * Returns: * pointen to body string **/ static char *ipr_list_opts(char *body, char *key, char *list_str) { int start_len = 0; char *string_buf = _("Select one of the following"); if (!body) { body = realloc(body, strlen(string_buf) + 8); sprintf(body, "\n%s:\n\n", string_buf); } start_len = strlen(body); body = realloc(body, start_len + strlen(key) + strlen(_(list_str)) + 16); sprintf(body + start_len, " %s. %s\n", key, _(list_str)); return body; } /** * ipr_end_list - * @body: * * Returns: * pointen to body string **/ static char *ipr_end_list(char *body) { int start_len = 0; char *string_buf = _("Selection"); start_len = strlen(body); body = realloc(body, start_len + strlen(string_buf) + 16); sprintf(body + start_len, "\n\n%s: %%1", string_buf); return body; } /** * screen_driver - Main driver for curses based display * @screen: s_node * @header_lines: number of header lines * @i_con: i_container struct * * Returns: * screen_output struct **/ static struct screen_output *screen_driver(s_node *screen, int header_lines, i_container *i_con) { WINDOW *w_pad = NULL,*w_page_header = NULL; /* windows to hold text */ FIELD **fields = NULL; /* field list for forms */ FORM *form = NULL; /* form for input fields */ int rc = 0; /* the status of the screen */ char *status; /* displays screen operations */ char buffer[100]; /* needed for special status strings */ bool invalid = false; /* invalid status display */ bool ground_cursor=false; int stdscr_max_y,stdscr_max_x; int w_pad_max_y=0,w_pad_max_x=0; /* limits of the windows */ int pad_l=0,pad_scr_r,pad_t=0,viewable_body_lines; int title_lines=2,footer_lines=2; /* position of the pad */ int center,len=0,ch; int i=0,j,k,row=0,col=0,bt_len=0; /* text positioning */ int field_width,num_fields=0; /* form positioning */ int h,w,t,l,o,b; /* form querying */ int active_field = 0; /* return to same active field after a quit */ bool pages = false,is_bottom = false; /* control page up/down in multi-page screens */ bool form_adjust = false; /* correct cursor position in multi-page screens */ bool refresh_stdscr = true; bool x_offscr,y_offscr; /* scrolling windows */ bool pagedn, fakepagedn; char *input = NULL; struct screen_output *s_out; struct screen_opts *temp; /* reference to another screen */ char *title,*body,*body_text; /* screen text */ i_container *temp_i_con; int num_i_cons = 0; int x, y; int f_flags; s_out = malloc(sizeof(struct screen_output)); /* passes an i_con and an rc value back to the function */ /* create text strings */ title = _(screen->title); body = screen->body; f_flags = screen->f_flags; if (f_flags & ENTER_FLAG) footer_lines += 3; else if (f_flags & FWD_FLAG) footer_lines++; if (f_flags & TOGGLE_FLAG) active_field = toggle_field; bt_len = strlen(body); body_text = calloc(bt_len + 1, sizeof(char)); /* determine max size needed and find where to put form fields and device * input in the text and add them * '%' marks a field. The number after '%' represents the field's width */ i=0; temp_i_con = i_con_head; while(body[i] != '\0') { if (body[i] == '\n') { row++; col = -1; body_text[len] = body[i]; len++; } else if ((body[i] == '%') && is_digit(body[i+1])) { fields = realloc(fields,sizeof(FIELD **) * (num_fields + 1)); i++; field_width = 0; sscanf(&(body[i]),"%d",&field_width); fields[num_fields] = new_field(1,field_width,row,col,0,0); if (field_width >= 30) field_opts_off(fields[num_fields],O_AUTOSKIP); if (field_width > 9) /* skip second digit of field index */ i++; if ((temp_i_con) && (temp_i_con->field_data[0]) && (f_flags & MENU_FLAG)) { set_field_buffer(fields[num_fields], 0, temp_i_con->field_data); input = field_buffer(fields[num_fields],0); temp_i_con = temp_i_con->next_item; num_i_cons++; } set_field_userptr(fields[num_fields],NULL); num_fields++; col += field_width-1; bt_len += field_width; body_text = realloc(body_text,bt_len); for(k = 0; k < field_width; k++) body_text[len+k] = ' '; len += field_width; } else { body_text[len] = body[i]; len++; } col++; i++; if (col > w_pad_max_x) w_pad_max_x = col; } body_text[len] = '\0'; w_pad_max_y = row; w_pad_max_y++; /* some forms may not display correctly if this is not included */ w_pad_max_x++; /* adds a column for return char \n : else, a blank line will appear in the pad */ w_pad = newpad(w_pad_max_y,w_pad_max_x); keypad(w_pad,TRUE); if (num_fields) { fields = realloc(fields,sizeof(FIELD **) * (num_fields + 1)); fields[num_fields] = NULL; form = new_form(fields); } /* make the form appear in the pad; not stdscr */ set_form_win(form,w_pad); set_form_sub(form,w_pad); while(1) { getmaxyx(stdscr,stdscr_max_y,stdscr_max_x); /* clear all windows before writing new text to them */ clear(); wclear(w_pad); if(pages) wclear(w_page_header); center = stdscr_max_x/2; len = strlen(title); /* add the title at the center of stdscr */ mvaddstr(0,(center-len/2)>0?center-len/2:0,title); /* set the boundaries of the pad based on the size of the screen and determine whether or not the user is able to scroll */ if ((stdscr_max_y - title_lines - footer_lines + 1) >= w_pad_max_y) { viewable_body_lines = w_pad_max_y; pad_t = 0; y_offscr = false; /*not scrollable*/ pages = false; for (i = 0; i < num_fields; i++) field_opts_on(fields[i],O_ACTIVE); } else { viewable_body_lines = stdscr_max_y - title_lines - footer_lines - 1; y_offscr = true; /*scrollable*/ if (f_flags & FWD_FLAG) { pages = true; w_page_header = subpad(w_pad,header_lines,w_pad_max_x,0,0); } } if (stdscr_max_x > w_pad_max_x) { pad_scr_r = w_pad_max_x; pad_l = 0; x_offscr = false; } else { pad_scr_r = stdscr_max_x; x_offscr = true; } move(stdscr_max_y - footer_lines,0); if (f_flags & ENTER_FLAG) { if (num_fields == 0) { addstr(_("\nPress Enter to Continue\n\n")); ground_cursor = true; } else addstr(_("\nOr leave blank and press Enter to cancel\n\n")); } if ((f_flags & FWD_FLAG) && !(f_flags & ENTER_FLAG)) addstr("\n"); if (f_flags & CONFIRM_FLAG) addstr(CONFIRM_KEY_LABEL); if (f_flags & EXIT_FLAG) addstr(EXIT_KEY_LABEL); if (f_flags & CANCEL_FLAG) addstr(CANCEL_KEY_LABEL); if (f_flags & CONFIRM_REC_FLAG) addstr(CONFIRM_REC_KEY_LABEL); if (f_flags & REFRESH_FLAG) addstr(REFRESH_KEY_LABEL); if (f_flags & TOGGLE_FLAG) addstr(TOGGLE_KEY_LABEL); if ((f_flags & FWD_FLAG) && y_offscr) { addstr(PAGE_DOWN_KEY_LABEL); addstr(PAGE_UP_KEY_LABEL); } post_form(form); /* add the body of the text to the screen */ if (pages) { wmove(w_page_header,0,0); waddstr(w_page_header,body_text); } wmove(w_pad,0,0); waddstr(w_pad,body_text); if (num_fields) { set_current_field(form,fields[active_field]); if (active_field == 0) form_driver(form,REQ_NEXT_FIELD); form_driver(form,REQ_PREV_FIELD); } refresh_stdscr = true; for(;;) { /* add global status if defined */ if (s_status.index) { rc = s_status.index; s_status.index = 0; } if (invalid) { status = (char *)screen_status[INVALID_OPTION_STATUS]; invalid = false; } else if (rc != 0) { if ((status = strchr(screen_status[rc],'%')) == NULL) { status = (char *)screen_status[rc]; } else if (status[1] == 'd') { sprintf(buffer,screen_status[rc],s_status.num); status = buffer; } else if (status[1] == 's') { sprintf(buffer,screen_status[rc],s_status.str); status = buffer; } rc = 0; } else status = NULL; move(stdscr_max_y-1,0); clrtoeol(); /* clear last line */ mvaddstr(stdscr_max_y-1,0,status); status = NULL; pagedn = false; if ((f_flags & FWD_FLAG) && y_offscr) { if (!is_bottom) mvaddstr(stdscr_max_y-footer_lines,stdscr_max_x-8,_("More...")); else mvaddstr(stdscr_max_y-footer_lines,stdscr_max_x-8," "); for (i = 0; i < num_fields; i++) { if (!fields[i]) continue; /* height, width, top, left, offscreen rows, buffer */ field_info(fields[i],&h,&w,&t,&l,&o,&b); /* turn all offscreen fields off */ if ((t >= (header_lines + pad_t)) && (t <= (viewable_body_lines + pad_t))) { field_opts_on(fields[i],O_ACTIVE); if (!form_adjust) continue; if (!(f_flags & TOGGLE_FLAG) || !toggle_field) set_current_field(form,fields[i]); form_adjust = false; } else if (field_opts_off(fields[i],O_ACTIVE) == E_CURRENT && toggle_field && i == (toggle_field - 1)) { pagedn = true; } } } if (f_flags & MENU_FLAG) { for (i = 0; i < num_fields; i++) { field_opts_off(fields[i],O_VISIBLE); field_opts_on(fields[i],O_VISIBLE); set_field_fore(fields[i],A_BOLD); } } if ((f_flags & TOGGLE_FLAG) && toggle_field && (((field_opts(fields[toggle_field-1]) & O_ACTIVE) != O_ACTIVE) || pagedn)) { pagedn = false; fakepagedn = true; ch = KEY_NPAGE; } else { fakepagedn = false; toggle_field = 0; if (refresh_stdscr) { touchwin(stdscr); refresh_stdscr = FALSE; } else touchline(stdscr,stdscr_max_y - 1,1); refresh(); if (pages) { touchwin(w_page_header); prefresh(w_page_header, 0, pad_l, title_lines, 0, title_lines + header_lines - 1, pad_scr_r - 1); touchwin(w_pad); prefresh(w_pad, pad_t + header_lines, pad_l, title_lines + header_lines, 0, title_lines + viewable_body_lines, pad_scr_r - 1); } else { touchwin(w_pad); prefresh(w_pad, pad_t, pad_l, title_lines, 0, title_lines + viewable_body_lines, pad_scr_r - 1); } if (ground_cursor) { move(stdscr_max_y - 1, stdscr_max_x - 1); refresh(); } ch = wgetch(w_pad); /* pause for user input */ } if (ch == KEY_RESIZE) break; if (IS_ENTER_KEY(ch)) { if (num_fields > 0) active_field = field_index(current_field(form)); if ((f_flags & ENTER_FLAG) && num_fields == 0) { rc = (CANCEL_FLAG | rc); goto leave; } else if (f_flags & ENTER_FLAG) { /* cancel if all fields are empty */ form_driver(form,REQ_VALIDATION); for (i = 0; i < num_fields; i++) { if (strlen(field_buffer(fields[i],0)) != 0) /* fields are not empty -> continue */ break; if (i == (num_fields - 1)) { /* all fields are empty */ rc = (CANCEL_FLAG | rc); goto leave; } } } if (num_fields > 0) input = field_buffer(fields[active_field],0); invalid = true; for (i = 0; i < screen->num_opts; i++) { temp = &(screen->options[i]); if ((temp->key[0] == '\n') || ((num_fields > 0)?(strcasecmp(input,temp->key) == 0):0)) { invalid = false; if ((temp->key[0] == '\n') && (num_fields > 0)) { /* store field data to existing i_con (which should already contain pointens) */ i_container *temp_i_con = i_con_head; form_driver(form,REQ_VALIDATION); for (i = 0; i < num_fields; i++) { strncpy(temp_i_con->field_data,field_buffer(fields[i],0),MAX_FIELD_SIZE); temp_i_con = temp_i_con->next_item; } } if (temp->screen_function == NULL) /* continue with function */ goto leave; toggle_field = 0; do rc = temp->screen_function(i_con_head); while (rc == REFRESH_SCREEN || rc & REFRESH_FLAG); /* if screen flags exist on rc and they don't match the screen's flags, return */ if ((rc & 0xF000) && !(screen->rc_flags & (rc & 0xF000))) goto leave; /* strip screen flags from rc */ rc &= ~(EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG); if (screen->rc_flags & REFRESH_FLAG) { s_status.index = rc; rc = (REFRESH_FLAG | rc); goto leave; } break; } } if (!invalid) { /*clear fields*/ form_driver(form,REQ_LAST_FIELD); for (i = 0; i < num_fields; i++) { form_driver(form,REQ_CLR_FIELD); form_driver(form,REQ_NEXT_FIELD); } break; } } else if (IS_EXIT_KEY(ch) && (f_flags & EXIT_FLAG)) { rc = (EXIT_FLAG | rc); goto leave; } else if (IS_CANCEL_KEY(ch) && ((f_flags & CANCEL_FLAG) || screen == &n_main_menu)) { rc = (CANCEL_FLAG | rc); goto leave; } else if ((IS_REFRESH_KEY(ch) && (f_flags & REFRESH_FLAG)) || (ch == KEY_HOME && (f_flags & REFRESH_FLAG))) { rc = REFRESH_SCREEN; if (num_fields > 1) toggle_field = field_index(current_field(form)) + 1; goto leave; } else if (IS_TOGGLE_KEY(ch) && (f_flags & TOGGLE_FLAG)) { rc = TOGGLE_SCREEN; if (num_fields > 1) toggle_field = field_index(current_field(form)) + 1; goto leave; } else if (IS_PGUP_KEY(ch) && (f_flags & FWD_FLAG) && y_offscr) { invalid = false; if (pad_t > 0) { pad_t -= (viewable_body_lines - header_lines + 1); rc = PGUP_STATUS; } else if ((f_flags & FWD_FLAG) && y_offscr) rc = TOP_STATUS; else invalid = true; if (!invalid) { if (pad_t < 0) pad_t = 0; is_bottom = false; form_adjust = true; refresh_stdscr = true; } } else if (IS_PGDN_KEY(ch) && (f_flags & FWD_FLAG) && y_offscr) { invalid = false; if ((stdscr_max_y - title_lines - footer_lines + 1) < (w_pad_max_y - pad_t)) { pad_t += (viewable_body_lines - header_lines + 1); rc = PGDN_STATUS; if ((stdscr_max_y - title_lines - footer_lines + 1) >= (w_pad_max_y - pad_t)) is_bottom = true; } else if (is_bottom) rc = BTM_STATUS; else invalid = true; if (fakepagedn) rc = 0; if (!invalid) { form_adjust = true; refresh_stdscr = true; } } else if (ch == KEY_RIGHT) { if (x_offscr && stdscr_max_x+pad_l0) pad_l--; else form_driver(form, REQ_PREV_CHAR); } else if (ch == KEY_UP) { if ((y_offscr || (num_fields == 0)) && !pages) { if (pad_t > 0) { pad_t--; form_adjust = true; is_bottom = false; } } else form_driver(form, REQ_PREV_FIELD); } else if (ch == KEY_DOWN) { if ((y_offscr || (num_fields == 0)) && !pages) { if (y_offscr && ((stdscr_max_y + pad_t ) < (w_pad_max_y + title_lines + footer_lines))) { pad_t++; form_adjust = true; } if (!((stdscr_max_y + pad_t) < (w_pad_max_y + title_lines + footer_lines))) is_bottom = true; } else form_driver(form, REQ_NEXT_FIELD); } else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if ((f_flags & MENU_FLAG) || (num_fields == 0)) { invalid = true; for (i = 0; i < screen->num_opts; i++) { temp = &(screen->options[i]); if ((temp->key) && (ch == temp->key[0])) { invalid = false; if (num_fields > 0) { active_field = field_index(current_field(form)); input = field_buffer(fields[active_field],0); if (active_field < num_i_cons) { temp_i_con = i_con_head; for (j = 0; j < active_field; j++) temp_i_con = temp_i_con->next_item; temp_i_con->field_data[0] = '\0'; getyx(w_pad,y,x); temp_i_con->y = y; temp_i_con->x = x; } toggle_field = field_index(current_field(form)) + 1; } if (temp->screen_function == NULL) goto leave; do rc = temp->screen_function(i_con_head); while (rc == REFRESH_SCREEN || rc & REFRESH_FLAG); if ((rc & 0xF000) && !(screen->rc_flags & (rc & 0xF000))) goto leave; rc &= ~(EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG); if (screen->rc_flags & REFRESH_FLAG) { rc = (REFRESH_FLAG | rc); goto leave; } break; } } } else if (isascii(ch)) form_driver(form, ch); } } leave: s_out->i_con = i_con_head; s_out->rc = rc; free(body_text); unpost_form(form); free_form(form); for (i = 0; i < num_fields; i++) { if (fields[i] != NULL) free_field(fields[i]); } free(fields); delwin(w_pad); delwin(w_page_header); return s_out; } static char *status_hdr[] = { /* . . . . . */ /*012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ "OPT Name Resource Path/Address Vendor Product ID Status", "OPT Name PCI/SCSI Location Description Status", "Name Resource Path/Address Vendor Product ID Status", "Name PCI/SCSI Location Description Status", "OPT SAS Port/SAS Address Description Active Status Info", "OPT SAS Port/SAS Address Description Active Status Info", "SAS Port/SAS Address Description Active Status Info", "SAS Port/SAS Address Description Active Status Info", "OPT Name Platform Location Description Status", "OPT Name SCSI Host/Resource Path Vendor Product ID Status", "OPT Name SCSI Host/Resource Path Vendor Product ID Status", "Name Platform Location Description Status", "OPT Name PCI/Host/Resource Path Serial Number Status", "OPT Name Physical Location Production ID Status", "Name Physical Location Serial Number Status"}; static char *status_sep[] = { "--- ------ -------------------------- -------- ------------------- -----------------", "--- ------ ------------------------- ---------------------------- -----------------", "------ -------------------------- -------- ---------------- -----------------", "------ ------------------------- ------------------------- -----------------", "--- --------------------- ------------------ ------ ----------------- ----------", "--- ---------------------- ------------------ ------ ----------------- ----------", "---------------------- ------------------ ------ ----------------- ----------", "---------------------- ------------------ ------ ----------------- ----------", "--- ------ -------------------------- ---------------------------- -----------------", "--- ------ ---------------------------- -------- ------------------- --------------", "--- ------ ---------------------------- -------- ------------------- --------------", "------ -------------------------- ---------------------------- ------------", "--- ------ ---------------------------------------- ------------- ------------", "--- ------ ---------------------------------------- ---------------- ------------", "------ ---------------------------------------- ------------- ------------", }; /** * status_header - * @buffer: data buffer * @num_lines: number of lines * @type: type index * * Returns: * buffer **/ static char *status_header(char *buffer, int *num_lines, int type) { int cur_len = strlen(buffer); int header_lines = 0; buffer = realloc(buffer, cur_len + strlen(status_hdr[type]) + strlen(status_sep[type]) + 8); cur_len += sprintf(buffer + cur_len, "%s\n", status_hdr[type]); cur_len += sprintf(buffer + cur_len, "%s\n", status_sep[type]); header_lines += 2; *num_lines = header_lines + *num_lines; return buffer; } /** * add_string_to_body - adds a string of data to fit in the text window, * putting carraige returns in when necessary * @body: * @new_text: * @line_fill: * @line_num: * * Returns: * body on success / 0 if no space available **/ /* NOTE: This routine expects the language conversion to be done before entering this routine */ static char *add_string_to_body(char *body, char *new_text, char *line_fill, int *line_num) { int body_len = 0; int new_text_len = strlen(new_text); int line_fill_len = strlen(line_fill); int rem_length; int new_text_offset = 0; int max_y, max_x; int len, i; int num_lines = 0; struct winsize w; if (body) body_len = strlen(body); len = body_len; if (use_curses) getmaxyx(stdscr,max_y,max_x); else { memset(&w, 0, sizeof(struct winsize)); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (w.ws_col > 0) max_x = w.ws_col; else max_x = 80; } rem_length = max_x - 1; for (i = 0; i < strlen(new_text); i++) { if (new_text[i] == '\n') num_lines++; } while (len != 0) { if (body[len--] == '\n') break; rem_length--; } while (1) { if (new_text_len < rem_length) { /* done, all data fits in current line */ body = realloc(body, body_len + new_text_len + 8); sprintf(body + body_len, "%s", &new_text[new_text_offset]); break; } len = rem_length; while (len != 0) { if (new_text[new_text_offset + len] == ' ') { /* found a place to \n */ break; } len--; } if (len == 0) /* no space to fit any word */ return 0; /* adjust len to compensate 0 based array */ len += 1; body = realloc(body, body_len + len + line_fill_len + 8); strncpy(body + body_len, new_text + new_text_offset, len); body_len += len; new_text_offset += len; new_text_len -= len; body_len += sprintf(body + body_len, "\n%s", line_fill); rem_length = max_x - 1 - line_fill_len; num_lines++; } if (line_num) *line_num = *line_num + num_lines; return body; } /** * add_line_to_body - adds a single line to the body text of the screen, it is * intented primarily to list items with the desciptor on the * left of the screen and ". . . :" to the data field for the * descriptor. * @body: * @new_text: * @field_data: * * Returns: * body **/ /* NOTE: This routine expects the language conversion to be done before entering this routine */ #define DETAILS_DESCR_LEN 40 static char *add_line_to_body(char *body, char *new_text, char *field_data) { int cur_len = 0, start_len = 0; int str_len, field_data_len; int i; if (body != NULL) start_len = cur_len = strlen(body); str_len = strlen(new_text); if (!field_data) { body = realloc(body, cur_len + str_len + 8); sprintf(body + cur_len, "%s\n", new_text); } else { field_data_len = strlen(field_data); if (str_len < DETAILS_DESCR_LEN) { body = realloc(body, cur_len + field_data_len + DETAILS_DESCR_LEN + 8); cur_len += sprintf(body + cur_len, "%s", new_text); for (i = cur_len; i < DETAILS_DESCR_LEN + start_len; i++) { if ((cur_len + start_len) % 2) cur_len += sprintf(body + cur_len, "."); else cur_len += sprintf(body + cur_len, " "); } sprintf(body + cur_len, " : %s\n", field_data); } else { body = realloc(body, cur_len + str_len + field_data_len + 8); sprintf(body + cur_len, "%s : %s\n",new_text, field_data); } } return body; } /** * __body_init_status - * @header: * @num_lines: number of lines * @type: type index * * Returns: * data buffer **/ static char *__body_init_status(char **header, int *num_lines, int type) { char *buffer = NULL; int header_lines = 0; int j; char *x_header; for (j = 0, x_header = _(header[j]); strlen(x_header) != 0; j++, x_header = _(header[j])) buffer = add_string_to_body(buffer, x_header, "", &header_lines); buffer = status_header(buffer, &header_lines, type); if (num_lines != NULL) *num_lines = header_lines; return buffer; } /** * body_init_status - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status(char *buffer[2], char **header, int *num_lines) { int z; for (z = 0; z < 2; z++) buffer[z] = __body_init_status(header, num_lines, z); } /** * body_init_status_conc - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status_conc(char *buffer[3], char **header, int *num_lines) { int z; for (z = 0; z < 3; z++) buffer[z] = __body_init_status(header, num_lines, (z + 8)); } /** * body_init_status_enclosure - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status_enclosure(char *buffer[2], char **header, int *num_lines) { buffer[0] = __body_init_status(header, num_lines, 12); buffer[1] = __body_init_status(header, num_lines, 13); } /** * __body_init - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * data buffer **/ static char *__body_init(char *buffer, char **header, int *num_lines) { int j; int header_lines = 0; char *x_header; if (num_lines) header_lines = *num_lines; for (j = 0, x_header = _(header[j]); strlen(x_header) != 0; j++, x_header = _(header[j])) { if (j == 0) buffer = add_string_to_body(buffer, x_header, "", &header_lines); else buffer = add_string_to_body(buffer, x_header, " ", &header_lines); } if (num_lines) *num_lines = header_lines; return buffer; } /** * body_init - * @header: * @num_lines: number of lines * * Returns: * data buffer **/ static char *body_init(char **header, int *num_lines) { if (num_lines) *num_lines = 0; return __body_init(NULL, header, num_lines); } /** * __complete_screen_driver_curses - * @n_screen: * @complete: % complete string * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int __complete_screen_driver_curses(s_node *n_screen, char *complete, int allow_exit) { int stdscr_max_y,stdscr_max_x,center,len; int ch; char *exit = _("Return to menu, current operations will continue."); char *exit_str; getmaxyx(stdscr,stdscr_max_y,stdscr_max_x); clear(); center = stdscr_max_x/2; len = strlen(n_screen->title); /* add the title at the center of stdscr */ mvaddstr(0,(center - len/2) > 0 ? center-len/2:0, n_screen->title); mvaddstr(2,0,n_screen->body); len = strlen(complete); mvaddstr(stdscr_max_y/2,(center - len/2) > 0 ? center - len/2:0, complete); if (allow_exit) { exit_str = malloc(strlen(exit) + strlen(EXIT_KEY_LABEL) + 8); sprintf(exit_str, EXIT_KEY_LABEL"%s",exit); mvaddstr(stdscr_max_y - 3, 2, exit_str); nodelay(stdscr, TRUE); do { ch = getch(); if (IS_EXIT_KEY(ch)) { nodelay(stdscr, FALSE); return EXIT_FLAG; } } while (ch != ERR); nodelay(stdscr, FALSE); } move(stdscr_max_y - 1, stdscr_max_x - 1); refresh(); return 0; } /** * __complete_screen_driver - * @n_screen: * @complete: % complete string * @allow_exit: allow_exit flag * * Returns: * 0 **/ static int __complete_screen_driver(s_node *n_screen, char *complete, int allow_exit) { fprintf(stdout, "\r%s", complete); fflush(stdout); return 0; } /** * complete_screen_driver - * @n_screen: * @percent: % complete value * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int complete_screen_driver(s_node *n_screen, int percent, int allow_exit) { char *complete = _("Complete"); char *percent_comp; int rc; percent_comp = malloc(strlen(complete) + 8); sprintf(percent_comp,"%3d%% %s", percent, complete); if (use_curses) rc = __complete_screen_driver_curses(n_screen, percent_comp, allow_exit); else rc = __complete_screen_driver(n_screen, percent_comp, allow_exit); free(percent_comp); return rc; } /** * time_screen_driver - * @n_screen: * @seconds: number of seconds * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int time_screen_driver(s_node *n_screen, unsigned long seconds, int allow_exit) { char *complete = _("Elapsed Time: "); char *comp_str; int rc, hours, minutes; hours = seconds/3600; seconds -= (hours * 3600); minutes = seconds/60; seconds -= (minutes *60); comp_str = malloc(strlen(complete) + 40); sprintf(comp_str,"%s%d:%02d:%02ld", complete, hours, minutes, seconds); if (use_curses) rc = __complete_screen_driver_curses(n_screen, comp_str, allow_exit); else rc = __complete_screen_driver(n_screen, comp_str, allow_exit); free(comp_str); return rc; } /** * evaluate_device - * @dev: ipr dev struct * @ioa: ipr ioa struct * @new_block_size: not used? * * Returns: * nothing **/ static void evaluate_device(struct ipr_dev *dev, struct ipr_ioa *ioa, int new_block_size) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_res_addr res_addr; struct ipr_dev_record *dev_record; int device_converted; int timeout = 60; u32 res_handle; if (scsi_dev_data) { res_handle = scsi_dev_data->handle; res_addr.bus = scsi_dev_data->channel; res_addr.target = scsi_dev_data->id; res_addr.lun = scsi_dev_data->lun; } else if (ipr_is_af_dasd_device(dev)) { dev_record = dev->dev_rcd; if (dev_record->no_cfgte_dev) return; res_handle = ntohl(dev->resource_handle); res_addr.bus = dev_record->type2.resource_addr.bus; res_addr.target = dev_record->type2.resource_addr.target; res_addr.lun = dev_record->type2.resource_addr.lun; } else return; /* send evaluate device down */ ipr_evaluate_device(&ioa->ioa, res_handle); device_converted = 0; while (!device_converted && timeout) { /* xxx FIXME how to determine evaluate device completed? */ device_converted = 1; sleep(1); timeout--; } } /** * verify_device - * @dev: ipr dev struct * * Returns: * nothing **/ static void verify_device(struct ipr_dev *dev) { int rc; struct ipr_cmd_status cmd_status; struct sense_data_t sense_data; if (ipr_fast) return; /* Send Test Unit Ready to start device if its a volume set */ if (ipr_is_volume_set(dev)) { ipr_test_unit_ready(dev, &sense_data); return; } if (ipr_is_af(dev)) { if ((rc = ipr_query_command_status(dev, &cmd_status))) return; if (cmd_status.num_records != 0 && cmd_status.record->status == IPR_CMD_STATUS_SUCCESSFUL) { if (ipr_get_blk_size(dev) == 512) evaluate_device(dev, dev->ioa, IPR_JBOD_BLOCK_SIZE); else if (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_JBOD_4K_BLOCK_SIZE) evaluate_device(dev, dev->ioa, IPR_JBOD_4K_BLOCK_SIZE); } } else if (dev->scsi_dev_data->type == TYPE_DISK) { rc = ipr_test_unit_ready(dev, &sense_data); if (!rc) { if (ipr_get_blk_size(dev) == dev->ioa->af_block_size && dev->ioa->qac_data->num_records != 0) { enable_af(dev); evaluate_device(dev, dev->ioa, dev->ioa->af_block_size); } else if (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_AF_4K_BLOCK_SIZE && dev->ioa->qac_data->num_records != 0) { enable_af(dev); evaluate_device(dev, dev->ioa, IPR_AF_4K_BLOCK_SIZE); } } } } /** * processing - displays "Processing" at the bottom of the screen * * Returns: * nothing **/ static void processing() { int max_y, max_x; if (!use_curses) return; getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Processing")); refresh(); } /** * main_menu - Main menu * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int main_menu(i_container *i_con) { int rc; int j; struct ipr_ioa *ioa; struct screen_output *s_out; struct scsi_dev_data *scsi_dev_data; processing(); check_current_config(false); for_each_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data || scsi_dev_data->type == IPR_TYPE_ADAPTER) continue; verify_device(&ioa->dev[j]); } } for (j = 0; j < n_main_menu.num_opts; j++) { n_main_menu.body = ipr_list_opts(n_main_menu.body, n_main_menu.options[j].key, n_main_menu.options[j].list_str); } n_main_menu.body = ipr_end_list(n_main_menu.body); s_out = screen_driver(&n_main_menu, 0, NULL); free(n_main_menu.body); n_main_menu.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); return rc; } /** * print_standalone_disks - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_standalone_disks(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; __for_each_standalone_disk(ioa, dev) { print_dev (k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_hotspare_disks - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_hotspare_disks(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; for_each_hot_spare(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_ses_devices - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_ses_devices(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; for_each_ses(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } static int print_dvd_tape_devices(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; for_each_dvd_tape(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_sas_ses_devices - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_sas_ses_devices(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; if (__ioa_is_spi(ioa)) return 0; for_each_ses(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_vsets - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_vsets(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { int k; int num_lines = 0; struct ipr_dev *vset, *dev; /* print volume set and associated devices*/ for_each_vset(ioa, vset) { print_dev(k, vset, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", vset); num_lines++; for_each_dev_in_vset(vset, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } } return num_lines; } /** * disk_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int disk_status(i_container *i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_disk_status.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data) continue; print_dev(k, &ioa->ioa, buffer, "%1", 2+k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; /* xxxx remove? */ __for_each_standalone_disk(ioa, dev) verify_device(dev); num_lines += print_standalone_disks(ioa, &i_con, buffer, 2); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 2); num_lines += print_vsets(ioa, &i_con, buffer, 2); num_lines += print_dvd_tape_devices(ioa, &i_con, buffer, 2); num_lines += print_ses_devices(ioa, &i_con, buffer, 2); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_disk_status.body = buffer[toggle&1]; s_out = screen_driver(&n_disk_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_disk_status.body = NULL; return rc; } /** * device_details_get_device - * @i_con: i_container struct * @device: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int device_details_get_device(i_container *i_con, struct ipr_dev **device) { i_container *temp_i_con; int invalid=0; int dev_num = 0; struct ipr_dev *cur_device; char *input; if (!i_con) return 1; for (temp_i_con = i_con_head; temp_i_con && !invalid; temp_i_con = temp_i_con->next_item) { cur_device = (struct ipr_dev *)(temp_i_con->data); if (!cur_device) continue; input = temp_i_con->field_data; if (!input) continue; if (strcmp(input, "1") == 0) { if (dev_num) { dev_num++; break; } else { *device = cur_device; dev_num++; } } else if ((strcmp(input, " ") != 0) && (input != NULL) && (strcmp(input, "") != 0)) { invalid = 1; } } if (invalid) /* The selection is not valid */ return 15; if (dev_num == 0) /* Invalid option. No devices selected. */ return 17; else if (dev_num != 1) /* Error. More than one unit was selected. */ return 16; return 0; } /** * ioa_details - get IOA details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *ioa_details(char *body, struct ipr_dev *dev) { struct ipr_ioa_vpd ioa_vpd; struct ipr_cfc_vpd cfc_vpd; struct ipr_cache_cap_vpd cc_vpd; struct ipr_dram_vpd dram_vpd; struct ipr_inquiry_page0 page0_inq; struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_dual_ioa_entry *ioa_entry; int rc, i; char buffer[200]; int cache_size, dram_size; u32 fw_level; memset(&ioa_vpd, 0, sizeof(ioa_vpd)); memset(&cfc_vpd, 0, sizeof(cfc_vpd)); memset(&cc_vpd, 0, sizeof(cc_vpd)); memset(&dram_vpd, 0, sizeof(dram_vpd)); ipr_inquiry(dev, IPR_STD_INQUIRY, &ioa_vpd, sizeof(ioa_vpd)); rc = ipr_inquiry(dev, 0, &page0_inq, sizeof(page0_inq)); for (i = 0; !rc && i < page0_inq.page_length; i++) { if (page0_inq.supported_page_codes[i] == 1) { ipr_inquiry(dev, 1, &cfc_vpd, sizeof(cfc_vpd)); break; } if (page0_inq.supported_page_codes[i] == 0xC4) { ipr_inquiry(dev, 0xC4, &cc_vpd, sizeof(cc_vpd)); break; } } ipr_inquiry(dev, 2, &dram_vpd, sizeof(dram_vpd)); fw_level = get_ioa_fw_version(dev->ioa); body = add_line_to_body(body,"", NULL); ipr_strncpy_0(buffer, scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Manufacturer"), buffer); ipr_strncpy_0(buffer, scsi_dev_data->product_id, IPR_PROD_ID_LEN); body = add_line_to_body(body,_("Machine Type and Model"), buffer); sprintf(buffer,"%08X", fw_level); body = add_line_to_body(body,_("Firmware Version"), buffer); if (!dev->ioa->ioa_dead) { ipr_strncpy_0(buffer, (char *)ioa_vpd.std_inq_data.serial_num, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Serial Number"), buffer); ipr_strncpy_0(buffer, (char *)ioa_vpd.ascii_part_num, IPR_VPD_PART_NUM_LEN); body = add_line_to_body(body,_("Part Number"), buffer); ipr_strncpy_0(buffer, (char *)ioa_vpd.ascii_plant_code, IPR_VPD_PLANT_CODE_LEN); body = add_line_to_body(body,_("Plant of Manufacturer"), buffer); if (cfc_vpd.cache_size[0]) { ipr_strncpy_0(buffer, (char *)cfc_vpd.cache_size, IPR_VPD_CACHE_SIZE_LEN); cache_size = strtoul(buffer, NULL, 16); sprintf(buffer,"%d MB", cache_size); body = add_line_to_body(body,_("Cache Size"), buffer); } if (ntohl(cc_vpd.write_cache_size)) { sprintf(buffer,"%d MB", ntohl(cc_vpd.write_cache_size)); body = add_line_to_body(body,_("Write Cache Size"), buffer); } if (ntohl(cc_vpd.data_store_size)) { sprintf(buffer,"%d MB", ntohl(cc_vpd.data_store_size)); body = add_line_to_body(body,_("DRAM Size"), buffer); } else { ipr_strncpy_0(buffer, (char *)dram_vpd.dram_size, IPR_VPD_DRAM_SIZE_LEN); dram_size = strtoul(buffer, NULL, 16); sprintf(buffer, "%d MB", dram_size); body = add_line_to_body(body,_("DRAM Size"), buffer); } } body = add_line_to_body(body,_("Resource Name"), dev->gen_name); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Physical location"), NULL); body = add_line_to_body(body,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), scsi_dev_data->res_path); sprintf(buffer,"%d", dev->ioa->host_num); body = add_line_to_body(body,_("SCSI Host Number"), buffer); if (strlen(dev->ioa->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->ioa->physical_location); if (dev->ioa->dual_raid_support) { ioa_entry = (struct ipr_dual_ioa_entry *) (((unsigned long)&dev->ioa->ioa_status.cap) + ntohl(dev->ioa->ioa_status.cap.length)); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Current Dual Adapter State"), dev->ioa->dual_state); body = add_line_to_body(body,_("Preferred Dual Adapter State"), dev->ioa->preferred_dual_state); if (ntohl(dev->ioa->ioa_status.num_entries)) { ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Remote Adapter Manufacturer"), buffer); ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_prod_id, IPR_PROD_ID_LEN); body = add_line_to_body(body,_("Remote Adapter Machine Type And Model"), buffer); ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_sn, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Remote Adapter Serial Number"), buffer); } } if (dev->ioa->asymmetric_access) { body = add_line_to_body(body,"", NULL); if (dev->ioa->asymmetric_access_enabled) body = add_line_to_body(body,_("Current Asymmetric Access State"), _("Enabled")); else body = add_line_to_body(body,_("Current Asymmetric Access State"), _("Disabled")); } if (dev->ioa->has_cache) { if (get_ioa_caching(dev->ioa) == IPR_IOA_REQUESTED_CACHING_DISABLED) body = add_line_to_body(body,_("Current Requested Caching Mode"), _("Disabled")); else body = add_line_to_body(body,_("Current Requested Caching Mode"), _("Default")); } return body; } /** * vset_array_details - get vset/array details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *vset_array_details(char *body, struct ipr_dev *dev) { struct ipr_read_cap16 read_cap16; unsigned long long max_user_lba_int; long double device_capacity; double lba_divisor; struct ipr_array_record *array_rcd = dev->array_rcd; char buffer[100]; int rc; int scsi_channel, scsi_id, scsi_lun; if (dev->scsi_dev_data) { scsi_channel = dev->scsi_dev_data->channel; scsi_id = dev->scsi_dev_data->id; scsi_lun = dev->scsi_dev_data->lun; } else if (!dev->ioa->sis64) { scsi_channel = array_rcd->type2.last_resource_addr.bus; scsi_id = array_rcd->type2.last_resource_addr.target; scsi_lun = array_rcd->type2.last_resource_addr.lun; } body = add_line_to_body(body,"", NULL); ipr_strncpy_0(buffer, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Manufacturer"), buffer); sprintf(buffer,"RAID %s", dev->prot_level_str); body = add_line_to_body(body,_("RAID Level"), buffer); if (ntohs(dev->stripe_size) > 1024) sprintf(buffer,"%d M", ntohs(dev->stripe_size)/1024); else sprintf(buffer,"%d k", ntohs(dev->stripe_size)); body = add_line_to_body(body,_("Stripe Size"), buffer); /* Do a read capacity to determine the capacity */ rc = ipr_read_capacity_16(dev, &read_cap16); if (!rc && (ntohl(read_cap16.max_user_lba_hi) || ntohl(read_cap16.max_user_lba_lo)) && ntohl(read_cap16.block_length)) { max_user_lba_int = ntohl(read_cap16.max_user_lba_hi); max_user_lba_int <<= 32; max_user_lba_int |= ntohl(read_cap16.max_user_lba_lo); device_capacity = max_user_lba_int + 1; lba_divisor = (1000*1000*1000) / ntohl(read_cap16.block_length); sprintf(buffer, "%.2Lf GB", device_capacity / lba_divisor); body = add_line_to_body(body, _("Capacity"), buffer); } if (ipr_is_volume_set(dev)) { body = add_line_to_body(body, _("Resource Name"), dev->dev_name); if (dev->ioa->sis64 && dev->array_rcd->type3.desc[0] != ' ') { ipr_strncpy_0(buffer, (char *)dev->array_rcd->type3.desc, sizeof (dev->array_rcd->type3.desc)); body = add_line_to_body(body,_("Label"), buffer); } } ipr_strncpy_0(buffer, (char *)dev->serial_number, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Serial Number"), buffer); body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body, _("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) { if (dev->scsi_dev_data) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); else { ipr_format_res_path(dev->dev_rcd->type3.res_path, dev->res_path_name, IPR_MAX_RES_PATH_LEN); body = add_line_to_body(body,_("Resource Path"), dev->res_path_name); } } if (dev->scsi_dev_data || !dev->ioa->sis64) { sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", scsi_channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", scsi_id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", scsi_lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); } return body; } /** * disk_extended_details - get extended disk details * @body: data buffer * @dev: ipr dev struct * @std_inq: ipr_std_inq_data_long struct * * Returns: * body **/ static char *disk_extended_details(char *body, struct ipr_dev *dev, struct ipr_std_inq_data_long *std_inq) { int rc, i; struct ipr_inquiry_page0 page0_inq; char buffer[100]; char *hex; rc = ipr_inquiry(dev, 0, &page0_inq, sizeof(page0_inq)); for (i = 0; !rc && i < page0_inq.page_length; i++) { if (page0_inq.supported_page_codes[i] == 0xC7) { /* FIXME 0xC7 is SCSD, do further research to find what needs to be tested for this affirmation.*/ /* xxx add_str_to_body that does the strncpy_0? */ body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Extended Details"), NULL); ipr_strncpy_0(buffer, (char *)std_inq->fru_number, IPR_STD_INQ_FRU_NUM_LEN); body = add_line_to_body(body,_("FRU Number"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->ec_level, IPR_STD_INQ_EC_LEVEL_LEN); body = add_line_to_body(body,_("EC Level"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->part_number, IPR_STD_INQ_PART_NUM_LEN); body = add_line_to_body(body,_("Part Number"), buffer); hex = (char *)&std_inq->std_inq_data; sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X", hex[0], hex[1], hex[2], hex[3], hex[4], hex[5], hex[6], hex[7]); body = add_line_to_body(body,_("Device Specific (Z0)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z1_term, IPR_STD_INQ_Z1_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z1)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z2_term, IPR_STD_INQ_Z2_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z2)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z3_term, IPR_STD_INQ_Z3_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z3)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z4_term, IPR_STD_INQ_Z4_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z4)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z5_term, IPR_STD_INQ_Z5_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z5)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z6_term, IPR_STD_INQ_Z6_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z6)"), buffer); break; } } return body; } /** * disk_details - get disk details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *disk_details(char *body, struct ipr_dev *dev) { int rc; struct ipr_std_inq_data_long std_inq; struct ipr_dev_record *device_record; struct ipr_dasd_inquiry_page3 page3_inq; struct ipr_read_cap16 read_cap16; unsigned long long max_user_lba_int; struct ipr_query_res_state res_state; long double device_capacity; double lba_divisor; char product_id[IPR_PROD_ID_LEN+1]; char vendor_id[IPR_VENDOR_ID_LEN+1]; char serial_num[IPR_SERIAL_NUM_LEN+1]; char buffer[100]; int len, scsi_channel, scsi_id, scsi_lun; device_record = (struct ipr_dev_record *)dev->dev_rcd; if (strlen(dev->physical_location) == 0) get_drive_phy_loc(dev->ioa); if (dev->scsi_dev_data) { scsi_channel = dev->scsi_dev_data->channel; scsi_id = dev->scsi_dev_data->id; scsi_lun = dev->scsi_dev_data->lun; } else if (device_record) { scsi_channel = device_record->type2.last_resource_addr.bus; scsi_id = device_record->type2.last_resource_addr.target; scsi_lun = device_record->type2.last_resource_addr.lun; } rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq)); if (!rc) { ipr_strncpy_0(vendor_id, (char *)std_inq.std_inq_data.vpids.vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)std_inq.std_inq_data.vpids.product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)std_inq.std_inq_data.serial_num, IPR_SERIAL_NUM_LEN); } else if (device_record) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)dev->serial_number, IPR_SERIAL_NUM_LEN); } else { ipr_strncpy_0(vendor_id, dev->scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, dev->scsi_dev_data->product_id, IPR_PROD_ID_LEN); serial_num[0] = '\0'; } body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Manufacturer"), vendor_id); body = add_line_to_body(body,_("Product ID"), product_id); rc = ipr_inquiry(dev, 0x03, &page3_inq, sizeof(page3_inq)); if (!rc) { len = sprintf(buffer, "%X%X%X%X", page3_inq.release_level[0], page3_inq.release_level[1], page3_inq.release_level[2], page3_inq.release_level[3]); if (isalnum(page3_inq.release_level[0]) && isalnum(page3_inq.release_level[1]) && isalnum(page3_inq.release_level[2]) && isalnum(page3_inq.release_level[3])) { sprintf(buffer + len, " (%c%c%c%c)", page3_inq.release_level[0], page3_inq.release_level[1], page3_inq.release_level[2], page3_inq.release_level[3]); } body = add_line_to_body(body, _("Firmware Version"), buffer); } if (strcmp(vendor_id,"IBMAS400") == 0) { sprintf(buffer, "%X", std_inq.as400_service_level); body = add_line_to_body(body,_("Level"), buffer); } if (serial_num[0] != '\0') body = add_line_to_body(body, _("Serial Number"), serial_num); /* Do a read capacity to determine the capacity */ memset(&read_cap16, 0, sizeof(read_cap16)); rc = ipr_read_capacity_16(dev, &read_cap16); if (!rc && (ntohl(read_cap16.max_user_lba_hi) || ntohl(read_cap16.max_user_lba_lo)) && ntohl(read_cap16.block_length)) { max_user_lba_int = ntohl(read_cap16.max_user_lba_hi); max_user_lba_int <<= 32; max_user_lba_int |= ntohl(read_cap16.max_user_lba_lo); device_capacity = max_user_lba_int + 1; lba_divisor = (1000*1000*1000) / ntohl(read_cap16.block_length); sprintf(buffer, "%.2Lf GB", device_capacity / lba_divisor); body = add_line_to_body(body, _("Capacity"), buffer); } if (strlen(dev->dev_name) > 0) body = add_line_to_body(body,_("Resource Name"), dev->dev_name); rc = ipr_query_resource_state(dev, &res_state); if (!rc && !res_state.not_oper && !res_state.not_ready) { if (ioa_is_spi(dev->ioa) && !res_state.prot_dev_failed) { if (ntohl(res_state.gscsi.data_path_width) == 16) body = add_line_to_body(body, _("Wide Enabled"), _("Yes")); else body = add_line_to_body(body, _("Wide Enabled"), _("No")); sprintf(buffer, "%d MB/s", (ntohl(res_state.gscsi.data_xfer_rate) * ntohl(res_state.gscsi.data_path_width))/(10 * 8)); if (res_state.gscsi.data_xfer_rate != 0xffffffff) body = add_line_to_body(body, _("Current Bus Throughput"), buffer); } } body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body ,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) { if (dev->scsi_dev_data) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); else { ipr_format_res_path(dev->dev_rcd->type3.res_path, dev->res_path_name, IPR_MAX_RES_PATH_LEN); body = add_line_to_body(body,_("Resource Path"), dev->res_path_name); } } sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", scsi_channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", scsi_id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", scsi_lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); if (strlen(dev->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->physical_location); body = disk_extended_details(body, dev, &std_inq); return body; } /** * get_ses_phy_loc - get ses physical location * @dev: ipr dev struct * * Returns: * body **/ int get_ses_phy_loc(struct ipr_dev *dev) { int rc, i, ret = 1; struct ses_inquiry_page0 ses_page0_inq; struct ses_serial_num_vpd ses_vpd_inq; struct esm_serial_num_vpd esm_vpd_inq; char buffer[100]; memset(&ses_vpd_inq, 0, sizeof(ses_vpd_inq)); rc = ipr_inquiry(dev, 0x00, &ses_page0_inq, sizeof(ses_page0_inq)); for (i = 0; !rc && i < ses_page0_inq.page_length; i++) { if (ses_page0_inq.supported_vpd_page[i] == 0x04) { ret = ipr_inquiry(dev, 0x04, &ses_vpd_inq, sizeof(ses_vpd_inq)); break; } } if (ret == 0 ) { dev->physical_location[0] = '\0'; strncat(dev->physical_location, "U", strlen("U")); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.feature_code, sizeof(ses_vpd_inq.feature_code)); strncat(dev->physical_location, buffer, strlen(buffer)); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.count, sizeof(ses_vpd_inq.count)); strncat(dev->physical_location, ".", strlen(".")); strncat(dev->physical_location, buffer, strlen(buffer)); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.ses_serial_num, sizeof(ses_vpd_inq.ses_serial_num)); strncat(dev->physical_location, ".", strlen(".")); strncat(dev->physical_location, buffer, strlen(buffer)); } if (strlen(dev->physical_location)) { for (i = 0; !rc && i < ses_page0_inq.page_length; i++) { if (ses_page0_inq.supported_vpd_page[i] == 0x01) { ret = ipr_inquiry(dev, 0x01, &esm_vpd_inq, sizeof(esm_vpd_inq)); break; } } if (ret == 0 ) { ipr_strncpy_0((char *)&dev->serial_number, (char *)&esm_vpd_inq.esm_serial_num[0], sizeof(esm_vpd_inq.esm_serial_num)); ipr_strncpy_0(buffer, (char *)esm_vpd_inq.frb_label, sizeof(esm_vpd_inq.frb_label)); strncat(dev->physical_location, "-", strlen("-")); strncat(dev->physical_location, buffer, strlen(buffer)); return 0; } } return 1; } int get_drive_phy_loc_with_ses_phy_loc(struct ipr_dev *ses, struct drive_elem_desc_pg *drive_data, int slot_id, char *buf, int conc_maint) { char buffer[DISK_PHY_LOC_LENGTH + 1], *first_hyphen; char unit_phy_loc[PHYSICAL_LOCATION_LENGTH+1]; ipr_strncpy_0(buffer, (char *)drive_data->dev_elem[slot_id].disk_physical_loc, DISK_PHY_LOC_LENGTH); if (strlen(ses->physical_location)) { if (strlen(buffer)) { ipr_strncpy_0(unit_phy_loc, ses->physical_location, PHYSICAL_LOCATION_LENGTH); first_hyphen = strchr(unit_phy_loc, '-'); if (first_hyphen != NULL) *first_hyphen = '\0'; sprintf(buf, "%s-%s", unit_phy_loc, buffer); } else sprintf(buf, "%s", "\0"); } else if (strlen(buffer)) { if (strlen(ses->ioa->physical_location)) { ipr_strncpy_0(unit_phy_loc, ses->ioa->physical_location, PHYSICAL_LOCATION_LENGTH); first_hyphen = strchr(unit_phy_loc, '-'); if (first_hyphen != NULL) *first_hyphen = '\0'; sprintf(buf, "%s-%s", unit_phy_loc, buffer); } else sprintf(buf, "%s", "\0"); } else sprintf(buf, "%s", "\0"); return 0; } int index_in_page2(struct ipr_encl_status_ctl_pg *ses_data, int slot_id) { int i; for (i = 1; i < IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES; i++) { if (ses_data->elem_status[i].slot_id == slot_id) return (i - 1); } return -1; } /** * ses_details - get ses details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *ses_details(char *body, struct ipr_dev *dev) { int rc; u8 release_level[4]; char product_id[IPR_PROD_ID_LEN+1]; char vendor_id[IPR_VENDOR_ID_LEN+1]; char buffer[100]; int len; if (strlen(dev->physical_location) == 0) get_ses_phy_loc(dev); ipr_strncpy_0(vendor_id, dev->scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, dev->scsi_dev_data->product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(buffer, (char *)&dev->serial_number, ESM_SERIAL_NUM_LEN); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Manufacturer"), vendor_id); body = add_line_to_body(body,_("Product ID"), product_id); body = add_line_to_body(body,_("Serial Number"), buffer); rc = ipr_get_fw_version(dev, release_level); if (!rc) { len = sprintf(buffer, "%X%X%X%X", release_level[0], release_level[1], release_level[2], release_level[3]); if (isalnum(release_level[0]) && isalnum(release_level[1]) && isalnum(release_level[2]) && isalnum(release_level[3])) { sprintf(buffer + len, " (%c%c%c%c)", release_level[0], release_level[1], release_level[2], release_level[3]); } body = add_line_to_body(body, _("Firmware Version"), buffer); } if (strlen(dev->gen_name) > 0) body = add_line_to_body(body,_("Resource Name"), dev->gen_name); body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body ,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); if (strlen(dev->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->physical_location); return body; } /** * device_details - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int device_details(i_container *i_con) { char *body = NULL; struct ipr_dev *dev; struct screen_output *s_out; s_node *n_screen; int rc; processing(); if ((rc = device_details_get_device(i_con, &dev))) return rc; if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) { n_screen = &n_adapter_details; body = ioa_details(body, dev); } else if (ipr_is_volume_set(dev)) { n_screen = &n_vset_details; body = vset_array_details(body, dev); } else if (ipr_is_ses(dev)) { n_screen = &n_ses_details; body = ses_details(body, dev); } else { n_screen = &n_device_details; body = disk_details(body, dev); } n_screen->body = body; s_out = screen_driver(n_screen, 0, i_con); free(n_screen->body); n_screen->body = NULL; rc = s_out->rc; free(s_out); return rc; } #define IPR_INCLUDE 0 #define IPR_REMOVE 1 #define IPR_ADD_HOT_SPARE 0 #define IPR_RMV_HOT_SPARE 1 #define IPR_FMT_JBOD 0 #define IPR_FMT_RECOVERY 1 /** * hot_spare_screen - Display choices for working with hot spares. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int hot_spare_screen(i_container *i_con) { int rc; struct screen_output *s_out; int loop; free_raid_cmds(); for (loop = 0; loop < n_hot_spare_screen.num_opts; loop++) { n_hot_spare_screen.body = ipr_list_opts(n_hot_spare_screen.body, n_hot_spare_screen.options[loop].key, n_hot_spare_screen.options[loop].list_str); } n_hot_spare_screen.body = ipr_end_list(n_hot_spare_screen.body); s_out = screen_driver(&n_hot_spare_screen, 0, NULL); free(n_hot_spare_screen.body); n_hot_spare_screen.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * raid_screen - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_screen(i_container *i_con) { int rc; struct screen_output *s_out; int loop; free_raid_cmds(); for (loop = 0; loop < n_raid_screen.num_opts; loop++) { n_raid_screen.body = ipr_list_opts(n_raid_screen.body, n_raid_screen.options[loop].key, n_raid_screen.options[loop].list_str); } n_raid_screen.body = ipr_end_list(n_raid_screen.body); s_out = screen_driver(&n_raid_screen, 0, NULL); free(n_raid_screen.body); n_raid_screen.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * raid_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_status(i_container *i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_raid_status.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data) continue; num_lines += print_hotspare_disks(ioa, &i_con, buffer, 2); num_lines += print_vsets(ioa, &i_con, buffer, 2); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "%s\n", _(no_dev_found)); } } toggle_field = 0; do { n_raid_status.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_status.body = NULL; return rc; } /** * is_array_in_use - * @ioa: ipr ioa struct * @array_id: array ID * * Returns: * 1 if the array is in use / 0 otherwise * * TODO: This routine no longer works. It was originally designed to work * with the 2.4 kernels. For now, the routine will just return 0. * The rest of the code is being left in place in case this kind of * test becomes possible in the future. * JWB - August 11, 2009 **/ static int is_array_in_use(struct ipr_ioa *ioa, u8 array_id) { int opens; struct ipr_dev *vset; return 0; for_each_vset(ioa, vset) { if (array_id == vset->array_id) { if (!vset->scsi_dev_data) continue; opens = num_device_opens(vset->scsi_dev_data->host, vset->scsi_dev_data->channel, vset->scsi_dev_data->id, vset->scsi_dev_data->lun); if (opens != 0) return 1; } } return 0; } /** * raid_stop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_stop(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; int header_lines; int toggle = 1; struct screen_output *s_out; processing(); /* empty the linked list that contains field pointens */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_stop.header, &header_lines); for_each_ioa(ioa) { for_each_vset(ioa, dev) { if (!dev->array_rcd->stop_cand) continue; if ((rc = is_array_in_use(ioa, dev->array_id))) continue; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; } } if (!found) { /* Stop Device Parity Protection Failed */ n_raid_stop_fail.body = body_init(n_raid_stop_fail.header, NULL); s_out = screen_driver(&n_raid_stop_fail, 0, i_con); free(n_raid_stop_fail.body); n_raid_stop_fail.body = NULL; rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_raid_stop.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_stop, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_stop.body = NULL; return rc; } /** * confirm_raid_stop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_stop(i_container *i_con) { struct ipr_dev *dev; struct ipr_array_record *array_record; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc, k; int found = 0; char *input; char *buffer[2]; i_container *temp_i_con; struct screen_output *s_out; int toggle = 1; int header_lines; found = 0; body_init_status(buffer, n_confirm_raid_stop.header, &header_lines); for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)(temp_i_con->data); if (!cur_raid_cmd) continue; input = temp_i_con->field_data; array_record = cur_raid_cmd->dev->array_rcd; if (strcmp(input, "1") == 0) { found = 1; cur_raid_cmd->do_cmd = 1; if (!array_record->issue_cmd) array_record->issue_cmd = 1; } else { cur_raid_cmd->do_cmd = 0; if (array_record->issue_cmd) array_record->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; rc = RC_SUCCESS; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", 2+k); for_each_dev_in_vset(cur_raid_cmd->dev, dev) print_dev(k, dev, buffer, "1", 2+k); } toggle_field = 0; do { n_confirm_raid_stop.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_stop, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_stop.body = NULL; return rc; } /** * raid_stop_complete - * * Returns: * 21 | EXIT_FLAG if success / 20 | EXIT_FLAG on failure **/ static int raid_stop_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; while(1) { done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if (status_record->command_code == IPR_STOP_ARRAY_PROTECTION && cur_raid_cmd->array_id == status_record->array_id) { if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) not_done = 1; else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) /* "Stop Parity Protection failed" */ done_bad = 1; break; } } } if (!not_done) { if (done_bad) /* "Stop Parity Protection failed" */ return (20 | EXIT_FLAG); check_current_config(false); /* "Stop Parity Protection completed successfully" */ return (21 | EXIT_FLAG); } not_done = 0; sleep(1); } } /** * do_confirm_raid_stop - * @i_con: i_container struct * * Returns: * 21 | EXIT_FLAG if success / 20 | EXIT_FLAG on failure **/ int do_confirm_raid_stop(i_container *i_con) { struct ipr_dev *vset; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; int max_y, max_x; struct ipr_res_addr *ra; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; vset = cur_raid_cmd->dev; rc = is_array_in_use(ioa, cur_raid_cmd->array_id); if (rc != 0) { syslog(LOG_ERR, _("Array %s is currently in use and cannot be deleted.\n"), vset->dev_name); return (20 | EXIT_FLAG); } getmaxyx(stdscr, max_y, max_x); move(max_y-1, 0); printw(_("Operation in progress - please wait")); refresh(); if (vset->scsi_dev_data) { ipr_allow_restart(vset, 0); ipr_set_manage_start_stop(vset); ipr_start_stop_stop(vset); ipr_write_dev_attr(vset, "delete", "1"); } if (vset->alt_path && vset->alt_path->scsi_dev_data) { ipr_allow_restart(vset->alt_path, 0); ipr_set_manage_start_stop(vset->alt_path); ipr_start_stop_stop(vset->alt_path); ipr_write_dev_attr(vset->alt_path, "delete", "1"); } ioa->num_raid_cmds++; } sleep(2); for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; ioa->num_raid_cmds = 0; rc = ipr_stop_array_protection(ioa); if (rc != 0) { if (vset->scsi_dev_data) { ra = &vset->res_addr[0]; ipr_scan(vset->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(vset, 1); } if (vset->alt_path && vset->alt_path->scsi_dev_data) { ra = &vset->alt_path->res_addr[0]; ipr_scan(vset->alt_path->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(vset->alt_path, 1); } return (20 | EXIT_FLAG); } } rc = raid_stop_complete(); return rc; } /** * raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_start(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle = 0; u8 array_id; processing(); /* empty the linked list that contains field pointens */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_start.header, &header_lines); for_each_ioa(ioa) { if (!ioa->start_array_qac_entry) continue; if (ioa->sis64) array_id = ioa->start_array_qac_entry->type3.array_id; else array_id = ioa->start_array_qac_entry->type2.array_id; add_raid_cmd_tail(ioa, &ioa->ioa, array_id); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } if (!found) { /* Start Device Parity Protection Failed */ n_raid_start_fail.body = body_init(n_raid_start_fail.header, NULL); s_out = screen_driver(&n_raid_start_fail, 0, i_con); free(n_raid_start_fail.body); n_raid_start_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_start, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_start.body = NULL; return rc; } /** * raid_start_loop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_start_loop(i_container *i_con) { int rc; struct array_cmd_data *cur_raid_cmd; int found = 0; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)(temp_i_con->data); if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { rc = configure_raid_start(temp_i_con); if (RC_SUCCESS == rc) { found = 1; cur_raid_cmd->do_cmd = 1; } else return rc; } else cur_raid_cmd->do_cmd = 0; } if (found != 0) { /* Go to verification screen */ rc = confirm_raid_start(NULL); rc |= EXIT_FLAG; } else rc = INVALID_OPTION_STATUS; /* "Invalid options specified" */ return rc; } /** * configure_raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_start(i_container *i_con) { char *input; int found; struct ipr_dev *dev; struct array_cmd_data *cur_raid_cmd; int rc, k; char *buffer[2]; struct ipr_ioa *ioa; i_container *i_con2 = NULL; i_container *i_con_head_saved; i_container *temp_i_con = NULL; i_container *temp_i_con2; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; cur_raid_cmd = (struct array_cmd_data *)(i_con->data); ioa = cur_raid_cmd->ioa; body_init_status(buffer, n_configure_raid_start.header, &header_lines); i_con_head_saved = i_con_head; i_con_head = NULL; for_each_af_dasd(ioa, dev) { if (!dev->scsi_dev_data) continue; if (dev->dev_rcd->start_cand && device_supported(dev)) { print_dev(k, dev, buffer, "%1", k); i_con2 = add_i_con(i_con2, "\0", dev); } } do { toggle_field = 0; do { n_configure_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_configure_raid_start, header_lines, i_con2); temp_i_con2 = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc > 0) goto leave; i_con2 = temp_i_con2; found = 0; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; input = temp_i_con->field_data; if (dev && strcmp(input, "1") == 0) { found = 1; dev->dev_rcd->issue_cmd = 1; } } if (found != 0) { /* Go to parameters screen */ rc = configure_raid_parameters(i_con); if ((rc & EXIT_FLAG) || !(rc & CANCEL_FLAG)) goto leave; /* User selected Cancel, clear out current selections for redisplay */ for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; input = temp_i_con->field_data; if (dev && (strcmp(input, "1") == 0)) dev->dev_rcd->issue_cmd = 0; } rc = REFRESH_FLAG; continue; } else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } } while (rc & REFRESH_FLAG); leave: i_con2 = free_i_con(i_con2); i_con_head = i_con_head_saved; for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_configure_raid_start.body = NULL; return rc; } /** * display_input - * @field_start_row: * @userprt: * * Returns: * 0 if success / non-zero on failure **/ int display_input(int field_start_row, int *userptr) { FIELD *input_fields[3]; FORM *form; WINDOW *box1_win, *field_win; int field_rows = 2; int field_cols = 16; char *input; int ch; int rc = RC_SUCCESS; input_fields[0] = new_field(1, 16, 0, 0, 0, 0); input_fields[1] = new_field(1, 3, 1, 0, 0, 0); input_fields[2] = NULL; set_field_buffer(input_fields[0],0, "Enter new value"); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[0], O_EDIT); box1_win = newwin(field_rows + 2, field_cols + 2, field_start_row - 1, 54); field_win = newwin(field_rows, field_cols, field_start_row, 55); box(box1_win,ACS_VLINE,ACS_HLINE); form = new_form(input_fields); set_current_field(form, input_fields[1]); set_form_win(form,field_win); set_form_sub(form,field_win); post_form(form); form_driver(form, REQ_FIRST_FIELD); wnoutrefresh(box1_win); while (1) { wnoutrefresh(field_win); doupdate(); ch = getch(); if (IS_ENTER_KEY(ch)) { form_driver(form,REQ_VALIDATION); input = field_buffer(input_fields[1],0); sscanf(input, "%d",userptr); break; } else if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } else if (isascii(ch)) form_driver(form, ch); } wclear(field_win); wrefresh(field_win); delwin(field_win); wclear(box1_win); wrefresh(box1_win); delwin(box1_win); return rc; } /** * configure_raid_parameters - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_parameters(i_container *i_con) { FIELD *input_fields[5]; FIELD *cur_field; FORM *form; ITEM **raid_item = NULL; struct array_cmd_data *cur_raid_cmd = (struct array_cmd_data *)(i_con->data); int raid_index_default; unsigned long raid_index = 0; unsigned long index; char buffer[24]; int rc, i; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap_entry; struct text_str { char line[16]; } *raid_menu_str = NULL, *stripe_menu_str = NULL; char raid_str[16], stripe_str[16], qdepth_str[4]; int ch, start_row; int cur_field_index; int selected_count = 0; int stripe_sz, stripe_sz_mask, stripe_sz_list[16]; struct prot_level *prot_level_list; int *userptr = NULL; int *retptr; int max_x,max_y,start_y,start_x; int qdepth, new_qdepth; getmaxyx(stdscr,max_y,max_x); getbegyx(stdscr,start_y,start_x); rc = RC_SUCCESS; ioa = cur_raid_cmd->ioa; supported_arrays = ioa->supported_arrays; cap_entry = supported_arrays->entry; /* determine number of devices selected for this parity set */ for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) selected_count++; } if (ioa->sis64) qdepth = selected_count * 16; else qdepth = selected_count * 4; cur_raid_cmd->qdepth = qdepth; /* set up raid lists */ raid_index = 0; raid_index_default = -1; prot_level_list = malloc(sizeof(struct prot_level)); prot_level_list[raid_index].is_valid = 0; for_each_cap_entry(cap_entry, supported_arrays) { if (selected_count <= cap_entry->max_num_array_devices && selected_count >= cap_entry->min_num_array_devices && ((selected_count % cap_entry->min_mult_array_devices) == 0)) { prot_level_list[raid_index].array_cap_entry = cap_entry; prot_level_list[raid_index].is_valid = 1; if (raid_index_default == -1 && !strcmp((char *)cap_entry->prot_level_str, "10")) raid_index_default = raid_index; else if (!strcmp((char *)cap_entry->prot_level_str, IPR_DEFAULT_RAID_LVL)) raid_index_default = raid_index; raid_index++; prot_level_list = realloc(prot_level_list, sizeof(struct prot_level) * (raid_index + 1)); prot_level_list[raid_index].is_valid = 0; } } if (!raid_index) return 69 | EXIT_FLAG; if (raid_index_default == -1) raid_index_default = 0; /* Title */ input_fields[0] = new_field(1, max_x - start_x, /* new field size */ 0, 0, /* upper left corner */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Protection Level */ input_fields[1] = new_field(1, 9, /* new field size */ 8, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Stripe Size */ input_fields[2] = new_field(1, 9, /* new field size */ 9, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Queue depth */ input_fields[3] = new_field(1, 3, /* new field size */ 10, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ input_fields[4] = NULL; raid_index = raid_index_default; cap_entry = prot_level_list[raid_index].array_cap_entry; stripe_sz = ntohs(cap_entry->recommended_stripe_size); form = new_form(input_fields); set_current_field(form, input_fields[1]); set_field_just(input_fields[0], JUSTIFY_CENTER); set_field_just(input_fields[3], JUSTIFY_LEFT); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[1], O_EDIT); field_opts_off(input_fields[2], O_EDIT); field_opts_off(input_fields[3], O_EDIT); set_field_buffer(input_fields[0], /* field to alter */ 0, /* number of buffer to alter */ _("Select Protection Level and Stripe Size")); /* string value to set */ form_opts_off(form, O_BS_OVERLOAD); flush_stdscr(); clear(); set_form_win(form,stdscr); set_form_sub(form,stdscr); post_form(form); mvaddstr(2, 1, _("Default array configurations are shown. To change")); mvaddstr(3, 1, _("setting hit \"c\" for options menu. Highlight desired")); mvaddstr(4, 1, _("option then hit Enter")); mvaddstr(6, 1, _("c=Change Setting")); mvaddstr(8, 0, "Protection Level . . . . . . . . . . . . :"); mvaddstr(9, 0, "Stripe Size . . . . . . . . . . . . . . :"); mvprintw(10, 0, "Queue Depth (default = %3d). . . . . . . :", qdepth); mvaddstr(max_y - 4, 0, _("Press Enter to Continue")); mvaddstr(max_y - 2, 0, EXIT_KEY_LABEL CANCEL_KEY_LABEL); form_driver(form, /* form to pass input to */ REQ_FIRST_FIELD ); /* form request code */ while (1) { sprintf(raid_str,"RAID %s",cap_entry->prot_level_str); set_field_buffer(input_fields[1], /* field to alter */ 0, /* number of buffer to alter */ raid_str); /* string value to set */ if (stripe_sz > 1024) sprintf(stripe_str,"%d M",stripe_sz/1024); else sprintf(stripe_str,"%d k",stripe_sz); set_field_buffer(input_fields[2], /* field to alter */ 0, /* number of buffer to alter */ stripe_str); /* string value to set */ sprintf(qdepth_str,"%3d",qdepth); set_field_buffer(input_fields[3], /* field to alter */ 0, /* number of buffer to alter */ qdepth_str); /* string value to set */ refresh(); ch = getch(); if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; goto leave; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; goto leave; } else if (ch == 'c') { cur_field = current_field(form); cur_field_index = field_index(cur_field); if (cur_field_index == 1) { /* count the number of valid entries */ index = 0; while (prot_level_list[index].is_valid) index++; /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointen to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); raid_menu_str = realloc(raid_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up protection level menu */ index = 0; while (prot_level_list[index].is_valid) { raid_item[index] = (ITEM *)NULL; sprintf(raid_menu_str[index].line,"RAID %s",prot_level_list[index].array_cap_entry->prot_level_str); raid_item[index] = new_item(raid_menu_str[index].line,""); userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } raid_item[index] = (ITEM *)NULL; start_row = 8; rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) { raid_index = *retptr; /* find recommended stripe size */ cap_entry = prot_level_list[raid_index].array_cap_entry; stripe_sz = ntohs(cap_entry->recommended_stripe_size); } } else if (cur_field_index == 2) { /* count the number of valid entries */ index = 0; for (i = 0; i < 16; i++) { stripe_sz_mask = 1 << i; if ((stripe_sz_mask & ntohs(cap_entry->supported_stripe_sizes)) == stripe_sz_mask) { index++; } } /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointen to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); stripe_menu_str = realloc(stripe_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up stripe size menu */ index = 0; for (i = 0; i < 16; i++) { stripe_sz_mask = 1 << i; raid_item[index] = (ITEM *)NULL; if ((stripe_sz_mask & ntohs(cap_entry->supported_stripe_sizes)) == stripe_sz_mask) { stripe_sz_list[index] = stripe_sz_mask; if (stripe_sz_mask > 1024) sprintf(stripe_menu_str[index].line,"%d M",stripe_sz_mask/1024); else sprintf(stripe_menu_str[index].line,"%d k",stripe_sz_mask); if (stripe_sz_mask == ntohs(cap_entry->recommended_stripe_size)) { sprintf(buffer,_("%s - recommend"),stripe_menu_str[index].line); raid_item[index] = new_item(buffer, ""); } else { raid_item[index] = new_item(stripe_menu_str[index].line, ""); } userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } } raid_item[index] = (ITEM *)NULL; start_row = 9; rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) stripe_sz = stripe_sz_list[*retptr]; } else if (cur_field_index == 3) { start_row = 10; rc = display_input(start_row, &new_qdepth); if (rc == EXIT_FLAG) goto leave; if (rc == CANCEL_FLAG) continue; if (new_qdepth > 128) qdepth = 128; else qdepth = new_qdepth; continue; } else continue; i = 0; while (raid_item[i] != NULL) free_item(raid_item[i++]); free(raid_item); raid_item = NULL; if (rc == EXIT_FLAG) goto leave; } else if (IS_ENTER_KEY(ch)) { /* set protection level and stripe size appropriately */ cur_raid_cmd->prot_level = cap_entry->prot_level; cur_raid_cmd->stripe_size = stripe_sz; cur_raid_cmd->qdepth = qdepth; goto leave; } else if (ch == KEY_RIGHT) form_driver(form, REQ_NEXT_CHAR); else if (ch == KEY_LEFT) form_driver(form, REQ_PREV_CHAR); else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if (ch == KEY_UP) form_driver(form, REQ_PREV_FIELD); else if (ch == KEY_DOWN) form_driver(form, REQ_NEXT_FIELD); else if (isascii(ch)) form_driver(form, ch); } leave: unpost_form(form); free_form(form); free_screen(NULL, NULL, input_fields); flush_stdscr(); return rc; } /** * confirm_raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_start(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc, k; char *buffer[2]; struct ipr_dev *dev; int header_lines; int toggle = 0; struct screen_output *s_out; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_raid_start.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd) { ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) print_dev(k, dev, buffer, "1", k); } } } toggle_field = 0; do { n_confirm_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_start, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_start.body = NULL; if (rc > 0) return rc; /* verify the devices are not in use */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; rc = is_array_in_use(ioa, cur_raid_cmd->array_id); if (rc != 0) { /* "Start Parity Protection failed." */ rc = RC_19_Create_Fail; syslog(LOG_ERR, _("Devices may have changed state. Command cancelled," " please issue commands again if RAID still desired %s.\n"), ioa->ioa.gen_name); return rc; } } /* now issue the start array command */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; rc = ipr_start_array_protection(ioa, cur_raid_cmd->stripe_size, cur_raid_cmd->prot_level); if (rc) return RC_19_Create_Fail; } rc = raid_start_complete(); return rc; } /** * raid_start_complete - * * Returns: * 0 if success / non-zero on failure **/ int raid_start_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int retry = 1; int timeout; int rc; u32 percent_cmplt = 0; int device_available = 0; int wait_device = 0; int exit_now = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_dev *dev; struct ipr_disk_attr attr; while (1) { rc = complete_screen_driver(&n_raid_start_complete, percent_cmplt, 1); if ((rc & EXIT_FLAG) || exit_now) { exit_now = 1; if (device_available) return EXIT_FLAG; } percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_START_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (!device_available && (status_record->resource_handle != IPR_IOA_RESOURCE_HANDLE)) { timeout = 30; while (retry && timeout) { check_current_config(false); dev = get_dev_from_handle(ioa, status_record->resource_handle); if (dev && dev->scsi_dev_data) { device_available = 1; if (ipr_init_new_dev(dev)) { sleep(1); timeout--; continue; } else retry = 0; if (ipr_get_dev_attr(dev, &attr)) { syslog(LOG_ERR, _("Unable to read queue_depth")); } else { attr.queue_depth = cur_raid_cmd->qdepth; if (ipr_set_dev_attr(dev, &attr, 1)) syslog(LOG_ERR, _("Unable to set queue_depth")); } } else break; } } if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; if (status_record->status == IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS) { syslog(LOG_ERR, _("Start parity protect to %s failed. " "SSDs and HDDs can not be mixed in an array.\n"), ioa->ioa.gen_name); rc = RC_22_Mixed_Block_Dev_Classes; } else if (status_record->status == IPR_CMD_STATUS_MIXED_LOG_BLK_SIZE) { syslog(LOG_ERR, _("Start parity protect to %s failed. " "non 4K disks and 4K disks can not be mixed in an array.\n"), ioa->ioa.gen_name); rc = RC_91_Mixed_Logical_Blk_Size; } else if (status_record->status == IPR_CMD_STATUS_UNSUPT_REQ_BLK_DEV_CLASS) { syslog(LOG_ERR, _("Start parity protect to %s failed. " "These device contained a conmination of block device class filed that was not supported in an array.\n"), ioa->ioa.gen_name); rc = RC_92_UNSUPT_REQ_BLK_DEV_CLASS; } else { syslog(LOG_ERR, _("Start parity protect to %s failed. " "Check device configuration for proper format.\n"), ioa->ioa.gen_name); rc = RC_FAILED; } } else if (!(device_available)) { wait_device++; /* if device did not show up after parity set created, allow exit */ if (wait_device == 20) syslog(LOG_ERR, _("Unable to set queue_depth")); else not_done = 1; } break; } } if (!not_done) { flush_stdscr(); if (done_bad) { if (status_record->status == IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS) return RC_22_Mixed_Block_Dev_Classes; if (status_record->status == IPR_CMD_STATUS_MIXED_LOG_BLK_SIZE) return RC_91_Mixed_Logical_Blk_Size; if (status_record->status == IPR_CMD_STATUS_UNSUPT_REQ_BLK_DEV_CLASS) return RC_92_UNSUPT_REQ_BLK_DEV_CLASS; /* Start Parity Protection failed. */ return RC_19_Create_Fail; } format_done = 1; check_current_config(false); /* Start Parity Protection completed successfully */ return RC_18_Array_Created; } not_done = 0; sleep(1); } } /** * raid_rebuild - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_rebuild(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *dev; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle=1; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(true); body_init_status(buffer, n_raid_rebuild.header, &header_lines); for_each_ioa(ioa) { ioa->num_raid_cmds = 0; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->rebuild_cand) continue; add_raid_cmd_tail(ioa, dev, 0); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, dev, buffer, "%1", k); enable_af(dev); found++; ioa->num_raid_cmds++; } } if (!found) { /* Start Device Parity Protection Failed */ n_raid_rebuild_fail.body = body_init(n_raid_rebuild_fail.header, NULL); s_out = screen_driver(&n_raid_rebuild_fail, 0, NULL); free(n_raid_rebuild_fail.body); n_raid_rebuild_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_rebuild.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_rebuild, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_rebuild.body = NULL; return rc; } /** * confirm_raid_rebuild - * @i_con: i_container struct * * Returns: * **/ int confirm_raid_rebuild(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; char *buffer[2]; int found = 0; char *input; int k; int header_lines; int toggle=1; i_container *temp_i_con; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { cur_raid_cmd->do_cmd = 1; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 1; found++; } else { cur_raid_cmd->do_cmd = 0; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; body_init_status(buffer, n_confirm_raid_rebuild.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); } do { n_confirm_raid_rebuild.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_rebuild, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_rebuild.body = NULL; if (rc) return rc; for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; if ((rc = ipr_rebuild_device_data(ioa))) /* Rebuild failed */ return (29 | EXIT_FLAG); } /* Rebuild started, view Parity Status Window for rebuild progress */ return (28 | EXIT_FLAG); } /** * raid_resync_complete - * * Returns: * EXIT_FLAG / 74 | EXIT_FLAG on success / 73 | EXIT_FLAG otherwise **/ static int raid_resync_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; u32 percent_cmplt = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; while (1) { rc = complete_screen_driver(&n_raid_resync_complete, percent_cmplt, 1); if (rc & EXIT_FLAG) return EXIT_FLAG; percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_RESYNC_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; syslog(LOG_ERR, _("Resync array to %s failed.\n"), ioa->ioa.gen_name); rc = RC_FAILED; } break; } } if (!not_done) { flush_stdscr(); if (done_bad) /* Resync array failed. */ return (73 | EXIT_FLAG); /* Resync completed successfully */ return (74 | EXIT_FLAG); } not_done = 0; sleep(1); } } /** * raid_resync - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_resync(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *dev, *array; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle=1; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(true); body_init_status(buffer, n_raid_resync.header, &header_lines); for_each_ioa(ioa) { ioa->num_raid_cmds = 0; for_each_array(ioa, array) { if (!array->array_rcd->resync_cand) continue; dev = array; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; ioa->num_raid_cmds++; } } if (!found) { /* Start Device Parity Protection Failed */ n_raid_resync_fail.body = body_init(n_raid_resync_fail.header, NULL); s_out = screen_driver(&n_raid_resync_fail, 0, NULL); free(n_raid_resync_fail.body); n_raid_resync_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_resync.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_resync, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_resync.body = NULL; return rc; } /** * confirm_raid_resync - * @i_con: i_container struct * * Returns: * EXIT_FLAG / 74 | EXIT_FLAG on success / 73 | EXIT_FLAG otherwise * INVALID_OPTION_STATUS if nothing to process **/ int confirm_raid_resync(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; char *buffer[2]; int found = 0; char *input; int k; int header_lines; int toggle=1; i_container *temp_i_con; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { cur_raid_cmd->do_cmd = 1; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 1; found++; } else { cur_raid_cmd->do_cmd = 0; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; body_init_status(buffer, n_confirm_raid_resync.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); } do { n_confirm_raid_resync.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_resync, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_resync.body = NULL; if (rc) return rc; for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; if ((rc = ipr_resync_array(ioa))) return (73 | EXIT_FLAG); } return raid_resync_complete(); } /** * do_raid_migrate - Process the command line Migrate Array command. Check the * arguments against the qac data to make sure that the * migration is possible. Then issue the migrate command. * @ioa: struct ipr_ioa * @qac_data: query array config data * @vset: struct ipr_dev * @raid_level: new raid level * @stripe_size: new stripe size * @num_devs: number of devices to add * @fd: file descriptor * * Returns: * 0 if success / non-zero on failure **/ int do_raid_migrate(struct ipr_ioa *ioa, struct ipr_array_query_data *qac_data, struct ipr_dev *vset, char *raid_level, int stripe_size, int num_devs, int fd) { int found = 0, rc = 0; int enc_raid_level; struct ipr_dev *dev = NULL; struct sysfs_dev *sdev; struct ipr_array_cap_entry *cap; struct ipr_array_record *array_rcd; struct ipr_dev_record *dev_rcd; struct ipr_supported_arrays *supported_arrays = NULL; /* check that the given raid level is valid */ for_each_supported_arrays_rcd(supported_arrays, qac_data) { cap = get_cap_entry(supported_arrays, raid_level); if (cap) break; } if (!cap) { fprintf(stderr, "RAID level %s is unsupported.\n", raid_level); return -EINVAL; } enc_raid_level = cap->prot_level; if (ioa->sis64) vset = get_array_from_vset(ioa, vset); /* loop through returned records - looking at array records */ for_each_array_rcd(array_rcd, qac_data) { if (vset->resource_handle == ipr_get_arr_res_handle(ioa, array_rcd)) { if (array_rcd->migrate_cand) { array_rcd->issue_cmd = 1; found = 1; } break; } } /* device is valid, but array migration is not supported */ if (!found) { fprintf(stderr, "Migrate array protection is not currently "); fprintf(stderr, "possible on the %s device.\n", vset->gen_name); return -EINVAL; } /* process stripe size */ if (stripe_size) { /* is stripe size supported? */ if ((stripe_size & ntohs(cap->supported_stripe_sizes)) != stripe_size) { fprintf(stderr, "Unsupported stripe size for "); fprintf(stderr, "selected adapter. %d\n", stripe_size); return -EINVAL; } } else /* get current stripe size */ if (ioa->sis64) stripe_size = array_rcd->type3.stripe_size; else stripe_size = array_rcd->type2.stripe_size; /* if adding devices, check that there are a valid number of them */ if (cap->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { rc = raid_create_check_num_devs(cap, num_devs, 0); if (rc != 0) { fprintf(stderr,"Incorrect number of devices given.\n"); fprintf(stderr,"Minimum number of devices = %d\n", cap->min_num_array_devices); fprintf(stderr,"Maximum number of devices = %d\n", cap->max_num_array_devices); return rc; } } /* if adding devs, verify them and set issue_cmd = 1 */ for_each_dev_rcd(dev_rcd, qac_data) { if (num_devs == 0) break; /* check the given devices for migrate candidate status */ for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (ipr_get_dev_res_handle(ioa, dev_rcd) == dev->resource_handle) { if (dev_rcd->migrate_array_prot_cand) dev_rcd->issue_cmd = 1; else { fprintf(stderr, "%s is not available " "for migrate array " "protection\n", dev->gen_name); return -EINVAL; } } } } /* send command to adapter */ rc = ipr_migrate_array_protection(ioa, qac_data, fd, stripe_size, enc_raid_level); return rc; } /** * raid_migrate_cmd - Command line handler for Migrate Array command. Process * the command arguments and prepare for do_raid_migrate(). * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_migrate_cmd(char **args, int num_args) { int i, fd, rc = 0; int next_raid_level = 0, raid_level_set = 0; int next_stripe_size = 0; int stripe_size = 0; int num_devs = 0; char *raid_level = IPR_DEFAULT_RAID_LVL; struct ipr_dev *dev = NULL, *vset = NULL; struct ipr_ioa *ioa = NULL; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); for (i = 0; i < num_args; i++) { if (strcmp(args[i], "-r") == 0) next_raid_level = 1; else if (strcmp(args[i], "-s") == 0) next_stripe_size = 1; else if (next_raid_level) { next_raid_level = 0; raid_level_set = 1; raid_level = args[i]; } else if (next_stripe_size) { next_stripe_size = 0; stripe_size = strtoul(args[i], NULL, 10); } else { dev = find_dev(args[i]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[i]); return -EINVAL; } if (ipr_is_volume_set(dev)) { if (vset) { fprintf(stderr, "Invalid parameters. " "Only one disk " "array can be specified.\n"); return -EINVAL; } vset = dev; ioa = vset->ioa; continue; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "Invalid device specified. " "Device must be formatted " "to Advanced Function Format.\n"); return -EINVAL; } else { /* keep track of these */ num_devs++; ipr_add_sysfs_dev(dev, &head_sdev, &tail_sdev); } } } if (vset == NULL) { fprintf(stderr, "No RAID device specified\n"); return -EINVAL; } if (!raid_level_set) { fprintf(stderr, "No RAID level specified\n"); return -EINVAL; } /* lock the ioa device */ fd = open_and_lock(ioa->ioa.gen_name); if (fd <= 1) return -EIO; /* query array configuration for volume set migrate status */ rc = __ipr_query_array_config(ioa, fd, 0, 0, 1, vset->array_id, &qac_data); if (rc != 0) { fprintf(stderr, "Array query failed. rc = %d\n", rc); close(fd); return -EINVAL; } /* Check arguments against the qac data and issue the migrate command */ rc = do_raid_migrate(ioa, &qac_data, vset, raid_level, stripe_size, num_devs, fd); close(fd); return rc; } /** * raid_migrate_complete - Display a status screen showing the command * completion percentage. * * Returns: * 0 if success / non-zero on failure **/ int raid_migrate_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; u32 percent_cmplt = 0; int device_available = 0; int exit_now = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_dev *dev; ENTER; while (1) { rc = complete_screen_driver(&n_raid_migrate_complete, percent_cmplt, 1); if (rc & EXIT_FLAG || exit_now) { exit_now = 1; if (device_available) return EXIT_FLAG; } percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_MIGRATE_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (!device_available && (status_record->resource_handle != IPR_IOA_RESOURCE_HANDLE)) { check_current_config(false); dev = get_dev_from_handle(ioa, status_record->resource_handle); if (dev && dev->scsi_dev_data) device_available = 1; } if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; ioa_err(ioa, "Migrate array protection failed.\n"); rc = RC_FAILED; } else if (!device_available) not_done = 1; break; } } if (!not_done) { flush_stdscr(); if (done_bad) /* Migrate Array Protection failed. */ return RC_80_Migrate_Prot_Fail; check_current_config(false); /* Migrate Array Protection completed successfully */ LEAVE; return RC_79_Migrate_Prot_Success; } not_done = 0; sleep(1); } } /** * confirm_raid_migrate - Confirm that the migration choices are correct. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_migrate() { struct array_cmd_data *cur_raid_cmd; int rc, k; char *buffer[2]; struct screen_output *s_out; int toggle = 1; int header_lines; ENTER; body_init_status(buffer, n_confirm_raid_migrate.header, &header_lines); /* Display the array being migrated. Also display any disks if they are required for the migration. */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; print_dev(k, cur_raid_cmd->dev, buffer, "1", 2+k); } rc = RC_SUCCESS; toggle_field = 0; do { n_confirm_raid_migrate.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_migrate, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_migrate.body = NULL; LEAVE; return rc; } /** * choose_migrate_disks - Choose the additional disks required for migration. * @ioa: * @qac: i_query_array_data struct * @cap_entry: * * Returns: * 0 if success / non-zero on failure **/ int choose_migrate_disks(struct ipr_ioa *ioa, struct ipr_array_query_data *qac, struct ipr_array_cap_entry *cap_entry) { struct array_cmd_data *tmp_raid_cmd; struct ipr_dev_record *dev_rcd; struct ipr_dev *dev; i_container *dev_i_con, *temp_i_con; int rc, min, max, mult; int k = 0; char *buffer[2]; char info[256]; int header_lines = 0; struct screen_output *s_out; int toggle = 0; int found = 0; char *input; ENTER; min = cap_entry->min_num_array_devices; max = cap_entry->max_num_array_devices; mult = cap_entry->min_mult_array_devices; sprintf(info, _("A minimum of %d disks must be selected.\n"), min); buffer[0] = add_string_to_body(NULL, info, "", &header_lines); buffer[1] = add_string_to_body(NULL, info, "", &header_lines); sprintf(info, _("A maximum of %d disks must be selected.\n"), max); buffer[0] = add_string_to_body(buffer[0], info, "", &header_lines); buffer[1] = add_string_to_body(buffer[1], info, "", &header_lines); sprintf(info, _("The number of disks selected must be a multiple of %d.\n\n"), mult); buffer[0] = add_string_to_body(buffer[0], info, "", &header_lines); buffer[1] = add_string_to_body(buffer[1], info, "", &header_lines); buffer[0] = status_header(buffer[0], &header_lines, 0); buffer[1] = status_header(buffer[1], &header_lines, 1); /* Ensure i_con_head gets set to the head of the disk candidate list in add_i_con() */ dev_i_con = NULL; for_each_dev_rcd(dev_rcd, qac) { if (!dev_rcd->migrate_array_prot_cand) continue; dev = get_dev_from_handle(ioa, ipr_get_dev_res_handle(ioa, dev_rcd)); if (dev) { print_dev(k, dev, buffer, "%1", 2); add_raid_cmd_tail(NULL, dev, 0); /* Add disk candidates to the i_con list. */ dev_i_con = add_i_con(dev_i_con, "\0", raid_cmd_tail); } } do { toggle_field = 0; do { n_raid_migrate_add_disks.body = buffer[toggle&1]; /* select disks to use */ s_out = screen_driver(&n_raid_migrate_add_disks, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc) break; found = 0; /* Loop through, count the selected devices and * set the do_cmd flag. */ for_each_icon(temp_i_con) { tmp_raid_cmd = temp_i_con->data; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; tmp_raid_cmd->do_cmd = 1; } } if (found) { rc = raid_create_check_num_devs(cap_entry, found, 1); if (rc == 0) break; if (rc == RC_77_Too_Many_Disks) { s_status.num = cap_entry->max_num_array_devices; s_status.index = RC_77_Too_Many_Disks; } if (rc == RC_78_Too_Few_Disks) { s_status.num = cap_entry->min_num_array_devices; s_status.index = RC_78_Too_Few_Disks; } if (rc == RC_25_Devices_Multiple) { s_status.num = cap_entry->min_mult_array_devices; s_status.index = RC_25_Devices_Multiple; } /* clear the do_cmd flag */ for_each_icon(temp_i_con) { tmp_raid_cmd = (struct array_cmd_data *)temp_i_con->data; input = temp_i_con->field_data; if (tmp_raid_cmd && strcmp(input, "1") == 0) tmp_raid_cmd->do_cmd = 0; } } rc = REFRESH_FLAG; } while (rc & REFRESH_FLAG); LEAVE; return rc; } /** * do_ipr_migrate_array_protection - Migrate array protection for an array * @array_i_con: i_container for selected array * @ioa: ipr ioa struct * @qac_data: struct ipr_array_query_data * @stripe_size: new or existing stripe size for array * @prot_level: new protection (RAID) level for array * @array_id: array id * * Returns: * 0 if success / non-zero on failure **/ int do_ipr_migrate_array_protection(i_container *array_i_con, struct ipr_ioa *ioa, struct ipr_array_query_data *qac, int stripe_size, int prot_level, u8 array_id) { int fd, rc; i_container *temp_i_con; struct array_cmd_data *acd; struct ipr_array_record *array_rcd; struct ipr_dev_record *dev_rcd; struct ipr_array_query_data new_qac; char *input; ENTER; /* Now that we have the selected array, lock the ioa * device, then issue another query to make sure that * the adapter's version of the QAC data matches what * we are processing. */ fd = open_and_lock(ioa->ioa.gen_name); if (fd <= 1) return fd; rc = __ipr_query_array_config(ioa, fd, 0, 0, 1, array_id, &new_qac); if (rc) { close(fd); return rc; } /* Set the "do it" bit for the select disks if needed. */ for_each_icon(temp_i_con) { acd = temp_i_con->data; input = temp_i_con->field_data; if (acd && strcmp(input, "1") == 0) for_each_dev_rcd(dev_rcd, &new_qac) if (acd->dev->resource_handle == ipr_get_dev_res_handle(ioa, dev_rcd)) dev_rcd->issue_cmd = 1; } /* get the array command data and associated data from the data field of the passed in array info container. */ acd = array_i_con->data; /* Set the "do it" bit for the selected array. */ for_each_array_rcd(array_rcd, &new_qac) if (acd->dev->resource_handle == ipr_get_arr_res_handle(ioa, array_rcd)) array_rcd->issue_cmd = 1; rc = ipr_migrate_array_protection(ioa, &new_qac, fd, stripe_size, prot_level); close(fd); LEAVE; return rc; } /** * configure_raid_migrate - The array to migrate has been selected, now get the * new RAID level. * @array_i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_migrate(i_container *array_i_con) { FIELD *input_fields[3]; FIELD *cur_field; FORM *form; ITEM **raid_item = NULL; struct array_cmd_data *cur_raid_cmd; int raid_index_default = 0; unsigned long raid_index = 0; unsigned long index; int rc, i; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_query_data qac; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap_entry; struct text_str { char line[16]; } *raid_menu_str = NULL; char raid_str[16]; char dev_str[60]; int ch, start_row; int cur_field_index; struct prot_level *prot_level_list; int *userptr = NULL; int *retptr; int max_x,max_y,start_y,start_x; i_container *saved_i_con_head; ENTER; getmaxyx(stdscr,max_y,max_x); getbegyx(stdscr,start_y,start_x); /* get the array command data and associated data from the data field of the passed in array info container. */ cur_raid_cmd = array_i_con->data; ioa = cur_raid_cmd->ioa; dev = cur_raid_cmd->dev; /* Get the QAC data for the chosen array. */ rc = ipr_query_array_config(ioa, 0, 0, 1, cur_raid_cmd->array_id, &qac); if (rc) return RC_80_Migrate_Prot_Fail | EXIT_FLAG; /* allows cancelling out if the user doesn't change the existing protection level. */ rc = CANCEL_FLAG; /* Save the current i_con_head, as it points to the list of array candidates. We need to restore this before leaving this routine. */ saved_i_con_head = i_con_head; i_con_head = NULL; /* set up raid lists */ prot_level_list = malloc(sizeof(struct prot_level)); prot_level_list[raid_index].is_valid = 0; for_each_supported_arrays_rcd(supported_arrays, &qac) { for_each_cap_entry(cap_entry, supported_arrays) { prot_level_list[raid_index].array_cap_entry = cap_entry; prot_level_list[raid_index].is_valid = 1; if (!strcmp((char *)cap_entry->prot_level_str, dev->prot_level_str)) raid_index_default = raid_index; raid_index++; prot_level_list = realloc(prot_level_list, sizeof(struct prot_level) * (raid_index + 1)); prot_level_list[raid_index].is_valid = 0; } } if (!raid_index) { /* We should never get here. */ free(prot_level_list); scsi_err(dev, "The query data for the array indicate " "that there are no supported RAID levels.\n"); return RC_80_Migrate_Prot_Fail | EXIT_FLAG; } /* Title */ input_fields[0] = new_field(1, max_x - start_x, /* new field size */ 0, 0, /* upper left corner */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Protection Level */ input_fields[1] = new_field(1, 9, /* new field size */ 8, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ input_fields[2] = NULL; raid_index = raid_index_default; cap_entry = prot_level_list[raid_index].array_cap_entry; form = new_form(input_fields); set_current_field(form, input_fields[1]); set_field_just(input_fields[0], JUSTIFY_CENTER); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[1], O_EDIT); set_field_buffer(input_fields[0], /* field to alter */ 0, /* number of buffer to alter */ _("Select Protection Level")); /* string value to set */ form_opts_off(form, O_BS_OVERLOAD); flush_stdscr(); clear(); set_form_win(form,stdscr); set_form_sub(form,stdscr); post_form(form); mvaddstr(2, 1, _("Current RAID protection level is shown. To change")); mvaddstr(3, 1, _("setting hit \"c\" for options menu. Highlight desired")); mvaddstr(4, 1, _("option then hit Enter")); mvaddstr(6, 1, _("c=Change Setting")); sprintf(dev_str, "%s - Protection Level . . . . . . :", dev->dev_name); mvaddstr(8, 0, dev_str); mvaddstr(max_y - 4, 0, _("Press Enter to Continue")); mvaddstr(max_y - 2, 0, EXIT_KEY_LABEL CANCEL_KEY_LABEL); form_driver(form, /* form to pass input to */ REQ_FIRST_FIELD ); /* form request code */ sprintf(raid_str, "RAID %s", dev->prot_level_str); while (1) { set_field_buffer(input_fields[1], /* field to alter */ 0, /* number of buffer to alter */ raid_str); /* string value to set */ refresh(); ch = getch(); if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } else if (ch == 'c') { cur_field = current_field(form); cur_field_index = field_index(cur_field); if (cur_field_index == 1) { /* count the number of valid entries */ index = 0; while (prot_level_list[index].is_valid) index++; /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointen to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); raid_menu_str = realloc(raid_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up protection level menu */ index = 0; while (prot_level_list[index].is_valid) { raid_item[index] = NULL; cap_entry = prot_level_list[index].array_cap_entry; sprintf(raid_menu_str[index].line, "RAID %s", cap_entry->prot_level_str); raid_item[index] = new_item(raid_menu_str[index].line,""); userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } raid_item[index] = NULL; start_row = 8; /* select the desired new protection level */ rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) { raid_index = *retptr; cap_entry = prot_level_list[raid_index].array_cap_entry; sprintf(raid_str, "RAID %s", cap_entry->prot_level_str); } /* clean up */ for (i=0; raid_item[i] != NULL; i++) free_item(raid_item[i]); free(raid_item); raid_item = NULL; } else continue; } else if (IS_ENTER_KEY(ch)) { /* don't change anything if Cancel or Exit */ if (rc != RC_SUCCESS) break; /* set protection level */ cur_raid_cmd->prot_level = cap_entry->prot_level; /* are additional devices needed? */ if (cap_entry->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { rc = choose_migrate_disks(ioa, &qac, cap_entry); if (rc) break; } else if (cap_entry->format_overlay_type != IPR_FORMAT_REMOVE_DEVICES) { /* We should never get here. */ scsi_err(dev, "The capability entry for the array " "does not have a valid overlay type.\n"); return RC_80_Migrate_Prot_Fail | EXIT_FLAG; } rc = confirm_raid_migrate(); if (rc) break; /* set up qac and send command to adapter */ rc = do_ipr_migrate_array_protection(array_i_con, ioa, &qac, ntohs(dev->stripe_size), cur_raid_cmd->prot_level, cur_raid_cmd->array_id); break; } else if (ch == KEY_RIGHT) form_driver(form, REQ_NEXT_CHAR); else if (ch == KEY_LEFT) form_driver(form, REQ_PREV_CHAR); else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if (ch == KEY_UP) form_driver(form, REQ_PREV_FIELD); else if (ch == KEY_DOWN) form_driver(form, REQ_NEXT_FIELD); else if (isascii(ch)) form_driver(form, ch); } /* restore i_con_head */ i_con_head = saved_i_con_head; free(prot_level_list); free(raid_menu_str); free(userptr); unpost_form(form); free_form(form); free_screen(NULL, NULL, input_fields); flush_stdscr(); LEAVE; return rc; } /** * process_raid_migrate - Process the list of arrays that can be migrated. * @buffer: * @header_lines: * @last_array_cmd: * * Returns: * 0 if success / non-zero on failure **/ int process_raid_migrate(char *buffer[], int header_lines) { struct array_cmd_data *acd, *saved_cmd_head; struct array_cmd_data *last_array_cmd; struct screen_output *s_out; int found = 0; int toggle = 1; i_container *temp_i_con; char *input; int rc = REFRESH_FLAG; ENTER; while (rc & REFRESH_FLAG) { toggle_field = 0; do { n_raid_migrate.body = buffer[toggle&1]; /* display disk array selection screen */ s_out = screen_driver(&n_raid_migrate, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc & EXIT_FLAG || rc & CANCEL_FLAG) break; found = 0; /* Only one migration is allowed at a time. Set the do_cmd * flag for the first one that is found. */ for_each_icon(temp_i_con) { acd = temp_i_con->data; input = temp_i_con->field_data; if (acd && strcmp(input, "1") == 0) { found++; acd->do_cmd = 1; break; } } if (found) { last_array_cmd = raid_cmd_tail; /* Go to protection level selection screen */ rc = configure_raid_migrate(temp_i_con); /* Keep the array information in the list, but * strip off any disk inforamtion. */ saved_cmd_head = raid_cmd_head; raid_cmd_head = last_array_cmd->next; free_raid_cmds(); raid_cmd_head = saved_cmd_head; raid_cmd_tail = last_array_cmd; last_array_cmd->next = NULL; /* Done on success or exit, otherwise refresh */ if (rc == RC_SUCCESS || rc & EXIT_FLAG) break; /* clear the do_cmd flag */ acd->do_cmd = 0; rc = REFRESH_FLAG; } else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } toggle++; } LEAVE; return rc; } /** * raid_migrate - GUI routine for migrate raid array protection. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_migrate(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *array, *dev; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; ENTER; processing(); /* make sure the i_con list is empty */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_migrate.header, &header_lines); for_each_primary_ioa(ioa) { for_each_array(ioa, array) { dev = array; if (!dev->array_rcd->migrate_cand) continue; if (ioa->sis64) dev = get_vset_from_array(ioa, dev); add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; } } if (!found) { n_raid_migrate_fail.body = body_init(n_raid_migrate_fail.header, NULL); s_out = screen_driver(&n_raid_migrate_fail, 0, i_con); free(n_raid_migrate_fail.body); n_raid_migrate_fail.body = NULL; rc = s_out->rc | CANCEL_FLAG; free(s_out); } else rc = process_raid_migrate(buffer, header_lines); /* Display a progress screen. */ if (rc == 0) return raid_migrate_complete(); /* Free the remaining raid commands. */ free_raid_cmds(); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_migrate.body = NULL; LEAVE; return rc; } /** * asym_access_menu - Display the Optimized and Non-Optimized choices. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int asym_access_menu(i_container *i_con) { struct array_cmd_data *acd; int start_row; int num_menu_items; int menu_index = 0; ITEM ** menu_item = NULL; int *userptr = NULL; int *new_state; int rc; acd = (struct array_cmd_data *)i_con->data; start_row = i_con->y + 2; num_menu_items = 3; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_item[menu_index] = new_item("Optimized",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Non-Optimized",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Not Set",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = NULL; rc = display_menu(menu_item, start_row, menu_index, &new_state); if (rc == RC_SUCCESS) { /* set field_data based on menu choice */ if (*new_state == IPR_ACTIVE_OPTIMIZED) sprintf(i_con->field_data, "Optimized"); else if (*new_state == IPR_ACTIVE_NON_OPTIMIZED) sprintf(i_con->field_data, "Non-Optimized"); else if (*new_state == IPR_ACTIVE_STANDBY) sprintf(i_con->field_data, "Not Set"); /* only issue the command if the state selection changed */ if (acd->dev->array_rcd->saved_asym_access_state != *new_state) acd->do_cmd = 1; else acd->do_cmd = 0; } else { /* set field_data based on existing value */ if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_OPTIMIZED) sprintf(i_con->field_data, "Optimized"); else if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) sprintf(i_con->field_data, "Non-Optimized"); else sprintf(i_con->field_data, "Not Set"); } menu_index = 0; while (menu_item[menu_index] != NULL) free_item(menu_item[menu_index++]); free(menu_item); free(userptr); menu_item = NULL; return rc; } /** * configure_asym_access - Configure the array setting for asymmetric access. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_asym_access(struct array_cmd_data *acd) { i_container *new_i_con, *i_con_head_saved; int header_lines = 0; char pref_str[20]; char buffer[128]; char *body = NULL; struct screen_output *s_out; int state = 0, rc = REFRESH_FLAG; i_con_head_saved = i_con_head; i_con_head = new_i_con = NULL; body = body_init(n_change_array_asym_access.header, &header_lines); sprintf(buffer, "Array: %s", acd->dev->dev_name); body = add_line_to_body(body, buffer, NULL); if (acd->dev->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) sprintf(buffer, "Current asymmetric access state: Optimized"); else sprintf(buffer, "Current asymmetric access state: Non-Optimized"); body = add_line_to_body(body, buffer, NULL); if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_OPTIMIZED) { sprintf(buffer, "Saved asymmetric access state: Optimized\n"); sprintf(pref_str, "Optimized"); } else if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) { sprintf(buffer, "Saved asymmetric access state: Non-Optimized\n"); sprintf(pref_str, "Non-Optimized"); } else { sprintf(buffer, "Saved asymmetric access state: Not Set\n"); sprintf(pref_str, "Not Set"); } body = add_line_to_body(body, buffer, NULL); header_lines += 2; body = add_line_to_body(body,_("Preferred Asymmetric Access State"), "%13"); new_i_con = add_i_con(new_i_con, pref_str, acd); n_change_array_asym_access.body = body; while (rc & REFRESH_FLAG) { s_out = screen_driver(&n_change_array_asym_access, header_lines, NULL); rc = s_out->rc; free(s_out); if (rc == RC_SUCCESS) { if (!acd->do_cmd) { rc = REFRESH_FLAG; break; } if (!strncmp(new_i_con->field_data, "Optimized", 8)) state = IPR_ACTIVE_OPTIMIZED; else if (!strncmp(new_i_con->field_data, "Non-Optimized", 8)) state = IPR_ACTIVE_NON_OPTIMIZED; else state = IPR_ACTIVE_STANDBY; acd->dev->array_rcd->issue_cmd = 1; acd->dev->array_rcd->saved_asym_access_state = state; rc = ipr_set_array_asym_access(acd->ioa); } } processing(); free(n_change_array_asym_access.body); n_change_array_asym_access.body = NULL; free_i_con(new_i_con); i_con_head = i_con_head_saved; return rc; } /** * process_asym_access - Process the arrays that support asymmetric access. * @buffer: * @header_lines: * * Returns: * 0 if success / non-zero on failure **/ int process_asym_access(char *buffer[], int header_lines) { struct screen_output *s_out; int found = 0; int toggle = 1; i_container *i_con; char *input; int rc; struct array_cmd_data *acd; while (1) { toggle_field = 0; do { n_asym_access.body = buffer[toggle&1]; /* display array selection screen */ s_out = screen_driver(&n_asym_access, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc & EXIT_FLAG || rc & CANCEL_FLAG) break; found = 0; /* do one at a time */ for_each_icon(i_con) { acd = i_con->data; input = i_con->field_data; if (acd && strcmp(input, "1") == 0) { found++; break; } } if (found) { /* Go to asymmetric access selection screen */ rc = configure_asym_access(acd); /* Done on success or exit */ if (rc == RC_SUCCESS || rc & EXIT_FLAG) break; /* clear the do_cmd flag */ acd->do_cmd = 0; } else s_status.index = INVALID_OPTION_STATUS; } return rc; } /** * asym_access - GUI routine for setting array asymmetric access. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int asym_access(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; struct ipr_ioa *ioa; struct ipr_dev *array; processing(); /* make sure the i_con list is empty */ i_con = free_i_con(i_con); check_current_config(false); rc = RC_SUCCESS; body_init_status(buffer, n_asym_access.header, &header_lines); for_each_primary_ioa(ioa) { if (!ioa->asymmetric_access || !ioa->asymmetric_access_enabled) continue; for_each_array(ioa, array) { if (!array->array_rcd->asym_access_cand) { syslog_dbg("candidate not set\n"); continue; } add_raid_cmd_tail(ioa, array, array->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, array, buffer, "%1", k+2); found++; } } if (!found) { n_asym_access_fail.body = body_init(n_asym_access_fail.header, NULL); s_out = screen_driver(&n_asym_access_fail, 0, i_con); free(n_asym_access_fail.body); n_asym_access_fail.body = NULL; rc = s_out->rc | CANCEL_FLAG; free(s_out); } else rc = process_asym_access(buffer, header_lines); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } return rc; } /** * raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_include(i_container *i_con) { int k; int found = 0; struct ipr_array_record *array_rcd; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_cap_entry *cap_entry; int rc = RC_SUCCESS; struct screen_output *s_out; int header_lines; int toggle = 1; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_raid_include.header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary) continue; for_each_vset(ioa, dev) { array_rcd = dev->array_rcd; cap_entry = get_raid_cap_entry(ioa->supported_arrays, dev->raid_level); if (!cap_entry || !cap_entry->include_allowed || !array_rcd->established) continue; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, dev, buffer, "%1", k); found++; } } if (!found) { /* Include Device Parity Protection Failed */ n_raid_include_fail.body = body_init(n_raid_include_fail.header, NULL); s_out = screen_driver(&n_raid_include_fail, 0, i_con); free(n_raid_include_fail.body); n_raid_include_fail.body = NULL; rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_include, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_include.body = NULL; return rc; } /** * configure_raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME * INVALID_OPTION_STATUS if nothing to process **/ int configure_raid_include(i_container *i_con) { int k; int found = 0; struct array_cmd_data *cur_raid_cmd = NULL; struct ipr_array_query_data *qac_data = calloc(1,sizeof(struct ipr_array_query_data)); struct ipr_dev_record *dev_rcd; struct scsi_dev_data *scsi_dev_data; char *buffer[2]; struct ipr_ioa *ioa; char *input; struct ipr_dev *vset, *dev; struct ipr_array_cap_entry *cap_entry; u8 min_mult_array_devices = 1; int rc = RC_SUCCESS; int header_lines; i_container *temp_i_con; int toggle = 0; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; cur_raid_cmd->do_cmd = 1; break; } } if (found != 1) return INVALID_OPTION_STATUS; i_con = free_i_con(i_con); body_init_status(buffer, n_configure_raid_include.header, &header_lines); ioa = cur_raid_cmd->ioa; vset = cur_raid_cmd->dev; /* Get Query Array Config Data */ rc = ipr_query_array_config(ioa, 0, 1, 0, cur_raid_cmd->array_id, qac_data); found = 0; if (rc != 0) free(qac_data); else { cur_raid_cmd->qac = qac_data; cap_entry = get_raid_cap_entry(ioa->supported_arrays, vset->raid_level); if (cap_entry) min_mult_array_devices = cap_entry->min_mult_array_devices; for_each_dev_rcd(dev_rcd, qac_data) { for_each_dev(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (!scsi_dev_data) continue; if (scsi_dev_data->handle == ipr_get_dev_res_handle(ioa, dev_rcd) && scsi_dev_data->opens == 0 && dev_rcd->include_cand && device_supported(dev)) { dev->dev_rcd = dev_rcd; i_con = add_i_con(i_con, "\0", dev); print_dev(k, dev, buffer, "%1", k); found++; break; } } } } if (found < min_mult_array_devices) { /* Include Device Parity Protection Failed */ n_configure_raid_include_fail.body = body_init(n_configure_raid_include_fail.header, NULL); s_out = screen_driver(&n_configure_raid_include_fail, 0, i_con); free(n_configure_raid_include_fail.body); n_configure_raid_include_fail.body = NULL; rc = s_out->rc; free(s_out); return rc; } toggle_field = 0; do { n_configure_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_configure_raid_include, header_lines, i_con); temp_i_con = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_configure_raid_include.body = NULL; i_con = temp_i_con; if (rc > 0) goto leave; /* first, count devices selected to be sure min multiple is satisfied */ found = 0; for_each_icon(temp_i_con) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) found++; } if (found % min_mult_array_devices != 0) { /* "Error: number of devices selected must be a multiple of %d" */ s_status.num = min_mult_array_devices; return 25 | REFRESH_FLAG; } for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { if (!dev->dev_rcd->issue_cmd) { dev->dev_rcd->issue_cmd = 1; dev->dev_rcd->known_zeroed = 1; } } else { if (dev->dev_rcd->issue_cmd) { dev->dev_rcd->issue_cmd = 0; dev->dev_rcd->known_zeroed = 0; } } } rc = confirm_raid_include(i_con); leave: return rc; } /** * confirm_raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_raid_include(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int k; char *buffer[2]; int rc = RC_SUCCESS; struct screen_output *s_out; int num_devs = 0; int dev_include_complete(u8 num_devs); int format_include_cand(); int header_lines; int toggle = 0; int need_formats = 0; body_init_status(buffer, n_confirm_raid_include.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->qac) continue; ioa = cur_raid_cmd->ioa; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->issue_cmd) continue; print_dev(k, dev, buffer, "1", k); if (!ipr_device_is_zeroed(dev)) need_formats = 1; num_devs++; } } do { n_confirm_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_include, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_include.body = NULL; if (rc) return rc; /* now format each of the devices */ if (need_formats) num_devs = format_include_cand(); rc = dev_include_complete(num_devs); free_devs_to_init(); return rc; } /** * format_include_cand - * * Returns: * number of devices processed **/ int format_include_cand() { struct scsi_dev_data *scsi_dev_data; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int num_devs = 0; int rc = 0; struct ipr_dev_record *device_record; int opens = 0; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; for_each_dev(ioa, dev) { device_record = dev->dev_rcd; if (!device_record || !device_record->issue_cmd) continue; /* get current "opens" data for this device to determine conditions to continue */ scsi_dev_data = dev->scsi_dev_data; if (scsi_dev_data) opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if (opens != 0) { syslog(LOG_ERR,_("Include Device not allowed, device in use - %s\n"), dev->gen_name); device_record->issue_cmd = 0; continue; } if (dev_init_head) { dev_init_tail->next = malloc(sizeof(struct devs_to_init_t)); dev_init_tail = dev_init_tail->next; } else dev_init_head = dev_init_tail = malloc(sizeof(struct devs_to_init_t)); memset(dev_init_tail, 0, sizeof(struct devs_to_init_t)); dev_init_tail->ioa = ioa; dev_init_tail->dev = dev; dev_init_tail->do_init = 1; /* Issue the format. Failure will be detected by query command status */ rc = ipr_format_unit(dev); num_devs++; } } return num_devs; } /** * update_include_qac_data - * @ioa: ipr ioa struct * @old_qac: ipr_array_query_data struct * @new_qac: ipr_array_query_data struct * * Returns: * 0 if success / 26 on failure **/ static int update_include_qac_data(struct ipr_ioa *ioa, struct ipr_array_query_data *old_qac, struct ipr_array_query_data *new_qac) { struct ipr_dev_record *old_dev_rcd, *new_dev_rcd; int found; for_each_dev_rcd(old_dev_rcd, old_qac) { if (!old_dev_rcd->issue_cmd) continue; found = 0; for_each_dev_rcd(new_dev_rcd, new_qac) { if (ipr_get_dev_res_handle(ioa, new_dev_rcd) != ipr_get_dev_res_handle(ioa, old_dev_rcd)) continue; new_dev_rcd->issue_cmd = 1; new_dev_rcd->known_zeroed = 1; found = 1; break; } if (!found) return 26; } return 0; } /** * do_array_include - * @ioa: ipr ioa struct * @array_id: array ID * @qac: ipr_array_query_data struct * * Returns: * 0 if success / non-zero on failure **/ static int do_array_include(struct ipr_ioa *ioa, int array_id, struct ipr_array_query_data *qac) { int fd, rc; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) return -EIO; rc = flock(fd, LOCK_EX); if (rc) goto out; rc = __ipr_query_array_config(ioa, fd, 0, 1, 0, array_id, &qac_data); if (rc) goto out; rc = update_include_qac_data(ioa, qac, &qac_data); if (rc) goto out; rc = ipr_add_array_device(ioa, fd, &qac_data); out: close(fd); return rc; } /** * dev_include_complete - * @num_devs: not used ? * * Returns: * 27 | EXIT_FLAG on success / 26 | EXIT_FLAG otherwise **/ int dev_include_complete(u8 num_devs) { int done_bad; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int not_done = 0; int rc; struct devs_to_init_t *cur_dev_init; u32 percent_cmplt = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_common_record *common_record; n_dev_include_complete.body = n_dev_include_complete.header[0]; while(1) { complete_screen_driver(&n_dev_include_complete, percent_cmplt, 0); percent_cmplt = 100; done_bad = 0; for_each_dev_to_init(cur_dev_init) { if (cur_dev_init->do_init) { rc = ipr_query_command_status(cur_dev_init->dev, &cmd_status); if (rc || cmd_status.num_records == 0) { cur_dev_init->cmplt = 100; continue; } status_record = cmd_status.record; if (status_record->status == IPR_CMD_STATUS_FAILED) { cur_dev_init->cmplt = 100; done_bad = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { cur_dev_init->cmplt = status_record->percent_complete; if (cur_dev_init->cmplt < percent_cmplt) percent_cmplt = cur_dev_init->cmplt; not_done = 1; } } } if (!not_done) { flush_stdscr(); if (done_bad) { /* "Include failed" */ rc = 26; return rc | EXIT_FLAG; } break; } not_done = 0; sleep(1); } complete_screen_driver(&n_dev_include_complete, percent_cmplt, 0); /* now issue the start array command with "known to be zero" */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; if (do_array_include(cur_raid_cmd->ioa, cur_raid_cmd->array_id, cur_raid_cmd->qac)) { rc = 26; continue; } } not_done = 0; percent_cmplt = 0; n_dev_include_complete.body = n_dev_include_complete.header[1]; while(1) { complete_screen_driver(&n_dev_include_complete, percent_cmplt, 1); percent_cmplt = 100; done_bad = 0; for_each_raid_cmd(cur_raid_cmd) { ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if (status_record->command_code == IPR_ADD_ARRAY_DEVICE && cur_raid_cmd->array_id == status_record->array_id) { if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) /* "Include failed" */ rc = RC_26_Include_Fail; break; } } } if (!not_done) { /* Call ioctl() to re-read partition table to handle change in size */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; common_record = cur_raid_cmd->dev->qac_entry; if (common_record && common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD) rc = ipr_write_dev_attr(cur_raid_cmd->dev, "rescan", "1"); } flush_stdscr(); if (done_bad) /* "Include failed" */ return RC_26_Include_Fail | EXIT_FLAG; /* "Include Unit completed successfully" */ complete_screen_driver(&n_dev_include_complete, percent_cmplt, 1); return RC_27_Include_Success | EXIT_FLAG; } not_done = 0; sleep(1); } } /** * add_format_device - * @dev: ipr dev struct * @blk_size: block size * * Returns: * nothing **/ static void add_format_device(struct ipr_dev *dev, int blk_size) { if (dev_init_head) { dev_init_tail->next = malloc(sizeof(struct devs_to_init_t)); dev_init_tail = dev_init_tail->next; } else dev_init_head = dev_init_tail = malloc(sizeof(struct devs_to_init_t)); memset(dev_init_tail, 0, sizeof(struct devs_to_init_t)); dev_init_tail->ioa = dev->ioa; if (ipr_is_af_dasd_device(dev)) dev_init_tail->dev_type = IPR_AF_DASD_DEVICE; else dev_init_tail->dev_type = IPR_JBOD_DASD_DEVICE; dev_init_tail->new_block_size = blk_size; dev_init_tail->dev = dev; } /** * configure_af_device - * @i_con: i_container struct * @action_code: include or remove * * Returns: * 0 if success / non-zero on failure **/ static int configure_af_device(i_container *i_con, int action_code) { int rc, j, k; struct scsi_dev_data *scsi_dev_data; int can_init; int dev_type; int new_block_size; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; int toggle = 0; int header_lines; s_node *n_screen; struct screen_output *s_out; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); /* Setup screen title */ if (action_code == IPR_INCLUDE) n_screen = &n_af_init_device; else n_screen = &n_jbod_init_device; body_init_status(buffer, n_screen->header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary) continue; for (j = 0; j < ioa->num_devices; j++) { can_init = 1; scsi_dev_data = ioa->dev[j].scsi_dev_data; /* If not a DASD, disallow format */ if (!scsi_dev_data || ipr_is_hot_spare(&ioa->dev[j]) || ipr_is_volume_set(&ioa->dev[j]) || !device_supported(&ioa->dev[j]) || (scsi_dev_data->type != TYPE_DISK && scsi_dev_data->type != IPR_TYPE_AF_DISK)) continue; /* If Advanced Function DASD */ if (ipr_is_af_dasd_device(&ioa->dev[j])) { if (action_code == IPR_INCLUDE) { if (ipr_device_is_zeroed(&ioa->dev[j])) continue; new_block_size = 0; } else if (ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_JBOD_4K_BLOCK_SIZE; else new_block_size = IPR_JBOD_BLOCK_SIZE; dev_type = IPR_AF_DASD_DEVICE; /* We allow the user to format the drive if nobody is using it */ if (ioa->dev[j].scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), ioa->dev[j].gen_name); continue; } if (ipr_is_array_member(&ioa->dev[j])) { if (ioa->dev[j].dev_rcd->no_cfgte_vol) can_init = 1; else can_init = 0; } else can_init = is_format_allowed(&ioa->dev[j]); } else if (scsi_dev_data->type == TYPE_DISK){ /* If on a JBOD adapter */ if (!ioa->qac_data->num_records) { if (action_code != IPR_REMOVE) continue; } else if (is_af_blocked(&ioa->dev[j], 0)) continue; if (action_code == IPR_REMOVE && !format_req(&ioa->dev[j])) continue; if (action_code == IPR_REMOVE) { if (ioa->support_4k && ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_JBOD_4K_BLOCK_SIZE; else new_block_size = IPR_JBOD_BLOCK_SIZE; } else { if (ioa->support_4k && ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_AF_4K_BLOCK_SIZE; else new_block_size = ioa->af_block_size; } dev_type = IPR_JBOD_DASD_DEVICE; /* We allow the user to format the drive if nobody is using it, or the device is read write protected. If the drive fails, then is replaced concurrently it will be read write protected, but the system may still have a use count for it. We need to allow the format to get the device into a state where the system can use it */ if (ioa->dev[j].scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), ioa->dev[j].gen_name); continue; } can_init = is_format_allowed(&ioa->dev[j]); } else continue; if (can_init) { add_format_device(&ioa->dev[j], new_block_size); print_dev(k, &ioa->dev[j], buffer, "%1", k); i_con = add_i_con(i_con,"\0",dev_init_tail); num_devs++; } } } if (!num_devs) { if (action_code == IPR_INCLUDE) { n_af_include_fail.body = body_init(n_af_include_fail.header, NULL); s_out = screen_driver(&n_af_include_fail, 0, i_con); free(n_af_include_fail.body); n_af_include_fail.body = NULL; } else { n_af_remove_fail.body = body_init(n_af_remove_fail.header, NULL); s_out = screen_driver(&n_af_remove_fail, 0, i_con); free(n_af_remove_fail.body); n_af_remove_fail.body = NULL; } rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_devs_to_init(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; return rc; } /** * af_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int af_include(i_container *i_con) { int rc; rc = configure_af_device(i_con, IPR_INCLUDE); return rc; } /** * af_remove - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int af_remove(i_container *i_con) { int rc; rc = configure_af_device(i_con, IPR_REMOVE); return rc; } /** * add_hot_spare - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int add_hot_spare(i_container *i_con) { int rc; do { rc = hot_spare(i_con, IPR_ADD_HOT_SPARE); } while (rc & REFRESH_FLAG); return rc; } /** * remove_hot_spare - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int remove_hot_spare(i_container *i_con) { int rc; do { rc = hot_spare(i_con, IPR_RMV_HOT_SPARE); } while (rc & REFRESH_FLAG); return rc; } /** * hot_spare - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int hot_spare(i_container *i_con, int action) { int rc, k; struct array_cmd_data *cur_raid_cmd; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; int found = 0; char *input; int toggle = 0; i_container *temp_i_con; struct screen_output *s_out; s_node *n_screen; int header_lines; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); if (action == IPR_ADD_HOT_SPARE) n_screen = &n_add_hot_spare; else n_screen = &n_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if ((action == IPR_ADD_HOT_SPARE && dev->dev_rcd->add_hot_spare_cand) || (action == IPR_RMV_HOT_SPARE && dev->dev_rcd->rmv_hot_spare_cand)) { add_raid_cmd_tail(ioa, NULL, 0); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; break; } } } if (!found) { if (action == IPR_ADD_HOT_SPARE) { n_add_hot_spare_fail.body = body_init(n_add_hot_spare_fail.header, NULL); s_out = screen_driver(&n_add_hot_spare_fail, 0, i_con); free(n_add_hot_spare_fail.body); n_add_hot_spare_fail.body = NULL; } else { n_remove_hot_spare_fail.body = body_init(n_remove_hot_spare_fail.header, NULL); s_out = screen_driver(&n_remove_hot_spare_fail, 0, i_con); free(n_remove_hot_spare_fail.body); n_remove_hot_spare_fail.body = NULL; } free(s_out); rc = EXIT_FLAG; goto leave; } do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con); temp_i_con = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); i_con = temp_i_con; if (rc > 0) { rc = EXIT_FLAG; goto leave; } found = 0; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (cur_raid_cmd) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { do { rc = select_hot_spare(temp_i_con, action); } while (rc & REFRESH_FLAG); if (rc > 0) goto leave; found = 1; cur_raid_cmd->do_cmd = 1; } else { cur_raid_cmd->do_cmd = 0; } } } if (found) { /* Go to verification screen */ rc = confirm_hot_spare(action); } else rc = REFRESH_FLAG | INVALID_OPTION_STATUS; leave: if (rc & CANCEL_FLAG) { s_status.index = (rc & ~CANCEL_FLAG); rc = (rc | REFRESH_FLAG) & ~CANCEL_FLAG; } i_con = free_i_con(i_con); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; free_raid_cmds(); return rc; } /** * select_hot_spare - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int select_hot_spare(i_container *i_con, int action) { struct array_cmd_data *cur_raid_cmd; int rc, k, found; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; char *input; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; struct ipr_dev_record *dev_rcd; i_container *temp_i_con,*i_con2 = NULL; i_container *temp_i_con2; struct screen_output *s_out = NULL; int header_lines; s_node *n_screen; int toggle = 0; i_container *i_con_head_saved; if (action == IPR_ADD_HOT_SPARE) n_screen = &n_select_add_hot_spare; else n_screen = &n_select_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); cur_raid_cmd = (struct array_cmd_data *) i_con->data; rc = RC_SUCCESS; ioa = cur_raid_cmd->ioa; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = NULL; for_each_af_dasd(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; dev_rcd = dev->dev_rcd; if (!scsi_dev_data) continue; if (device_supported(dev) && ((dev_rcd->add_hot_spare_cand && action == IPR_ADD_HOT_SPARE) || (dev_rcd->rmv_hot_spare_cand && action == IPR_RMV_HOT_SPARE))) { i_con2 = add_i_con(i_con2, "\0", dev); print_dev(k, dev, buffer, "%1", k); num_devs++; } } if (num_devs) { do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con2); temp_i_con2 = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; i_con2 = temp_i_con2; if (rc > 0) goto leave; found = 0; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (dev != NULL) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found = 1; dev->dev_rcd->issue_cmd = 1; } } } if (found) rc = RC_SUCCESS; else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } } else /* "No devices available for the selected hot spare operation" */ rc = 52 | CANCEL_FLAG; leave: i_con2 = free_i_con(i_con2); i_con_head = i_con_head_saved; return rc; } /** * confirm_hot_spare - * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_hot_spare(int action) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int rc, k; char *buffer[2]; int hot_spare_complete(int action); struct screen_output *s_out; s_node *n_screen; int header_lines; int toggle = 0; rc = RC_SUCCESS; if (action == IPR_ADD_HOT_SPARE) n_screen = &n_confirm_add_hot_spare; else n_screen = &n_confirm_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) print_dev(k, dev, buffer, "1", k); } } do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if(rc > 0) goto leave; rc = hot_spare_complete(action); leave: return rc; } /** * hot_spare_complete - * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int hot_spare_complete(int action) { int rc; struct ipr_ioa *ioa; struct array_cmd_data *cmd; for_each_ioa(ioa) { for_each_raid_cmd(cmd) { if (cmd->do_cmd == 0 || ioa != cmd->ioa) continue; flush_stdscr(); if (action == IPR_ADD_HOT_SPARE) rc = ipr_add_hot_spare(ioa); else rc = ipr_remove_hot_spare(ioa); if (rc != 0) { if (action == IPR_ADD_HOT_SPARE) /* Enable Device as Hot Spare failed */ return 55 | EXIT_FLAG; else /* Disable Device as Hot Spare failed */ return 56 | EXIT_FLAG; } } } flush_stdscr(); if (action == IPR_ADD_HOT_SPARE) /* "Enable Device as Hot Spare Completed successfully" */ rc = 53 | EXIT_FLAG; else /* "Disable Device as Hot Spare Completed successfully" */ rc = 54 | EXIT_FLAG; return rc; } /** * disk_unit_recovery - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int disk_unit_recovery(i_container *i_con) { int rc; struct screen_output *s_out; int loop; for (loop = 0; loop < n_disk_unit_recovery.num_opts; loop++) { n_disk_unit_recovery.body = ipr_list_opts(n_disk_unit_recovery.body, n_disk_unit_recovery.options[loop].key, n_disk_unit_recovery.options[loop].list_str); } n_disk_unit_recovery.body = ipr_end_list(n_disk_unit_recovery.body); s_out = screen_driver(&n_disk_unit_recovery, 0, NULL); free(n_disk_unit_recovery.body); n_disk_unit_recovery.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * get_elem_status - * @dev: ipr dev struct * @ses: ipr dev struct * @ses_data: ipr_encl_status_ctl_pg struct * @ses_config: ipr_ses_config_pg struct * * Returns: * ipr_drive_elem_status struct if success / NULL otherwise **/ static struct ipr_drive_elem_status * get_elem_status(struct ipr_dev *dev, struct ipr_dev *ses, struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { struct ipr_ioa *ioa = dev->ioa; struct ipr_res_addr *ra; struct ipr_drive_elem_status *elem_status, *overall; int bus = ses->scsi_dev_data->channel; int box, slot, is_vses; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; if (ipr_receive_diagnostics(ses, 2, ses_data, sizeof(*ses_data))) return NULL; if (ipr_receive_diagnostics(ses, 1, ses_cfg, sizeof(*ses_cfg))) return NULL; overall = ipr_get_overall_elem(ses_data, ses_cfg); if (!ioa_is_spi(dev->ioa) && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; for_each_elem_status(elem_status, ses_data, ses_cfg) { slot = elem_status->slot_id; for_each_ra(ra, dev) { if (is_vses && (ra->bus != slot || ra->target != 0)) continue; if (ioa->hop_count == IPR_2BIT_HOP) { if (!is_vses && (ra->bus != bus || ra->phy_2bit_hop.box != box || ra->phy_2bit_hop.phy != slot)) continue; } else { if (!is_vses && (ra->bus != bus || ra->phy.box != box || ra->phy.phy != slot)) continue; } return elem_status; } } return NULL; } /** * get_elem_status_64bit - * @dev: ipr dev struct * @ses: ipr dev struct * @ses_data: ipr_encl_status_ctl_pg struct * @ses_config: ipr_ses_config_pg struct * * Returns: * ipr_drive_elem_status struct if success / NULL otherwise **/ static struct ipr_drive_elem_status * get_elem_status_64bit(struct ipr_dev *dev, struct ipr_dev *ses, struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { struct ipr_res_path *rp; struct ipr_drive_elem_status *elem_status; int slot, res_path_len, dev_slot, ses_path_len; if (ipr_receive_diagnostics(ses, 2, ses_data, sizeof(*ses_data))) return NULL; if (ipr_receive_diagnostics(ses, 1, ses_cfg, sizeof(*ses_cfg))) return NULL; ses_path_len = strlen(ses->res_path_name); res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len-2), NULL, 16); if (ses_path_len != res_path_len) return NULL; for_each_elem_status(elem_status, ses_data, ses_cfg) { slot = elem_status->slot_id; for_each_rp(rp, dev) { if (!memcmp(&rp->res_path_bytes, &ses->res_path[0], ses_path_len/3) && slot == dev_slot) return elem_status; } } return NULL; } /** * wait_for_new_dev - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * * Returns: * nothing **/ static void wait_for_new_dev(struct ipr_ioa *ioa, struct ipr_res_addr *res_addr) { int time = 12; struct ipr_dev *dev; struct ipr_res_addr_aliases aliases; struct ipr_res_addr *ra; ipr_query_res_addr_aliases(ioa, res_addr, &aliases); for_each_ra_alias(ra, &aliases) ipr_scan(ioa, ra->bus, ra->target, ra->lun); while (time--) { check_current_config(false); for_each_ra_alias(ra, &aliases) { if ((dev = get_dev_from_addr(ra))) { ipr_init_new_dev(dev); return; } } sleep(5); } } /** * get_dev_from_res_path - * @res_addr: ipr_res_addr struct * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_res_path(struct ipr_res_path *res_path) { struct ipr_ioa *ioa; int j; struct scsi_dev_data *scsi_dev_data; char res_path_name[IPR_MAX_RES_PATH_LEN]; ipr_format_res_path(res_path->res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); for_each_primary_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data) continue; if (!strncmp(scsi_dev_data->res_path, res_path_name, IPR_MAX_RES_PATH_LEN)) return &ioa->dev[j]; } } return NULL; } static void wait_for_new_dev_64bit(struct ipr_ioa *ioa, struct ipr_res_path *res_path) { int time = 12; struct ipr_dev *dev; struct ipr_res_path_aliases aliases; struct ipr_res_path *rp; ipr_query_res_path_aliases(ioa, res_path, &aliases); if (aliases.length < (4 + 2 * sizeof(struct ipr_res_path))) { aliases.length = 4 + 2 * sizeof(struct ipr_res_path); memcpy(&aliases.res_path[0], res_path, 2 * sizeof(struct ipr_res_path)); } ipr_scan(ioa, 0, -1, -1); while (time--) { check_current_config(false); for_each_rp_alias(rp, &aliases) { if ((dev = get_dev_from_res_path(rp))) { ipr_init_new_dev(dev); return; } } sleep(5); } } static struct ipr_dev *delete_secondary_sysfs_name(struct ipr_dev *delete_dev) { struct ipr_ioa *ioa; struct ipr_dev *dev, *ses; for_each_secondary_ioa(ioa) { for_each_ses(ioa, ses) { for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; if (!strncmp(dev->res_path_name, delete_dev->res_path_name, strlen(delete_dev->res_path_name))) return dev; } } } return NULL; } int remove_or_add_back_device_64bit(struct ipr_dev *dev) { struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status; struct ipr_ses_config_pg ses_cfg; int res_path_len, dev_slot; struct ipr_dev *sec_dev, *tmp_dev; char new_sysfs_res_path[IPR_MAX_RES_PATH_LEN]; int rc; res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len - 2), NULL, 16); if (!ipr_read_dev_attr(dev, "resource_path", new_sysfs_res_path, IPR_MAX_RES_PATH_LEN)) if (strncmp(dev->res_path_name, new_sysfs_res_path, sizeof(dev->res_path_name))) for_each_dev(dev->ioa, tmp_dev) if (!strncmp(tmp_dev->res_path_name, new_sysfs_res_path, sizeof(tmp_dev->res_path_name))) { dev = tmp_dev; break; } evaluate_device(dev, dev->ioa, 0); if (ipr_receive_diagnostics(dev->ses[0], 2, &ses_data, sizeof(ses_data))) return INVALID_OPTION_STATUS; if (ipr_receive_diagnostics(dev->ses[0], 1, &ses_cfg, sizeof(ses_cfg))) return INVALID_OPTION_STATUS; for_each_elem_status(elem_status, &ses_data, &ses_cfg) { if (elem_status->slot_id == dev_slot && elem_status->status == IPR_DRIVE_ELEM_STATUS_POPULATED ) { wait_for_new_dev_64bit(dev->ioa, &(dev->res_path[0])); rc = 0; break; } else if (elem_status->slot_id == dev_slot && elem_status->status == IPR_DRIVE_ELEM_STATUS_EMPTY ) { ipr_write_dev_attr(dev, "delete", "1"); ipr_del_zeroed_dev(dev); sec_dev = delete_secondary_sysfs_name(dev); if (sec_dev) { evaluate_device(sec_dev, dev->ioa, 0); ipr_write_dev_attr(sec_dev, "delete", "1"); ipr_del_zeroed_dev(sec_dev); } rc = 1; break; } }/*for_each_elem_status*/ return rc; } /** * process_conc_maint - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int process_conc_maint(i_container *i_con, int action) { i_container *temp_i_con; int found = 0; struct ipr_dev *dev, *ses; char *input; struct ipr_ioa *ioa; int k, rc; struct ipr_dev_record *dev_rcd; struct ipr_encl_status_ctl_pg ses_data; struct ipr_ses_config_pg ses_cfg; struct ipr_drive_elem_status *elem_status, *overall; char *buffer[3]; int header_lines; int toggle=0; s_node *n_screen; struct screen_output *s_out; struct ipr_res_addr res_addr; struct ipr_res_path res_path[2]; int max_y, max_x; for_each_icon(temp_i_con) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { dev = (struct ipr_dev *)temp_i_con->data; if (dev != NULL) found++; } } if (found != 1) return INVALID_OPTION_STATUS; if (dev->ioa->sis64) memcpy(&res_path, &(dev->res_path), sizeof(struct ipr_res_path)*2); else memcpy(&res_addr, &(dev->res_addr), sizeof(res_addr)); if (ipr_is_af_dasd_device(dev) && (action == IPR_VERIFY_CONC_REMOVE || action == IPR_WAIT_CONC_REMOVE)) { /* issue suspend device bus to verify operation is allowable */ if (!ipr_can_remove_device(dev)) return INVALID_OPTION_STATUS; /* FIXME */ } else if (num_device_opens(res_addr.host, res_addr.bus, res_addr.target, res_addr.lun)) return INVALID_OPTION_STATUS; /* FIXME */ ioa = dev->ioa; ses = dev->ses[0]; if (ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) return INVALID_OPTION_STATUS; if ((action == IPR_VERIFY_CONC_REMOVE) || (action == IPR_VERIFY_CONC_ADD)) { elem_status->select = 1; elem_status->identify = 1; } else if (action == IPR_WAIT_CONC_REMOVE) { elem_status->select = 1; elem_status->remove = 1; elem_status->identify = 1; } else if (action == IPR_WAIT_CONC_ADD) { elem_status->select = 1; elem_status->insert = 1; elem_status->identify = 1; } overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->insert = 0; overall->remove = 0; overall->identify = 0; if (action == IPR_WAIT_CONC_REMOVE || action == IPR_WAIT_CONC_ADD) overall->disable_resets = 1; if (action == IPR_VERIFY_CONC_REMOVE) n_screen = &n_verify_conc_remove; else if (action == IPR_VERIFY_CONC_ADD) n_screen = &n_verify_conc_add; else if (action == IPR_WAIT_CONC_REMOVE) n_screen = &n_wait_conc_remove; else n_screen = &n_wait_conc_add; processing(); for (k = 0; k < 3; k++) { buffer[k] = __body_init_status(n_screen->header, &header_lines, (k + 8)); buffer[k] = print_device(dev, buffer[k], "1", (k + 5)); } rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (rc) { for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } return 30 | EXIT_FLAG; } /* call screen driver */ do { n_screen->body = buffer[toggle%3]; s_out = screen_driver(n_screen, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if (ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) return 30 | EXIT_FLAG;; /* turn light off flashing light */ overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = 0; overall->insert = 0; overall->remove = 0; overall->identify = 0; elem_status->select = 1; elem_status->insert = 0; elem_status->remove = 0; elem_status->identify = 0; ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); /* call function to complete conc maint */ if (!rc) { if (action == IPR_VERIFY_CONC_REMOVE) { rc = process_conc_maint(i_con, IPR_WAIT_CONC_REMOVE); if (!rc) { dev_rcd = dev->dev_rcd; getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Operation in progress - please wait")); refresh(); if (dev->ioa->sis64) { if (!remove_or_add_back_device_64bit(dev)) { clear(); move(max_y/2,0); printw(_(" Disk was not removed!") ); refresh(); sleep(5); } } else { ipr_write_dev_attr(dev, "delete", "1"); evaluate_device(dev, dev->ioa, 0); ipr_del_zeroed_dev(dev); } } } else if (action == IPR_VERIFY_CONC_ADD) { rc = process_conc_maint(i_con, IPR_WAIT_CONC_ADD); if (!rc) { getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Operation in progress - please wait")); refresh(); if (ioa->sis64) wait_for_new_dev_64bit(ioa, res_path); else wait_for_new_dev(ioa, &res_addr); } } } return rc; } /** * format_in_prog - check to determine if format is in progress * @dev: ipr dev struct * * Returns: * 0 if format is not in progress / 1 if format is in progress **/ static int format_in_prog(struct ipr_dev *dev) { struct ipr_cmd_status cmd_status; struct sense_data_t sense_data; int rc; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if (!rc && cmd_status.num_records != 0 && cmd_status.record->status == IPR_CMD_STATUS_IN_PROGRESS) return 1; } else if (ipr_is_gscsi(dev)) { rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70 && (sense_data.sense_key & 0x0F) == 0x02 && /* NOT READY */ sense_data.add_sense_code == 0x04 && /* LOGICAL UNIT NOT READY */ sense_data.add_sense_code_qual == 0x04) /* FORMAT IN PROGRESS */ return 1; } return 0; } /** * free_empty_slot - * @dev: ipr dev struct * * Returns: * nothing **/ static void free_empty_slot(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return; if (dev->scsi_dev_data->type != IPR_TYPE_EMPTY_SLOT) return; free(dev->scsi_dev_data); dev->scsi_dev_data = NULL; free(dev); } /** * free_empty_slots - * @dev: ipr dev struct * @num: number of slots to free * * Returns: * nothing **/ static void free_empty_slots(struct ipr_dev **dev, int num) { int i; for (i = 0; i < num; i++) { if (!dev[i] || !dev[i]->scsi_dev_data) continue; if (dev[i]->scsi_dev_data->type != IPR_TYPE_EMPTY_SLOT) continue; free_empty_slot(dev[i]); dev[i] = NULL; } } /** * get_res_addrs - * @dev: ipr dev struct * * Returns: * 0 **/ static int get_res_addrs(struct ipr_dev *dev) { struct ipr_res_addr *ra; struct ipr_res_addr_aliases aliases; int i = 0; ipr_query_res_addr_aliases(dev->ioa, &(dev->res_addr[0]), &aliases); for_each_ra_alias(ra, &aliases) { memcpy(&(dev->res_addr[i]), ra, sizeof(*ra)); if (++i >= IPR_DEV_MAX_PATHS) break; } scsi_dbg(dev, "Resource address aliases: %08X %08X\n", IPR_GET_PHYSICAL_LOCATOR(&(dev->res_addr[0])), IPR_GET_PHYSICAL_LOCATOR(&(dev->res_addr[1]))); return 0; } /** * get_res_path - * @dev: ipr dev struct * * Returns: * 0 **/ static int get_res_path(struct ipr_dev *dev) { struct ipr_res_path *rp; struct ipr_res_path_aliases aliases; int i = 0; ipr_query_res_path_aliases(dev->ioa, &(dev->res_path[0]), &aliases); for_each_rp_alias(rp, &aliases) { memcpy(&(dev->res_path[i]), rp, sizeof(*rp)); if (++i >= IPR_DEV_MAX_PATHS) break; } while (i < IPR_DEV_MAX_PATHS) memset(&dev->res_path[i++], 0xff, sizeof(struct ipr_res_path)); return 0; } /** * alloc_empty_slot - * @ses: ipr dev struct * @slot: slot number * @is_vses: vses flag * * Returns: * ipr_dev struct **/ static struct ipr_dev *alloc_empty_slot(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; int bus = ses->scsi_dev_data->channel; int box; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; dev = calloc(1, sizeof(*dev)); scsi_dev_data = calloc(1, sizeof(*scsi_dev_data)); scsi_dev_data->type = IPR_TYPE_EMPTY_SLOT; scsi_dev_data->host = ioa->host_num; scsi_dev_data->lun = 0; if (is_vses) { scsi_dev_data->channel = slot; scsi_dev_data->id = 0; } else { scsi_dev_data->channel = bus; if (ioa->hop_count == IPR_2BIT_HOP) scsi_dev_data->id = slot | (box << 6); else scsi_dev_data->id = slot | (box << 5); } dev->res_addr[0].bus = scsi_dev_data->channel; dev->res_addr[0].target = scsi_dev_data->id; dev->res_addr[0].lun = 0; dev->scsi_dev_data = scsi_dev_data; dev->ses[0] = ses; dev->ioa = ioa; dev->physical_location[0] = '\0'; strncat(dev->physical_location, phy_loc, strlen(phy_loc)); get_res_addrs(dev); return dev; } /** * alloc_empty_slot_64bit - * @ses: ipr dev struct * @slot: slot number * @is_vses: vses flag * * Returns: * ipr_dev struct **/ static struct ipr_dev *alloc_empty_slot_64bit(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; char slot_buf[50]; int n, len; dev = calloc(1, sizeof(*dev)); scsi_dev_data = calloc(1, sizeof(*scsi_dev_data)); scsi_dev_data->type = IPR_TYPE_EMPTY_SLOT; scsi_dev_data->host = ioa->host_num; n = sprintf(slot_buf, "%02X",slot); strncpy(scsi_dev_data->res_path, ses->scsi_dev_data->res_path, strlen(ses->scsi_dev_data->res_path)); scsi_dev_data->res_path[strlen(ses->scsi_dev_data->res_path) -2] = slot_buf[0]; scsi_dev_data->res_path[strlen(ses->scsi_dev_data->res_path) -1] = slot_buf[1]; strncpy(dev->res_path_name, scsi_dev_data->res_path, strlen(scsi_dev_data->res_path)); len = strlen(ses->scsi_dev_data->res_path); memcpy(&dev->res_path[0], &ses->res_path[0], sizeof(struct ipr_res_path)); dev->res_path[0].res_path_bytes[len/3] = slot; dev->scsi_dev_data = scsi_dev_data; dev->ses[0] = ses; dev->ioa = ioa; dev->physical_location[0] = '\0'; strncat(dev->physical_location, phy_loc, strlen(phy_loc)); get_res_path(dev); return dev; } /** * can_perform_conc_action - * @dev: ipr dev struct * @action: action type * * Returns: * 1 if able to perform the action / 0 otherwise **/ static int can_perform_conc_action(struct ipr_dev *dev, int action) { if (action == IPR_CONC_REMOVE) { if (format_in_prog(dev)) return 0; if (!ipr_can_remove_device(dev)) return 0; } else if (dev->scsi_dev_data && action != IPR_CONC_IDENTIFY) return 0; return 1; } /** * get_dev_for_slot - * @ses: ipr dev struct * @slot: slot number * @is_vses: action type * * Returns: * ipr_dev struct if success / NULL otherwise **/ static struct ipr_dev *get_dev_for_slot(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct ipr_res_addr *ra; int bus = ses->scsi_dev_data->channel; int box, i; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; for_each_ra(ra, dev) { if (is_vses && (ra->bus != slot || ra->target != 0)) continue; if (ioa->hop_count == IPR_2BIT_HOP) { if (!is_vses && (ra->bus != bus || ra->phy_2bit_hop.box != box || ra->phy_2bit_hop.phy != slot)) continue; } else { if (!is_vses && (ra->bus != bus || ra->phy.box != box || ra->phy.phy != slot)) continue; } for (i = 0; i < IPR_DEV_MAX_PATHS; i++) { if (dev->ses[i]) continue; dev->ses[i] = ses; } dev->physical_location[0] = '\0'; if (strlen(phy_loc)) strncat(dev->physical_location, phy_loc, strlen(phy_loc)); return dev; } } return NULL; } /** * get_dev_for_slot_64bit - * @ses: ipr dev struct * @slot: slot number * @is_vses: action type * * Returns: * ipr_dev struct if success / NULL otherwise **/ static struct ipr_dev *get_dev_for_slot_64bit(struct ipr_dev *ses, int slot, char *phy_loc) { struct ipr_dev *dev; struct ipr_res_path *rp; int i, dev_slot, res_path_len,ses_path_len; for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; ses_path_len = strlen(ses->res_path_name); res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len - 2), NULL, 16); if (ses_path_len != res_path_len) continue; for_each_rp(rp, dev) { if ( !memcmp(&rp->res_path_bytes, &ses->res_path[0], ses_path_len/3) && slot == dev_slot ) { for (i = 0; i < IPR_DEV_MAX_PATHS; i++) { if (dev->ses[i]) continue; dev->ses[i] = ses; break; } dev->physical_location[0] = '\0'; if (strlen(phy_loc)) strncat(dev->physical_location, phy_loc, strlen(phy_loc)); return dev; } } } return NULL; } /** * search_empty_dev64 - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * 1 if duplicate device detected / 0 otherwise **/ static int search_empty_dev64(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; u8 empty_res_path[8]; memset(&empty_res_path, 0xff, sizeof(struct ipr_res_path)); if (memcmp(&dev->res_path[1], &empty_res_path, sizeof(struct ipr_res_path))) return 0; for (i = 0; i < num_devs; i++) { if (devs[i]->scsi_dev_data && devs[i]->scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT && !strncmp(dev->physical_location, devs[i]->physical_location, PHYSICAL_LOCATION_LENGTH)) { memcpy(&devs[i]->res_path[1], &dev->res_path[0], sizeof(struct ipr_res_path)); devs[i]->ses[1] = dev->ses[0]; return 1; } } return 0; } /** * conc_dev_is_dup - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * 1 if duplicate device detected / 0 otherwise **/ static int conc_dev_is_dup(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; for (i = 0; i < num_devs; i++) if (dev == devs[i]) return 1; return 0; } /** * dev_is_dup - indicate whether or not the devices are the same * @first: ipr dev struct * @second: ipr dev struct * * Returns: * 1 if devices are the same / 0 otherwise **/ static int dev_is_dup(struct ipr_dev *first, struct ipr_dev *second) { struct ipr_res_addr *first_ra; struct ipr_res_addr *second_ra; if (first->ioa != second->ioa) return 0; for_each_ra(first_ra, first) { for_each_ra(second_ra, second) { if (!memcmp(first_ra, second_ra, sizeof(*first_ra))) return 1; } } return 0; } /** * dev_is_dup64 - indicate whether or not the devices are the same * @first: ipr dev struct * @second: ipr dev struct * * Returns: * 1 if devices are the same / 0 otherwise **/ static int dev_is_dup64(struct ipr_dev *first, struct ipr_dev *second) { struct ipr_res_path *first_rp; struct ipr_res_path *second_rp; struct ipr_res_path no_exist_rp; if (first->ioa != second->ioa) return 0; memset(&no_exist_rp, 0xff, sizeof(struct ipr_res_path)); for_each_rp(first_rp, first) { for_each_rp(second_rp, second) { if (!memcmp(first_rp, &no_exist_rp, sizeof(*first_rp)) || !memcmp(second_rp, &no_exist_rp, sizeof(*second_rp))) continue; if (!memcmp(first_rp, second_rp, sizeof(*first_rp))) return 1; } } return 0; } /** * find_dup - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * ipr_dev struct if duplicate found / NULL otherwise **/ static struct ipr_dev *find_dup(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; for (i = 0; i < num_devs; i++) { if (dev->ioa->sis64) { if (dev_is_dup64(dev, devs[i])) return devs[i]; } else { if (dev_is_dup(dev, devs[i])) return devs[i]; } } return NULL; } /** * remove_dup_dev - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * num_devs **/ static int remove_dup_dev(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i, j; for (i = 0; i < num_devs; i++) { if (dev == devs[i]) { free_empty_slot(dev); devs[i] = NULL; for (j = (i + 1); j < num_devs; j++) { devs[j - 1] = devs[j]; devs[j] = NULL; } return (num_devs - 1); } } return num_devs; } /** * remove_conc_dups - * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * num_devs **/ static int remove_conc_dups(struct ipr_dev **devs, int num_devs) { int i; struct ipr_dev *dev; for (i = 0; i < num_devs; i++) { dev = find_dup(devs[i], &devs[i+1], (num_devs - i - 1)); if (!dev) continue; num_devs = remove_dup_dev(dev, devs, num_devs); } return num_devs; } /** * get_conc_devs - * @ret: ipr dev struct * @action: action typoe * * Returns: * num)devs **/ static int get_conc_devs(struct ipr_dev ***ret, int action) { struct ipr_ioa *ioa; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_dev **devs = NULL; struct ipr_dev *ses, *dev; int num_devs = 0; int ses_bus, scsi_id_found, is_spi, is_vses; struct ipr_ses_config_pg ses_cfg; struct drive_elem_desc_pg drive_data; char phy_loc[PHYSICAL_LOCATION_LENGTH + 1]; int times, index; for_each_primary_ioa(ioa) { is_spi = ioa_is_spi(ioa); for_each_hotplug_dev(ioa, dev) { if (ioa->sis64) get_res_path(dev); else get_res_addrs(dev); } for_each_ses(ioa, ses) { times = 5; if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); while (times--) { if (!ipr_receive_diagnostics(ses, 2, &ses_data, sizeof(ses_data))) break; } if (times < 0 ) continue; if (ipr_receive_diagnostics(ses, 1, &ses_cfg, sizeof(ses_cfg))) continue; if (ipr_receive_diagnostics(ses, 7, &drive_data, sizeof(drive_data))) continue; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); ses_bus = ses->scsi_dev_data->channel; scsi_id_found = 0; if (!is_spi && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; scsi_dbg(ses, "%s\n", is_vses ? "Found VSES" : "Found real SES"); for_each_elem_status(elem_status, &ses_data, &ses_cfg) { index = index_in_page2(&ses_data, elem_status->slot_id); if (index != -1) get_drive_phy_loc_with_ses_phy_loc(ses, &drive_data, index, phy_loc, 0); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_UNSUPP) continue; if (elem_status->status == IPR_DRIVE_ELEM_STATUS_NO_ACCESS) continue; if (is_spi && (scsi_id_found & (1 << elem_status->slot_id))) continue; scsi_id_found |= (1 << elem_status->slot_id); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_EMPTY) { if (ioa->sis64) { dev = alloc_empty_slot_64bit(ses, elem_status->slot_id, is_vses, phy_loc); if (!search_empty_dev64(dev, devs, num_devs)){ devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = dev; } else free_empty_slot(dev); } else { devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = alloc_empty_slot(ses, elem_status->slot_id, is_vses, phy_loc); } continue; } if (ioa->sis64) dev = get_dev_for_slot_64bit(ses, elem_status->slot_id, phy_loc); else dev = get_dev_for_slot(ses, elem_status->slot_id, is_vses, phy_loc); if (dev && !can_perform_conc_action(dev, action)) continue; if (dev && conc_dev_is_dup(dev, devs, num_devs)) continue; else if (!dev) { if (ioa->sis64) dev = alloc_empty_slot_64bit(ses, elem_status->slot_id, is_vses, phy_loc); else dev = alloc_empty_slot(ses, elem_status->slot_id, is_vses, phy_loc); } devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = dev; } } } num_devs = remove_conc_dups(devs, num_devs); *ret = devs; return num_devs; } /** * start_conc_maint - * @i_con: i_container struct * @action: action type * * Returns: * 0 if success / non-zero on failure FIXME **/ int start_conc_maint(i_container *i_con, int action) { int rc, s_rc, i, k; char *buffer[3]; int num_lines = 0; struct screen_output *s_out; struct ipr_dev **devs; int toggle = 0; s_node *n_screen; int header_lines; int num_devs; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); /* Setup screen title */ if (action == IPR_CONC_REMOVE) n_screen = &n_concurrent_remove_device; else n_screen = &n_concurrent_add_device; body_init_status_conc(buffer, n_screen->header, &header_lines); num_devs = get_conc_devs(&devs, action); for (i = 0; i < num_devs; i++) { print_dev_conc(k, devs[i], buffer, "%1", k); i_con = add_i_con(i_con,"\0", devs[i]); num_lines++; } if (num_lines == 0) { for (k = 0; k < 3; k++) buffer[k] = add_string_to_body(buffer[k], _("(No Eligible Devices Found)"), "", NULL); } do { n_screen->body = buffer[toggle%3]; s_out = screen_driver(n_screen, header_lines, i_con); s_rc = s_out->rc; free(s_out); toggle++; } while (s_rc == TOGGLE_SCREEN); for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if (!s_rc) { if (action == IPR_CONC_REMOVE) rc = process_conc_maint(i_con, IPR_VERIFY_CONC_REMOVE); else rc = process_conc_maint(i_con, IPR_VERIFY_CONC_ADD); } if (rc & CANCEL_FLAG) { s_status.index = (rc & ~CANCEL_FLAG); rc = (rc | REFRESH_FLAG) & ~CANCEL_FLAG; } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * concurrent_add_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int concurrent_add_device(i_container *i_con) { return start_conc_maint(i_con, IPR_CONC_ADD); } /** * concurrent_remove_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int concurrent_remove_device(i_container *i_con) { return start_conc_maint(i_con, IPR_CONC_REMOVE); } /** * path_details - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int path_details(i_container *i_con) { int rc, header_lines = 2; char *body = NULL; struct ipr_dev *dev; struct screen_output *s_out; char location[512]; char *name; rc = RC_SUCCESS; processing(); if ((rc = device_details_get_device(i_con, &dev))) return rc; if (strlen(dev->dev_name)) name = dev->dev_name; else name = dev->gen_name; if (dev->ioa->sis64) sprintf(location, "%d/%s", dev->ioa->host_num,dev->scsi_dev_data->res_path); else sprintf(location, "%s/%d:%d:%d:%d", dev->ioa->pci_address, dev->ioa->host_num, dev->res_addr[0].bus, dev->res_addr[0].target, dev->res_addr[0].lun); body = add_line_to_body(body, _("Device"), name); if (strlen(dev->physical_location)) body = add_line_to_body(body, _("Location"), dev->physical_location); else body = add_line_to_body(body, _("Location"), location); body = __body_init(body, n_path_details.header, &header_lines); body = status_header(body, &header_lines, 6); body = print_path_details(dev, body); n_path_details.body = body; s_out = screen_driver(&n_path_details, header_lines, i_con); free(n_path_details.body); n_path_details.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * path_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int path_status(i_container * i_con) { int rc, k, header_lines; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int toggle = 1, num_devs = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); for (k = 0; k < 2; k++) buffer[k] = __body_init_status(n_path_status.header, &header_lines, k); for_each_sas_ioa(ioa) { if (ioa->ioa_dead) continue; for_each_disk(ioa, dev) { buffer[0] = __print_device(dev, buffer[0], "%1", 1, 1, 1, 0, 0, 1, 0, 1, 0 ,0, 0, 0); buffer[1] = __print_device(dev, buffer[1], "%1", 1, 1, 0, 0, 0, 0, 0, 1, 0 ,0, 0, 0); i_con = add_i_con(i_con, "\0", dev); num_devs++; } } if (!num_devs) /* "No SAS disks available" */ return 76; do { n_path_status.body = buffer[toggle&1]; s_out = screen_driver(&n_path_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_path_status.body = NULL; return rc; } /** * init_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int init_device(i_container *i_con) { int rc, k; struct scsi_dev_data *scsi_dev_data; struct ipr_dev_record *device_record; int can_init; int dev_type; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_init_device.header, &header_lines); for_each_primary_ioa(ioa) { if (ioa->ioa_dead) continue; for_each_dev(ioa, dev) { can_init = 1; scsi_dev_data = dev->scsi_dev_data; /* If not a DASD, disallow format */ if (!scsi_dev_data || ipr_is_hot_spare(dev) || ipr_is_volume_set(dev) || !device_supported(dev) || (scsi_dev_data->type != TYPE_DISK && scsi_dev_data->type != IPR_TYPE_AF_DISK)) continue; /* If Advanced Function DASD */ if (ipr_is_af_dasd_device(dev)) { dev_type = IPR_AF_DASD_DEVICE; device_record = dev->dev_rcd; /* We allow the user to format the drive if nobody is using it */ if (scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), dev->gen_name); continue; } if (ipr_is_array_member(dev)) { if (device_record->no_cfgte_vol) can_init = 1; else can_init = 0; } else can_init = is_format_allowed(dev); } else if (scsi_dev_data->type == TYPE_DISK) { /* We allow the user to format the drive if nobody is using it */ if (scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), dev->gen_name); continue; } dev_type = IPR_JBOD_DASD_DEVICE; can_init = is_format_allowed(dev); } else continue; if (can_init) { add_format_device(dev, 0); print_dev(k, dev, buffer, "%1", k); i_con = add_i_con(i_con,"\0",dev_init_tail); num_devs++; } } } if (!num_devs) /* "No units available for initialize and format" */ return 49; do { n_init_device.body = buffer[toggle&1]; s_out = screen_driver(&n_init_device, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_init_device.body = NULL; free_devs_to_init(); return rc; } /** * confirm_init_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_init_device(i_container *i_con) { char *input; char *buffer[2]; struct devs_to_init_t *cur_dev_init; int rc = RC_SUCCESS; struct screen_output *s_out; int header_lines = 0; int toggle = 0; int k; i_container *temp_i_con; int found = 0; int post_attention = 0; struct ipr_dev *dev; for_each_icon(temp_i_con) { if (temp_i_con->data == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; cur_dev_init =(struct devs_to_init_t *)(temp_i_con->data); cur_dev_init->do_init = 1; dev = cur_dev_init->dev; if (ipr_is_gscsi(dev)) post_attention++; } else { cur_dev_init = (struct devs_to_init_t *)(temp_i_con->data); cur_dev_init->do_init = 0; } } if (!found) return INVALID_OPTION_STATUS; if (post_attention) { for (k = 0; k < 2; k++) { header_lines = 0; buffer[k] = add_string_to_body(NULL, _("ATTENTION! System crash may occur " "if selected device is in use. Data loss will " "occur on selected device. Proceed with " "caution.\n\n"), "", &header_lines); buffer[k] = __body_init(buffer[k],n_confirm_init_device.header, &header_lines); buffer[k] = status_header(buffer[k], &header_lines, k); } } else { body_init_status(buffer, n_confirm_init_device.header, &header_lines); } for_each_dev_to_init(cur_dev_init) { if (!cur_dev_init->do_init) continue; print_dev(k, cur_dev_init->dev, buffer, "1", k); } do { n_confirm_init_device.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_init_device, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_init_device.body = NULL; return rc; } /** * dev_init_complete - * @num_devs: number of devices * * Returns: * 50 | EXIT_FLAG if success / 51 | EXIT_FLAG on failure **/ static int dev_init_complete(u8 num_devs) { int done_bad = 0; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int not_done = 0; int rc = 0; struct devs_to_init_t *dev; u32 percent_cmplt = 0; struct sense_data_t sense_data; struct ipr_ioa *ioa; int pid = 1; char dev_type[100]; int type; while(1) { if (pid) rc = complete_screen_driver(&n_dev_init_complete, percent_cmplt, 0); if (rc & EXIT_FLAG) { pid = fork(); if (pid) return rc; rc = 0; } percent_cmplt = 100; for_each_dev_to_init(dev) { if (!dev->do_init || dev->done) continue; if (ipr_read_dev_attr(dev->dev, "type", dev_type, 100)) { dev->done = 1; continue; } if (sscanf(dev_type, "%d", &type) != 1) { dev->done = 1; continue; } if (dev->dev_type == IPR_AF_DASD_DEVICE) { if (type != IPR_TYPE_AF_DISK) { dev->done = 1; continue; } rc = ipr_query_command_status(dev->dev, &cmd_status); if (rc || cmd_status.num_records == 0) { dev->done = 1; continue; } status_record = cmd_status.record; if (status_record->status == IPR_CMD_STATUS_FAILED) { done_bad = 1; dev->done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else dev->done = 1; } else if (dev->dev_type == IPR_JBOD_DASD_DEVICE) { if (type != TYPE_DISK) { dev->done = 1; continue; } /* Send Test Unit Ready to find percent complete in sense data. */ rc = ipr_test_unit_ready(dev->dev, &sense_data); if (rc < 0) { dev->done = 1; done_bad = 1; continue; } if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70 && (sense_data.sense_key & 0x0F) == 0x02 && sense_data.add_sense_code == 0x04 && sense_data.add_sense_code_qual == 0x04) { dev->cmplt = ((int)sense_data.sense_key_spec[1]*100)/0x100; if (dev->cmplt < percent_cmplt) percent_cmplt = dev->cmplt; not_done = 1; } else dev->done = 1; } } if (!not_done) { for_each_dev_to_init(dev) { if (!dev->do_init) continue; ioa = dev->ioa; if (ipr_is_gscsi(dev->dev)) { rc = ipr_test_unit_ready(dev->dev, &sense_data); if (rc) { done_bad = 1; } else if (!ipr_is_af_blk_size(ioa, dev->new_block_size)) { ipr_write_dev_attr(dev->dev, "rescan", "1"); ipr_init_dev(dev->dev); } } if (dev->new_block_size != 0) { if (ipr_is_af_blk_size(ioa, dev->new_block_size)) enable_af(dev->dev); evaluate_device(dev->dev, ioa, dev->new_block_size); } if (ipr_is_af_blk_size(ioa, dev->new_block_size) || ipr_is_af_dasd_device(dev->dev)) ipr_add_zeroed_dev(dev->dev); } flush_stdscr(); if (done_bad) { if (!pid) exit(0); /* Initialize and format failed */ return 51 | EXIT_FLAG; } format_done = 1; check_current_config(false); if (!pid) exit(0); /* Initialize and format completed successfully */ return 50 | EXIT_FLAG; } not_done = 0; sleep(1); } } /** * send_dev_inits - * @i_con: i_container struct * * Returns: * 50 | EXIT_FLAG if success / 51 | EXIT_FLAG on failure **/ int send_dev_inits(i_container *i_con) { u8 num_devs = 0; struct devs_to_init_t *cur_dev_init; int rc = 0; struct ipr_query_res_state res_state; u8 ioctl_buffer[IOCTL_BUFFER_SIZE]; struct ipr_mode_parm_hdr *mode_parm_hdr; struct ipr_block_desc *block_desc; struct scsi_dev_data *scsi_dev_data; struct ipr_ioa *ioa; int status; int opens; u8 failure = 0; int max_y, max_x; u8 length; if (use_curses) { getmaxyx(stdscr,max_y,max_x); mvaddstr(max_y-1,0,wait_for_next_screen); refresh(); } for_each_dev_to_init(cur_dev_init) { if (cur_dev_init->do_init && cur_dev_init->dev_type == IPR_AF_DASD_DEVICE) { num_devs++; ioa = cur_dev_init->ioa; rc = ipr_query_resource_state(cur_dev_init->dev, &res_state); if (rc != 0) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } scsi_dev_data = cur_dev_init->dev->scsi_dev_data; if (!scsi_dev_data) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if ((opens != 0) && (!res_state.read_write_prot)) { syslog(LOG_ERR, _("Cannot obtain exclusive access to %s\n"), cur_dev_init->dev->gen_name); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } ipr_disable_qerr(cur_dev_init->dev); if (cur_dev_init->new_block_size == IPR_JBOD_BLOCK_SIZE || cur_dev_init->new_block_size == IPR_JBOD_4K_BLOCK_SIZE) { /* Issue mode select to change block size */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; memset(ioctl_buffer, 0, 255); mode_parm_hdr->block_desc_len = sizeof(struct ipr_block_desc); block_desc = (struct ipr_block_desc *)(mode_parm_hdr + 1); /* Setup block size */ block_desc->block_length[0] = 0x00; block_desc->block_length[1] = cur_dev_init->new_block_size >> 8; block_desc->block_length[2] = cur_dev_init->new_block_size & 0xff; rc = ipr_mode_select(cur_dev_init->dev,ioctl_buffer, sizeof(struct ipr_block_desc) + sizeof(struct ipr_mode_parm_hdr)); if (rc != 0) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } } /* Issue the format. Failure will be detected by query command status */ rc = ipr_format_unit(cur_dev_init->dev); /* FIXME Mandatory lock? */ } else if (cur_dev_init->do_init && cur_dev_init->dev_type == IPR_JBOD_DASD_DEVICE) { num_devs++; ioa = cur_dev_init->ioa; scsi_dev_data = cur_dev_init->dev->scsi_dev_data; if (!scsi_dev_data) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if (opens) { syslog(LOG_ERR, _("Cannot obtain exclusive access to %s\n"), cur_dev_init->dev->gen_name); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } ipr_disable_qerr(cur_dev_init->dev); /* Issue mode select to setup block size */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; memset(ioctl_buffer, 0, 255); mode_parm_hdr->block_desc_len = sizeof(struct ipr_block_desc); block_desc = (struct ipr_block_desc *)(mode_parm_hdr + 1); /* xxx Setup block size */ if (cur_dev_init->new_block_size == ioa->af_block_size) { block_desc->block_length[0] = 0x00; block_desc->block_length[1] = ioa->af_block_size >> 8; block_desc->block_length[2] = ioa->af_block_size & 0xff; } else { block_desc->block_length[0] = 0x00; block_desc->block_length[1] = cur_dev_init->new_block_size >> 8; block_desc->block_length[2] = cur_dev_init->new_block_size & 0xff; } length = sizeof(struct ipr_block_desc) + sizeof(struct ipr_mode_parm_hdr); status = ipr_mode_select(cur_dev_init->dev, &ioctl_buffer, length); if (status && ipr_is_af_blk_size(ioa, cur_dev_init->new_block_size)) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } /* Issue format */ status = ipr_format_unit(cur_dev_init->dev); /* FIXME Mandatory lock? */ if (status) { /* Send a device reset to cleanup any old state */ rc = ipr_reset_device(cur_dev_init->dev); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } } } if (num_devs) rc = dev_init_complete(num_devs); if (failure == 0) return rc; else /* "Initialize and format failed" */ return 51 | EXIT_FLAG; } /** * reclaim_cache - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int reclaim_cache(i_container* i_con) { int rc; struct ipr_ioa *ioa; struct ipr_reclaim_query_data *reclaim_buffer; struct ipr_reclaim_query_data *cur_reclaim_buffer; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; int k; processing(); /* empty the linked list that contains field pointens */ i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_reclaim_cache.header, &header_lines); reclaim_buffer = malloc(sizeof(struct ipr_reclaim_query_data) * num_ioas); for (ioa = ipr_ioa_head, cur_reclaim_buffer = reclaim_buffer; ioa != NULL; ioa = ioa->next, cur_reclaim_buffer++) { rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, cur_reclaim_buffer); if (rc != 0) { ioa->reclaim_data = NULL; continue; } ioa->reclaim_data = cur_reclaim_buffer; if (cur_reclaim_buffer->reclaim_known_needed || cur_reclaim_buffer->reclaim_unknown_needed) { print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0",ioa); found++; } } if (!found) { /* "No Reclaim IOA Cache Storage is necessary" */ rc = 38; goto leave; } do { n_reclaim_cache.body = buffer[toggle&1]; s_out = screen_driver(&n_reclaim_cache, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); leave: for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_reclaim_cache.body = NULL; return rc; } /** * confirm_reclaim - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_reclaim(i_container *i_con) { struct ipr_ioa *ioa, *reclaim_ioa; struct scsi_dev_data *scsi_dev_data; struct ipr_query_res_state res_state; struct ipr_dev *dev; int k, rc; char *buffer[2]; i_container* temp_i_con; struct screen_output *s_out; int header_lines; int toggle=1; int ioa_num = 0; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *) temp_i_con->data; if (!ioa) continue; if (strcmp(temp_i_con->field_data, "1") != 0) continue; if (ioa->reclaim_data->reclaim_known_needed || ioa->reclaim_data->reclaim_unknown_needed) { reclaim_ioa = ioa; ioa_num++; } } if (ioa_num == 0) /* "Invalid option. No devices selected." */ return 17; else if (ioa_num > 1) /* "Error. More than one unit was selected." */ return 16; /* One IOA selected, ready to proceed */ body_init_status(buffer, n_confirm_reclaim.header, &header_lines); for_each_dev(reclaim_ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (!scsi_dev_data || !ipr_is_af(dev)) continue; /* Do a query resource state to see whether or not the device will be affected by the operation */ rc = ipr_query_resource_state(dev, &res_state); if (rc != 0) res_state.not_oper = 1; if (!res_state.read_write_prot && !res_state.not_oper) continue; print_dev(k, dev, buffer, "1", k); } i_con = free_i_con(i_con); /* Save the chosen IOA for later use */ add_i_con(i_con, "\0", reclaim_ioa); do { n_confirm_reclaim.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_reclaim, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_reclaim.body = NULL; return rc; } /** * reclaim_warning - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int reclaim_warning(i_container *i_con) { struct ipr_ioa *reclaim_ioa; int k, rc; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; reclaim_ioa = (struct ipr_ioa *)i_con->data; if (!reclaim_ioa) /* "Invalid option. No devices selected." */ return 17; for (k = 0; k < 2; k++) { buffer[k] = __body_init_status(n_confirm_reclaim_warning.header, &header_lines, k); buffer[k] = print_device(&reclaim_ioa->ioa, buffer[k], "1", k); } do { n_confirm_reclaim_warning.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_reclaim_warning, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_reclaim_warning.body = NULL; return rc; } /** * get_reclaim_results - * @buf: ipr_reclaim_query_data struct * * Returns: * ipr_reclaim_results struct **/ static char *get_reclaim_results(struct ipr_reclaim_query_data *buf) { char *body = NULL; char buffer[32]; if (buf->reclaim_known_performed) { body = add_string_to_body(body, _("IOA cache storage reclamation has completed. " "Use the number of lost sectors to decide whether " "to restore data from the most recent save media " "or to continue with possible data loss.\n"), "", NULL); if (buf->num_blocks_needs_multiplier) sprintf(buffer, "%12d", ntohs(buf->num_blocks) * IPR_RECLAIM_NUM_BLOCKS_MULTIPLIER); else sprintf(buffer, "%12d", ntohs(buf->num_blocks)); body = add_line_to_body(body, _("Number of lost sectors"), buffer); } else if (buf->reclaim_unknown_performed) { body = add_string_to_body(body, _("IOA cache storage reclamation has completed. " "The number of lost sectors could not be determined.\n"), "", NULL); } else { body = add_string_to_body(body, _("IOA cache storage reclamation has failed.\n"), "", NULL); } return body; } /** * print_reclaim_results - display reclaim results * @buf: ipr_reclaim_query_data struct * * Returns: * nothing **/ static void print_reclaim_results(struct ipr_reclaim_query_data *buf) { char *body = get_reclaim_results(buf); printf("%s", body); free(body); } /** * reclaim_result - * @i_con: i_container struct * * Returns: * EXIT_FLAG if success / 37 | EXIT_FLAG on failure **/ int reclaim_result(i_container *i_con) { int rc; struct ipr_ioa *reclaim_ioa; int max_y,max_x; struct screen_output *s_out; int action; reclaim_ioa = (struct ipr_ioa *) i_con->data; action = IPR_RECLAIM_PERFORM; /* Everything going according to plan. Proceed with reclaim. */ getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw("Please wait - reclaim in progress"); refresh(); if (reclaim_ioa->reclaim_data->reclaim_unknown_needed) action |= IPR_RECLAIM_UNKNOWN_PERM; rc = ipr_reclaim_cache_store(reclaim_ioa, action, reclaim_ioa->reclaim_data); if (rc != 0) /* "Reclaim IOA Cache Storage failed" */ return (EXIT_FLAG | 37); memset(reclaim_ioa->reclaim_data, 0, sizeof(struct ipr_reclaim_query_data)); rc = ipr_reclaim_cache_store(reclaim_ioa, IPR_RECLAIM_QUERY, reclaim_ioa->reclaim_data); if (rc != 0) /* "Reclaim IOA Cache Storage failed" */ return (EXIT_FLAG | 37); n_reclaim_result.body = get_reclaim_results(reclaim_ioa->reclaim_data); s_out = screen_driver(&n_reclaim_result, 0, NULL); free(s_out); free(n_reclaim_result.body); n_reclaim_result.body = NULL; rc = ipr_reclaim_cache_store(reclaim_ioa, IPR_RECLAIM_RESET, reclaim_ioa->reclaim_data); if (rc != 0) rc = (EXIT_FLAG | 37); else rc = EXIT_FLAG; return rc; } /** * battery_maint - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int battery_maint(i_container *i_con) { int rc, k; struct ipr_reclaim_query_data *cur_reclaim_buffer; struct ipr_ioa *ioa; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; i_con = free_i_con(i_con); body_init_status(buffer, n_battery_maint.header, &header_lines); cur_reclaim_buffer = malloc(sizeof(struct ipr_reclaim_query_data) * num_ioas); for (ioa = ipr_ioa_head; ioa; ioa = ioa->next, cur_reclaim_buffer++) { rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY | IPR_RECLAIM_EXTENDED_INFO, cur_reclaim_buffer); if (rc != 0) { ioa->reclaim_data = NULL; continue; } ioa->reclaim_data = cur_reclaim_buffer; if (cur_reclaim_buffer->rechargeable_battery_type) { i_con = add_i_con(i_con, "\0",ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } } if (!found) { /* No configured resources contain a cache battery pack */ rc = 44; goto leave; } do { n_battery_maint.body = buffer[toggle&1]; s_out = screen_driver(&n_battery_maint, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); leave: for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_battery_maint.body = NULL; return rc; } /** * get_battery_info - * @ioa: ipr ioa struct * * Returns: * char * **/ static char *get_battery_info(struct ipr_ioa *ioa) { struct ipr_reclaim_query_data *reclaim_data = ioa->reclaim_data; char buffer[128]; struct ipr_ioa_vpd ioa_vpd; struct ipr_cfc_vpd cfc_vpd; char product_id[IPR_PROD_ID_LEN+1]; char serial_num[IPR_SERIAL_NUM_LEN+1]; char *body = NULL; memset(&ioa_vpd, 0, sizeof(ioa_vpd)); memset(&cfc_vpd, 0, sizeof(cfc_vpd)); ipr_inquiry(&ioa->ioa, IPR_STD_INQUIRY, &ioa_vpd, sizeof(ioa_vpd)); ipr_inquiry(&ioa->ioa, 1, &cfc_vpd, sizeof(cfc_vpd)); ipr_strncpy_0(product_id, (char *)ioa_vpd.std_inq_data.vpids.product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)cfc_vpd.serial_num, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Resource Name"), ioa->ioa.gen_name); body = add_line_to_body(body,_("Serial Number"), serial_num); body = add_line_to_body(body,_("Type"), product_id); body = add_line_to_body(body,_("PCI Address"), ioa->pci_address); if (ioa->sis64) body = add_line_to_body(body,_("Resource Path"), ioa->ioa.scsi_dev_data->res_path); sprintf(buffer,"%d", ioa->host_num); body = add_line_to_body(body,_("SCSI Host Number"), buffer); switch (reclaim_data->rechargeable_battery_type) { case IPR_BATTERY_TYPE_NICD: sprintf(buffer,_("Nickel Cadmium (NiCd)")); break; case IPR_BATTERY_TYPE_NIMH: sprintf(buffer,_("Nickel Metal Hydride (NiMH)")); break; case IPR_BATTERY_TYPE_LIION: sprintf(buffer,_("Lithium Ion (LiIon)")); break; default: sprintf(buffer,_("Unknown")); break; } body = add_line_to_body(body,_("Battery type"), buffer); switch (reclaim_data->rechargeable_battery_error_state) { case IPR_BATTERY_NO_ERROR_STATE: sprintf(buffer,_("No battery warning")); break; case IPR_BATTERY_WARNING_STATE: sprintf(buffer,_("Battery warning issued")); break; case IPR_BATTERY_ERROR_STATE: sprintf(buffer,_("Battery error issued")); break; default: sprintf(buffer,_("Unknown")); break; } body = add_line_to_body(body,_("Battery state"), buffer); sprintf(buffer,"%d",htons(reclaim_data->raw_power_on_time)); body = add_line_to_body(body,_("Power on time (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->adjusted_power_on_time)); body = add_line_to_body(body,_("Adjusted power on time (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->estimated_time_to_battery_warning)); body = add_line_to_body(body,_("Estimated time to warning (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->estimated_time_to_battery_failure)); body = add_line_to_body(body,_("Estimated time to error (days)"), buffer); if (reclaim_data->conc_maint_battery) sprintf(buffer, "%s", _("Yes")); else sprintf(buffer, "%s", _("No")); body = add_line_to_body(body,_("Concurrently maintainable battery pack"), buffer); if (reclaim_data->battery_replace_allowed) sprintf(buffer, "%s", _("Yes")); else sprintf(buffer, "%s", _("No")); body = add_line_to_body(body,_("Battery pack can be safely replaced"), buffer); return body; } /** * show_battery_info - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int show_battery_info(struct ipr_ioa *ioa) { struct screen_output *s_out; int rc; n_show_battery_info.body = get_battery_info(ioa); s_out = screen_driver(&n_show_battery_info, 0, NULL); free(n_show_battery_info.body); n_show_battery_info.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_force_battery_error - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_force_battery_error(void) { int rc,k; struct ipr_ioa *ioa; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_force_battery_error.header, &header_lines); for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; print_dev(k, &ioa->ioa, buffer, "2", k); } do { n_confirm_force_battery_error.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_force_battery_error, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_force_battery_error.body = NULL; return rc; } /** * enable_battery - * @i_con: i_container struct * * Returns: * 71 | EXIT_FLAG if success / 70 | EXIT_FLAG on failure **/ int enable_battery(i_container *i_con) { int rc, reclaim_rc; struct ipr_ioa *ioa; reclaim_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_RESET_BATTERY_ERROR | IPR_RECLAIM_EXTENDED_INFO, ioa->reclaim_data); if (rc != 0) reclaim_rc = 70; if (ioa->reclaim_data->action_status != IPR_ACTION_SUCCESSFUL) { ioa_err(ioa, "Start IOA cache failed.\n"); reclaim_rc = 70; } } if (reclaim_rc != RC_SUCCESS) return reclaim_rc | EXIT_FLAG; return 71 | EXIT_FLAG; } /** * confirm_enable_battery - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_enable_battery(void) { int rc,k; struct ipr_ioa *ioa; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_start_cache.header, &header_lines); for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; print_dev(k, &ioa->ioa, buffer, "3", k); } do { n_confirm_start_cache.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_start_cache, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_start_cache.body = NULL; return rc; } /** * battery_fork - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int battery_fork(i_container *i_con) { int rc = 0; int force_error = 0; struct ipr_ioa *ioa; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)temp_i_con->data; if (ioa == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "3") == 0) { ioa->ioa.is_reclaim_cand = 1; return confirm_enable_battery(); } else if (strcmp(input, "2") == 0) { force_error++; ioa->ioa.is_reclaim_cand = 1; } else if (strcmp(input, "1") == 0) { rc = show_battery_info(ioa); return rc; } else ioa->ioa.is_reclaim_cand = 0; } if (force_error) { rc = confirm_force_battery_error(); return rc; } return INVALID_OPTION_STATUS; } /** * force_battery_error - * @i_con: i_container struct * * Returns: * 72 | EXIT_FLAG if success / 43 | EXIT_FLAG on failure **/ int force_battery_error(i_container *i_con) { int rc, reclaim_rc; struct ipr_ioa *ioa; struct ipr_reclaim_query_data reclaim_buffer; reclaim_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_FORCE_BATTERY_ERROR, &reclaim_buffer); if (rc != 0) reclaim_rc = 43; if (reclaim_buffer.action_status != IPR_ACTION_SUCCESSFUL) { ioa_err(ioa, "Battery Force Error failed.\n"); reclaim_rc = 43; } } if (reclaim_rc != RC_SUCCESS) return reclaim_rc | EXIT_FLAG; return 72 | EXIT_FLAG; } /** * bus_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int bus_config(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; u8 ioctl_buffer[255]; struct ipr_mode_parm_hdr *mode_parm_hdr; struct ipr_mode_page_28_header *modepage_28_hdr; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_bus_config.header, &header_lines); for_each_ioa(ioa) { if ((!ioa_is_spi(ioa) || ioa->is_aux_cache) && !ipr_debug) continue; /* mode sense page28 to focal point */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; rc = ipr_mode_sense(&ioa->ioa, 0x28, mode_parm_hdr); if (rc != 0) continue; modepage_28_hdr = (struct ipr_mode_page_28_header *) (mode_parm_hdr + 1); if (modepage_28_hdr->num_dev_entries == 0) continue; i_con = add_i_con(i_con,"\0",(char *)ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } if (!found) { n_bus_config_fail.body = body_init(n_bus_config_fail.header, NULL); s_out = screen_driver(&n_bus_config_fail, 0, NULL); free(n_bus_config_fail.body); n_bus_config_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_bus_config.body = buffer[toggle&1]; s_out = screen_driver(&n_bus_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_bus_config.body = NULL; return rc; } enum attr_type {qas_capability_type, scsi_id_type, bus_width_type, max_xfer_rate_type}; struct bus_attr { enum attr_type type; u8 bus; struct ipr_scsi_buses *page_28; struct ipr_ioa *ioa; struct bus_attr *next; }; struct bus_attr *bus_attr_head = NULL; /** * get_bus_attr_buf - Start array protection for an array * @bus_attr: bus_attr struct * * Returns: * bus_attr struct **/ static struct bus_attr *get_bus_attr_buf(struct bus_attr *bus_attr) { if (!bus_attr) { bus_attr_head = malloc(sizeof(struct bus_attr)); bus_attr_head->next = NULL; return bus_attr_head; } bus_attr->next = malloc(sizeof(struct bus_attr)); bus_attr->next->next = NULL; return bus_attr->next; } /** * free_all_bus_attr_buf - * * Returns: * nothing **/ static void free_all_bus_attr_buf(void) { struct bus_attr *bus_attr; while (bus_attr_head) { bus_attr = bus_attr_head->next; free(bus_attr_head); bus_attr_head = bus_attr; } } /** * get_changeable_bus_attr - * @ioa: ipr ioa struct * @page28: ipr_scsi_buses struct * @num_buses: number of busses * * Returns: * nothing **/ static void get_changeable_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *page28, int num_buses) { int j; struct ipr_dev *dev; for (j = 0; j < num_buses; j++) { page28->bus[j].qas_capability = 0; if (ioa->scsi_id_changeable) page28->bus[j].scsi_id = 1; else page28->bus[j].scsi_id = 0; page28->bus[j].bus_width = 1; for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (j != dev->scsi_dev_data->channel) continue; if (dev->scsi_dev_data->id <= 7) continue; page28->bus[j].bus_width = 0; break; } page28->bus[j].max_xfer_rate = 1; } if (ioa->protect_last_bus && num_buses) { page28->bus[num_buses-1].qas_capability = 0; page28->bus[num_buses-1].scsi_id = 0; page28->bus[num_buses-1].bus_width = 0; page28->bus[num_buses-1].max_xfer_rate = 0; } } /** * change_bus_attr - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int change_bus_attr(i_container *i_con) { struct ipr_ioa *ioa; struct ipr_dev *dev; int rc, j; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct scsi_dev_data *scsi_dev_data; char scsi_id_str[5][16]; char max_xfer_rate_str[5][16]; i_container *temp_i_con; int found = 0; char *input; struct bus_attr *bus_attr = NULL; int header_lines = 0; char *body = NULL; char buffer[128]; struct screen_output *s_out; u8 bus_num; int bus_attr_menu(struct ipr_ioa *ioa, struct bus_attr *bus_attr, int row, int header_lines); rc = RC_SUCCESS; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)temp_i_con->data; if (!ioa) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; i_con = free_i_con(i_con); /* zero out user page 28 page, this data is used to indicate which fields the user changed */ memset(&page_28_cur, 0, sizeof(struct ipr_scsi_buses)); memset(&page_28_chg, 0, sizeof(struct ipr_scsi_buses)); /* mode sense page28 to get current parms */ rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc != 0) /* "Change SCSI Bus configurations failed" */ return 46 | EXIT_FLAG; get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); body = body_init(n_change_bus_attr.header, &header_lines); sprintf(buffer, "Adapter Location: %s\n", ioa->pci_address); body = add_line_to_body(body, buffer, NULL); header_lines++; for (j = 0; j < page_28_cur.num_buses; j++) { if (ioa->protect_last_bus && j == (page_28_cur.num_buses - 1)) continue; sprintf(buffer,_(" BUS%d"), j); body = add_line_to_body(body, buffer, NULL); if (page_28_chg.bus[j].qas_capability) { body = add_line_to_body(body,_("QAS Capability"), "%9"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = qas_capability_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; if (page_28_cur.bus[j].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) i_con = add_i_con(i_con,_("Disabled"),bus_attr); else i_con = add_i_con(i_con,_("Enabled"),bus_attr); } if (page_28_chg.bus[j].scsi_id) { body = add_line_to_body(body,_("Host SCSI ID"), "%3"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = scsi_id_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; sprintf(scsi_id_str[j],"%d",page_28_cur.bus[j].scsi_id); i_con = add_i_con(i_con,scsi_id_str[j],bus_attr); } if (page_28_chg.bus[j].bus_width) { /* check if 8 bit bus is allowable with current configuration before enabling option */ for_each_dev(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (scsi_dev_data && (scsi_dev_data->id & 0xF8) && (j == scsi_dev_data->channel)) { page_28_chg.bus[j].bus_width = 0; break; } } } if (page_28_chg.bus[j].bus_width) { body = add_line_to_body(body,_("Wide Enabled"), "%4"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = bus_width_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; if (page_28_cur.bus[j].bus_width == 16) i_con = add_i_con(i_con,_("Yes"),bus_attr); else i_con = add_i_con(i_con,_("No"),bus_attr); } if (page_28_chg.bus[j].max_xfer_rate) { body = add_line_to_body(body,_("Maximum Bus Throughput"), "%9"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = max_xfer_rate_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; sprintf(max_xfer_rate_str[j],"%d MB/s", (page_28_cur.bus[j].max_xfer_rate * page_28_cur.bus[j].bus_width)/(10 * 8)); i_con = add_i_con(i_con,max_xfer_rate_str[j],bus_attr); } } n_change_bus_attr.body = body; while (1) { s_out = screen_driver(&n_change_bus_attr, header_lines, i_con); rc = s_out->rc; free(s_out); for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { bus_attr = (struct bus_attr *)temp_i_con->data; rc = bus_attr_menu(ioa, bus_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; bus_num = bus_attr->bus; if (bus_attr->type == qas_capability_type) { if (page_28_cur.bus[bus_num].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) sprintf(temp_i_con->field_data,_("Disable")); else sprintf(temp_i_con->field_data,_("Enable")); } else if (bus_attr->type == scsi_id_type) { sprintf(temp_i_con->field_data,"%d",page_28_cur.bus[bus_num].scsi_id); } else if (bus_attr->type == bus_width_type) { if (page_28_cur.bus[bus_num].bus_width == 16) sprintf(temp_i_con->field_data,_("Yes")); else sprintf(temp_i_con->field_data,_("No")); } else if (bus_attr->type == max_xfer_rate_type) { sprintf(temp_i_con->field_data,"%d MB/s", (page_28_cur.bus[bus_num].max_xfer_rate * page_28_cur.bus[bus_num].bus_width)/(10 * 8)); } break; } } if (rc) goto leave; } leave: free_all_bus_attr_buf(); free(n_change_bus_attr.body); n_change_bus_attr.body = NULL; free_i_con(i_con); return rc; } /** * confirm_change_bus_attr - * @i_con: i_container struct * * Returns: * 0 if success / 45 | EXIT_FLAG on failure **/ int confirm_change_bus_attr(i_container *i_con) { struct ipr_ioa *ioa; int rc, j; struct ipr_scsi_buses *page_28_cur; struct ipr_scsi_buses page_28_chg; char scsi_id_str[5][16]; char max_xfer_rate_str[5][16]; int header_lines = 0; char *body = NULL; char buffer[128]; struct screen_output *s_out; rc = RC_SUCCESS; page_28_cur = bus_attr_head->page_28; ioa = bus_attr_head->ioa; get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur->num_buses); body = body_init(n_confirm_change_bus_attr.header, &header_lines); body = add_line_to_body(body, ioa->ioa.gen_name, NULL); header_lines++; for (j = 0; j < page_28_cur->num_buses; j++) { if (ioa->protect_last_bus && j == (page_28_cur->num_buses - 1)) continue; sprintf(buffer,_(" BUS%d"), j); body = add_line_to_body(body, buffer, NULL); if (page_28_chg.bus[j].qas_capability) { if (page_28_cur->bus[j].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) body = add_line_to_body(body, _("QAS Capability"), _("Disable")); else body = add_line_to_body(body, _("QAS Capability"), _("Enable")); } if (page_28_chg.bus[j].scsi_id) { sprintf(scsi_id_str[j],"%d",page_28_cur->bus[j].scsi_id); body = add_line_to_body(body,_("Host SCSI ID"), scsi_id_str[j]); } if (page_28_chg.bus[j].bus_width) { if (page_28_cur->bus[j].bus_width == 16) body = add_line_to_body(body,_("Wide Enabled"), _("Yes")); else body = add_line_to_body(body,_("Wide Enabled"), _("No")); } if (page_28_chg.bus[j].max_xfer_rate) { sprintf(max_xfer_rate_str[j],"%d MB/s", (page_28_cur->bus[j].max_xfer_rate * page_28_cur->bus[j].bus_width)/(10 * 8)); body = add_line_to_body(body,_("Maximum Bus Throughput"), max_xfer_rate_str[j]); } } n_confirm_change_bus_attr.body = body; s_out = screen_driver(&n_confirm_change_bus_attr, header_lines, NULL); rc = s_out->rc; free(s_out); free(n_confirm_change_bus_attr.body); n_confirm_change_bus_attr.body = NULL; if (rc) return rc; processing(); rc = ipr_set_bus_attr(ioa, page_28_cur, 1); if (!rc) rc = 45 | EXIT_FLAG; return rc; } /** * bus_attr_menu - * @ioa: ipr ioa struct * @bus_attr: bus_attr struct * @start_row: starting row number * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure **/ int bus_attr_menu(struct ipr_ioa *ioa, struct bus_attr *bus_attr, int start_row, int header_lines) { int i, scsi_id, found; int num_menu_items; int menu_index = 0; ITEM **menu_item = NULL; struct ipr_scsi_buses *page_28_cur; struct ipr_dev *dev; int *userptr; int *retptr; u8 bus_num; int max_bus_width, max_xfer_rate; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ page_28_cur = bus_attr->page_28; bus_num = bus_attr->bus; if (bus_attr->type == qas_capability_type) { num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_item[0] = new_item("Enable",""); userptr[0] = 2; set_item_userptr(menu_item[0], (char *)&userptr[0]); menu_item[1] = new_item("Disable",""); userptr[1] = 1; set_item_userptr(menu_item[1], (char *)&userptr[1]); menu_item[2] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].qas_capability != *retptr)) { page_28_cur->bus[bus_num].qas_capability = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == scsi_id_type) { num_menu_items = 16; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; for (scsi_id = 0, found = 0; scsi_id < 16; scsi_id++, found = 0) { for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (scsi_id == dev->scsi_dev_data->id && bus_num == dev->scsi_dev_data->channel) { found = 1; break; } } if (!found) { switch (scsi_id) { case 0: menu_item[menu_index] = new_item("0",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 1: menu_item[menu_index] = new_item("1",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 2: menu_item[menu_index] = new_item("2",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 3: menu_item[menu_index] = new_item("3",""); userptr[menu_index] = 3; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 4: menu_item[menu_index] = new_item("4",""); userptr[menu_index] = 4; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 5: menu_item[menu_index] = new_item("5",""); userptr[menu_index] = 5; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 6: menu_item[menu_index] = new_item("6",""); userptr[menu_index] = 6; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 7: menu_item[menu_index] = new_item("7",""); userptr[menu_index] = 7; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; default: break; } } } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].scsi_id != *retptr)) { page_28_cur->bus[bus_num].scsi_id = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == bus_width_type) { num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("No",""); userptr[menu_index] = 8; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; max_bus_width = 16; if (max_bus_width == 16) { menu_item[menu_index] = new_item("Yes",""); userptr[menu_index] = 16; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].bus_width != *retptr)) { page_28_cur->bus[bus_num].bus_width = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == max_xfer_rate_type) { num_menu_items = 7; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; max_xfer_rate = get_max_bus_speed(ioa, bus_num); switch (max_xfer_rate) { case 320: menu_item[menu_index] = new_item("320 MB/s",""); userptr[menu_index] = 3200; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 160: menu_item[menu_index] = new_item("160 MB/s",""); userptr[menu_index] = 1600; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 80: menu_item[menu_index] = new_item(" 80 MB/s",""); userptr[menu_index] = 800; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 40: menu_item[menu_index] = new_item(" 40 MB/s",""); userptr[menu_index] = 400; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 20: menu_item[menu_index] = new_item(" 20 MB/s",""); userptr[menu_index] = 200; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 10: menu_item[menu_index] = new_item(" 10 MB/s",""); userptr[menu_index] = 100; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 5: menu_item[menu_index] = new_item(" 5 MB/s",""); userptr[menu_index] = 50; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; default: menu_item[menu_index] = new_item(" ",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].max_xfer_rate != ((*retptr) * 8)/page_28_cur->bus[bus_num].bus_width)) { page_28_cur->bus[bus_num].max_xfer_rate = ((*retptr) * 8)/page_28_cur->bus[bus_num].bus_width; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * display_menu - * @menu_item: menu items * @menu_start_row: start row * @menu_index_max: max menu index * @userptrptr: * * Returns: * 0 if success / non-zero on failure FIXME **/ int display_menu(ITEM **menu_item, int menu_start_row, int menu_index_max, int **userptrptr) { ITEM *cur_item; MENU *menu; WINDOW *box1_win, *box2_win, *field_win; int menu_rows, menu_cols, menu_rows_disp; int menu_index; int use_box2 = 0; int cur_item_index; int ch, i; int rc = RC_SUCCESS; int max_y,max_x; getmaxyx(stdscr,max_y,max_x); menu = new_menu(menu_item); scale_menu(menu, &menu_rows, &menu_cols); menu_rows_disp = menu_rows; /* check if fits in current screen, + 1 is added for bottom border, +1 is added for buffer */ if ((menu_start_row + menu_rows + 1 + 1) > max_y) { menu_start_row = max_y - (menu_rows + 1 + 1); if (menu_start_row < (8 + 1)) { /* size is adjusted by 2 to allow for top and bottom border, add +1 for buffer */ menu_rows_disp = max_y - (8 + 2 + 1); menu_start_row = (8 + 1); } } if (menu_rows > menu_rows_disp) { set_menu_format(menu, menu_rows_disp, 1); use_box2 = 1; } set_menu_mark(menu, "*"); scale_menu(menu, &menu_rows, &menu_cols); box1_win = newwin(menu_rows + 2, menu_cols + 2, menu_start_row - 1, 54); field_win = newwin(menu_rows, menu_cols, menu_start_row, 55); set_menu_win(menu, field_win); post_menu(menu); box(box1_win,ACS_VLINE,ACS_HLINE); if (use_box2) { box2_win = newwin(menu_rows + 2, 3, menu_start_row - 1, 54 + menu_cols + 1); box(box2_win,ACS_VLINE,ACS_HLINE); /* initialize scroll bar */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "#"); /* put down arrows in */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "v"); } wnoutrefresh(box1_win); menu_index = 0; while (1) { if (use_box2) wnoutrefresh(box2_win); wnoutrefresh(field_win); doupdate(); ch = getch(); if (IS_ENTER_KEY(ch)) { cur_item = current_item(menu); cur_item_index = item_index(cur_item); *userptrptr = item_userptr(menu_item[cur_item_index]); break; } else if ((ch == KEY_DOWN) || (ch == '\t')) { if (use_box2 && (menu_index < (menu_index_max - 1))) { menu_index++; if (menu_index == menu_rows_disp) { /* put up arrows in */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "^"); } if (menu_index == (menu_index_max - 1)) { /* remove down arrows */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "#"); } } menu_driver(menu,REQ_DOWN_ITEM); } else if (ch == KEY_UP) { if (use_box2 && menu_index != 0) { menu_index--; if (menu_index == (menu_index_max - menu_rows_disp - 1)) { /* put down arrows in */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "v"); } if (menu_index == 0) { /* remove up arrows */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "#"); } } menu_driver(menu,REQ_UP_ITEM); } else if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } } unpost_menu(menu); free_menu(menu); wclear(field_win); wrefresh(field_win); delwin(field_win); wclear(box1_win); wrefresh(box1_win); delwin(box1_win); if (use_box2) { wclear(box2_win); wrefresh(box2_win); delwin(box2_win); } return rc; } /** * driver_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int driver_config(i_container *i_con) { int rc, k; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle = 0; processing(); /* empty the linked list that contains field pointens */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_driver_config.header, &header_lines); for_each_ioa(ioa) { i_con = add_i_con(i_con,"\0",ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); } do { n_driver_config.body = buffer[toggle&1]; s_out = screen_driver(&n_driver_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_driver_config.body = NULL; return rc; } /** * get_log_level - return the log level * @ioa: ipr ioa struct * * Returns: * log level value if success / 0 on failure **/ int get_log_level(struct ipr_ioa *ioa) { char value_str[100]; int value; if (ipr_read_host_attr(ioa, "log_level", value_str, 100) < 0) return 0; sscanf(value_str, "%d", &value); return value; } /** * set_log_level - set the log level * @ioa: ipr ioa struct * @log_level: log level string * * Returns: * nothing **/ void set_log_level(struct ipr_ioa *ioa, char *log_level) { ipr_write_host_attr(ioa, "log_level", log_level, strlen(log_level)); } /** * change_driver_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int change_driver_config(i_container *i_con) { int rc, i, digits; int found = 0; i_container *temp_i_con; struct ipr_ioa *ioa; struct screen_output *s_out; char buffer[128]; char *body = NULL; char *input; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)(temp_i_con->data); if (ioa == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return 2; i_con = free_i_con(i_con); /* i_con to return field data */ i_con = add_i_con(i_con,"",NULL); body = body_init(n_change_driver_config.header, NULL); sprintf(buffer,"Adapter Location: %s\n", ioa->pci_address); body = add_line_to_body(body, buffer, NULL); sprintf(buffer,"%d", get_log_level(ioa)); body = add_line_to_body(body,_("Current Log Level"), buffer); body = add_line_to_body(body,_("New Log Level"), "%2"); n_change_driver_config.body = body; s_out = screen_driver(&n_change_driver_config, 0, i_con); free(n_change_driver_config.body); n_change_driver_config.body = NULL; rc = s_out->rc; if (!rc) { for (i = 0, digits = 0; i < strlen(i_con->field_data); i++) { if (isdigit(i_con->field_data[i])) { digits++; } else if (!isspace(i_con->field_data[i])) { rc = 48; break; } } if (!rc && digits) { set_log_level(ioa, i_con->field_data); rc = 57; } } free(s_out); return rc; } /** * disk_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int disk_config(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_disk_config.header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary || ioa->ioa_dead || !ioa->ioa.scsi_dev_data) continue; num_lines += print_standalone_disks(ioa, &i_con, buffer, 0); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 0); num_lines += print_vsets(ioa, &i_con, buffer, 0); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_disk_config.body = buffer[toggle&1]; s_out = screen_driver(&n_disk_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_disk_config.body = NULL; return rc; } struct disk_config_attr { int option; int queue_depth; int format_timeout; int tcq_enabled; }; /** * disk_config_menu - * @disk_config_attr: disk_config_attr struct * @start_row: start row number * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure FIXME **/ static int disk_config_menu(struct disk_config_attr *disk_config_attr, int start_row, int header_lines) { int i; int num_menu_items; int menu_index; ITEM **menu_item = NULL; int *retptr; int *userptr = NULL; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ if (disk_config_attr->option == 1) { /* queue depth*/ rc = display_input(start_row, &disk_config_attr->queue_depth); if (disk_config_attr->queue_depth > 128) disk_config_attr->queue_depth = 128; } else if (disk_config_attr->option == 3) { /* tag command queue.*/ num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("No",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Yes",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) disk_config_attr->tcq_enabled = *retptr; i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (disk_config_attr->option == 2) { /* format t.o.*/ num_menu_items = 5; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("8 hr",""); userptr[menu_index] = 8; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("4 hr",""); userptr[menu_index] = 4; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("3 hr",""); userptr[menu_index] = 3; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("2 hr",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("1 hr",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) disk_config_attr->format_timeout = *retptr; i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * change_disk_config - * @i_con: i_container struct * * Returns: * non-zero **/ int change_disk_config(i_container * i_con) { int rc; i_container *i_con_head_saved; struct ipr_dev *dev; char qdepth_str[4]; char format_timeout_str[5]; char buffer[128]; i_container *temp_i_con; int found = 0; char *input; struct disk_config_attr disk_config_attr[3]; struct disk_config_attr *config_attr = NULL; struct ipr_disk_attr disk_attr; int header_lines = 0; char *body = NULL; struct screen_output *s_out; struct ipr_array_record *array_record; int tcq_warning = 0; int tcq_blocked = 0; int tcq_enabled = 0; rc = RC_SUCCESS; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; rc = ipr_get_dev_attr(dev, &disk_attr); if (rc) return 66 | EXIT_FLAG; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = i_con = NULL; body = body_init(n_change_disk_config.header, &header_lines); sprintf(buffer, "Device: %s %s\n", dev->scsi_dev_data->sysfs_device_name, dev->dev_name); header_lines += 2; if (ipr_is_volume_set(dev) || page0x0a_setup(dev)) header_lines += 1; /* xxx else if (ipr_is_af_dasd_device(dev) && disk_attr.queue_depth > 1) tcq_warning = 1; else if (ipr_is_gscsi(dev) && disk_attr.tcq_enabled) tcq_warning = 1; else tcq_blocked = 1; */ if (tcq_warning) { body = add_line_to_body(body, buffer, NULL); body = add_string_to_body(body, _("Note: Tagged queuing to this device is ENABLED " "and it does not support the required " "control mode page settings to run safely in this mode!\n\n"), "", &header_lines); } else if (tcq_blocked) { body = add_line_to_body(body, buffer, NULL); body = add_string_to_body(body, _("Note: Tagged queuing to this device is disabled by default " "since it does not support the required control mode page " "settings to run safely in this mode.\n\n"), "", &header_lines); } if (ipr_force) tcq_warning = tcq_blocked = 0; if (!tcq_blocked || ipr_is_gscsi(dev)) { body = add_line_to_body(body,_("Queue Depth"), "%3"); disk_config_attr[0].option = 1; disk_config_attr[0].queue_depth = disk_attr.queue_depth; sprintf(qdepth_str,"%d",disk_config_attr[0].queue_depth); i_con = add_i_con(i_con, qdepth_str, &disk_config_attr[0]); } array_record = dev->array_rcd; if (array_record && ipr_is_vset_record(array_record->common.record_id)) { /* VSET, no further fields */ } else if (ipr_is_af_dasd_device(dev)) { disk_config_attr[1].option = 2; disk_config_attr[1].format_timeout = disk_attr.format_timeout / (60 * 60); body = add_line_to_body(body,_("Format Timeout"), "%4"); sprintf(format_timeout_str,"%d hr",disk_config_attr[1].format_timeout); i_con = add_i_con(i_con, format_timeout_str, &disk_config_attr[1]); } else if (!tcq_blocked) { disk_config_attr[2].option = 3; disk_config_attr[2].tcq_enabled = disk_attr.tcq_enabled; body = add_line_to_body(body,_("Tag Command Queueing"), "%4"); tcq_enabled = disk_config_attr[2].tcq_enabled; if (disk_config_attr[2].tcq_enabled) i_con = add_i_con(i_con,_("Yes"),&disk_config_attr[2]); else i_con = add_i_con(i_con,_("No"),&disk_config_attr[2]); } n_change_disk_config.body = body; while (1) { s_out = screen_driver(&n_change_disk_config, header_lines, i_con); rc = s_out->rc; free(s_out); found = 0; for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { config_attr = (struct disk_config_attr *)temp_i_con->data; rc = disk_config_menu(config_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; if (config_attr->option == 1) { sprintf(temp_i_con->field_data,"%d", config_attr->queue_depth); disk_attr.queue_depth = config_attr->queue_depth; } else if (config_attr->option == 2) { sprintf(temp_i_con->field_data,"%d hr", config_attr->format_timeout); disk_attr.format_timeout = config_attr->format_timeout * 60 * 60; } else if (config_attr->option == 3) { if (config_attr->tcq_enabled) sprintf(temp_i_con->field_data,_("Yes")); else sprintf(temp_i_con->field_data,_("No")); disk_attr.tcq_enabled = config_attr->tcq_enabled; } found++; break; } } if (rc) goto leave; if (!found) break; } ipr_set_dev_attr(dev, &disk_attr, 1); leave: free(n_change_disk_config.body); n_change_disk_config.body = NULL; free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } struct ioa_config_attr { int option; int preferred_primary; int gscsi_only_ha; int active_active; int caching; }; /** * ioa_config_menu - * @ioa_config_attr: ipr_config_attr struct * @start_row: start row * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure **/ int ioa_config_menu(struct ioa_config_attr *ioa_config_attr, int start_row, int header_lines) { int i; int num_menu_items; int menu_index; ITEM **menu_item = NULL; int *retptr; int *userptr = NULL; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ if (ioa_config_attr->option == 1 || ioa_config_attr->option == 2 || ioa_config_attr->option == 3 || ioa_config_attr->option == 4) { num_menu_items = 4; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; if (ioa_config_attr->option == 1) menu_item[menu_index] = new_item("None",""); else if (ioa_config_attr->option == 2) menu_item[menu_index] = new_item("RAID",""); else if (ioa_config_attr->option == 3) menu_item[menu_index] = new_item("Disabled",""); else menu_item[menu_index] = new_item("Default",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; if (ioa_config_attr->option == 1) menu_item[menu_index] = new_item("Primary",""); else if (ioa_config_attr->option == 2) menu_item[menu_index] = new_item("JBOD",""); else if (ioa_config_attr->option == 3) menu_item[menu_index] = new_item("Enabled",""); else menu_item[menu_index] = new_item("Disabled",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) { if (ioa_config_attr->option == 1) ioa_config_attr->preferred_primary = *retptr; else if (ioa_config_attr->option == 2) ioa_config_attr->gscsi_only_ha = *retptr; else if (ioa_config_attr->option == 3) ioa_config_attr->active_active = *retptr; else ioa_config_attr->caching = *retptr; } i = 0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * change_ioa_config - * @i_con: i_container struct * * Returns: * non-zero on exit **/ int change_ioa_config(i_container * i_con) { int rc; i_container *i_con_head_saved; struct ipr_dev *dev; char pref_str[20]; char buffer[128]; i_container *temp_i_con; int found = 0; char *input; struct ioa_config_attr ioa_config_attr[3]; struct ioa_config_attr *config_attr = NULL; struct ipr_ioa_attr ioa_attr; int header_lines = 0, index = 0; char *body = NULL; struct screen_output *s_out; rc = RC_SUCCESS; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; rc = ipr_get_ioa_attr(dev->ioa, &ioa_attr); if (rc) return 66 | EXIT_FLAG; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = i_con = NULL; body = body_init(n_change_ioa_config.header, &header_lines); sprintf(buffer, "Adapter: %s/%d %s %s\n", dev->ioa->pci_address, dev->ioa->host_num, dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_line_to_body(body, buffer, NULL); header_lines += 2; if (dev->ioa->dual_raid_support) { body = add_line_to_body(body,_("Preferred Dual Adapter State"), "%13"); ioa_config_attr[index].option = 1; ioa_config_attr[index].preferred_primary = ioa_attr.preferred_primary; if (ioa_attr.preferred_primary) sprintf(pref_str, "Primary"); else sprintf(pref_str, "None"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->gscsi_only_ha) { body = add_line_to_body(body,_("High-Availability Mode"), "%13"); ioa_config_attr[index].option = 2; ioa_config_attr[index].gscsi_only_ha = ioa_attr.gscsi_only_ha; if (ioa_attr.gscsi_only_ha) sprintf(pref_str, "JBOD"); else sprintf(pref_str, "Normal"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->asymmetric_access) { body = add_line_to_body(body,_("Active/Active Mode"), "%13"); ioa_config_attr[index].option = 3; ioa_config_attr[index].active_active = ioa_attr.active_active; if (ioa_attr.active_active) sprintf(pref_str, "Enabled"); else sprintf(pref_str, "Disabled"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->has_cache) { body = add_line_to_body(body,_("IOA Caching Mode"), "%13"); ioa_config_attr[index].option = 4; ioa_config_attr[index].caching = ioa_attr.caching; if (ioa_attr.caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) sprintf(pref_str, "Default"); else sprintf(pref_str, "Disabled"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } n_change_ioa_config.body = body; while (1) { s_out = screen_driver(&n_change_ioa_config, header_lines, i_con); rc = s_out->rc; free(s_out); found = 0; for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { config_attr = (struct ioa_config_attr *)temp_i_con->data; rc = ioa_config_menu(config_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; if (config_attr->option == 1) { if (config_attr->preferred_primary) sprintf(temp_i_con->field_data, "Primary"); else sprintf(temp_i_con->field_data, "None"); ioa_attr.preferred_primary = config_attr->preferred_primary; } else if (config_attr->option == 2) { if (config_attr->gscsi_only_ha) sprintf(temp_i_con->field_data, "JBOD"); else sprintf(temp_i_con->field_data, "Normal"); ioa_attr.gscsi_only_ha = config_attr->gscsi_only_ha; } else if (config_attr->option == 3) { if (config_attr->active_active) sprintf(temp_i_con->field_data, "Enabled"); else sprintf(temp_i_con->field_data, "Disabled"); ioa_attr.active_active = config_attr->active_active; } else if (config_attr->option == 4) { if (config_attr->caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) sprintf(temp_i_con->field_data, "Default"); else sprintf(temp_i_con->field_data, "Disabled"); ioa_attr.caching = config_attr->caching; } found++; break; } } if (rc) goto leave; if (!found) break; } processing(); rc = ipr_set_ioa_attr(dev->ioa, &ioa_attr, 1); check_current_config(false); leave: free(n_change_ioa_config.body); n_change_ioa_config.body = NULL; free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } /** * ioa_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ioa_config(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_ioa_config.header, &header_lines); for_each_ioa(ioa) { if (ioa->ioa.scsi_dev_data == NULL) continue; if (!ioa->dual_raid_support && !ioa->gscsi_only_ha) continue; print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_ioa_config.body = buffer[toggle&1]; s_out = screen_driver(&n_ioa_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_ioa_config.body = NULL; return rc; } /** * download_ucode - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int download_ucode(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct ipr_dev *dev, *vset; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 0; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_download_ucode.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data || ioa->ioa_dead) continue; print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; if (ioa->is_secondary) continue; num_lines += print_standalone_disks(ioa, &i_con, buffer, 0); num_lines += print_sas_ses_devices(ioa, &i_con, buffer, 0); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 0); /* print volume set associated devices*/ for_each_vset(ioa, vset) { num_lines++; for_each_dev_in_vset(vset, dev) { print_dev(k, dev, buffer, "%1", k); i_con = add_i_con(i_con, "\0", dev); num_lines++; } } } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_download_ucode.body = buffer[toggle&1]; s_out = screen_driver(&n_download_ucode, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_download_ucode.body = NULL; return rc; } /** * update_ucode - * @dev: ipr dev struct * @fw_image: ipr_fw_images struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ static int update_ucode(struct ipr_dev *dev, struct ipr_fw_images *fw_image) { char *body; int header_lines, status, time = 0; pid_t pid, done; u8 release_level[5]; u32 fw_version; int rc = 0; body = body_init(n_download_ucode_in_progress.header, &header_lines); n_download_ucode_in_progress.body = body; time_screen_driver(&n_download_ucode_in_progress, time, 0); pid = fork(); if (pid) { do { done = waitpid(pid, &status, WNOHANG); sleep(1); time++; time_screen_driver(&n_download_ucode_in_progress, time, 0); } while (done == 0); } else { if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) ipr_update_ioa_fw(dev->ioa, fw_image, 1); else ipr_update_disk_fw(dev, fw_image, 1); exit(0); } ipr_get_fw_version(dev, release_level); fw_version = release_level[0] << 24 | release_level[1] << 16 | release_level[2] << 8 | release_level[3]; if (fw_version != fw_image->version) rc = 67 | EXIT_FLAG; free(n_download_ucode_in_progress.body); n_download_ucode_in_progress.body = NULL; return rc; } /** * process_choose_ucode - * @dev: ipr dev struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ int process_choose_ucode(struct ipr_dev *dev) { struct ipr_fw_images *list; struct ipr_fw_images *fw_image; char *body = NULL; int header_lines = 0; int list_count, rc, i; char buffer[256]; int found = 0; i_container *i_con_head_saved; i_container *i_con = NULL; i_container *temp_i_con; char *input; int version_swp; char *version; struct screen_output *s_out; u8 release_level[4]; if (!dev->scsi_dev_data) return 67 | EXIT_FLAG; if (dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) rc = get_ioa_firmware_image_list(dev->ioa, &list); else if (dev->scsi_dev_data->type == TYPE_ENCLOSURE || dev->scsi_dev_data->type == TYPE_PROCESSOR) rc = get_ses_firmware_image_list(dev, &list); else rc = get_dasd_firmware_image_list(dev, &list); if (rc < 0) return 67 | EXIT_FLAG; else list_count = rc; ipr_get_fw_version(dev, release_level); if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) { sprintf(buffer, _("Adapter to download: %-8s %-16s\n"), dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_string_to_body(NULL, buffer, "", &header_lines); sprintf(buffer, _("Adapter Location: %s.%d/\n"), dev->ioa->pci_address, dev->ioa->host_num); } else { sprintf(buffer, _("Device to download: %-8s %-16s\n"), dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_string_to_body(NULL, buffer, "", &header_lines); sprintf(buffer, _("Device Location: %d:%d:%d\n"), dev->res_addr[0].bus, dev->res_addr[0].target, dev->res_addr[0].lun); } body = add_string_to_body(body, buffer, "", &header_lines); if (isprint(release_level[0]) && isprint(release_level[1]) && isprint(release_level[2]) && isprint(release_level[3])) sprintf(buffer, "%s%02X%02X%02X%02X (%c%c%c%c)\n\n", _("The current microcode for this device is: "), release_level[0], release_level[1], release_level[2], release_level[3], release_level[0], release_level[1], release_level[2], release_level[3]); else sprintf(buffer, "%s%02X%02X%02X%02X\n\n", _("The current microcode for this device is: "), release_level[0], release_level[1], release_level[2], release_level[3]); body = add_string_to_body(body, buffer, "", &header_lines); body = __body_init(body, n_choose_ucode.header, &header_lines); i_con_head_saved = i_con_head; i_con_head = i_con = NULL; if (list_count) { for (i = 0; i < list_count; i++) { version_swp = htonl(list[i].version); version = (char *)&version_swp; if (isprint(version[0]) && isprint(version[1]) && isprint(version[2]) && isprint(version[3])) sprintf(buffer," %%1 %.8X (%c%c%c%c) %s %s",list[i].version, version[0], version[1], version[2], version[3], list[i].date, list[i].file); else sprintf(buffer," %%1 %.8X %s %s",list[i].version, list[i].date, list[i].file); body = add_line_to_body(body, buffer, NULL); i_con = add_i_con(i_con, "\0", &list[i]); } } else { body = add_line_to_body(body,"(No available images)",NULL); } n_choose_ucode.body = body; s_out = screen_driver(&n_choose_ucode, header_lines, i_con); free(s_out); free(n_choose_ucode.body); n_choose_ucode.body = NULL; for_each_icon(temp_i_con) { fw_image =(struct ipr_fw_images *)(temp_i_con->data); if (dev == NULL) continue; input = temp_i_con->field_data; if (input == NULL) continue; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) goto leave; body = body_init(n_confirm_download_ucode.header, &header_lines); version_swp = htonl(fw_image->version); version = (char *)&version_swp; if (isprint(version[0]) && isprint(version[1]) && isprint(version[2]) && isprint(version[3])) sprintf(buffer," 1 %.8X (%c%c%c%c) %s %s\n",fw_image->version, version[0], version[1], version[2], version[3], fw_image->date, fw_image->file); else sprintf(buffer," 1 %.8X %s %s\n",fw_image->version,fw_image->date, fw_image->file); body = add_line_to_body(body, buffer, NULL); n_confirm_download_ucode.body = body; s_out = screen_driver(&n_confirm_download_ucode, header_lines, i_con); free(n_confirm_download_ucode.body); n_confirm_download_ucode.body = NULL; if (!s_out->rc) rc = update_ucode(dev, fw_image); free(s_out); leave: free(list); i_con = free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } /** * choose_ucode - * @i_con: i_container struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ int choose_ucode(i_container * i_con) { i_container *temp_i_con; struct ipr_dev *dev; char *input; int rc; int dev_num = 0; if (!i_con) return 1; for_each_icon(temp_i_con) { if (!temp_i_con->data) continue; if (!temp_i_con->field_data) continue; dev =(struct ipr_dev *)(temp_i_con->data); input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { rc = process_choose_ucode(dev); if (rc & EXIT_FLAG) return rc; dev_num++; } } if (dev_num == 0) /* Invalid option. No devices selected. */ return 17; return 0; } #define MAX_CMD_LENGTH 1000 /** * log_menu - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int log_menu(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, loop; struct stat file_stat; struct screen_output *s_out; int offset = 0; sprintf(cmnd,"%s/boot.msg",log_root_dir); rc = stat(cmnd, &file_stat); if (rc) /* file does not exist - do not display option */ offset--; for (loop = 0; loop < (n_log_menu.num_opts + offset); loop++) { n_log_menu.body = ipr_list_opts(n_log_menu.body, n_log_menu.options[loop].key, n_log_menu.options[loop].list_str); } n_log_menu.body = ipr_end_list(n_log_menu.body); s_out = screen_driver(&n_log_menu, 0, NULL); free(n_log_menu.body); n_log_menu.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); return rc; } /** * ibm_storage_log_tail - * @i_con: i_container struct * * Returns: * 1 if no status / 65 on failure **/ int ibm_storage_log_tail(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, len; def_prog_mode(); rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f messages", log_root_dir); len += sprintf(cmnd + len," | grep ipr | "); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ localhost kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/\\^M//g\" | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { len = sprintf(cmnd,"cd %s; zcat -f messages | grep ipr |", log_root_dir); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ localhost kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * select_log_file - * @dir_entry: dirent struct * * Returns: * 1 if "messages" if found in the dir_entry->d_name / 0 otherwise **/ static int select_log_file(const struct dirent *dir_entry) { if (dir_entry) { if (strstr(dir_entry->d_name, "messages") == dir_entry->d_name) return 1; } return 0; } /** * compare_log_file - * @log_file1: * @log_file2: * * Returns: * 0 if success / non-zero on failure FIXME **/ static int compare_log_file(const struct dirent **first_dir, const struct dirent **second_dir) { char name1[MAX_CMD_LENGTH], name2[MAX_CMD_LENGTH]; struct stat stat1, stat2; if (strcmp((*first_dir)->d_name, "messages") == 0) return 1; if (strcmp((*second_dir)->d_name, "messages") == 0) return -1; sprintf(name1, "%s/%s", log_root_dir, (*first_dir)->d_name); sprintf(name2, "%s/%s", log_root_dir, (*second_dir)->d_name); if (stat(name1, &stat1)) return 1; if (stat(name2, &stat2)) return -1; if (stat1.st_mtime < stat2.st_mtime) return -1; if (stat1.st_mtime > stat2.st_mtime) return 1; return 0; } /** * ibm_storage_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ibm_storage_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (num_dir_entries) dirent = log_files; if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len," | grep ipr-err | "); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ localhost kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/\\^M//g\" | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { len = sprintf(cmnd,"cd %s; zcat -f %s | grep ipr |", log_root_dir, (*dirent)->d_name); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ localhost kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * kernel_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int kernel_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } if (num_dir_entries) dirent = log_files; rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { /* Probably have a read-only root file system */ len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, "| %s", FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { sprintf(cmnd, "cd %s; zcat -f %s | sed \"s/\\^M//g\" >> "_PATH_TMP".ipr.err/errors", log_root_dir, (*dirent)->d_name); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries > 0) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * iprconfig_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int iprconfig_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (num_dir_entries) dirent = log_files; if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len," | grep iprconfig | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { len = sprintf(cmnd,"cd %s; zcat -f %s | grep iprconfig ", log_root_dir, (*dirent)->d_name); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * kernel_root - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int kernel_root(i_container *i_con) { int rc; struct screen_output *s_out; char *body = NULL; i_con = free_i_con(i_con); /* i_con to return field data */ i_con = add_i_con(i_con,"",NULL); body = body_init(n_kernel_root.header, NULL); body = add_line_to_body(body,_("Current root directory"), log_root_dir); body = add_line_to_body(body,_("New root directory"), "%39"); n_kernel_root.body = body; s_out = screen_driver(&n_kernel_root, 0, i_con); free(n_kernel_root.body); n_kernel_root.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_kernel_root - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_kernel_root(i_container *i_con) { int rc; DIR *dir; char *input; struct screen_output *s_out; char *body = NULL; input = strip_trailing_whitespace(i_con->field_data); if (strlen(input) == 0) return (EXIT_FLAG | 61); dir = opendir(input); if (dir == NULL) /* "Invalid directory" */ return 59; else { closedir(dir); if (strcmp(log_root_dir, input) == 0) /*"Root directory unchanged"*/ return (EXIT_FLAG | 61); } body = body_init(n_confirm_kernel_root.header, NULL); body = add_line_to_body(body,_("New root directory"), input); n_confirm_kernel_root.body = body; s_out = screen_driver(&n_confirm_kernel_root, 0, i_con); free(n_confirm_kernel_root.body); n_confirm_kernel_root.body = NULL; rc = s_out->rc; if (rc == 0) { /*"Root directory changed to %s"*/ strcpy(log_root_dir, i_con->field_data); s_status.str = log_root_dir; rc = (EXIT_FLAG | 60); } else /*"Root directory unchanged"*/ rc = (EXIT_FLAG | 61); free(s_out); return rc; } /** * set_default_editor - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int set_default_editor(i_container *i_con) { int rc = 0; struct screen_output *s_out; char *body = NULL; i_con = free_i_con(i_con); i_con = add_i_con(i_con,"",NULL); body = body_init(n_set_default_editor.header, NULL); body = add_line_to_body(body, _("Current editor"), editor); body = add_line_to_body(body, _("New editor"), "%39"); n_set_default_editor.body = body; s_out = screen_driver(&n_set_default_editor, 0, i_con); free(n_set_default_editor.body); n_set_default_editor.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_set_default_editor - * @i_con: i_container struct * * Returns: * EXIT_FLAG | 62 if editor changed / EXIT_FLAG | 63 if editor unchanged **/ int confirm_set_default_editor(i_container *i_con) { int rc; char *input; struct screen_output *s_out; char *body = NULL; input = strip_trailing_whitespace(i_con->field_data); if (strlen(input) == 0) /*"Editor unchanged"*/ return (EXIT_FLAG | 63); if (strcmp(editor, input) == 0) /*"Editor unchanged"*/ return (EXIT_FLAG | 63); body = body_init(n_confirm_set_default_editor.header, NULL); body = add_line_to_body(body,_("New editor"), input); n_confirm_set_default_editor.body = body; s_out = screen_driver(&n_confirm_set_default_editor, 0, i_con); free(n_confirm_set_default_editor.body); n_confirm_set_default_editor.body = NULL; rc = s_out->rc; if (rc == 0) { /*"Editor changed to %s"*/ strcpy(editor, i_con->field_data); s_status.str = editor; rc = (EXIT_FLAG | 62); } else /*"Editor unchanged"*/ rc = (EXIT_FLAG | 63); free(s_out); return rc; } /** * restore_log_defaults - * @i_con: i_container struct * * Returns: * 64 **/ int restore_log_defaults(i_container *i_con) { strcpy(log_root_dir, DEFAULT_LOG_DIR); strcpy(editor, DEFAULT_EDITOR); return 64; /*"Default log values restored"*/ } /** * ibm_boot_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ibm_boot_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc; int len; struct stat file_stat; sprintf(cmnd,"%s/boot.msg",log_root_dir); rc = stat(cmnd, &file_stat); if (rc) return 2; /* "Invalid option specified" */ def_prog_mode(); rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ len += sprintf(cmnd + len, "boot.msg"); len += sprintf(cmnd + len," | grep ipr |"); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { len = sprintf(cmnd,"cd %s; zcat -f boot.msg | grep ipr | " "sed 's/<[0-9]>ipr-err: //g' | sed 's/<[0-9]>ipr: //g'", log_root_dir); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if ((rc != 0) && (rc != 127)) { s_status.num = rc; return 65; /* "Editor returned %d. Try setting the default editor" */ } else return 1; /* return with no status */ } int get_ses_ioport_status(struct ipr_dev *ses) { struct ipr_query_io_port io_status; int rc = ipr_query_io_dev_port(ses, &io_status); if (!rc) ses->active_suspend = io_status.port_state; else ses->active_suspend = IOA_DEV_PORT_UNKNOWN; return rc; } /** * get_status - * @dev: ipr dev struct * @buf: data buffer * @percent: * @path_status: * * Returns: * nothing **/ static void get_status(struct ipr_dev *dev, char *buf, int percent, int path_status) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_ioa *ioa = dev->ioa; struct ipr_query_res_state res_state; int status, rc; int format_req = 0; int blk_size = 0; struct ipr_mode_pages mode_pages; struct ipr_block_desc *block_desc; struct sense_data_t sense_data; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int percent_cmplt = 0; int format_in_progress = 0; int resync_in_progress = 0; struct ipr_res_redundancy_info info; if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &ioa->ioa) { if (!scsi_dev_data->online) sprintf(buf, "Offline"); else if (ioa->ioa_dead) sprintf(buf, "Not Operational"); else if (ioa->nr_ioa_microcode) sprintf(buf, "Not Ready"); else sprintf(buf, "Operational"); } else if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT) { sprintf(buf, "Empty"); } else { if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (!rc) { for_each_cmd_status(status_record, &cmd_status) { if (dev->array_id != status_record->array_id) continue; if (status_record->status != IPR_CMD_STATUS_IN_PROGRESS) continue; if (status_record->command_code == IPR_RESYNC_ARRAY_PROTECTION) resync_in_progress = 1; percent_cmplt = status_record->percent_complete; } } } else if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if ((rc == 0) && (cmd_status.num_records != 0)) { status_record = cmd_status.record; if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL && status_record->status != IPR_CMD_STATUS_FAILED) { percent_cmplt = status_record->percent_complete; format_in_progress++; } } } else if (ipr_is_gscsi(dev)) { /* Send Test Unit Ready to find percent complete in sense data. */ rc = ipr_test_unit_ready(dev, &sense_data); if ((rc == CHECK_CONDITION) && ((sense_data.error_code & 0x7F) == 0x70) && ((sense_data.sense_key & 0x0F) == 0x02) && /* NOT READY */ (sense_data.add_sense_code == 0x04) && /* LOGICAL UNIT NOT READY */ (sense_data.add_sense_code_qual == 0x04)) {/* FORMAT IN PROGRESS */ percent_cmplt = ((int)sense_data.sense_key_spec[1]*100)/0x100; format_in_progress++; } } memset(&res_state, 0, sizeof(res_state)); if (ipr_is_af(dev)) { rc = ipr_query_resource_state(dev, &res_state); if (rc != 0) res_state.not_oper = 1; } else if (ipr_is_gscsi(dev)) { format_req = 0; /* Issue mode sense/mode select to determine if device needs to be formatted */ status = ipr_mode_sense(dev, 0x0a, &mode_pages); if (status == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) { format_req = 1; } else { if (!status) { if (mode_pages.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)(mode_pages.data); blk_size = block_desc->block_length[2] + (block_desc->block_length[1] << 8) + (block_desc->block_length[0] << 16); if ((blk_size != IPR_JBOD_BLOCK_SIZE) && (blk_size != IPR_JBOD_4K_BLOCK_SIZE)) format_req = 1; } } /* check device with test unit ready */ rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) format_req = 1; else if (rc) res_state.not_oper = 1; } } if (path_status) rc = ipr_query_res_redundancy_info(dev, &info); if (ioa->ioa_dead) sprintf(buf, "Unknown"); else if (path_status && !rc) { if (info.healthy_paths > 1) sprintf(buf, "Redundant"); else if (info.healthy_paths) sprintf(buf, "Single"); else sprintf(buf, "No Paths"); } else if (format_in_progress) sprintf(buf, "%d%% Formatted", percent_cmplt); else if (ioa->sis64 && ipr_is_remote_af_dasd_device(dev)) { if (!scsi_dev_data) sprintf(buf, "Missing"); else sprintf(buf, "Remote"); } else if (!ioa->sis64 && ioa->is_secondary && !scsi_dev_data) sprintf(buf, "Remote"); else if (!scsi_dev_data) sprintf(buf, "Missing"); else if (!scsi_dev_data->online) { if (dev->scsi_dev_data->type == TYPE_ENCLOSURE) { get_ses_ioport_status(dev); if (dev->active_suspend == IOA_DEV_PORT_SUSPEND) sprintf(buf, "Suspend"); else sprintf(buf, "Offline"); } } else if (res_state.not_oper || res_state.not_func) sprintf(buf, "Failed"); else if (res_state.read_write_prot) sprintf(buf, "R/W Protected"); else if (res_state.prot_dev_failed) sprintf(buf, "Failed"); else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { if (res_state.prot_resuming) { if (!percent || (percent_cmplt == 0)) sprintf(buf, "Rebuilding"); else sprintf(buf, "%d%% Rebuilt", percent_cmplt); } else if (resync_in_progress) { if (!percent || (percent_cmplt == 0)) sprintf(buf, "Checking"); else sprintf(buf, "%d%% Checked", percent_cmplt); } else if (res_state.prot_suspended) sprintf(buf, "Degraded"); else if (res_state.degraded_oper || res_state.service_req) sprintf(buf, "Degraded"); else if ((dev->ioa->asymmetric_access && dev->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) || (!dev->ioa->is_secondary && !dev->ioa->asymmetric_access)) sprintf(buf, "Optimized"); else sprintf(buf, "Non-Optimized"); } else if (format_req) sprintf(buf, "Format Required"); else if (ipr_device_is_zeroed(dev)) sprintf(buf, "Zeroed"); else sprintf(buf, "Active"); } } static const struct { u8 status; char *desc; } path_status_desc[] = { { IPR_PATH_CFG_NO_PROB, "Functional" }, { IPR_PATH_CFG_DEGRADED, "Degraded" }, { IPR_PATH_CFG_FAILED, "Failed" }, { IPR_PATH_CFG_SUSPECT, "Suspect" }, { IPR_PATH_NOT_DETECTED, "Missing" }, { IPR_PATH_INCORRECT_CONN, "Incorrect connect" } }; /** * get_phy_state - * @state: state value * * Returns: * state description **/ static const char *get_phy_state(u8 state) { int i; u8 mstate = state & IPR_PATH_CFG_STATUS_MASK; for (i = 0; i < ARRAY_SIZE(path_status_desc); i++) { if (path_status_desc[i].status == mstate) return path_status_desc[i].desc; } return "Unknown"; } static const struct { u8 type; char *desc; } path_type_desc[] = { { IPR_PATH_CFG_IOA_PORT, "IOA port" }, { IPR_PATH_CFG_EXP_PORT, "Expander port" }, { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } }; /** * get_phy_type - * @type: type value * * Returns: * type description **/ static const char *get_phy_type(u8 type) { int i; u8 mtype = type & IPR_PATH_CFG_TYPE_MASK; for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { if (path_type_desc[i].type == mtype) return path_type_desc[i].desc; } return "Unknown"; } static const char *link_rate[] = { "Enabled", "Disabled", "Reset Prob", "SpinupHold", "Port Selec", "Resetting", "Unknown", "Unknown", "1.5Gbps", "3.0Gbps", "6.0Gbps", "Enabled", "Enabled", "Enabled", "Enabled", "Enabled" }; static const struct { u8 state; char *desc; } path_state_desc[] = { { IPR_PATH_STATE_NO_INFO, "Unknown" }, { IPR_PATH_HEALTHY, "Healthy" }, { IPR_PATH_DEGRADED, "Degraded" }, { IPR_PATH_FAILED, "Failed" } }; /** * get_path_state - * @path_state: path_state value * * Returns: * path state description **/ static const char *get_path_state(u8 path_state) { int i; u8 state = path_state & IPR_PATH_STATE_MASK; for (i = 0; i < ARRAY_SIZE(path_state_desc); i++) { if (path_state_desc[i].state == state) return path_state_desc[i].desc; } return "Unknown"; } /** * print_phy - * @cfg: ipr_fabric_config_element struct * @body: message buffer * * Returns: * body **/ static char *print_phy(struct ipr_fabric_config_element *cfg, char *body) { int len = strlen(body); body = realloc(body, len + 256); body[len] = '\0'; len += sprintf(body + len, "%2X/%08X%08X ", cfg->phy, ntohl(cfg->wwid[0]), ntohl(cfg->wwid[1])); len += sprintf(body + len, " %-17s ", get_phy_type(cfg->type_status)); len += sprintf(body + len, " %-17s", get_phy_state(cfg->type_status)); len += sprintf(body + len, " %s \n", link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK]); return body; } /** * print_phy64 - * @cfg: ipr_fabric_config_element struct * @body: message buffer * * Returns: * body **/ static char *print_phy64(struct ipr_fabric_config_element64 *cfg, char *body, int res_path_len) { int i, ff_len; int len = strlen(body); char buffer[IPR_MAX_RES_PATH_LEN]; body = realloc(body, len + 256); body[len] = '\0'; ipr_format_res_path_wo_hyphen(cfg->res_path, buffer, IPR_MAX_RES_PATH_LEN); ff_len = res_path_len - strlen(buffer); for ( i = 0; i < ff_len; i++) strncat(buffer, "F", strlen("F")); len += sprintf(body + len, "%s", buffer); len += sprintf(body + len, "/%08X%08X", ntohl(cfg->wwid[0]), ntohl(cfg->wwid[1])); len += sprintf(body + len, " %-17s ", get_phy_type(cfg->type_status)); len += sprintf(body + len, " %-17s", get_phy_state(cfg->type_status)); len += sprintf(body + len, " %s \n", link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK]); return body; } /** * print_path - * @ioa: ipr_fabric_descriptor struct * @body: message buffer * * Returns: * body **/ static char *print_path(struct ipr_fabric_descriptor *fabric, char *body) { int len = strlen(body); struct ipr_fabric_config_element *cfg; body = realloc(body, len + 256); body[len] = '\0'; len += sprintf(body + len, "%2X/ " "Physical Path ", fabric->ioa_port); if (fabric->path_state & IPR_PATH_ACTIVE) len += sprintf(body + len, " Yes "); else if (fabric->path_state & IPR_PATH_NOT_ACTIVE) len += sprintf(body + len, " No "); else len += sprintf(body + len, " ??? "); len += sprintf(body + len, "%-17s \n", get_path_state(fabric->path_state)); for_each_fabric_cfg(fabric, cfg) body = print_phy(cfg, body); return body; } /** * print_path64 - * @ioa: ipr_fabric_descriptor64 struct * @body: message buffer * * Returns: * body **/ static char *print_path64(struct ipr_fabric_descriptor64 *fabric, char *body) { int len = strlen(body); struct ipr_fabric_config_element64 *cfg; char buffer[IPR_MAX_RES_PATH_LEN]; int max_res_path_len = 0; body = realloc(body, len + 256); body[len] = '\0'; ipr_format_res_path_wo_hyphen(fabric->res_path, buffer, IPR_MAX_RES_PATH_LEN); len += sprintf(body + len, "%s " "Resource Path ", buffer); if (fabric->path_state & IPR_PATH_ACTIVE) len += sprintf(body + len, " Yes "); else if (fabric->path_state & IPR_PATH_NOT_ACTIVE) len += sprintf(body + len, " No "); else len += sprintf(body + len, " ??? "); len += sprintf(body + len, "%-17s \n", get_path_state(fabric->path_state)); for_each_fabric_cfg(fabric, cfg) { ipr_format_res_path_wo_hyphen(cfg->res_path, buffer, IPR_MAX_RES_PATH_LEN); if (strlen(buffer) > max_res_path_len) max_res_path_len = strlen(buffer); } for_each_fabric_cfg(fabric, cfg) { body = print_phy64(cfg, body, max_res_path_len); } return body; } /** * print_path_details - * @dev: ipr dev struct * @body: message buffer * * Returns: * body **/ static char *print_path_details(struct ipr_dev *dev, char *body) { int rc, len = 0; struct ipr_res_redundancy_info info; struct ipr_fabric_descriptor *fabric; struct ipr_fabric_descriptor64 *fabric64; rc = ipr_query_res_redundancy_info(dev, &info); if (rc) return body; if (body) len = strlen(body); body = realloc(body, len + 256); body[len] = '\0'; if (dev->ioa->sis64) for_each_fabric_desc64(fabric64, &info) body = print_path64(fabric64, body); else for_each_fabric_desc(fabric, &info) body = print_path(fabric, body); return body; } /** * __print_device - * @dev: ipr dev struct * @body: message buffer * @option: * @sd: * @sg: * @vpd: * @percent: * @indent: * @res_path: * @ra: * @path_status: * * Returns: * char * buffer **/ char *__print_device(struct ipr_dev *dev, char *body, char *option, int sd, int sg, int vpd, int percent, int indent, int res_path, int ra, int path_status, int hw_loc, int conc_main, int ioa_flag, int serial_num) { u16 len = 0; struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; int i; char *dev_name = dev->dev_name; char *gen_name = dev->gen_name; char node_name[7], buf[100], raid_str[48]; char res_path_name[IPR_MAX_RES_PATH_LEN]; int tab_stop = 0; int loc_len = 0; char vendor_id[IPR_VENDOR_ID_LEN + 1]; char product_id[IPR_PROD_ID_LEN + 1]; struct ipr_ioa *ioa = dev->ioa, *ioa_phy_loc; bool is4k = false, isri = false; /* In cases where we're having problems with the device */ if (!ioa) return body; if (ioa_flag) ioa_phy_loc = dev->ioa; if (body) len = strlen(body); body = realloc(body, len + 256); if (sd && strlen(dev_name) > 5) ipr_strncpy_0(node_name, &dev_name[5], 6); else if (sg && (strlen(gen_name) > 5)) ipr_strncpy_0(node_name, &gen_name[5], 6); else node_name[0] = '\0'; if (option) len += sprintf(body + len, " %s ", option); if (scsi_dev_data && scsi_dev_data->type == TYPE_ENCLOSURE && (serial_num == 1 || hw_loc == 1)) len += sprintf(body + len, "%-8s ", node_name); else len += sprintf(body + len, "%-6s ", node_name); if (hw_loc) { if (ioa_flag) { if (strlen(ioa_phy_loc->physical_location)) { loc_len = sprintf(body + len, "%-40s ", ioa_phy_loc->physical_location); len += loc_len; } else { for (i = 0; i < 40-loc_len; i++) body[len+i] = ' '; len += 40-loc_len; } } else { if (strlen(dev->physical_location)) { if (conc_main) { loc_len = sprintf(body + len, "%-26s ", dev->physical_location); len += loc_len; } else { loc_len = sprintf(body + len, "%-38s ", dev->physical_location); len += loc_len; } } else { if (conc_main) { loc_len = sprintf(body + len, "%d/%d:", ioa->host_num,ioa->host_num); len += loc_len; } else { for (i = 0; i < 39-loc_len; i++) body[len+i] = ' '; len += 39-loc_len; } } } } else { if (res_path) { if (ioa->sis64) if (serial_num == 1) { if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER) loc_len = sprintf(body + len, "%s/%-28d", ioa->pci_address, ioa->host_num); else { ipr_format_res_path(dev->res_path[ra].res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); loc_len = sprintf(body + len, "%s/%d/%-24s", ioa->pci_address, ioa->host_num, res_path_name); } } else if (conc_main) { ipr_format_res_path(dev->res_path[ra].res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); loc_len = sprintf(body + len, "%d/%-26s ", ioa->host_num, res_path_name); } else loc_len = sprintf(body + len, "%-26s ",scsi_dev_data ? scsi_dev_data->res_path : ""); else /*32bit*/ if (serial_num == 1) { if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER) loc_len = sprintf(body + len, "%s/%-29d:", ioa->pci_address, ioa->host_num); else loc_len = sprintf(body + len, "%s/%d", ioa->pci_address, ioa->host_num); } else if (conc_main) loc_len = sprintf(body + len, "%d/%d:", ioa->host_num,ioa->host_num); else loc_len = sprintf(body + len, "%d:", ioa->host_num); len += loc_len; } else { loc_len = sprintf(body + len, "%s/%d:", ioa->pci_address, ioa->host_num); len += loc_len; } } if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &ioa->ioa) { if (serial_num == 1 ) { if (!res_path || !ioa->sis64) { for (i = 0; i < 28-loc_len; i++) body[len+i] = ' '; len += 28-loc_len; } len += sprintf(body + len,"%-13s ", ioa->yl_serial_num); } else if (hw_loc == 1 ) { if (!res_path || !ioa->sis64) { for (i = 0; i < 40-loc_len; i++) body[len+i] = ' '; len += 40-loc_len; } len += sprintf(body + len,"%-19s ", scsi_dev_data->product_id); } else { if (!res_path || !ioa->sis64) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } if (!vpd) { tab_stop = sprintf(body + len,"%s %s", get_bus_desc(ioa), get_ioa_desc(dev->ioa)); len += tab_stop; for (i = 0; i < 29-tab_stop; i++) body[len+i] = ' '; len += 29-tab_stop; } else len += sprintf(body + len,"%-8s %-19s ", scsi_dev_data->vendor_id, scsi_dev_data->product_id); } } else if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT) { if (!res_path || !ioa->sis64) { if (hw_loc) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } else { tab_stop = sprintf(body + len,"%d:%d: ", dev->res_addr[ra].bus, dev->res_addr[ra].target); loc_len += tab_stop; len += tab_stop; for (i = 0; i < 29-loc_len; i++) body[len+i] = ' '; len += 29-loc_len; } } len += sprintf(body + len, "%-8s %-19s ", " ", " "); } else { if (serial_num) { if (!res_path || !ioa->sis64) { for (i = 0; i < 26-loc_len; i++) body[len+i] = ' '; len += 26-loc_len; } } else if (hw_loc) { if (!res_path || !ioa->sis64) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } } else { if (!res_path || !ioa->sis64) { tab_stop = sprintf(body + len,"%d:%d:%d ", dev->res_addr[ra].bus, dev->res_addr[ra].target, dev->res_addr[ra].lun); loc_len += tab_stop; len += tab_stop; if (conc_main && (hw_loc != 1)) { for (i = 0; i < 29-loc_len; i++) body[len+i] = ' '; len += 29-loc_len; } else { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } } } if (vpd) { if (scsi_dev_data) { if (dev->qac_entry && ipr_is_device_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } else { ipr_strncpy_0(vendor_id, scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, scsi_dev_data->product_id, IPR_PROD_ID_LEN); } } else if (dev->qac_entry) { if (ipr_is_device_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } else if (ipr_is_vset_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } } if (hw_loc) { len += sprintf(body + len, "%-19s ", product_id); } else { len += sprintf(body + len, "%-8s %-19s ", vendor_id, product_id); } } else { if (ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) is4k = true; else is4k = false; if (ipr_is_hot_spare(dev)) { if (dev->block_dev_class & IPR_SSD) { if (dev->read_intensive & IPR_RI) isri = true; else isri = false; sprintf(buf, "%s%sSSD Hot Spare", is4k ? "4K " : "", isri ? "RI " : ""); } else sprintf(buf, "%s Hot Spare", is4k ? "4K " : ""); len += sprintf(body + len, "%-28s ", buf); } else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { if (dev->block_dev_class & IPR_SSD) { if (dev->read_intensive & IPR_RI) isri = true; else isri = false; sprintf(buf, "RAID %s%s%s SSD Array", get_prot_level_str(ioa->supported_arrays, dev->raid_level), is4k ? " 4K" : "", isri ? " RI" : ""); } else sprintf(buf, "RAID %s%s Array", get_prot_level_str(ioa->supported_arrays, dev->raid_level), is4k ? " 4K" : ""); len += sprintf(body + len, "%-28s ", buf); } else if (ipr_is_array_member(dev)) { if (indent) if (dev->block_dev_class & IPR_SSD) { if (dev->read_intensive & IPR_RI) isri = true; else isri = false; sprintf(raid_str," RAID %s%s%s SSD Member", dev->prot_level_str, is4k ? " 4K" : "", isri ? " RI" : ""); } else sprintf(raid_str," RAID %s%s Array Member", dev->prot_level_str, is4k ? " 4K" : ""); else if (dev->block_dev_class & IPR_SSD) { if (dev->read_intensive & IPR_RI) isri = true; else isri = false; sprintf(raid_str,"RAID %s%s SSD %s Member", dev->prot_level_str, is4k ? " 4K" : "", isri ? " RI" : ""); } else sprintf(raid_str,"RAID %s%s Array Member", dev->prot_level_str, is4k ? " 4K" : ""); len += sprintf(body + len, "%-28s ", raid_str); } else if (ipr_is_af_dasd_device(dev)) if (dev->block_dev_class & IPR_SSD) { if (dev->read_intensive & IPR_RI) len += sprintf(body + len, "%-28s ", is4k ? "Advanced Function 4K RI SSD" : "Advanced Function RI SSD"); else len += sprintf(body + len, "%-28s ", is4k ? "Advanced Function 4K SSD" : "Advanced Function SSD"); } else len += sprintf(body + len, "%-28s ", is4k ? "Advanced Function 4K Disk" : "Advanced Function Disk"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_ENCLOSURE) { if (serial_num == 1) len += sprintf(body + len, "%-13s ", (char *)&dev->serial_number); else len += sprintf(body + len, "%-28s ", "Enclosure"); } else if (scsi_dev_data && scsi_dev_data->type == TYPE_PROCESSOR) len += sprintf(body + len, "%-28s ", "Processor"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_ROM) len += sprintf(body + len, "%-28s ", "CD/DVD"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_TAPE) len += sprintf(body + len, "%-28s ", "Tape"); else if (ioa->ioa_dead) len += sprintf(body + len, "%-28s ", "Unavailable Device"); else { len += sprintf(body + len, "%-28s ", is4k ? "Physical 4K Disk" : "Physical Disk"); } } } get_status(dev, buf, percent, path_status); sprintf(body + len, "%s\n", buf); return body; } /** * print_device - * @dev: ipr dev struct * @body: message buffer * @option: * @type: bitfield - type if information to print * * Returns: * char * buffer **/ /* * Print the given device to a buffer. The type parameter is defined as * a bitfield which has the following behavior: * * 0: print sd, resource address (sis32)/resource path (sis64), device VPD * 1: print sd, pci/scsi location, device description, print % complete * 2: print sg, resource address (sis32)/resource path (sis64), device VPD * 3: print sd, pci/scsi location, device description, indent array members, print % complete * 4: print sg, pci/scsi location, device description, print % complete * 5: print sd, hardware location, status * 6: print sd, the first resource adress(sis32)/resource path(sis64), dev VPD * 7: print sd, the second resource adress(sis32)/resource path(sis64), dev VPD * 8: print sg, pci/host/resource path, serial number, status * 9: print sg, physical location, production id, status */ char *print_device(struct ipr_dev *dev, char *body, char *option, int type) { int sd, sg, vpd, percent, indent, res_path, hw_loc, conc_main, ioa_flag, serial_num; sd = sg = vpd = percent = indent = res_path = hw_loc = conc_main = ioa_flag = serial_num = 0; if ((type & 0x1000) == 0x1000) ioa_flag = 1; type &= 0x0fff; if (type == 2 || type == 4) sg = 1; else sd = 1; if (type == 0 || type == 2 || type == 6 || type == 7) { vpd = 1; res_path = 1; } else percent = 1; if (type == 3) indent = 1; if (type == 5) { hw_loc = 1; conc_main = 1; sg = 1; } if (type == 6 || type == 7) { conc_main = 1; sg = 1; } if (type == 8) { sg = 1; if (dev->ioa->sis64) res_path = 1; else res_path = 0; serial_num = 1; } if (type == 9) { sg = 1; hw_loc = 1; if (dev->ioa->sis64) res_path = 1; else res_path = 0; vpd = 1; } return __print_device(dev, body, option, sd, sg, vpd, percent, indent, res_path, type&1, 0, hw_loc, conc_main, ioa_flag, serial_num); } /** * usage - display usage information * * Returns: * nothing **/ static void usage() { printf(_("Usage: iprconfig [options]\n")); printf(_(" Options: -e name Default editor for viewing error logs\n")); printf(_(" -k dir Kernel messages root directory\n")); printf(_(" -c command Command line configuration\n")); printf(_(" See man page for complete details\n")); printf(_(" --version Print iprconfig version\n")); printf(_(" --debug Enable additional error logging\n")); printf(_(" --force Disable safety checks. See man page for details.\n")); printf(_(" Use quotes around parameters with spaces\n")); } /** * find_and_add_dev - * @name: device name * * Returns: * ipr_dev struct if success / NULL on failure **/ static struct ipr_dev *find_and_add_dev(char *name) { struct ipr_dev *dev = find_dev(name); if (!dev) { syslog(LOG_ERR, _("Invalid device specified: %s\n"), name); return NULL; } ipr_add_sysfs_dev(dev, &head_sdev, &tail_sdev); return dev; } /** * raid_create_add_dev - * @name: device name * * Returns: * 0 if success / non-zero on failure **/ static int raid_create_add_dev(char *name) { struct ipr_dev *dev = find_and_add_dev(name); if (!dev) return -EINVAL; if (ipr_is_gscsi(dev)) { if (is_af_blocked(dev, 0)) return -EINVAL; if (!is_format_allowed(dev)) return -EIO; if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) add_format_device(dev, IPR_AF_4K_BLOCK_SIZE); else add_format_device(dev, dev->ioa->af_block_size); dev_init_tail->do_init = 1; } return 0; } /** * raid_create_check_num_devs - * @ioa: ipr_array_cap_entry struct * @num_devs: number of devices * * Returns: * 0 if success / non-zero on failure **/ static int raid_create_check_num_devs(struct ipr_array_cap_entry *cap, int num_devs, int err_type) { if (num_devs < cap->min_num_array_devices || num_devs > cap->max_num_array_devices || (num_devs % cap->min_mult_array_devices) != 0) { syslog(LOG_ERR, _("Invalid number of devices (%d) selected for " "RAID %s array.\n" "Must select a minimum of %d devices, a " "maximum of %d devices, " "and must be a multiple of %d devices.\n"), num_devs, cap->prot_level_str, cap->min_num_array_devices, cap->max_num_array_devices, cap->min_mult_array_devices); if (err_type == 0) return -EINVAL; else if (num_devs > cap->min_num_array_devices) return RC_77_Too_Many_Disks; else if (num_devs < cap->min_num_array_devices) return RC_78_Too_Few_Disks; else if ((num_devs % cap->min_num_array_devices) != 0) return RC_25_Devices_Multiple; } return 0; } /** * curses_init - initialize curses * * Returns: * nothing **/ static void curses_init() { /* makes program compatible with all terminals - originally didn't display text correctly when user was running xterm */ setenv("TERM", "vt100", 1); setlocale(LC_ALL, ""); initscr(); } /** * format_devices - * @args: argument vector * @num_args: number of arguments * @blksz: block size * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_devices(char **args, int num_args, int fmt_flag) { int i, rc, blksz; struct ipr_dev *dev; for (i = 0; i < num_args; i++) { dev = find_dev(args[i]); if (!dev) { fprintf(stderr, "Invalid device: %s\n", args[i]); continue; } if (!dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) { fprintf(stderr, "Invalid device specified: %s. 4K disks not supprted on this adapter", args[i]); continue; } if (fmt_flag == IPR_FMT_JBOD ) { if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) blksz = IPR_JBOD_4K_BLOCK_SIZE; else blksz = IPR_JBOD_BLOCK_SIZE; } else if (fmt_flag == IPR_FMT_RECOVERY) blksz = 0; if (!ipr_is_af_dasd_device(dev) && !ipr_is_gscsi(dev)) continue; if (ipr_is_af_dasd_device(dev)) { if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) continue; if (!is_format_allowed(dev)) continue; } else { if (!is_format_allowed(dev)) continue; } add_format_device(dev, blksz); dev_init_tail->do_init = 1; } rc = send_dev_inits(NULL); free_devs_to_init(); return IPR_XLATE_DEV_FMT_RC(rc); } /** * recovery_format - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int recovery_format(char **args, int num_args) { return format_devices(args, num_args, IPR_FMT_RECOVERY); } /** * format_for_jbod - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_for_jbod(char **args, int num_args) { return format_devices(args, num_args, IPR_FMT_JBOD); } /** * format_for_raid - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_for_raid(char **args, int num_args) { int i, rc, blk_size; struct ipr_dev *dev; for (i = 0; i < num_args; i++) { dev = find_and_add_dev(args[i]); if (!dev || !can_format_for_raid(dev)) { fprintf(stderr, "Invalid device: %s\n", args[i]); return -EINVAL; } if (!dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) { fprintf(stderr, "Invalid device specified: %s. 4K disks not supprted on this adapter", args[i]); return -EINVAL; } if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) blk_size = IPR_AF_4K_BLOCK_SIZE; else blk_size = dev->ioa->af_block_size; add_format_device(dev, blk_size); dev_init_tail->do_init = 1; } rc = send_dev_inits(NULL); free_devs_to_init(); return IPR_XLATE_DEV_FMT_RC(rc); } /** * raid_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_create(char **args, int num_args) { int i, num_devs = 0, rc; int non_4k_count = 0, is_4k_count = 0; int next_raid_level, next_stripe_size, next_qdepth, next_label; char *raid_level = IPR_DEFAULT_RAID_LVL; char label[8]; int stripe_size, qdepth, zeroed_devs; struct ipr_dev *dev; struct sysfs_dev *sdev; struct ipr_ioa *ioa = NULL; struct ipr_array_cap_entry *cap; u8 array_id; label[0] = '\0'; next_raid_level = 0; next_stripe_size = 0; next_qdepth = 0; next_label = 0; stripe_size = 0; qdepth = 0; zeroed_devs = 0; for (i = 0; i < num_args; i++) { if (strcmp(args[i], "-z") == 0) zeroed_devs = 1; else if (strcmp(args[i], "-r") == 0) next_raid_level = 1; else if (strcmp(args[i], "-s") == 0) next_stripe_size = 1; else if (strcmp(args[i], "-q") == 0) next_qdepth = 1; else if (strcmp(args[i], "-l") == 0) next_label = 1; else if (next_raid_level) { next_raid_level = 0; raid_level = args[i]; } else if (next_stripe_size) { next_stripe_size = 0; stripe_size = strtoul(args[i], NULL, 10); } else if (next_qdepth) { next_qdepth = 0; qdepth = strtoul(args[i], NULL, 10); } else if (next_label) { next_label = 0; if (strlen(args[i]) > (sizeof(label) - 1)) { syslog(LOG_ERR, _("RAID array label too long. Maximum length is %d characters\n"), (int)(sizeof(label) - 1)); return -EINVAL; } strcpy(label, args[i]); } else if ((dev = find_dev(args[i]))) { num_devs++; if (zeroed_devs) ipr_add_zeroed_dev(dev); if (raid_create_add_dev(args[i])) return -EIO; } } for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (!dev) { syslog(LOG_ERR, _("Cannot find device\n")); return -EINVAL; } if (!ioa) ioa = dev->ioa; else if (ioa != dev->ioa) { syslog(LOG_ERR, _("All devices must be attached to the same adapter.\n")); return -EINVAL; } if (dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) is_4k_count++; else non_4k_count++; } if (is_4k_count > 0 && non_4k_count > 0) { syslog(LOG_ERR, _("4K disks and 5XX disks can not be mixed in an array.\n")); return -EINVAL; } if (!ioa) { syslog(LOG_ERR, _("No valid devices specified.\n")); return -EINVAL; } cap = get_cap_entry(ioa->supported_arrays, raid_level); if (!cap) { syslog(LOG_ERR, _("Invalid RAID level for selected adapter. %s\n"), raid_level); return -EINVAL; } if (stripe_size && ((cap->supported_stripe_sizes & stripe_size) != stripe_size)) { syslog(LOG_ERR, _("Unsupported stripe size for selected adapter. %d\n"), stripe_size); return -EINVAL; } if ((rc = raid_create_check_num_devs(cap, num_devs, 0))) return rc; if (!stripe_size) stripe_size = ntohs(cap->recommended_stripe_size); if (!qdepth) { if (ioa->sis64) qdepth = num_devs * 16; else qdepth = num_devs * 4; } if (dev_init_head) { rc = send_dev_inits(NULL); free_devs_to_init(); if (IPR_XLATE_DEV_FMT_RC(rc)) return rc; } check_current_config(false); for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (!dev) { syslog(LOG_ERR, _("Cannot find device: %s\n"), sdev->sysfs_device_name); return -EIO; } if (!ipr_is_af_dasd_device(dev)) { scsi_err(dev, "Invalid device type\n"); return -EIO; } dev->dev_rcd->issue_cmd = 1; } if (!ioa->start_array_qac_entry) { scsi_err(dev, "Invalid device type\n"); return -EIO; } if (ioa->sis64) { array_id = ioa->start_array_qac_entry->type3.array_id; if (strlen(label)) strcpy((char *)ioa->start_array_qac_entry->type3.desc, label); } else array_id = ioa->start_array_qac_entry->type2.array_id; add_raid_cmd_tail(ioa, &ioa->ioa, array_id); raid_cmd_tail->qdepth = qdepth; raid_cmd_tail->do_cmd = 1; rc = ipr_start_array_protection(ioa, stripe_size, cap->prot_level); if (rc) return rc; rc = raid_start_complete(); format_done = 1; if (rc == RC_18_Array_Created) return 0; else return -EIO; } /** * hot_spare_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int hot_spare_delete(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; return ipr_remove_hot_spare(dev->ioa); } /** * hot_spare_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int hot_spare_create(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; return ipr_add_hot_spare(dev->ioa); } /** * raid_include_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_include_cmd(char **args, int num_args) { int i, rc, zeroed = 0; struct ipr_dev *dev, *vset = NULL; struct ipr_ioa *ioa; struct devs_to_init_t *dev_init; for (i = 0; i < num_args; i++) { if (!strcmp(args[i], "-z")) zeroed = 1; } for (i = 0; i < num_args; i++) { if (!strcmp(args[i], "-z")) continue; dev = find_and_add_dev(args[i]); if (!dev) return -EINVAL; if (ipr_is_volume_set(dev)) { if (vset) { fprintf(stderr, "Invalid parameters. Only one " "disk array can be specified at a time.\n"); return -EINVAL; } vset = dev; ioa = vset->ioa; continue; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "Invalid device specified. Device must " "be formatted to Advanced Function Format.\n"); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; if (!zeroed) { if (!is_format_allowed(dev)) { fprintf(stderr, "Format not allowed to %s\n", args[i]); return -EIO; } add_format_device(dev, 0); dev_init_tail->do_init = 1; } } if (!vset) { fprintf(stderr, "Invalid parameters. A disk array must be specified.\n"); return -EINVAL; } for_each_dev_to_init(dev_init) if (vset->block_dev_class != dev_init->dev->block_dev_class) { fprintf(stderr, "Invalid parameter. Can not mix SSDs and HDDs.\n"); return -EINVAL; } if (!zeroed) { rc = send_dev_inits(NULL); free_devs_to_init(); if (IPR_XLATE_DEV_FMT_RC(rc)) return rc; } return do_array_include(vset->ioa, vset->array_id, vset->ioa->qac_data); } /** * raid_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_delete(char **args, int num_args) { int rc; struct ipr_dev *dev; struct ipr_res_addr *ra; dev = find_and_add_dev(args[0]); if (!dev || !ipr_is_volume_set(dev) || dev->ioa->is_secondary) { syslog(LOG_ERR, _("Invalid device specified: %s\n"), args[0]); return -EINVAL; } dev->array_rcd->issue_cmd = 1; if (dev->scsi_dev_data) { ipr_allow_restart(dev, 0); ipr_set_manage_start_stop(dev); ipr_start_stop_stop(dev); ipr_write_dev_attr(dev, "delete", "1"); } if (dev->alt_path && dev->alt_path->scsi_dev_data) { ipr_allow_restart(dev->alt_path, 0); ipr_set_manage_start_stop(dev->alt_path); ipr_start_stop_stop(dev->alt_path); ipr_write_dev_attr(dev->alt_path, "delete", "1"); } sleep(2); rc = ipr_stop_array_protection(dev->ioa); if (rc != 0) { if (dev->scsi_dev_data) { ra = &dev->res_addr[0]; ipr_scan(dev->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(dev, 1); } if (dev->alt_path && dev->alt_path->scsi_dev_data) { ra = &dev->alt_path->res_addr[0]; ipr_scan(dev->alt_path->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(dev->alt_path, 1); } } return rc; } /** * printf_device - Display the given device * @dev: ipr dev struct * @type: type of information to print * * Returns: * nothing **/ static void printf_device(struct ipr_dev *dev, int type) { char *buf = print_device(dev, NULL, NULL, type); if (buf) { printf("%s", buf); free(buf); } } /** * __printf_device - * @dev: ipr dev struct * @sd: * @sg: * @vpd: * @percent: * @indent: * @res_path: * * Returns: * nothing **/ static void __printf_device(struct ipr_dev *dev, int sd, int sg, int vpd, int percent, int indent, int res_path) { char *buf = __print_device(dev, NULL, NULL, sd, sg, vpd, percent, indent, res_path, 0, 0, 0, 0, 0, 0); if (buf) { printf("%s", buf); free(buf); } } /** * printf_vsets - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_vsets(struct ipr_ioa *ioa, int type) { struct ipr_dev *vset, *dev; for_each_vset(ioa, vset) { printf_device(vset, type); for_each_dev_in_vset(vset, dev) printf_device(dev, type); } } /** * printf_hot_spare_disks - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_hot_spare_disks(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_hot_spare(ioa, dev) printf_device(dev, type); } /** * printf_standlone_disks - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_standlone_disks(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_standalone_disk(ioa, dev) printf_device(dev, type); } /** * printf_ses_devices - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_ses_devices(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_ses(ioa, dev) printf_device(dev, type); } /** * printf_ioa - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_ioa(struct ipr_ioa *ioa, int type) { if (!ioa->ioa.scsi_dev_data) return; printf_device(&ioa->ioa, type); printf_standlone_disks(ioa, type); printf_hot_spare_disks(ioa, type); printf_vsets(ioa, type); printf_ses_devices(ioa, type); } /** * query_raid_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_create(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int num = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; if (!ioa->qac_data->num_records) return 0; for_each_disk(ioa, dev) { if (!device_supported(dev)) continue; if (ipr_is_af_dasd_device(dev) && !dev->dev_rcd->start_cand) continue; if (!num) printf("%s\n%s\n", status_hdr[2], status_sep[2]); __printf_device(dev, 1, 1, 1, 0, 0, 0); num++; } if (num) printf("\b\n"); return 0; } /** * query_raid_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_delete(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int num = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_vset(ioa, dev) { if (!dev->array_rcd->stop_cand) continue; if (is_array_in_use(ioa, dev->array_id)) continue; if (!num) printf("%s\n%s\n", status_hdr[3], status_sep[3]); printf_device(dev, 3); num++; } return 0; } /** * query_hot_spare_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_hot_spare_create(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->add_hot_spare_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } return 0; } /** * query_hot_spare_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_hot_spare_delete(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_hot_spare(ioa, dev) { if (!dev->dev_rcd->rmv_hot_spare_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } return 0; } /** * query_raid_consistency_check - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_consistency_check(char **args, int num_args) { struct ipr_dev *dev, *array; struct ipr_ioa *ioa; int hdr = 0; for_each_ioa(ioa) { for_each_array(ioa, array) { if (!array->array_rcd->resync_cand) continue; dev = array; if (ioa->sis64) dev = get_vset_from_array(ioa, dev); if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(dev, 1); } } return 0; } /** * raid_consistency_check - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_consistency_check(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64) dev = get_array_from_vset(dev->ioa, dev); if (!dev->array_rcd->resync_cand) { fprintf(stderr, "%s is not a candidate for checking.\n", args[0]); return -EINVAL; } dev->array_rcd->issue_cmd = 1; return ipr_resync_array(dev->ioa); } /** * start_ioa_cache - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int start_ioa_cache(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_reclaim_query_data reclaim_buffer; int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_RESET_BATTERY_ERROR | IPR_RECLAIM_EXTENDED_INFO, &reclaim_buffer); if (rc) return rc; if (reclaim_buffer.action_status != IPR_ACTION_SUCCESSFUL) rc = -EIO; return rc; } /** * force_cache_battery_error - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int force_cache_battery_error(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_reclaim_query_data reclaim_buffer; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } return ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_FORCE_BATTERY_ERROR, &reclaim_buffer); } /** * set_bus_width - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_bus_width(char **args, int num_args) { int rc; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int width = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); if (!page_28_chg.bus[bus].bus_width) { fprintf(stderr, "Bus width not changeable for this device\n"); return -EINVAL; } page_28_cur.bus[bus].bus_width = width; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * set_bus_speed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_bus_speed(char **args, int num_args) { int rc, max_speed, new_xfer_rate; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int speed = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); max_speed = get_max_bus_speed(ioa, bus); new_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(speed, page_28_cur.bus[bus].bus_width); if (speed > max_speed) { fprintf(stderr, "Max speed allowed: %d MB/sec\n", max_speed); return -EINVAL; } page_28_cur.bus[bus].max_xfer_rate = new_xfer_rate; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * set_initiator_id - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_initiator_id(char **args, int num_args) { int rc; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int scsi_id = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; if (scsi_id > 7) { fprintf(stderr, "Host scsi id must be < 7\n"); return -EINVAL; } ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); if (!page_28_chg.bus[bus].scsi_id) { fprintf(stderr, "SCSI ID is not changeable for this adapter.\n"); return -EINVAL; } for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (scsi_id == dev->scsi_dev_data->id && bus == dev->scsi_dev_data->channel) { fprintf(stderr, "SCSI ID %d conflicts with a device\n", scsi_id); return -EINVAL; } } page_28_cur.bus[bus].scsi_id = scsi_id; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * find_slot - * @devs: ipr_dev struct * @num_devs: number of devices * @slot: slot description * * Returns: * ipr_dev struct on success / NULL otherwise **/ static struct ipr_dev *find_slot(struct ipr_dev **devs, int num_devs, char *slot) { int i; for (i = 0; i < num_devs; i++) { syslog_dbg("Looking for slot %s at pos %d\n", slot, i); if (!strncmp(devs[i]->physical_location, slot, strlen(slot))) return devs[i]; } return NULL; } /** * __add_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __add_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_ses_config_pg ses_cfg; int rc; if (!ses) { fprintf(stderr, "Invalid device\n"); return -EINVAL; } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Cannot find SES device for specified device\n"); return -EINVAL; } elem_status->select = 1; elem_status->remove = 0; elem_status->insert = on; elem_status->identify = on; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = on; rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (!on) { if (dev->ioa->sis64) wait_for_new_dev_64bit(dev->ioa, &(dev->res_path[0])); else wait_for_new_dev(dev->ioa, &(dev->res_addr[0])); } return rc; } /** * __remove_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __remove_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_ses_config_pg ses_cfg; int rc; if (!dev->ses) { fprintf(stderr, "Invalid device specified\n"); return -EINVAL; } if (!on && ipr_is_af_dasd_device(dev)) { if (!ipr_can_remove_device(dev)) { scsi_err(dev, "Cannot remove device\n"); return -EINVAL; } } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Invalid device specified\n"); return -EINVAL; } elem_status->select = 1; elem_status->remove = on; elem_status->insert = 0; elem_status->identify = on; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = on; rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (!on) { if (dev->ioa->sis64) { if (!remove_or_add_back_device_64bit(dev)) { printf(_("Disk is not removed physically, Add it back automically\n")); sleep(5); } } else { ipr_write_dev_attr(dev, "delete", "1"); evaluate_device(dev, dev->ioa, 0); ipr_del_zeroed_dev(dev); } } return rc; } /** * add_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int add_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_ADD); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __add_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * remove_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int remove_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_REMOVE); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __remove_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * remove_disk - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int remove_disk(char **args, int num_args) { int num_devs, i; struct ipr_dev **devs; struct ipr_dev *dev = find_dev(args[0]); int on = strtoul(args[1], NULL, 10); int rc = -EINVAL; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } num_devs = get_conc_devs(&devs, IPR_CONC_REMOVE); for (i = 0; i < num_devs; i++) { if (devs[i] == dev) { rc = __remove_device(dev, on); break; } } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * __identify_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __identify_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status; struct ipr_ses_config_pg ses_cfg; if (!dev->ses) { fprintf(stderr, "Invalid device specified\n"); return -EINVAL; } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Invalid device specified\n"); return -EINVAL; } elem_status->select = 1; elem_status->identify = on; elem_status->insert = 0; elem_status->remove = 0; return ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); } /** * identify_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int identify_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_IDENTIFY); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __identify_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * identify_disk - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int identify_disk(char **args, int num_args) { int num_devs, i; struct ipr_dev **devs; struct ipr_dev *dev = find_dev(args[0]); int rc = -EINVAL; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } num_devs = get_conc_devs(&devs, IPR_CONC_IDENTIFY); for (i = 0; i < num_devs; i++) { if (devs[i] == dev) { rc = __identify_device(dev, strtoul(args[1], NULL, 10)); break; } } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * set_log_level_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_log_level_cmd(char **args, int num_args) { char buf[4]; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa != dev) { fprintf(stderr, "Device is not an IOA\n"); return -EINVAL; } snprintf(buf, sizeof(buf), "%s\n", args[1]); set_log_level(dev->ioa, buf); return 0; } /** * set_tcq_enable - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_tcq_enable(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.tcq_enabled = strtoul(args[1], NULL, 10); if (attr.tcq_enabled != 0 && attr.tcq_enabled != 1) { fprintf(stderr, "Invalid parameter\n"); return -EINVAL; } return ipr_set_dev_attr(dev, &attr, 1); } /** * set_qdepth - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_qdepth(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.queue_depth = strtoul(args[1], NULL, 10); if (attr.queue_depth > 255) { fprintf(stderr, "Invalid queue depth %s\n", args[1]); return -EINVAL; } return ipr_set_dev_attr(dev, &attr, 1); } /** * set_format_timeout - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_format_timeout(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.format_timeout = strtoul(args[1], NULL, 10) * 3600; return ipr_set_dev_attr(dev, &attr, 1); } /** * update_ioa_ucode - * @ioa: ipr ioa struct * @file: file name * * Returns: * 0 if success / non-zero on failure **/ static int update_ioa_ucode(struct ipr_ioa *ioa, char *file) { struct ipr_fw_images image; int rc; strcpy(image.file, file); image.version = get_ioa_ucode_version(file); image.has_header = 0; rc = ipr_update_ioa_fw(ioa, &image, 1); if (image.version != get_ioa_fw_version(ioa)) return -EIO; return rc; } /** * update_dev_ucode - * @dev: ipr dev struct * @file: file name * * Returns: * 0 if success / non-zero on failure **/ static int update_dev_ucode(struct ipr_dev *dev, char *file) { struct ipr_fw_images image; int rc; strcpy(image.file, file); if (dev->scsi_dev_data->type == TYPE_ENCLOSURE || dev->scsi_dev_data->type == TYPE_PROCESSOR) { image.version = get_ses_ucode_version(file); } else { image.version = get_dasd_ucode_version(file, 1); image.has_header = 1; } rc = ipr_update_disk_fw(dev, &image, 1); if (image.version != get_dev_fw_version(dev)) return -EIO; return rc; } /** * update_ucode_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int update_ucode_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa == dev) return update_ioa_ucode(dev->ioa, args[1]); else return update_dev_ucode(dev, args[1]); } /** * disrupt_device_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int disrupt_device_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "%s is not an Advanced Function disk\n", args[0]); return -EINVAL; } return ipr_disrupt_device(dev); } /** * raid_rebuild_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_rebuild_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "%s is not an Advanced Function disk\n", args[0]); return -EINVAL; } enable_af(dev); dev->dev_rcd->issue_cmd = 1; return ipr_rebuild_device_data(dev->ioa); } /** * __reclaim - Reclaim the specified IOA's write cache. * @args: argument vector * @num_args: number of arguments * @action: * * Returns: * 0 if success / non-zero on failure **/ static int __reclaim(char **args, int num_args, int action) { struct ipr_reclaim_query_data buf; struct ipr_dev *dev; struct ipr_ioa *ioa; int rc; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; rc = ipr_reclaim_cache_store(ioa, action, &buf); if (rc != 0) return rc; memset(&buf, 0, sizeof(buf)); rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, &buf); if (rc != 0) return rc; print_reclaim_results(&buf); return 0; } /** * reclaim - Reclaim the specified IOA's write cache. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int reclaim(char **args, int num_args) { return __reclaim(args, num_args, IPR_RECLAIM_PERFORM); } /** * reclaim_unknown - Reclaim the specified IOA's write cache and allow * unknown data loss. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int reclaim_unknown(char **args, int num_args) { return __reclaim(args, num_args, IPR_RECLAIM_PERFORM | IPR_RECLAIM_UNKNOWN_PERM); } /** * query_path_details - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_path_details(char **args, int num_args) { char *buf; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } buf = print_path_details(dev, NULL); if (buf) { printf("%s\n%s\n", status_hdr[6], status_sep[6]); printf("%s", buf); free(buf); } return 0; } /** * printf_path_status - * @dev: ipr dev struct * * Returns: * nothing **/ static void printf_path_status(struct ipr_dev *dev) { char *buf = __print_device(dev, NULL, NULL, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0); if (buf) { printf("%s", buf); free(buf); } } /** * printf_ioa_path_status - * @ioa: ipr ioa struct * * Returns: * 0 **/ static int printf_ioa_path_status(struct ipr_ioa *ioa) { struct ipr_dev *dev; if (__ioa_is_spi(ioa)) return 0; for_each_disk(ioa, dev) printf_path_status(dev); return 0; } /** * query_path_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_path_status(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev = NULL; int hdr = 1; if (!num_args) { for_each_sas_ioa(ioa) { if (hdr) { printf("%s\n%s\n", status_hdr[2], status_sep[2]); hdr = 0; } printf_ioa_path_status(ioa); } return 0; } dev = find_dev(args[0]); if (!dev) return -ENXIO; printf("%s\n%s\n", status_hdr[2], status_sep[2]); if (&dev->ioa->ioa == dev) printf_ioa_path_status(dev->ioa); else printf_path_status(dev); return 0; } /** * query_recommended_stripe_size - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_recommended_stripe_size(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", ntohs(cap->recommended_stripe_size)); return 0; } /** * query_supp_stripe_sizes - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_supp_stripe_sizes(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; int i; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (!cap) return 0; for (i = 0; i < (sizeof(cap->supported_stripe_sizes) * 8); i++) if (ntohs(cap->supported_stripe_sizes) & (1 << i)) printf("%d ", (1 << i)); printf("\n"); return 0; } /** * query_min_mult_in_array - Start array protection for an array * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_min_mult_in_array(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->min_mult_array_devices); return 0; } /** * query_min_array_devices - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_min_array_devices(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->min_num_array_devices); return 0; } /** * query_max_array_devices - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_max_array_devices(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->max_num_array_devices); return 0; } /** * query_include_allowed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_include_allowed(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) { printf("no\n"); return 0; } cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (!cap) { printf("no\n"); return 0; } if (cap->include_allowed) printf("yes\n"); else printf("no\n"); return 0; } /** * query_raid_levels - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_levels(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return 0; for_each_cap_entry(cap, dev->ioa->supported_arrays) printf("%s ", cap->prot_level_str); printf("\n"); return 0; } /** * query_bus_width - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_bus_width(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", page_28.bus[bus].bus_width); return 0; } /** * query_bus_speed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_bus_speed(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", IPR_BUS_XFER_RATE_TO_THRUPUT(page_28.bus[bus].max_xfer_rate, page_28.bus[bus].bus_width)); return 0; } /** * query_initiator_id - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_initiator_id(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", page_28.bus[bus].scsi_id); return 0; } /** * query_add_remove - * @action: action type * * Returns: * 0 **/ static int query_add_remove(int action) { int i, num_devs; struct ipr_dev **devs; num_devs = get_conc_devs(&devs, action); for (i = 0; i < num_devs; i++) { if (i == 0) printf("%s\n%s\n", status_hdr[11], status_sep[11]); printf_device(devs[i], 5); } free_empty_slots(devs, num_devs); free(devs); return 0; } /** * query_remove_device - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_remove_device(char **args, int num_args) { return query_add_remove(IPR_CONC_REMOVE); } /** * query_add_device - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_add_device(char **args, int num_args) { return query_add_remove(IPR_CONC_ADD); } /** * query_log_level - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_log_level(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa != dev) { fprintf(stderr, "Device is not an IOA\n"); return -EINVAL; } printf("%d\n", get_log_level(dev->ioa)); return 0; } /** * query_tcq_enable - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_tcq_enable(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.tcq_enabled); return 0; } /** * query_qdepth - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_qdepth(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.queue_depth); return 0; } /** * query_format_timeout - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_timeout(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_af_dasd_device(dev)) { dprintf("%s is not an Advanced Function disk\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.format_timeout / 3600); return 0; } /** * query_ucode_level - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ucode_level(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); u32 level, level_sw; char *asc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa == dev) printf("%08X\n", get_ioa_fw_version(dev->ioa)); else { level = get_dev_fw_version(dev); level_sw = htonl(level); asc = (char *)&level_sw; if (isprint(asc[0]) && isprint(asc[1]) && isprint(asc[2]) && isprint(asc[3])) printf("%.8X (%c%c%c%c)\n", level, asc[0], asc[1], asc[2], asc[3]); else printf("%.8X\n", level); } return 0; } /** * query_format_for_raid - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_for_raid(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_primary_ioa(ioa) { for_each_disk(ioa, dev) { if (!can_format_for_raid(dev)) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } if (strlen(dev->dev_name)) printf_device(dev, 0); else printf_device(dev, 2); } } return 0; } /** * query_raid_rebuild - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_rebuild(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->rebuild_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * query_recovery_format - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_recovery_format(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_primary_ioa(ioa) { for_each_disk(ioa, dev) { if (ipr_is_hot_spare(dev) || !device_supported(dev)) continue; if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) continue;; if (!is_format_allowed(dev)) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } if (strlen(dev->dev_name)) printf_device(dev, 0); else printf_device(dev, 2); } } return 0; } /** * query_devices_include - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_devices_include(char **args, int num_args) { int rc, hdr = 0;; struct ipr_array_query_data qac_data; struct ipr_dev *dev; struct ipr_ioa *ioa; struct ipr_dev *vset = find_dev(args[0]); struct ipr_dev_record *dev_rcd; if (!vset) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = vset->ioa; memset(&qac_data, 0, sizeof(qac_data)); rc = ipr_query_array_config(ioa, 0, 1, 0, vset->array_id, &qac_data); if (rc) return rc; for_each_dev_rcd(dev_rcd, &qac_data) { for_each_disk(ioa, dev) { if (dev->scsi_dev_data->handle == ipr_get_dev_res_handle(ioa, dev_rcd) && dev_rcd->include_cand && device_supported(dev)) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); break; } } } return 0; } /** * query_arrays_include - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_include(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; struct ipr_array_cap_entry *cap_entry; struct ipr_array_record *array_rcd; int hdr = 0; for_each_primary_ioa(ioa) { for_each_vset(ioa, vset) { array_rcd = vset->array_rcd; cap_entry = get_raid_cap_entry(ioa->supported_arrays, vset->raid_level); if (!cap_entry || !cap_entry->include_allowed || !array_rcd->established) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(vset, 1); } } return 0; } /** * query_reclaim - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_reclaim(char **args, int num_args) { struct ipr_reclaim_query_data buf; struct ipr_ioa *ioa; int hdr = 0, rc; for_each_ioa(ioa) { memset(&buf, 0, sizeof(buf)); rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, &buf); if (rc != 0) continue; if (buf.reclaim_known_needed || buf.reclaim_unknown_needed) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(&ioa->ioa, 2); } } return 0; } /** * query_arrays_raid_migrate - Show the arrays that can be migrated to a * different protection level. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_raid_migrate(char **args, int num_args) { int hdr = 0; struct ipr_dev *array, *vset; struct ipr_ioa *ioa; for_each_primary_ioa(ioa) { for_each_array(ioa, array) { if (!array->array_rcd->migrate_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } if (ioa->sis64) { vset = get_vset_from_array(ioa, array); printf_device(vset, 1); } else printf_device(array, 1); } } return 0; } /** * query_devices_raid_migrate - Given an array, show the AF disks that are * candidates to be used in a migration. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_devices_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; struct ipr_dev_record *dev_rcd; int hdr = 0; int rc = 0; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc != 0) return rc; for_each_dev_rcd(dev_rcd, &qac_data) { if (!dev_rcd->migrate_array_prot_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } dev = get_dev_from_handle(dev->ioa, ipr_get_dev_res_handle(dev->ioa, dev_rcd)); if (dev) printf_device(dev, 2); } return 0; } /** * query_raid_levels_raid_migrate - Given an array, display the protection * levels to which the array can be migrated. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_levels_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *sa_rcd; struct ipr_array_cap_entry *cap_entry; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; for_each_supported_arrays_rcd(sa_rcd, &qac_data) for_each_cap_entry(cap_entry, sa_rcd) printf("%s ", cap_entry->prot_level_str); printf("\n"); return 0; } /** * query_stripe_sizes_raid_migrate - Given an array and a protection level, * show the valid stripe sizes to which the * array can be migrated. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_stripe_sizes_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int i; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *sa_rcd; struct ipr_array_cap_entry *cap_entry; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; for_each_supported_arrays_rcd(sa_rcd, &qac_data) { for_each_cap_entry(cap_entry, sa_rcd) { if (strcmp(args[1], (char *)cap_entry->prot_level_str)) continue; for (i = 0; i < (sizeof(cap_entry->supported_stripe_sizes) * 8); i++) if (ntohs(cap_entry->supported_stripe_sizes) & (1 << i)) printf("%d ", (1 << i)); printf("\n"); break; } } return 0; } /** * query_devices_min_max_raid_migrate - Given an array and a protection level, * show the minimum and maximum number of * devices needed to do the migration. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ int query_devices_min_max_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; /* check that the given raid level is valid */ for_each_supported_arrays_rcd(supported_arrays, &qac_data) { cap = get_cap_entry(supported_arrays, args[1]); if (cap) break; } if (!cap) { fprintf(stderr, "RAID level %s is unsupported.\n", args[1]); return -EINVAL; } if (cap->format_overlay_type == IPR_FORMAT_REMOVE_DEVICES) fprintf(stderr, "%d %s will be removed from the array\n", cap->min_num_array_devices, cap->min_num_array_devices == 1 ? "device" : "devices"); else if (cap->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { fprintf(stderr, "Minimum number of devices required = %d\n", cap->min_num_array_devices); fprintf(stderr, "Maximum number of devices allowed = %d\n", cap->max_num_array_devices); fprintf(stderr, "Number of devices must be a multiple of %d\n", cap->min_mult_array_devices); } else { fprintf(stderr, "Unknown overlay type in qac data\n"); return -EINVAL; } return 0; } /** * query_ioas_asym_access - Show the ioas that support asymmetric access. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioas_asym_access(char **args, int num_args) { int hdr = 0; struct ipr_ioa *ioa; for_each_ioa(ioa) { if (!ioa->asymmetric_access) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(&ioa->ioa, 2); } return 0; } /** * query_arrays_asym_access - Show the arrays that support asymmetric access. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_asym_access(char **args, int num_args) { int hdr = 0; struct ipr_ioa *ioa; struct ipr_dev *array; for_each_ioa(ioa) { if (!ioa->asymmetric_access || !ioa->asymmetric_access_enabled) continue; for_each_array(ioa, array) { if (!array->array_rcd->asym_access_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } if (ioa->sis64) printf_device(array, 4); else printf_device(array, 1); } } return 0; } /** * query_ioa_asym_access_mode - Show the current asymmetric access state for * the given IOA. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioa_asym_access_mode(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (dev->ioa->asymmetric_access_enabled) printf("Enabled\n"); else if (dev->ioa->asymmetric_access) printf("Disabled\n"); else printf("Unsupported\n"); return 0; } /** * query_array_asym_access_mode - Show the current asymmetric access state for * the given array. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_array_asym_access_mode(char **args, int num_args) { struct ipr_dev *array; array = find_dev(args[0]); if (!array) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_array(array)) { if (array->ioa->sis64 && ipr_is_volume_set(array)) array = get_array_from_vset(array->ioa, array); else { fprintf(stderr, "%s is not an array.\n", array->gen_name); return -EINVAL; } } if (!array->array_rcd->asym_access_cand) printf("Unsupported\n"); else if (array->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) printf("Optimized\n"); else if (array->array_rcd->current_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) printf("Not Optimized\n"); else scsi_dbg(array, "Unrecognized state - %d.", array->array_rcd->current_asym_access_state); return 0; } /** * query_format_for_jbod - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_for_jbod(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; for_each_primary_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (ipr_device_is_zeroed(dev)) continue; if ((ipr_is_array_member(dev) && dev->dev_rcd->no_cfgte_vol) || (!ipr_is_array_member(dev) && is_format_allowed(dev))) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } } return 0; } /** * show_dev_details - * @dev: ipr dev struct * * Returns: * nothing **/ static void show_dev_details(struct ipr_dev *dev) { char *body = NULL; if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &dev->ioa->ioa) body = ioa_details(body, dev); else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) body = vset_array_details(body, dev); else if (ipr_is_ses(dev)) body = ses_details(body, dev); else body = disk_details(body, dev); printf("%s\n", body); free(body); } /** * show_jbod_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_jbod_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_jbod_disk(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 0); } } return 0; } /** * show_all_af_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_all_af_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_af_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_af_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_standalone_af_disk(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_hot_spares - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_hot_spares(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_hot_spare(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_details - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_details(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Missing device: %s\n", args[0]); return -EINVAL; } show_dev_details(dev); return 0; } /** * __print_status - * @args: argument vector * @num_args: number of arguments * @percent: percent value * * Returns: * 0 if success / non-zero on failure **/ static int __print_status(char **args, int num_args, int percent) { struct ipr_dev *dev = find_dev(args[0]); char buf[100]; if (!dev) { printf("Missing\n"); return -EINVAL; } get_status(dev, buf, percent, 0); printf("%s\n", buf); return 0; } /** * print_alt_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int print_alt_status(char **args, int num_args) { return __print_status(args, num_args, 1); } /** * print_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int print_status(char **args, int num_args) { return __print_status(args, num_args, 0); } /** * __show_config - * @type: type value * * Returns: * 0 if success / non-zero on failure **/ static int __show_config(int type) { struct ipr_ioa *ioa; printf("%s\n%s\n", status_hdr[(type&1)+2], status_sep[(type&1)+2]); for_each_ioa(ioa) printf_ioa(ioa, type); return 0; } /** * show_config - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_config(char **args, int num_args) { return __show_config(3); } /** * show_alt_config - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_alt_config(char **args, int num_args) { return __show_config(2); } /** * show_ioas - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_ioas(char **args, int num_args) { struct ipr_ioa *ioa; if (num_ioas) printf("%s\n%s\n", status_hdr[2], status_sep[2]); else printf("No IOAs found\n"); for_each_ioa(ioa) printf_device(&ioa->ioa, 2); return 0; } /** * show_arrays - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_arrays(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; int hdr = 0; for_each_ioa(ioa) { for_each_vset(ioa, vset) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(vset, 3); } } return 0; } /** * battery_info - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int battery_info(char **args, int num_args) { struct ipr_reclaim_query_data reclaim; struct ipr_dev *dev; char *buf; int rc; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_QUERY | IPR_RECLAIM_EXTENDED_INFO, &reclaim); if (rc) return rc; dev->ioa->reclaim_data = &reclaim; buf = get_battery_info(dev->ioa); printf("%s\n", buf); dev->ioa->reclaim_data = NULL; free(buf); return 0; } /** * get_ha_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int get_ha_mode(char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (!dev) return -ENXIO; if (dev->ioa->in_gscsi_only_ha) printf("JBOD\n"); else printf("Normal\n"); return 0; } /** * __set_ha_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int __set_ha_mode(char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (!dev) return -ENXIO; if (!strncasecmp(args[1], "Normal", 4)) return set_ha_mode(dev->ioa, 0); else if (!strncasecmp(args[1], "JBOD", 4)) return set_ha_mode(dev->ioa, 1); return -EINVAL; } /** * __set_preferred_primary - * @sg_name: sg device name * @preferred_primary: preferred primary value * * Returns: * 0 if success / non-zero on failure **/ static int __set_preferred_primary(char *sg_name, int preferred_primary) { struct ipr_dev *dev = find_gen_dev(sg_name); if (dev) return set_preferred_primary(dev->ioa, preferred_primary); return -ENXIO; } /** * set_primary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_primary(char **args, int num_args) { return __set_preferred_primary(args[0], 1); } /** * set_secondary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_secondary(char **args, int num_args) { return __set_preferred_primary(args[0], 0); } /** * set_all_primary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_all_primary(char **args, int num_args) { struct ipr_ioa *ioa; for_each_ioa(ioa) set_preferred_primary(ioa, 1); return 0; } /** * set_all_secondary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_all_secondary(char **args, int num_args) { struct ipr_ioa *ioa; for_each_ioa(ioa) set_preferred_primary(ioa, 0); return 0; } /** * check_and_set_active_active - Do some sanity checks and then set the * active/active state to the requested mode. * @dev_name: specified IOA * @mode: mode to set - enable or disable * * Returns: * 0 if success / non-zero on failure **/ static int check_and_set_active_active(char *dev_name, int mode) { struct ipr_dev *dev; dev = find_dev(dev_name); if (!dev) { fprintf(stderr, "Cannot find %s\n", dev_name); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", dev_name); return -EINVAL; } /* Check that asymmetric access is supported by the adapter. */ if (!dev->ioa->asymmetric_access) { ioa_err(dev->ioa, "IOA does not support asymmetric access.\n"); return -EINVAL; } /* Check the state of asymmetric access. */ if (dev->ioa->asymmetric_access_enabled == mode) { ioa_dbg(dev->ioa, "Asymmetric access is already %s.\n", mode == 0 ? "disabled" : "enabled"); return 0; } return set_active_active_mode(dev->ioa, mode); } /** * get_drive_phy_loc - get drive physical location * @ioa: ipr ioa struct * * Returns: * body **/ static int get_drive_phy_loc(struct ipr_ioa *ioa) { struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_dev *ses, *dev; struct ipr_ses_config_pg ses_cfg; int ses_bus, scsi_id_found, is_spi, is_vses; struct drive_elem_desc_pg drive_data; char phy_loc[PHYSICAL_LOCATION_LENGTH + 1]; int times, index; for_each_ioa(ioa) { is_spi = ioa_is_spi(ioa); for_each_hotplug_dev(ioa, dev) { if (ioa->sis64) get_res_path(dev); else get_res_addrs(dev); } for_each_ses(ioa, ses) { times = 5; if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); while (times--) { if (!ipr_receive_diagnostics(ses, 2, &ses_data, sizeof(ses_data))) break; } if (times < 0 ) continue; if (ipr_receive_diagnostics(ses, 1, &ses_cfg, sizeof(ses_cfg))) continue; if (ipr_receive_diagnostics(ses, 7, &drive_data, sizeof(drive_data))) continue; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); ses_bus = ses->scsi_dev_data->channel; scsi_id_found = 0; if (!is_spi && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; scsi_dbg(ses, "%s\n", is_vses ? "Found VSES" : "Found real SES"); for_each_elem_status(elem_status, &ses_data, &ses_cfg) { index = index_in_page2(&ses_data, elem_status->slot_id); if (index != -1) get_drive_phy_loc_with_ses_phy_loc(ses, &drive_data, index, phy_loc, 0); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_UNSUPP) continue; if (elem_status->status == IPR_DRIVE_ELEM_STATUS_NO_ACCESS) continue; if (is_spi && (scsi_id_found & (1 << elem_status->slot_id))) continue; scsi_id_found |= (1 << elem_status->slot_id); if (ioa->sis64) dev = get_dev_for_slot_64bit(ses, elem_status->slot_id, phy_loc); else dev = get_dev_for_slot(ses, elem_status->slot_id, is_vses, phy_loc); if (!dev) continue; } } } return 0; } /** * set_ioa_asymmetric_access - Change the asymmetric access mode for an IOA. * Enabling occurs by sending a change multi adapter * assignment command from ipr_set_ioa_attr(). * Disabling occurs by resetting the adapter and * then sending a mode select * page 24 in ipr_set_active_active_mode(). * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ioa_asymmetric_access(char **args, int num_args) { if (!strncasecmp(args[1], "Enable", 4)) return check_and_set_active_active(args[0], 1); else if (!strncasecmp(args[1], "Disable", 4)) return check_and_set_active_active(args[0], 0); return -EINVAL; } /** * set_array_asymmetric_access - Set the desired asymmetric access state of the * array to "Optimized" or "Non-Optimized". * * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_array_asymmetric_access(char **args, int num_args) { int state; struct ipr_dev *array = NULL; array = find_dev(args[0]); if (!array) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_array(array)) { if (array->ioa->sis64 && ipr_is_volume_set(array)) array = get_array_from_vset(array->ioa, array); else { scsi_err(array, "Given device is not an array."); return -EINVAL; } } /* Check that the adapter is a primary adapter. */ if (array->ioa->is_secondary) { ioa_err(array->ioa, "Asymmetric access commands must be issued " "for arrays on the primary IOA"); return -EINVAL; } /* Check that asymmetric access is supported by the adapter. */ if (!array->ioa->asymmetric_access) { ioa_err(array->ioa, "Asymmetric access is not supported."); return -EINVAL; } /* Check that asymmetric access is enabled on the adapter. */ if (!array->ioa->asymmetric_access_enabled) { ioa_err(array->ioa, "Asymmetric access is not enabled."); return -EINVAL; } /* Optimized or Non-optimized? */ if (!strncasecmp(args[1], "Optimized", 3)) state = IPR_ACTIVE_OPTIMIZED; else if (!strncasecmp(args[1], "Non-Optimized", 3)) state = IPR_ACTIVE_NON_OPTIMIZED; else { scsi_err(array, "Unrecognized state given for asymmetric access."); return -EINVAL; } if (array->array_rcd->saved_asym_access_state == state) { scsi_dbg(array, "Array is already set to requested state."); return 0; } if (array->array_rcd->asym_access_cand) array->array_rcd->issue_cmd = 1; else { scsi_err(array, "%s is not a candidate for asymmetric access.", array->dev_name); return -EINVAL; } array->array_rcd->saved_asym_access_state = state; return ipr_set_array_asym_access(array->ioa); } /** * query_array_label - Display the device name for the specified label * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_array_label(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; for_each_ioa(ioa) { if (!ioa->sis64) continue; for_each_vset(ioa, vset) { if (!strcmp((char *)vset->array_rcd->type3.desc, args[0])) { fprintf(stdout, "%s\n", vset->dev_name); return 0; } } } return -ENODEV; } /** * query_array - Display the array device name for the specified device location * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_array(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; struct ipr_dev *dev; for_each_ioa(ioa) { get_drive_phy_loc(ioa); for_each_vset(ioa, vset) { for_each_dev_in_vset(vset, dev) { strip_trailing_whitespace(dev->physical_location); if (!strcmp(dev->physical_location, args[0])) { fprintf(stdout, "%s\n", vset->dev_name); return 0; } } } } return -ENODEV; } /** * query_device - Display the device name for the specified device location * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_device(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; for_each_ioa(ioa) { get_drive_phy_loc(ioa); for_each_disk(ioa, dev) { strip_trailing_whitespace(dev->physical_location); if (!strcmp(dev->physical_location, args[0])) { if (strlen(dev->dev_name)) fprintf(stdout, "%s\n", dev->dev_name); else fprintf(stdout, "%s\n", dev->gen_name); return 0; } } } return -ENODEV; } /** * query_location - Display the physical location for the specified device name * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_location(char **args, int num_args) { struct ipr_dev *dev, *tmp, *cdev; int num_devs = 0; dev = find_dev(args[0]); if (!dev) return -ENODEV; get_drive_phy_loc(dev->ioa); if (!ipr_is_volume_set(dev)){ fprintf(stdout, "%s\n", dev->physical_location); return 0; } for_each_dev_in_vset(dev, tmp) { cdev = tmp; num_devs++; } if (num_devs != 1) return -EINVAL; fprintf(stdout, "%s\n", cdev->physical_location); return 0; } /** * query_ioa_caching - Show whether or not user requested caching mode is set * to default or disabled. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioa_caching(char **args, int num_args) { int mode; struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (!dev->ioa->has_cache) { printf("none\n"); return 0; } mode = get_ioa_caching(dev->ioa); switch (mode) { case IPR_IOA_REQUESTED_CACHING_DISABLED: printf("disabled\n"); break; case IPR_IOA_CACHING_DISABLED_DUAL_ENABLED: printf("disabled, 0x3\n"); break; case IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED: printf("default, 0x3\n"); break; default: printf("default\n"); break; } return 0; } /** * set_ioa_caching - Set the requested IOA caching mode to default or disabled. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ioa_caching(char **args, int num_args) { int mode, rc; struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache) { return -EINVAL; } if (!dev->ioa->has_cache) { fprintf(stderr, "%s does not have cache.\n", args[0]); return -EINVAL; } if (!strncasecmp(args[1], "Default", 3)) mode = IPR_IOA_SET_CACHING_DEFAULT; else if (!strncasecmp(args[1], "Disabled", 3)) mode = IPR_IOA_SET_CACHING_DISABLED; else if (!strncasecmp(args[1], "Dual-Disabled", 8)) mode = IPR_IOA_SET_CACHING_DUAL_DISABLED; else if (!strncasecmp(args[1], "Dual-Enabled", 8)) mode = IPR_IOA_SET_CACHING_DUAL_ENABLED; else { scsi_err(dev, "Unrecognized mode given for IOA caching."); return -EINVAL; } rc = ipr_change_cache_parameters(dev->ioa , mode); return rc; } /** * get_live_dump - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int get_live_dump (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (dev) return ipr_get_live_dump(dev->ioa); return -ENXIO; } /** * query_ses_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ses_mode (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); struct ipr_ses_inquiry_pageC3 pageC3_inq; int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_ses(dev)) { fprintf(stderr, "Invalid device type\n"); return -EINVAL; } memset(&pageC3_inq, 0, sizeof(pageC3_inq)); rc = ipr_inquiry(dev, 0xc3, &pageC3_inq, sizeof(pageC3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } printf("%c%c%c%c\n", pageC3_inq.mode[0], pageC3_inq.mode[1], pageC3_inq.mode[2], pageC3_inq.mode[3]); return 0; } /** * set_ses_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ses_mode (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); int mode = atoi(args[1]); int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_ses(dev)) { fprintf(stderr, "Invalid device type\n"); return -EINVAL; } rc = ipr_set_ses_mode(dev, mode); if (rc != 0) { scsi_dbg(dev, "Change SES mode command failed"); return -EIO; } return 0; } /** * show_enclosure_info - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int show_enclosure_info(struct ipr_dev *ses) { struct screen_output *s_out; int rc; if (ses->scsi_dev_data->type == TYPE_ENCLOSURE) n_show_enclosure_info.body = ses_details(n_show_enclosure_info.body, ses); else n_show_enclosure_info.body = ioa_details(n_show_enclosure_info.body, ses); s_out = screen_driver(&n_show_enclosure_info, 0, NULL); free(n_show_enclosure_info.body); n_show_enclosure_info.body = NULL; rc = s_out->rc; free(s_out); return rc; } int ipr_suspend_disk_enclosure(i_container *i_con) { int rc, suspend_rc; struct ipr_ioa *ioa; struct ipr_dev *ses; suspend_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (ses->is_suspend_cand) { rc = ipr_suspend_device_bus(ses, &ses->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (rc !=0 ) suspend_rc = RC_82_Suspended_Fail; else rc = ipr_write_dev_attr(ses, "state", "offline\n"); } } } if (suspend_rc != RC_SUCCESS) return suspend_rc | EXIT_FLAG; return RC_81_Suspended_Success | EXIT_FLAG; } int ipr_resume_disk_enclosure(i_container *i_con) { int rc, resume_rc; struct ipr_ioa *ioa; struct ipr_dev *ses; resume_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (ses->is_resume_cand) { rc = ipr_resume_device_bus(ses, &ses->res_addr[0]); if (rc !=0 ) resume_rc = RC_86_Enclosure_Resume_Fail; else ipr_write_dev_attr(ses, "state", "running\n"); } } } if (resume_rc != RC_SUCCESS) return resume_rc | EXIT_FLAG; return RC_85_Enclosure_Resume_Success | EXIT_FLAG; } /** * confirm_suspend_disk_enclosure - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_suspend_disk_enclosure(void) { int rc,k; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle= 0; rc = RC_SUCCESS; body_init_status_enclosure(buffer, n_confirm_suspend_disk_enclosure.header, &header_lines); for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (!ses->is_suspend_cand) continue; print_dev_enclosure(k, ses, buffer, "2", k); } } do { n_confirm_suspend_disk_enclosure.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_suspend_disk_enclosure, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_suspend_disk_enclosure.body = NULL; return rc; } /** * confirm_resume_disk_enclosure - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_resume_disk_enclosure(void) { int rc,k; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle= 0; rc = RC_SUCCESS; body_init_status_enclosure(buffer, n_confirm_resume_disk_enclosure.header, &header_lines); for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (!ses->is_resume_cand) continue; print_dev_enclosure(k, ses, buffer, "3", k); } } do { n_confirm_resume_disk_enclosure.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_resume_disk_enclosure, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_resume_disk_enclosure.body = NULL; return rc; } /** * enclosures_fork - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int enclosures_fork(i_container *i_con) { int rc = 0, suspend_flag =0, resume_flag = 0; struct ipr_dev *ses; struct ipr_ioa *ioa; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { ses = (struct ipr_dev *)temp_i_con->data; if (ses == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "3") == 0) { if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) return RC_89_Invalid_Dev_For_Resume; if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return RC_90_Enclosure_Is_Unknown; if (ses->active_suspend == IOA_DEV_PORT_ACTIVE) return RC_84_Enclosure_Is_Active; ses->is_resume_cand = 1; resume_flag = 1; } else if (strcmp(input, "2") == 0) { if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) return RC_88_Invalid_Dev_For_Suspend; if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return RC_90_Enclosure_Is_Unknown; if (!strncmp((char *)&ses->serial_number, (char *)&ses->ioa->yl_serial_num[0], ESM_SERIAL_NUM_LEN)) return RC_87_No_Suspend_Same_Seri_Num; if (ses->active_suspend == IOA_DEV_PORT_SUSPEND) return RC_83_Enclosure_Is_Suspend; ses->is_suspend_cand = 1; suspend_flag = 1; } else if (strcmp(input, "1") == 0) { rc = show_enclosure_info(ses); return rc; } } if (suspend_flag) { rc =confirm_suspend_disk_enclosure(); for_each_ioa(ioa) for_each_ses(ioa, ses) ses->is_suspend_cand = 0; return rc; } if (resume_flag) { rc = confirm_resume_disk_enclosure(); for_each_ioa(ioa) for_each_ses(ioa, ses) ses->is_resume_cand = 0; return rc; } return INVALID_OPTION_STATUS; } /** * enclosures_maint - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int enclosures_maint(i_container *i_con) { int rc, k, first_ses; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=0; char product_id[IPR_PROD_ID_LEN + 1]; processing(); i_con = free_i_con(i_con); body_init_status_enclosure(buffer, n_enclosures_maint.header, &header_lines); check_current_config(false); for_each_ioa(ioa) { if (!ioa->sis64) continue; first_ses = 1; for_each_ses(ioa, ses) { ipr_strncpy_0(product_id, ses->scsi_dev_data->product_id, IPR_PROD_ID_LEN); if (!strncmp(product_id,"5888", strlen("5888")) || !strncmp(product_id, "EDR1", strlen("EDR1"))) { if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); if (first_ses) { i_con = add_i_con(i_con, "\0",ioa); print_dev_enclosure(k, &ioa->ioa, buffer, "%1", (k | 0x1000)); first_ses = 0; } i_con = add_i_con(i_con, "\0",ses); print_dev_enclosure(k, ses, buffer, "%1", k); } } } do { n_enclosures_maint.body = buffer[toggle&1]; s_out = screen_driver(&n_enclosures_maint, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_enclosures_maint.body = NULL; return rc; } /** * suspend_disk_enclosure - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int suspend_disk_enclosure(char **args, int num_args) { struct ipr_dev *ses; int rc = 0; ses = find_dev(args[0]); get_ses_ioport_status(ses); if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) { fprintf(stderr,"Incorrect device type specified. Please specify a valid SAS device to suspend.\n"); return 1; } if (ses->scsi_dev_data && ses->scsi_dev_data->type != TYPE_ENCLOSURE) { fprintf(stderr, "Selected device is not SAS expander.\n"); return 1; } if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); if (!strncmp((char *)&ses->serial_number, (char *)&ses->ioa->yl_serial_num[0], ESM_SERIAL_NUM_LEN)) { fprintf(stderr, "Could not suspend an expander from the RAID controller with the same serial number.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_SUSPEND) { printf("The expander is already suspended\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return 90; rc = ipr_suspend_device_bus(ses, &ses->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (!rc) { rc = ipr_write_dev_attr(ses, "state", "offline\n"); printf("Selected disk enclosure was suspended successfully.\n"); return rc; } fprintf(stderr, "Failed to suspend this SAS expander.\n"); return 1; } /** * resume_disk_enclosure - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int resume_disk_enclosure(char **args, int num_args) { struct ipr_dev *ses; int rc = 0; ses = find_dev(args[0]); get_ses_ioport_status(ses); if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) { fprintf(stderr,"Incorrect device type specified. Please specify a valid SAS device to resume.\n"); return 1; } if (ses->scsi_dev_data && ses->scsi_dev_data->type != TYPE_ENCLOSURE) { fprintf(stderr, "Selected device is not SAS expander.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_ACTIVE) { printf("The expander is already in active state.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) { printf("The expander is an unknown state.\n"); return 1; } rc = ipr_resume_device_bus(ses, &ses->res_addr[0]); if (!rc) { ipr_write_dev_attr(ses, "state", "running\n"); printf("Selected disk enclosure was resumed successfully.\n"); return rc; } printf("Failed to resume this SAS expander.\n"); return 1; } /** * query_disk_enclosure_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_disk_enclosure_status(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *ses; int rc = 0; printf("%s\n%s\n", status_hdr[14], status_sep[14]); for_each_ioa(ioa) { printf_device((struct ipr_dev *)ioa, 0x8 | 0x1000); for_each_ses(ioa, ses) { get_ses_ioport_status(ses); if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); printf_device(ses, 0x8); } } return rc; } static char *af_dasd_perf (char *body, struct ipr_dev *dev) { struct ipr_dasd_perf_counters_log_page30 log; char buffer[200]; unsigned long long idle_loop_count; u32 idle_count; memset(&log, 0, sizeof(log)); body = add_line_to_body(body,"", NULL); if (!ipr_is_log_page_supported(dev, 0x30)) { body = add_line_to_body(body,_("Log page 30 not supported"), NULL); return body; } ipr_log_sense(dev, 0x30, &log, sizeof(log)); sprintf(buffer,"%d", ntohs(log.dev_no_seeks)); body = add_line_to_body(body,_("Seek = 0 disk"), buffer); sprintf(buffer,"%d", ntohs(log.dev_seeks_2_3)); body = add_line_to_body(body,_("Seek >= 2/3 disk"), buffer); sprintf(buffer,"%d", ntohs(log.dev_seeks_1_3)); body = add_line_to_body(body,_("Seek >= 1/3 and < 2/3 disk"), buffer); sprintf(buffer,"%d", ntohs(log.dev_seeks_1_6)); body = add_line_to_body(body,_("Seek >= 1/6 and < 1/3 disk"), buffer); sprintf(buffer,"%d", ntohs(log.dev_seeks_1_12)); body = add_line_to_body(body,_("Seek >= 1/12 and < 1/6 disk"), buffer); sprintf(buffer,"%d", ntohs(log.dev_seeks_0)); body = add_line_to_body(body,_("Seek > 0 and < 1/12 disk"), buffer); body = add_line_to_body(body,"", NULL); sprintf(buffer,"%d", ntohs(log.dev_read_buf_overruns)); body = add_line_to_body(body,_("Device Read Buffer Overruns"), buffer); sprintf(buffer,"%d", ntohs(log.dev_write_buf_underruns)); body = add_line_to_body(body,_("Device Write Buffer Underruns"), buffer); sprintf(buffer,"%d", ntohl(log.dev_cache_read_hits)); body = add_line_to_body(body,_("Device Cache Read Hits"), buffer); sprintf(buffer,"%d", ntohl(log.dev_cache_partial_read_hits)); body = add_line_to_body(body,_("Device Partial Read Hits"), buffer); sprintf(buffer,"%d", ntohl(log.dev_cache_write_hits)); body = add_line_to_body(body,_("Device Cache Write Hits"), buffer); sprintf(buffer,"%d", ntohl(log.dev_cache_fast_write_hits)); body = add_line_to_body(body,_("Device Cache Fast Writes"), buffer); body = add_line_to_body(body,"", NULL); sprintf(buffer,"%d", ntohl(log.ioa_dev_read_ops)); body = add_line_to_body(body,_("IOA Issued Device Reads"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_dev_write_ops)); body = add_line_to_body(body,_("IOA Issued Device Writes"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_cache_read_hits)); body = add_line_to_body(body,_("IOA Cache Read Hits"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_cache_partial_read_hits)); body = add_line_to_body(body,_("IOA Cache Partial Read Hits"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_cache_write_hits)); body = add_line_to_body(body,_("IOA Cache Write Hits"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_cache_fast_write_hits)); body = add_line_to_body(body,_("IOA Cache Fast Writes"), buffer); sprintf(buffer,"%d", ntohl(log.ioa_cache_emu_read_hits)); body = add_line_to_body(body,_("IOA Emulated Read Hits"), buffer); body = add_line_to_body(body,"", NULL); sprintf(buffer,"%08X%08X", ntohl(log.ioa_idle_loop_count[0]), ntohl(log.ioa_idle_loop_count[1])); idle_loop_count = strtoull(buffer, NULL, 16); sprintf(buffer,"%llu", idle_loop_count); body = add_line_to_body(body,_("IOA Idle Loop Count"), buffer); idle_count = ntohl(log.ioa_idle_count_value); sprintf(buffer,"%d", idle_count); body = add_line_to_body(body,_("IOA Idle Count"), buffer); sprintf(buffer,"%d", log.ioa_idle_units); body = add_line_to_body(body,_("IOA Idle Units"), buffer); idle_count = idle_loop_count * ( idle_count * pow(10, -log.ioa_idle_units)); sprintf(buffer,"%d seconds.", idle_count); body = add_line_to_body(body,_("IOA Idle Time"), buffer); return body; } static int show_perf (char **args, int num_args) { char *body = NULL; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (ipr_is_af_dasd_device(dev)) { body = af_dasd_perf(body, dev); } else { fprintf(stderr, "%s is not a valid DASD\n", args[0]); return -EINVAL; } printf("%s\n", body); free(body); return 0; } static const struct { char *cmd; int min_args; int unlimited_max; int max_args; int (*func)(char **, int); char *usage; } command [] = { { "show-config", 0, 0, 0, show_config, "" }, { "show-alt-config", 0, 0, 0, show_alt_config, "" }, { "show-ioas", 0, 0, 0, show_ioas, "" }, { "show-arrays", 0, 0, 0, show_arrays, "" }, { "show-battery-info", 1, 0, 1, battery_info, "sg5" }, { "show-details", 1, 0, 1, show_details, "sda" }, { "show-hot-spares", 0, 0, 0, show_hot_spares, "" }, { "show-af-disks", 0, 0, 0, show_af_disks, "" }, { "show-all-af-disks", 0, 0, 0, show_all_af_disks, "" }, { "show-jbod-disks", 0, 0, 0, show_jbod_disks, "" }, { "status", 1, 0, 1, print_status, "sda" }, { "alt-status", 1, 0, 1, print_alt_status, "sda" }, { "query-raid-create", 1, 0, 1, query_raid_create, "sg5" }, { "query-raid-delete", 1, 0, 1, query_raid_delete, "sg5" }, { "query-hot-spare-create", 1, 0, 1, query_hot_spare_create, "sg5" }, { "query-hot-spare-delete", 1, 0, 1, query_hot_spare_delete, "sg5" }, { "query-raid-consistency-check", 0, 0, 0, query_raid_consistency_check, "" }, { "query-format-for-jbod", 0, 0, 0, query_format_for_jbod, "" }, { "query-reclaim", 0, 0, 0, query_reclaim, "" }, { "query-arrays-raid-include", 0, 0, 0, query_arrays_include, "" }, { "query-devices-raid-include", 1, 0, 1, query_devices_include, "sdb" }, { "query-arrays-raid-migrate", 0, 0, 0, query_arrays_raid_migrate, "" }, { "query-devices-raid-migrate", 1, 0, 1, query_devices_raid_migrate, "sda" }, { "query-raid-levels-raid-migrate", 1, 0, 1, query_raid_levels_raid_migrate, "sda" }, { "query-stripe-sizes-raid-migrate", 2, 0, 2, query_stripe_sizes_raid_migrate, "sg5 10" }, { "query-devices-min-max-raid-migrate", 2, 0, 2, query_devices_min_max_raid_migrate, "sg5 10" }, { "query-ioas-asymmetric-access", 0, 0, 0, query_ioas_asym_access, "" }, { "query-arrays-asymmetric-access", 0, 0, 0, query_arrays_asym_access, "" }, { "query-ioa-asymmetric-access-mode", 1, 0, 1, query_ioa_asym_access_mode, "sg5" }, { "query-array-asymmetric-access-mode", 1, 0, 1, query_array_asym_access_mode, "sda" }, { "query-recovery-format", 0, 0, 0, query_recovery_format, "" }, { "query-raid-rebuild", 0, 0, 0, query_raid_rebuild, "" }, { "query-format-for-raid", 0, 0, 0, query_format_for_raid, "" }, { "query-ucode-level", 1, 0, 1, query_ucode_level, "sda" }, { "query-format-timeout", 1, 0, 1, query_format_timeout, "sg6" }, { "query-qdepth", 1, 0, 1, query_qdepth, "sda" }, { "query-tcq-enable", 1, 0, 1, query_tcq_enable, "sda" }, { "query-log-level", 1, 0, 1, query_log_level, "sg5" }, { "query-add-device", 0, 0, 0, query_add_device, "" }, { "query-remove-device", 0, 0, 0, query_remove_device, "" }, { "query-initiator-id", 2, 0, 2, query_initiator_id, "sg5 0" }, { "query-bus-speed", 2, 0, 2, query_bus_speed, "sg5 0" }, { "query-bus-width", 2, 0, 2, query_bus_width, "sg5 0" }, { "query-supported-raid-levels", 1, 0, 1, query_raid_levels, "sg5" }, { "query-include-allowed", 2, 0, 2, query_include_allowed, "sg5 5" }, { "query-max-devices-in-array", 2, 0, 2, query_max_array_devices, "sg5 5" }, { "query-min-devices-in-array", 2, 0, 2, query_min_array_devices, "sg5 5" }, { "query-min-mult-in-array", 2, 0, 2, query_min_mult_in_array, "sg5 5" }, { "query-supp-stripe-sizes", 2, 0, 2, query_supp_stripe_sizes, "sg5 5" }, { "query-recommended-stripe-size", 2, 0, 2, query_recommended_stripe_size, "sg5 5" }, { "query-path-status", 0, 0, 1, query_path_status, "sg5" }, { "query-path-details", 1, 0, 1, query_path_details, "sda" }, { "query-ioa-caching", 1, 0, 1, query_ioa_caching, "sg5" }, { "query-array-label", 1, 0, 1, query_array_label, "mylabel" }, { "query-array", 1, 0, 1, query_array, "U5886.001.P915059-P1-D1" }, { "query-device", 1, 0, 1, query_device, "U5886.001.P915059-P1-D1" }, { "query-location", 1, 0, 1, query_location, "sg5" }, { "set-ioa-caching", 2, 0, 2, set_ioa_caching, "sg5 [Default | Disabled]" }, { "primary", 1, 0, 1, set_primary, "sg5" }, { "secondary", 1, 0, 1, set_secondary, "sg5" }, { "set-all-primary", 0, 0, 0, set_all_primary, "" }, { "set-all-secondary", 0, 0, 0, set_all_secondary, "" }, { "query-ha-mode", 1, 0, 1, get_ha_mode, "sg5" }, { "set-ha-mode", 2, 0, 2, __set_ha_mode, "sg5 [Normal | JBOD]" }, { "set-ioa-asymmetric-access-mode", 2, 0, 2, set_ioa_asymmetric_access, "sg5 [Enabled | Disabled]" }, { "set-array-asymmetric-access-mode", 2, 0, 2, set_array_asymmetric_access, "sda [Optimized | Non-Optimized]" }, { "raid-create", 1, 1, 0, raid_create, "-r 5 -s 64 sda sdb sg6 sg7" }, { "raid-delete", 1, 0, 1, raid_delete, "sdb" }, { "raid-include", 2, 0, 17, raid_include_cmd, "sda sg6 sg7" }, { "format-for-raid", 1, 1, 0, format_for_raid, "sda sdb sdc" }, { "format-for-jbod", 1, 1, 0, format_for_jbod, "sg6 sg7 sg8" }, { "recovery-format", 1, 1, 0, recovery_format, "sda sg7" }, { "hot-spare-create", 1, 0, 1, hot_spare_create, "sg6" }, { "hot-spare-delete", 1, 0, 1, hot_spare_delete, "sg6" }, { "reclaim-cache", 1, 0, 1, reclaim, "sg5" }, { "reclaim-unknown-cache", 1, 0, 1, reclaim_unknown, "sg5" }, { "force-cache-battery-error", 1, 0, 1, force_cache_battery_error, "sg5" }, { "start-ioa-cache", 1, 0, 1, start_ioa_cache, "sg5" }, { "raid-consistency-check", 1, 0, 1, raid_consistency_check, "sg5" }, { "raid-rebuild", 1, 0, 1, raid_rebuild_cmd, "sg6" }, { "disrupt-device", 1, 0, 1, disrupt_device_cmd, "sg6" }, { "update-ucode", 2, 0, 2, update_ucode_cmd, "sg5 /root/ucode.bin" }, { "set-format-timeout", 2, 0, 2, set_format_timeout, "sg6 4" }, { "set-qdepth", 2, 0, 2, set_qdepth, "sda 16" }, { "set-tcq-enable", 2, 0, 2, set_tcq_enable, "sda 0" }, { "set-log-level", 2, 0, 2, set_log_level_cmd, "sg5 2" }, { "identify-disk", 2, 0, 2, identify_disk, "sda 1" }, { "identify-slot", 2, 0, 2, identify_slot, "U5886.001.P915059-P1-D1 1" }, { "remove-disk", 2, 0, 2, remove_disk, "sda 1" }, { "remove-slot", 2, 0, 2, remove_slot, "U5886.001.P915059-P1-D1 1" }, { "add-slot", 2, 0, 2, add_slot, "U58886.001.P915059-P1-D1 1" }, { "set-initiator-id", 3, 0, 3, set_initiator_id, "sg5 0 7" }, { "set-bus-speed", 3, 0, 3, set_bus_speed, "sg5 0 320" }, { "set-bus-width", 3, 0, 3, set_bus_width, "sg5 0 16" }, { "raid-migrate", 3, 1, 0, raid_migrate_cmd, "-r 10 [-s 256] sda sg6 sg7" }, { "get-live-dump", 1, 0, 1, get_live_dump, "sg5" }, { "query-ses-mode", 1, 0, 1, query_ses_mode, "sg8"}, { "set-ses-mode", 2, 0, 2, set_ses_mode, "sg8 2"}, { "query-disk-enclosure-status", 0, 0, 0, query_disk_enclosure_status}, { "suspend-disk-enclosure", 1, 0, 1, suspend_disk_enclosure, "sg8"}, { "resume-disk-enclosure", 1, 0, 1, resume_disk_enclosure, "sg8 "}, { "show-perf", 1, 0, 1, show_perf, "sg8"}, }; /** * non_intenactive_cmd - process a command line command * @cmd: command string * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int non_intenactive_cmd(char *cmd, char **args, int num_args) { int rc, i; exit_func = cmdline_exit_func; closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); check_current_config(false); for (i = 0; i < ARRAY_SIZE(command); i++) { if (strcmp(cmd, command[i].cmd) != 0) continue; if (num_args < command[i].min_args) { fprintf(stderr, "Not enough arguments specified.\n"); fprintf(stderr, "Usage: iprconfig -c %s %s\n", cmd, command[i].usage); return -EINVAL; } if (!command[i].unlimited_max && num_args > command[i].max_args) { fprintf(stderr, "Too many arguments specified.\n"); fprintf(stderr, "Usage: iprconfig -c %s %s\n", cmd, command[i].usage); return -EINVAL; } rc = command[i].func(args, num_args); exit_func(); return rc; } exit_func(); usage(); return -EINVAL; } int check_sg_module() { DIR *sg_dirfd; char devpath[PATH_MAX]; sprintf(devpath, "%s", "/sys/module/sg"); sg_dirfd = opendir(devpath); if (!sg_dirfd) { syslog_dbg("Failed to open sg parameter.\n"); return -1; } closedir(sg_dirfd); return 0; } /** * main - program entry point * @argc: number of arguments * @argv: argument vector * * Returns: * 0 if success / non-zero on failure **/ int main(int argc, char *argv[]) { int next_editor, next_dir, next_cmd, next_args, i, rc = 0; char parm_editor[200], parm_dir[200], cmd[200]; int non_intenactive = 0; strcpy(parm_dir, DEFAULT_LOG_DIR); strcpy(parm_editor, DEFAULT_EDITOR); openlog("iprconfig", LOG_PID | /* Include the PID with each error */ LOG_CONS, /* Write to system console if there is an error sending to system logger */ LOG_USER); if (argc > 1) { next_editor = 0; next_dir = 0; next_cmd = 0; next_args = 0; for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else if (strcmp(argv[i], "-e") == 0) next_editor = 1; else if (strcmp(argv[i], "-k") == 0) next_dir = 1; else if (strcmp(argv[i], "-c") == 0) next_cmd = 1; else if (next_editor) { strcpy(parm_editor, argv[i]); next_editor = 0; } else if (next_dir) { strcpy(parm_dir, argv[i]); next_dir = 0; } else if (next_cmd) { strcpy(cmd, argv[i]); non_intenactive = 1; next_cmd = 0; next_args = 1; } else if (next_args) { add_args = realloc(add_args, sizeof(*add_args) * (num_add_args + 1)); add_args[num_add_args] = malloc(strlen(argv[i]) + 1); strcpy(add_args[num_add_args], argv[i]); num_add_args++; } else { usage(); exit(1); } } } strcpy(log_root_dir, parm_dir); strcpy(editor, parm_editor); if (check_sg_module() == -1) system("modprobe sg"); exit_func = tool_exit_func; tool_init(0); if (non_intenactive) return non_intenactive_cmd(cmd, add_args, num_add_args); use_curses = 1; curses_init(); cbreak(); /* take input chars one at a time, no wait for \n */ keypad(stdscr,TRUE); noecho(); s_status.index = 0; main_menu(NULL); if (head_zdev){ check_current_config(false); ipr_cleanup_zeroed_devs(); } while (head_zdev) { struct screen_output *s_out; i_container *i_con; n_exit_confirm.body = body_init(n_exit_confirm.header, NULL); for (i = 0; i < n_exit_confirm.num_opts; i++) { n_exit_confirm.body = ipr_list_opts(n_exit_confirm.body, n_exit_confirm.options[i].key, n_exit_confirm.options[i].list_str); } n_exit_confirm.body = ipr_end_list(n_exit_confirm.body); s_out = screen_driver(&n_exit_confirm, 0, NULL); free(n_exit_confirm.body); n_exit_confirm.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); check_current_config(false); ipr_cleanup_zeroed_devs(); } clear(); refresh(); endwin(); exit(0); return 0; } iprutils/iprupdate.c0000644000000000000000000001126412427235652013604 0ustar rootroot/** * IBM IPR adapter microcode update utility * * (C) Copyright 2000, 2008 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprupdate.c,v 1.23 2008/11/20 01:20:20 wboyer Exp $ */ #include #include #include #include #ifndef iprlib_h #include "iprlib.h" #endif #include char *tool_name = "iprupdate"; static int force_devs; static int force_ioas; static int ioa_needs_update(struct ipr_ioa *ioa, int silent) { u32 fw_version = get_ioa_fw_version(ioa); if (fw_version >= ioa->msl) return 0; if (!silent) ioa_info(ioa, "Adapter needs microcode update\n"); return 1; } static int dev_needs_update(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq_data; struct ipr_dasd_inquiry_page3 page3_inq; struct unsupported_af_dasd *unsupp_af; if (!dev->scsi_dev_data || ipr_is_volume_set(dev) || (dev->scsi_dev_data->type != TYPE_DISK && dev->scsi_dev_data->type != IPR_TYPE_AF_DISK)) return 0; memset(&std_inq_data, 0, sizeof(std_inq_data)); if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data))) return 0; if (ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq))) return 0; unsupp_af = get_unsupp_af(&std_inq_data, &page3_inq); if (!unsupp_af) return 0; if (!disk_needs_msl(unsupp_af, &std_inq_data)) return 0; scsi_info(dev, "Device needs microcode update\n"); return 1; } static int update_ioa_fw(struct ipr_ioa *ioa, int force) { int rc; struct ipr_fw_images *list; if (!ioa_needs_update(ioa, 1) && !force) return 0; rc = get_ioa_firmware_image_list(ioa, &list); if (rc < 1) { if (ioa->should_init) ipr_log_ucode_error(ioa); return rc; } rc = ipr_update_ioa_fw(ioa, list, force); free(list); return rc; } static int update_disk_fw(struct ipr_dev *dev) { int rc; struct ipr_fw_images *list; if (polling_mode && !dev->should_init) return 0; if (!dev->scsi_dev_data || ipr_is_volume_set(dev) || (dev->scsi_dev_data->type != TYPE_DISK && dev->scsi_dev_data->type != IPR_TYPE_AF_DISK)) return 0; rc = get_dasd_firmware_image_list(dev, &list); if (rc > 0) { rc = ipr_update_disk_fw(dev, list, force_devs); free(list); } return rc; } static int download_needed(struct ipr_ioa *ioa) { struct ipr_dev *dev; int rc = 0; rc |= ioa_needs_update(ioa, 0); for_each_dev(ioa, dev) rc |= dev_needs_update(dev); return rc; } static int any_download_needed() { struct ipr_ioa *ioa; int rc = 0; tool_init(1); check_current_config(false); for_each_ioa(ioa) rc |= download_needed(ioa); return rc; } static int update_ioa(struct ipr_ioa *ioa) { struct ipr_dev *dev; int rc = 0; if (polling_mode && !ioa->should_init) return 0; rc |= update_ioa_fw(ioa, force_ioas); for_each_dev(ioa, dev) rc |= update_disk_fw(dev); return rc; } static int __update_all() { struct ipr_ioa *ioa; int rc = 0; polling_mode = 1; tool_init(1); check_current_config(false); for_each_ioa(ioa) rc |= update_ioa(ioa); return rc; } static void update_all() { __update_all(); } static void kevent_handler(char *buf) { polling_mode = 0; if (!strncmp(buf, "change@/class/scsi_host", 23) || (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host"))) scsi_host_kevent(buf, update_ioa); else if (!strncmp(buf, "add@/class/scsi_generic", 23) || (!strncmp(buf, "add@/devices/pci", 16) && strstr(buf, "scsi_generic"))) scsi_dev_kevent(buf, find_gen_dev, update_disk_fw); else if (!strncmp(buf, "add@/block/sd", 13)) scsi_dev_kevent(buf, find_blk_dev, update_disk_fw); } int main(int argc, char *argv[]) { int i, rc; int check_levels = 0; openlog("iprupdate", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else if (strcmp(argv[i], "--force-devs") == 0) force_devs = 1; else if (strcmp(argv[i], "--force-ioas") == 0) force_ioas = 1; else if (strcmp(argv[i], "--check_only") == 0) check_levels = 1; else { printf("Usage: iprupdate [options]\n"); printf(" Options: --version Print iprupdate version\n"); printf(" --daemon Run as a daemon\n"); printf(" --check_only Check for needed updates\n"); exit(1); } } if (ipr_force) force_devs = force_ioas = 1; ipr_sg_required = 1; if (check_levels) return any_download_needed(); rc = __update_all(); if (!daemonize) return rc; force_devs = force_ioas = 0; ipr_daemonize(); return handle_events(update_all, 60, kevent_handler); } iprutils/iprlib.c0000644000000000000000000066213712427235652013103 0ustar rootroot/** * IBM IPR adapter utility library * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprlib.c,v 1.127 2009/10/30 18:46:39 klebers Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif static void default_exit_func() { } struct ipr_array_query_data *ipr_qac_data = NULL; int num_ioas = 0; struct ipr_ioa *ipr_ioa_head = NULL; struct ipr_ioa *ipr_ioa_tail = NULL; void (*exit_func) (void) = default_exit_func; int daemonize = 0; int ipr_debug = 0; int ipr_force = 0; int ipr_sg_required = 0; int polling_mode = 0; int ipr_fast = 0; int format_done = 0; static int ipr_mode5_write_buffer = 0; static int first_time_check_zeroed_dev = 0; struct sysfs_dev *head_zdev = NULL; struct sysfs_dev *tail_zdev = NULL; static int ipr_force_polling = 0; static int ipr_force_uevents = 0; static char *hotplug_dir = NULL; static struct scsi_dev_data *scsi_dev_table = NULL; /* This table includes both unsupported 522 disks and disks that support being formatted to 522, but require a minimum microcode level. The disks that require a minimum level of microcode will be marked by the supported_with_min_ucode_level flag set to true. */ struct unsupported_af_dasd unsupported_af[] = { { /* 00 Mako & Hammerhead */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DRVS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 01 Swordfish */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DRHS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 02 Neptune */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DNES "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 03 Thornback, Stingray, & Manta */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DMVS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 04 Discovery I */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DDYS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 05 US73 (Discovery II) - fixme - are we supporting this drive? */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"IC35L D2 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"S5DE"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 06 Apollo (Ext. Scallop 9GB) - LID: SC09 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC09"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C749"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 07 Apollo (Ext. Scallop 18GB) - LID: SC18 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC18"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 08 Apollo (Ext. Scallop 36GB) - LID: SC36 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST336605L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC36"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 09 Apollo (Ext. Scallop 73GB) - LID: SC73 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST373405L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC73"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 10 Apollo (non-external 9GB) - LID: AT1x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT1 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C54E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 11 Apollo (non-external 18GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 12 Apollo (non-external 36GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST336605L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 13 Apollo (non-external 73GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST373405L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 14 Odyssey (18, 36, 73GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 53L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C51A"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 15 Odyssey (146+GB) - fixme - not in drive list; remove from table? */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 53L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C51A"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 16 Gemini (18, 36, 73GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 07L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50F"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 17 Gemini (146+GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 07L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50F"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 18 Daytona */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"IC35L DY "}, compare_product_id_byte: {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"S28C"}, min_ucode_mask: {1, 1, 1, 1} } }; struct unsupported_dasd unsupported_dasd [] = { { /* Piranha 18 GB, 15k RPM 160 MB/s - UCPR018 - 4322 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"UCPR018 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 18 GB, 15k RPM 320 MB/s - XCPR018 - 4325 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR018 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 35 GB, 15k RPM 160 MB/s - UCPR036 - 4323 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"UCPR036 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 35 GB, 15k RPM 320 MB/s - XCPR036 - 4326 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR036 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monza 70 GB, 15k RPM 320 MB/s - XCPR073 - 4327 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR073 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monza 141 GB, 15k RPM 320 MB/s - XCPR146 - 4328 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR146 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monaco 282 GB, 15k RPM 320 MB/s - XCPR282 - 4329 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR282 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, } }; struct ses_table_entry { char product_id[17]; char compare_product_id_byte[17]; u32 max_bus_speed_limit; int block_15k_devices; }; static const struct ses_table_entry ses_table[] = { {"2104-DL1 ", "XXXXXXXXXXXXXXXX", 80, 0}, {"2104-TL1 ", "XXXXXXXXXXXXXXXX", 80, 0}, {"HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"2104-DU3 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"2104-TU3 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"St V1S2 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160, 0} }; /*---------- static subroutine/function declearation starts here --------*/ static int sg_ioctl_by_name(char *, u8 *, void *, u32, u32, struct sense_data_t *, u32); /*---------- subroutine/function code starts here ---------*/ /** * ses_table_entry - * @ioa: ipr ioa struct * @bus: * * Returns: * ses_table_entry pointer if success / NULL on failure **/ static const struct ses_table_entry *get_ses_entry(struct ipr_ioa *ioa, int bus) { int i, j, matches; struct ipr_dev *dev = ioa->dev; const struct ses_table_entry *ste = ses_table; for (i = 0; i < ioa->num_devices; i++, dev++) { if (!dev->scsi_dev_data) continue; if (dev->scsi_dev_data->channel == bus && dev->scsi_dev_data->type == TYPE_ENCLOSURE) break; } if (i == ioa->num_devices) return NULL; if (strncmp(dev->scsi_dev_data->vendor_id, "IBM", 3)) return NULL; for (i = 0; i < ARRAY_SIZE(ses_table); i++, ste++) { for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) { if (ste->compare_product_id_byte[j] == 'X') { if (dev->scsi_dev_data->product_id[j] == ste->product_id[j]) matches++; else break; } else matches++; } if (matches == IPR_PROD_ID_LEN) return ste; } return NULL; } /** * get_cap_entry - Returns a pointer to a capability entry that corresponds to * the given raid level. * @supported_arrays: ipr_supported_arrays struct * @raid_level: character string representation of the raid level * * Returns: * ipr_array_cap_entry pointer if success / NULL on failure **/ struct ipr_array_cap_entry * get_cap_entry(struct ipr_supported_arrays *supported_arrays, char *raid_level) { struct ipr_array_cap_entry *cap; for_each_cap_entry(cap, supported_arrays) { if (!strcmp((char *)cap->prot_level_str, raid_level)) return cap; } return NULL; } /** * get_raid_cap_entry - Returns a pointer to a capability entry that corresponds * to the given protection level. * @supported_arrays: ipr_supported_arrays struct * @prot_level: protection level * * Returns: * ipr_array_cap_entry pointer if success / NULL on failure **/ struct ipr_array_cap_entry * get_raid_cap_entry(struct ipr_supported_arrays *supported_arrays, u8 prot_level) { struct ipr_array_cap_entry *cap; if (!supported_arrays) return NULL; for_each_cap_entry(cap, supported_arrays) { if (cap->prot_level == prot_level) return cap; } return NULL; } /** * get_prot_level_str - Returns the string representation of the protection * level the given the numeric protection level. * @supported_arrays: ipr_supported_arrays struct * @prot_level: protection level * * Returns: * protection level string if success / NULL on failure **/ char *get_prot_level_str(struct ipr_supported_arrays *supported_arrays, int prot_level) { struct ipr_array_cap_entry *cap; cap = get_raid_cap_entry(supported_arrays, prot_level); if (cap) return (char *)cap->prot_level_str; return NULL; } /** * get_vset_from_array - Given an array, return the corresponding * volume set device. * @ioa: ipr ioa struct * @array: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ struct ipr_dev *get_vset_from_array(struct ipr_ioa *ioa, struct ipr_dev *array) { struct ipr_dev *vset; for_each_vset(ioa, vset) if (vset->array_id == array->array_id) return vset; return array; } /** * get_array_from_vset - Given a volume set, return the corresponding * array device. * @ioa: ipr ioa struct * @vset: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ struct ipr_dev *get_array_from_vset(struct ipr_ioa *ioa, struct ipr_dev *vset) { struct ipr_dev *array; for_each_array(ioa, array) if (array->array_id == vset->array_id) return array; return vset; } static ssize_t sysfs_read_attr(const char *devpath, const char *attr, void *value, size_t len) { char attrpath[256]; ssize_t ret; int fd; sprintf(attrpath, "%s/%s", devpath, attr); fd = open(attrpath, O_RDONLY); if (fd < 0) return -1; ret = read(fd, value, len); close(fd); if (ret < 0) return -1; return ret; } static ssize_t sysfs_write_attr(const char *devpath, const char *attr, void *value, size_t len) { char attrpath[256]; ssize_t ret; int fd; sprintf(attrpath, "%s/%s", devpath, attr); fd = open(attrpath, O_WRONLY); if (fd < 0) return -1; ret = write(fd, value, len); close(fd); if (ret < 0) return -1; return ret; } /** * ipr_find_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * * Returns: * sysfs_dev pointer if success / NULL on failure **/ struct sysfs_dev * ipr_find_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev *head) { struct sysfs_dev *sdev; if (!dev->scsi_dev_data) return NULL; for (sdev = head; sdev; sdev = sdev->next) { if (!strcmp(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name)) break; } return sdev; } /** * ipr_sysfs_dev_to_dev - * @sdev: sysfs dev struct * * Returns: * ipr_dev pointer if success / NULL on failure **/ struct ipr_dev *ipr_sysfs_dev_to_dev(struct sysfs_dev *sdev) { struct ipr_dev *dev; struct ipr_ioa *ioa; for_each_ioa(ioa) { for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (!strcmp(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name)) return dev; } } return NULL; } /** * ipr_find_zeroed_dev - * @dev: ipr dev struct * * Returns: * results of call to ipr_find_sysfs_dev **/ struct sysfs_dev * ipr_find_zeroed_dev(struct ipr_dev *dev) { return ipr_find_sysfs_dev(dev, head_zdev); } /** * ipr_device_is_zeroed - * @dev: ipr dev struct * * Returns: * 1 if the given devices is zeroed / 0 if the device is not zeroed **/ int ipr_device_is_zeroed(struct ipr_dev *dev) { if (ipr_find_zeroed_dev(dev)) return 1; return 0; } /** * ipr_add_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * @tail: sysfs dev struct * * Returns: * nothing **/ void ipr_add_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev **head, struct sysfs_dev **tail) { struct sysfs_dev *sdev = ipr_find_sysfs_dev(dev, *head); if (!dev->scsi_dev_data) return; if (!sdev) { sdev = calloc(1, sizeof(struct sysfs_dev)); strcpy(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name); if (!(*head)) { *tail = *head = sdev; } else { (*tail)->next = sdev; sdev->prev = *tail; *tail = sdev; } } } /** * ipr_add_zeroed_dev - * @dev: ipr dev struct * * Returns: * nothing **/ void ipr_add_zeroed_dev(struct ipr_dev *dev) { ipr_add_sysfs_dev(dev, &head_zdev, &tail_zdev); } /** * ipr_del_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * @tail: sysfs dev struct * * Returns: * nothing **/ void ipr_del_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev **head, struct sysfs_dev **tail) { struct sysfs_dev *sdev = ipr_find_sysfs_dev(dev, *head); if (!sdev || !dev->scsi_dev_data) return; if (sdev == *head) { *head = (*head)->next; if (!(*head)) *tail = NULL; else (*head)->prev = NULL; } else if (sdev == *tail) { *tail = (*tail)->prev; (*tail)->next = NULL; } else { sdev->next->prev = sdev->prev; sdev->prev->next = sdev->next; } } /** * ipr_del_zeroed_dev - * @dev: ipr dev struct * * Returns: * nothing **/ void ipr_del_zeroed_dev(struct ipr_dev *dev) { ipr_del_sysfs_dev(dev, &head_zdev, &tail_zdev); } /** * ipr_update_qac_with_zeroed_devs * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_update_qac_with_zeroed_devs(struct ipr_ioa *ioa) { struct sysfs_dev *zdev; struct ipr_dev_record *dev_rcd; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; int i; if (!ioa->qac_data) return; for (i = 0; i < ioa->num_devices; i++) { zdev = ipr_find_zeroed_dev(&ioa->dev[i]); if (!zdev && ipr_is_af_dasd_device(&ioa->dev[i])) { memset(&mode_pages, 0, sizeof(mode_pages)); ipr_mode_sense(&ioa->dev[i], 0x20, &mode_pages); page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (page->format_completed) { dev_rcd = (struct ipr_dev_record *)ioa->dev[i].qac_entry; dev_rcd->known_zeroed = 1; } } else if (zdev && ioa->dev[i].qac_entry) { dev_rcd = (struct ipr_dev_record *)ioa->dev[i].qac_entry; dev_rcd->known_zeroed = 1; } } } void set_devs_format_completed() { struct ipr_ioa *ioa; struct ipr_dev *dev; if (format_done) { for_each_ioa(ioa) { for_each_dev(ioa, dev) { if (ipr_is_af_dasd_device(dev) && ipr_device_is_zeroed(dev)) ipr_set_format_completed_bit(dev); } } format_done = 0; } } /** * ipr_cleanup_zeroed_devs - * * Returns: * nothing **/ void ipr_cleanup_zeroed_devs() { struct ipr_ioa *ioa; struct ipr_dev *dev; struct sysfs_dev *zdev; for_each_ioa(ioa) { for_each_dev(ioa, dev) { zdev = ipr_find_zeroed_dev(dev); if (!zdev) continue; if (dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_DISK) ipr_del_zeroed_dev(dev); else if (ipr_is_array_member(dev)) ipr_del_zeroed_dev(dev); else if (ipr_is_hot_spare(dev)) ipr_del_zeroed_dev(dev); } } } /** * get_max_bus_speed - * @ioa: ipr ioa struct * @bus: bus number * * Returns: * maximum bus speed value **/ int get_max_bus_speed(struct ipr_ioa *ioa, int bus) { const struct ses_table_entry *ste = get_ses_entry(ioa, bus); u32 fw_version; int max_xfer_rate = IPR_MAX_XFER_RATE; char devpath[PATH_MAX]; char value[16]; ssize_t len; sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_read_attr(devpath, "fw_version", value, 16); if (len < 0) return -1; sscanf(value, "%8X", &fw_version); if (ipr_force) return IPR_MAX_XFER_RATE; if (fw_version < ioa->msl) max_xfer_rate = IPR_SAFE_XFER_RATE; if (ste && ste->max_bus_speed_limit < max_xfer_rate) return ste->max_bus_speed_limit; return max_xfer_rate; } struct ioa_parms { int ccin; int scsi_id_changeable; u32 msl; char *fw_name; }; static const struct ioa_parms ioa_parms [] = { {.ccin = 0x5702, .scsi_id_changeable = 1, .msl = 0x02080029, .fw_name = "44415254" }, {.ccin = 0x1974, .scsi_id_changeable = 1, .msl = 0x02080037, .fw_name = "44415254" }, {.ccin = 0x5703, .scsi_id_changeable = 1, .msl = 0x03090059, .fw_name = "5052414D" }, {.ccin = 0x1975, .scsi_id_changeable = 1, .msl = 0x03090068, .fw_name = "5052414D" }, {.ccin = 0x5709, .scsi_id_changeable = 0, .msl = 0x030D0047, .fw_name = "5052414E" }, {.ccin = 0x1976, .scsi_id_changeable = 0, .msl = 0x030D0056, .fw_name = "5052414E" }, {.ccin = 0x570A, .scsi_id_changeable = 0, .msl = 0x020A004E, .fw_name = "44415255" }, {.ccin = 0x570B, .scsi_id_changeable = 0, .msl = 0x020A004E, .fw_name = "44415255" }, {.ccin = 0x2780, .scsi_id_changeable = 0, .msl = 0x030E0040, .fw_name = "5349530E" }, }; struct chip_details { u16 vendor; u16 device; const char *desc; }; static const struct chip_details chip_details []= { { .vendor = 0x1069, .device = 0xB166, "PCI-X" }, /* Gemstone */ { .vendor = 0x1014, .device = 0x028C, "PCI-X" }, /* Citrine */ { .vendor = 0x1014, .device = 0x0180, "PCI-X" }, /* Snipe */ { .vendor = 0x1014, .device = 0x02BD, "PCI-X" }, /* Obsidian */ { .vendor = 0x1014, .device = 0x0339, "PCI-E" }, /* Obsidian-E */ { .vendor = 0x9005, .device = 0x0503, "PCI-X" }, /* Scamp */ { .vendor = 0x1014, .device = 0x033D, "PCI-E" }, /* CRoC-FPGA */ { .vendor = 0x1014, .device = 0x034A, "PCI-E" }, /* CRoCodile */ }; /** * get_chip_details - * @ioa: ipr ioa struct * * Returns: * chip_details struct pointer if success / NULL on failure **/ static const struct chip_details *get_chip_details(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(chip_details); i++) { if (ioa->pci_vendor == chip_details[i].vendor && ioa->pci_device == chip_details[i].device) return &chip_details[i]; } return NULL; } struct ioa_details { u16 subsystem_vendor; u16 subsystem_device; const char *ioa_desc; int is_spi:1; }; static const struct ioa_details ioa_details [] = { {.subsystem_vendor = 0x1014, .subsystem_device = 0x028D, "PCI-X SAS RAID Adapter", .is_spi = 0} }; /** * get_ioa_details - * @ioa: ipr ioa struct * * Returns: * ioa_details struct pointer if success / NULL on failure **/ static const struct ioa_details *get_ioa_details(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(ioa_details); i++) { if (ioa->subsystem_vendor == ioa_details[i].subsystem_vendor && ioa->subsystem_device == ioa_details[i].subsystem_device) return &ioa_details[i]; } return NULL; } bool ipr_is_af_blk_size(struct ipr_ioa *ioa, int blk_sz) { if (blk_sz == ioa->af_block_size || (ioa->support_4k && blk_sz == IPR_AF_4K_BLOCK_SIZE)) return true; else return false; } /** * ipr_improper_device_type - * @dev: ipr dev struct * * Returns: * 1 if the device is improper / 0 if the device is not improper **/ int ipr_improper_device_type(struct ipr_dev *dev) { if (dev->rescan) return 1; if (!dev->scsi_dev_data) return 0; if (!dev->ioa->qac_data || !dev->ioa->qac_data->num_records) return 0; if (dev->scsi_dev_data->type == IPR_TYPE_AF_DISK && !dev->qac_entry && (ipr_get_blk_size(dev) == IPR_JBOD_BLOCK_SIZE || (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_JBOD_4K_BLOCK_SIZE))) return 1; if (dev->scsi_dev_data->type == TYPE_DISK && dev->qac_entry && ipr_is_af_blk_size(dev->ioa, ipr_get_blk_size(dev))) return 1; return 0; } /** * mode_sense - * @dev: ipr dev struct * @page: page number * @buff: buffer * @sense_data sense_data_t struct * * Returns: * 0 if success / non-zero on failure **/ static int mode_sense(struct ipr_dev *dev, u8 page, void *buff, struct sense_data_t *sense_data) { u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; u8 length = IPR_MODE_SENSE_LENGTH; /* xxx FIXME? */ char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { scsi_dbg(dev, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SENSE; cdb[2] = page; cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, sense_data, IPR_INTERNAL_DEV_TIMEOUT); close(fd); return rc; } /** * ipr_tcq_mode - * @ioa: ipr ioa struct * * Returns: * the tcq mode type **/ static enum ipr_tcq_mode ioa_get_tcq_mode(struct ipr_ioa *ioa) { struct ipr_mode_pages mode_pages; struct sense_data_t sense_data; int rc; rc = mode_sense(&ioa->ioa, 0x28, &mode_pages, &sense_data); if (rc && sense_data.sense_key == ILLEGAL_REQUEST) return IPR_TCQ_NACA; if (rc) return IPR_TCQ_DISABLE; return IPR_TCQ_FROZEN; } /** * __ioa_is_spi - Determine if the IOA is a SCSI Parallel Interface (SPI) * adapter. * @ioa: ipr ioa struct * * Returns: * 1 if the IOA is an SPI adapter / 0 otherwise **/ int __ioa_is_spi(struct ipr_ioa *ioa) { struct ipr_mode_pages mode_pages; struct sense_data_t sense_data; int rc; rc = mode_sense(&ioa->ioa, 0x28, &mode_pages, &sense_data); if (rc && sense_data.sense_key == ILLEGAL_REQUEST) return 0; return 1; } /** * ioa_is_spi - Determine if the IOA is a SCSI Parallel Interface (SPI) adapter. * @ioa: ipr ioa struct * * Returns: * 1 if the IOA is an SPI adapter / 0 otherwise **/ int ioa_is_spi(struct ipr_ioa *ioa) { const struct ioa_details *details = get_ioa_details(ioa); if (details) return details->is_spi; return __ioa_is_spi(ioa); } static const char *raid_desc = "SCSI RAID Adapter"; static const char *jbod_desc = "SCSI Adapter"; static const char *sas_raid_desc = "SAS RAID Adapter"; static const char *sas_jbod_desc = "SAS Adapter"; static const char *aux_cache_desc = "Aux Cache Adapter"; static const char *def_chip_desc = "PCI"; /** * get_bus_desc - * @ioa: ipr ioa struct * * Returns: * FIXME **/ const char *get_bus_desc(struct ipr_ioa *ioa) { const struct chip_details *chip = get_chip_details(ioa); if (!chip) return def_chip_desc; return chip->desc; } /** * get_ioa_desc - * @ioa: ipr ioa struct * * Returns: * FIXME **/ const char *get_ioa_desc(struct ipr_ioa *ioa) { const struct ioa_details *details = get_ioa_details(ioa); if (details) return details->ioa_desc; else if (ioa->is_aux_cache) return aux_cache_desc; else if (!ioa_is_spi(ioa)) { if (ioa->qac_data->num_records) return sas_raid_desc; return sas_jbod_desc; } else if (ioa->qac_data->num_records) return raid_desc; return jbod_desc; } /** * get_ioa_fw - * @ioa: ipr ioa struct * * Returns: * pointer to ioa_parms entry / NULL on failure **/ static const struct ioa_parms *get_ioa_fw(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(ioa_parms); i++) { if (ioa->ccin == ioa_parms[i].ccin) return &ioa_parms[i]; } return NULL; } /** * setup_ioa_parms * @ioa: ipr ioa struct * * Returns: * nothing **/ static void setup_ioa_parms(struct ipr_ioa *ioa) { const struct ioa_parms *ioa_parms = get_ioa_fw(ioa); ioa->scsi_id_changeable = 1; if (ioa_parms) { ioa->scsi_id_changeable = ioa_parms->scsi_id_changeable; ioa->msl = ioa_parms->msl; } } /** * find_ioa - * @host_no: host number int * * Returns: * pointer to ipr_ioa / NULL on failure **/ struct ipr_ioa *find_ioa(int host_no) { struct ipr_ioa *ioa; for_each_ioa(ioa) if (ioa->host_num == host_no) return ioa; return NULL; } /** * _find_dev - * @blk: ipr ioa struct * @compare: pointer to a comparison function * * Returns: * ipr_dev pointer if success / NULL on failure **/ static struct ipr_dev *_find_dev(char *blk, int (*compare) (struct ipr_dev *, char *)) { struct ipr_ioa *ioa; struct ipr_dev *dev; char *name = malloc(strlen(_PATH_DEV) + strlen(blk) + 1); if (!name) return NULL; if (strncmp(blk, _PATH_DEV, strlen(_PATH_DEV))) sprintf(name, _PATH_DEV"%s", blk); else sprintf(name, "%s", blk); for_each_ioa(ioa) { if (!compare(&ioa->ioa, name)) { free(name); return &ioa->ioa; } for_each_dev(ioa, dev) { if (!compare(dev, name)) { free(name); return dev; } } } free(name); return NULL; } /** * blk_compare - * @dev: ipr ioa struct * @name: character string to compare * * Returns: * integer result of the strcmp() **/ static int blk_compare(struct ipr_dev *dev, char *name) { return strcmp(dev->dev_name, name); } /** * find_blk_dev - * @blk: block device name * * Returns: * result of _find_dev() **/ struct ipr_dev *find_blk_dev(char *blk) { return _find_dev(blk, blk_compare); } /** * gen_compare - * @dev: ipr dev struct * @name: character string to compare * * Returns: * integer result of the strcmp() **/ static int gen_compare(struct ipr_dev *dev, char *name) { return strcmp(dev->gen_name, name); } /** * find_gen_dev - * @gen: generic device name * * Returns: * result of _find_dev() **/ struct ipr_dev *find_gen_dev(char *gen) { return _find_dev(gen, gen_compare); } /** * find_dev - * @name: device name to find * * Returns: * ipr_dev pointer **/ struct ipr_dev *find_dev(char *name) { struct ipr_dev *dev = find_blk_dev(name); if (!dev) dev = find_gen_dev(name); return dev; } /** * ipr_uevents_supported - indicate if uevents are supported * * Returns: * 1 if success / 0 on failure **/ static int ipr_uevents_supported() { struct ipr_ioa *ioa = ipr_ioa_head; char devpath[PATH_MAX]; char value[16]; ssize_t len; if (!ioa) return 0; sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_read_attr(devpath, "uevent", value, 16); return len > 0; } #define NETLINK_KOBJECT_UEVENT 15 /** * poll_forever - * @poll_func: pointer to polling function * @poll_delay: integer sleep delay value * * Returns: * nothing **/ static void poll_forever(void (*poll_func) (void), int poll_delay) { while (1) { sleep(poll_delay); poll_func(); } } /** * handle_events - * @poll_func: pointer to polling function * @poll_delay: integer sleep delay value * @prot_level: pointer to event handler function * * Returns: * 0 if poll_forever() ever returns **/ int handle_events(void (*poll_func) (void), int poll_delay, void (*kevent_handler) (char *)) { struct sockaddr_nl snl; int sock, rc, len, flags; int uevent_rcvd = ipr_force_uevents; char buf[1024]; if (ipr_force_polling) { poll_forever(poll_func, poll_delay); return 0; } memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 1; sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (sock == -1) { syslog_dbg("Failed to get socket\n"); poll_forever(poll_func, poll_delay); return 0; } rc = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); if (rc < 0) { syslog_dbg("Failed to bind to socket\n"); close(sock); poll_forever(poll_func, poll_delay); return 0; } if (!ipr_force_uevents && !ipr_uevents_supported()) { if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { syslog(LOG_ERR, "Failed to fcntl socket\n"); close(sock); poll_forever(poll_func, poll_delay); return 0; } } while (1) { len = recv(sock, &buf, sizeof(buf), 0); if (!uevent_rcvd && len < 0) { poll_func(); sleep(poll_delay); continue; } if (len < 0) { syslog_dbg("kevent recv failed.\n"); poll_func(); sleep(poll_delay); continue; } if (!uevent_rcvd) { flags = fcntl(sock, F_GETFL); if (flags == -1) { syslog_dbg("F_GETFL Failed\n"); poll_func(); sleep(poll_delay); continue; } if (flags & O_NONBLOCK) { if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1) { syslog_dbg("F_SETFL Failed\n"); poll_func(); sleep(poll_delay); continue; } } uevent_rcvd = 1; } kevent_handler(buf); } close(sock); return 0; } /** * parse_option - parse options from the command line * @opt: character string option value * * Returns: * 1 if success / 0 if the option isn't recognized **/ int parse_option(char *opt) { if (strcmp(opt, "--version") == 0) { printf("%s: %s\n", tool_name, IPR_VERSION_STR); exit(0); } if (strcmp(opt, "--daemon") == 0) daemonize = 1; else if (strcmp(opt, "--debug") == 0) ipr_debug = 1; else if (strcmp(opt, "--force") == 0) ipr_force = 1; else if (strcmp(opt, "--use-polling") == 0) ipr_force_polling = 1; else if (strcmp(opt, "--use-uevents") == 0) ipr_force_uevents = 1; else if (strcmp(opt, "--fast") == 0) ipr_fast = 1; else if (strcmp(opt, "--deferred-write-buffer") == 0) ipr_mode5_write_buffer = 0; else if (strcmp(opt, "--mode5-write-buffer") == 0) ipr_mode5_write_buffer = 1; else return 0; return 1; } /** * get_pci_attr - * @sysfs_pci_device: sysfs_device struct * @attr: attribute string * * Returns: * pci attribute value if success / 0 on failure **/ static int get_pci_attr(char *devpath, char *attr) { int temp, fd, len; char path[PATH_MAX]; char data[200]; sprintf(path, "%s/%s", devpath, attr); if ((fd = open(path, O_RDONLY)) < 0) { syslog_dbg("Failed to open %s\n", path); return 0; } len = read(fd, data, 200); if (len < 0) { syslog_dbg("Failed to read %s\n", path); close(fd); return 0; } sscanf(data, "0x%4X", &temp); close(fd); return temp; } /** * get_pci_attrs - sets vendor and device information * @ioa: ipr ioa struct * @sysfs_pci_device: sysfs_device struct * * Returns: * nothing **/ static void get_pci_attrs(struct ipr_ioa *ioa, char *pci_path) { ioa->subsystem_vendor = get_pci_attr(pci_path, "subsystem_vendor"); ioa->subsystem_device = get_pci_attr(pci_path, "subsystem_device"); ioa->pci_vendor = get_pci_attr(pci_path, "vendor"); ioa->pci_device = get_pci_attr(pci_path, "device"); } static struct ipr_ioa *old_ioa_head; static struct ipr_ioa *old_ioa_tail; static int old_num_ioas; static struct scsi_dev_data *old_scsi_dev_table; struct ipr_array_query_data *old_qac_data; /** * free_current_config - frees current configuration data structures * * Returns: * nothing **/ static void free_current_config() { struct ipr_ioa *ioa; for (ioa = ipr_ioa_head; ioa;) { ioa = ioa->next; free(ipr_ioa_head); ipr_ioa_head = ioa; } free(ipr_qac_data); free(scsi_dev_table); ipr_ioa_head = NULL; ipr_ioa_tail = NULL; num_ioas = 0; scsi_dev_table = NULL; ipr_qac_data = NULL; } /** * save_old_config - saves current configuration data structures * * Returns: * nothing **/ static void save_old_config() { if (old_ioa_head) { free_current_config(); return; } old_ioa_head = ipr_ioa_head; old_ioa_tail = ipr_ioa_tail; old_num_ioas = num_ioas; old_scsi_dev_table = scsi_dev_table; old_qac_data = ipr_qac_data; ipr_qac_data = NULL; scsi_dev_table = NULL; ipr_ioa_head = NULL; ipr_ioa_tail = NULL; num_ioas = 0; } /** * free_old_config - frees old configuration data structures * * Returns: * nothing **/ static void free_old_config() { struct ipr_ioa *ioa; /* Free up all the old memory */ for (ioa = old_ioa_head; ioa;) { ioa = ioa->next; free(old_ioa_head); old_ioa_head = ioa; } free(old_qac_data); free(old_scsi_dev_table); old_ioa_head = NULL; old_ioa_tail = NULL; old_num_ioas = 0; old_scsi_dev_table = NULL; old_qac_data = NULL; } /** * same_ioa - compares two ioas to determine if they are the same * @first: ipr ioa struct * @second: ipr ioa struct * * Returns: * 0 if ioas are not the same / 1 if ioas are the same **/ static int same_ioa(struct ipr_ioa *first, struct ipr_ioa *second) { if (strcmp(first->pci_address, second->pci_address)) return 0; if (strcmp(first->host_name, second->host_name)) return 0; if (first->ccin != second->ccin) return 0; if (first->host_num != second->host_num) return 0; if (first->pci_vendor != second->pci_vendor) return 0; if (first->pci_device != second->pci_device) return 0; if (first->subsystem_vendor != second->subsystem_vendor) return 0; if (first->subsystem_device != second->subsystem_device) return 0; return 1; } /** * same_scsi_dev - compares two scsi devices to determin if they are the same * @first: scsi_dev_data struct * @second: scsi_dev_data struct * * Returns: * 0 if scsi devs are not the same / 1 if scsi devs are the same **/ static int same_scsi_dev(struct scsi_dev_data *first, struct scsi_dev_data *second) { if (!first || !second) return 0; if (first->host != second->host) return 0; if (first->channel != second->channel) return 0; if (first->id != second->id) return 0; if (first->lun != second->lun) return 0; if (first->type != second->type) return 0; if (first->online != second->online) return 0; if (strcmp(first->vendor_id, second->vendor_id)) return 0; if (strcmp(first->product_id, second->product_id)) return 0; if (strcmp(first->sysfs_device_name, second->sysfs_device_name)) return 0; if (strcmp(first->dev_name, second->dev_name)) return 0; if (strcmp(first->gen_name, second->gen_name)) return 0; return 1; } /** * same_dev_rcd - compares two device records to determine if they are the same * @first: ipr_dev struct * @second: ipr_dev struct * * Returns: * 0 if devices are not the same / 1 if devices are the same **/ static int same_dev_rcd(struct ipr_dev *first, struct ipr_dev *second) { if (memcmp(&first->res_addr, &second->res_addr, sizeof(first->res_addr))) return 0; if (memcmp(first->vendor_id, second->vendor_id, IPR_VENDOR_ID_LEN)) return 0; if (memcmp(first->product_id, second->product_id, IPR_PROD_ID_LEN)) return 0; if (memcmp(first->serial_number, second->serial_number, IPR_SERIAL_NUM_LEN)) return 0; return 1; } /** * same_dev - compares two devices to determine if they are the same * @first: ipr_dev struct * @second: ipr_dev struct * * Returns: * 0 if devices are not the same / 1 if devices are the same **/ static int same_dev(struct ipr_dev *first, struct ipr_dev *second) { if (strcmp(first->dev_name, second->dev_name)) return 0; if (strcmp(first->gen_name, second->gen_name)) return 0; if (!first->scsi_dev_data && !second->scsi_dev_data) { if (!ipr_is_af_dasd_device(first) || !ipr_is_af_dasd_device(second)) return 0; if (!same_dev_rcd(first, second)) return 0; } else if (!same_scsi_dev(first->scsi_dev_data, second->scsi_dev_data)) return 0; return 1; } /** * dev_init_allowed - * @dev: ipr dev struct * * Returns: * 1 if initialization is allowed / 0 if initialization is not allowed **/ static int dev_init_allowed(struct ipr_dev *dev) { struct ipr_query_res_state res_state; if (!ipr_is_af_dasd_device(dev)) return 1; if (!ipr_query_resource_state(dev, &res_state)) { if (!res_state.read_write_prot && !res_state.prot_dev_failed) return 1; } return 0; } /** * resolve_dev - * @new: ipr dev struct * @old: ipr dev struct * * Returns: * nothing **/ static void resolve_dev(struct ipr_dev *new, struct ipr_dev *old) { new->init_not_allowed = !dev_init_allowed(new); if (!old->init_not_allowed || new->init_not_allowed) new->should_init = 0; if (!new->ioa->sis64 && new->ioa->is_secondary && !old->ioa->is_secondary && ipr_is_af_dasd_device(new) && new->scsi_dev_data) new->rescan = 1; if (!new->ioa->sis64 && !new->ioa->is_secondary && old->ioa->is_secondary && ipr_is_af_dasd_device(new) && !new->scsi_dev_data) new->rescan = 1; } /** * resolve_ioa - * @ioa: ipr ioa struct * @old_ioa: ipr ioa struct * * Returns: * nothing **/ static void resolve_ioa(struct ipr_ioa *ioa, struct ipr_ioa *old_ioa) { struct ipr_dev *dev, *old_dev; ioa->should_init = 0; for_each_dev(ioa, dev) { for_each_dev(old_ioa, old_dev) { if (!same_dev(dev, old_dev)) continue; memcpy(&dev->attr, &old_dev->attr, sizeof(dev->attr)); dev->rescan = old_dev->rescan; resolve_dev(dev, old_dev); break; } } } /** * resolve_old_config - * * Returns: * nothing **/ static void resolve_old_config() { struct ipr_ioa *ioa, *old_ioa; struct ipr_dev *dev; for_each_ioa(ioa) { ioa->should_init = 1; for_each_dev(ioa, dev) dev->should_init = 1; } for_each_ioa(ioa) { __for_each_ioa(old_ioa, old_ioa_head) { if (!same_ioa(ioa, old_ioa)) continue; resolve_ioa(ioa, old_ioa); break; } } free_old_config(); } struct ipr_pci_slot { char slot_name[PATH_MAX]; char physical_name[PATH_MAX]; char pci_device[PATH_MAX]; }; static struct ipr_pci_slot *pci_slot; static unsigned int num_pci_slots; /** * ipr_select_phy_location - * @dirent: dirent struct * * Returns: * 1 if d_name == "phy_location"/ 0 otherwise **/ static int ipr_select_phy_location(const struct dirent *dirent) { if (strstr(dirent->d_name, "phy_location")) return 1; return 0; } static int ipr_select_pci_address(const struct dirent *dirent) { if (strstr(dirent->d_name, "address")) return 1; return 0; } /** * read_attr_file - * @path: path name of device/file to open * @out: character array * * Returns: * 0 if success / non-zero on failure **/ static int read_attr_file(char *path, char *out, int size) { int fd, len; if ((fd = open(path, O_RDONLY)) < 0) { syslog_dbg("Failed to open %s\n", path); return -EIO; } len = read(fd, out, size); if (len < 0) { syslog_dbg("Failed to read %s\n", path); close(fd); return -EIO; } if (out[strlen(out) - 1] == '\n') out[strlen(out) - 1] = '\0'; close(fd); return 0; } /** * ipr_add_slot_location - * @path: path of device/file to open * @name: name of device/file to open * * Returns: * 0 if success / non-zero on failure **/ static int ipr_add_slot_location(char *path, char *name) { struct ipr_pci_slot *slot; char fpath[PATH_MAX]; int rc; sprintf(fpath, "%s%s/phy_location", path, name); pci_slot = realloc(pci_slot, sizeof(*pci_slot) * (num_pci_slots + 1)); slot = &pci_slot[num_pci_slots]; memset(slot, 0, sizeof(*slot)); strcpy(slot->slot_name, name); rc = read_attr_file(fpath, slot->physical_name, sizeof(slot->physical_name)); if (rc) { pci_slot = realloc(pci_slot, sizeof(*pci_slot) * num_pci_slots); return rc; } num_pci_slots++; return 0; } /** * ipr_add_slot_address - * @path: path of device/file to open * @name: name of device/file to open * * Returns: * 0 if success / non-zero on failure **/ static int ipr_add_slot_address(char *path, char *name) { struct ipr_pci_slot *slot; char fpath[PATH_MAX]; int rc; sprintf(fpath, "%s%s/address", path, name); pci_slot = realloc(pci_slot, sizeof(*pci_slot) * (num_pci_slots + 1)); slot = &pci_slot[num_pci_slots]; memset(slot, 0, sizeof(*slot)); strcpy(slot->slot_name, name); strcpy(slot->physical_name, name); rc = read_attr_file(fpath, slot->pci_device, sizeof(slot->pci_device)); if (rc) { pci_slot = realloc(pci_slot, sizeof(*pci_slot) * num_pci_slots); return rc; } strcat(slot->pci_device, ".0"); num_pci_slots++; return 0; } /** * ipr_get_pci_slots - * * Returns: * nothing **/ static void ipr_get_pci_slots() { char rootslot[PATH_MAX], slot[PATH_MAX]; char slotpath[PATH_MAX], attr[PATH_MAX]; char devspec[PATH_MAX], locpath[PATH_MAX]; char loc_code[1024], *last_hyphen, *prev_hyphen; int num_slots, i, j, rc, num_attrs; int loc_code_not_found = 0; struct dirent **slotdir, **dirent; struct stat statbuf; struct ipr_ioa *ioa; for_each_ioa(ioa) ioa->physical_location[0] = '\0'; for_each_ioa(ioa) { memset(devspec, 0, sizeof(devspec)); sprintf(attr, "/sys/bus/pci/devices/%s/devspec", ioa->pci_address); rc = read_attr_file(attr, devspec, PATH_MAX); if (rc) continue; memset(loc_code, 0, sizeof(loc_code)); sprintf(locpath, "/proc/device-tree%s/ibm,loc-code", devspec); rc = read_attr_file(locpath, loc_code, sizeof(loc_code)); if (rc) { loc_code_not_found = 1; continue; } last_hyphen = strrchr(loc_code, '-'); if (last_hyphen && last_hyphen[1] == 'T') { *last_hyphen = '\0'; prev_hyphen = strrchr(loc_code, '-'); if (prev_hyphen && prev_hyphen[1] != 'C') *last_hyphen = '-'; } strcpy(ioa->physical_location, loc_code); } if (loc_code_not_found == 0) return; sprintf(rootslot, "/sys/bus/pci/slots/"); num_slots = scandir(rootslot, &slotdir, NULL, alphasort); if (num_slots <= 0) { return; } for (i = 0; i < num_slots; i++) { sprintf(slot, "%s%s", rootslot, slotdir[i]->d_name); rc = scandir(slot, &dirent, ipr_select_phy_location, alphasort); if (rc > 0) { ipr_add_slot_location(rootslot, slotdir[i]->d_name); while (rc--) free(dirent[rc]); free(dirent); } else { rc = scandir(slot, &dirent, ipr_select_pci_address, alphasort); if (rc <= 0) continue; ipr_add_slot_address(rootslot, slotdir[i]->d_name); while (rc--) free(dirent[rc]); free(dirent); } } while (num_slots--) free(slotdir[num_slots]); free(slotdir); for (i = 0; i < num_pci_slots; i++) { if (!pci_slot[i].pci_device) continue; sprintf(slotpath, "/sys/bus/pci/devices/%s/", pci_slot[i].slot_name); num_attrs = scandir(slotpath, &slotdir, NULL, alphasort); if (num_attrs <= 0) continue; for (j = 0; j < num_attrs; j++) { sprintf(attr, "%s/%s", slotpath, slotdir[j]->d_name); rc = stat(attr, &statbuf); if (rc || !S_ISDIR(statbuf.st_mode)) continue; if (!strcmp(slotdir[j]->d_name, ".")) continue; if (!strcmp(slotdir[j]->d_name, "..")) continue; strcpy(pci_slot[i].pci_device, slotdir[j]->d_name); break; } while (num_attrs--) free(slotdir[num_attrs]); free(slotdir); } for_each_ioa(ioa) { if (strlen(ioa->physical_location) == 0) { for (i = 0; i < num_pci_slots; i++) { if (strcmp(pci_slot[i].pci_device, ioa->pci_address) && strcmp(pci_slot[i].slot_name, ioa->pci_address)) continue; strcpy(ioa->physical_location, pci_slot[i].physical_name); break; } } } free(pci_slot); pci_slot = NULL; num_pci_slots = 0; } /** * tool_init - * @save_state: integer flag - whether or not to save the old config * * Returns: * nothing **/ void tool_init(int save_state) { int temp, fw_type; struct ipr_ioa *ipr_ioa; DIR *dirfd, *host_dirfd; struct dirent *dent, *host_dent; ssize_t len; save_old_config(); dirfd = opendir("/sys/bus/pci/drivers/ipr"); if (!dirfd) { syslog_dbg("Failed to open ipr driver bus. %m\n"); dirfd = opendir("/sys/bus/pci/drivers/ipr"); if (!dirfd) { syslog_dbg("Failed to open ipr driver bus on second attempt. %m\n"); return; } } while ((dent = readdir(dirfd)) != NULL) { int channel, bus, device, function; char devpath[PATH_MAX]; char buff[256]; if (sscanf(dent->d_name, "%x:%x:%x.%x", &channel, &bus, &device, &function) != 4) continue; ipr_ioa = (struct ipr_ioa*)malloc(sizeof(struct ipr_ioa)); memset(ipr_ioa,0,sizeof(struct ipr_ioa)); /* PCI address */ strcpy(ipr_ioa->pci_address, dent->d_name); ipr_ioa->host_num = -1; sprintf(devpath, "/sys/bus/pci/drivers/ipr/%s", dent->d_name); host_dirfd = opendir(devpath); if (!host_dirfd) { exit_on_error("Failed to open scsi_host class.\n"); continue; } while ((host_dent = readdir(host_dirfd)) != NULL) { char scsipath[PATH_MAX]; char fw_str[256]; if (strncmp(host_dent->d_name, "host", 4)) continue; if (sscanf(host_dent->d_name, "host%d", &ipr_ioa->host_num) != 1) continue; strcpy(ipr_ioa->host_name, host_dent->d_name); get_pci_attrs(ipr_ioa, devpath); sprintf(scsipath, "%s/%s/scsi_host/%s", devpath, ipr_ioa->host_name, ipr_ioa->host_name); len = sysfs_read_attr(scsipath, "fw_type", fw_str, 256); if (len < 0) fw_type = 0; else sscanf(fw_str, "%d", &fw_type); if (ipr_debug) syslog(LOG_INFO, "tool_init: fw_type attr = %d.\n", fw_type); break; } closedir(host_dirfd); if (ipr_ioa->host_num < 0) { syslog_dbg("No SCSI Host found on IPR device %s\n", ipr_ioa->pci_address); free(ipr_ioa); continue; } if (fw_type == IPR_SIS64) { sprintf(devpath, "/sys/bus/scsi/devices/%d:%d:0:0", ipr_ioa->host_num, IPR_IOAFP_VIRTUAL_BUS); ipr_ioa->sis64 = 1; } else sprintf(devpath, "/sys/bus/scsi/devices/%d:255:255:255", ipr_ioa->host_num); len = sysfs_read_attr(devpath, "model", buff, 16); if (len < 0 || (sscanf(buff, "%4X", &temp) != 1)) { exit_on_error("Cannot read SCSI device model.\n"); continue; } ipr_ioa->ccin = temp; setup_ioa_parms(ipr_ioa); ipr_ioa->next = NULL; ipr_ioa->num_raid_cmds = 0; if (ipr_ioa_tail) { ipr_ioa_tail->next = ipr_ioa; ipr_ioa_tail = ipr_ioa; } else ipr_ioa_tail = ipr_ioa_head = ipr_ioa; num_ioas++; } closedir(dirfd); if (!save_state) free_old_config(); ipr_get_pci_slots(); return; } /** * ipr_reset_adapter - * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_reset_adapter(struct ipr_ioa *ioa) { char devpath[PATH_MAX]; sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); sysfs_write_attr(devpath, "reset_host", "1", 1); } /** * ipr_read_host_attr - * @ioa: ipr ioa struct * @name: attribute name * @value: value to be set * @value_len: length of value string * * Returns: * >= 0 if success / < 0 on failure **/ int ipr_read_host_attr(struct ipr_ioa *ioa, char *name, void *value, size_t value_len) { ssize_t len; char path[PATH_MAX]; sprintf(path, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_read_attr(path, name, value, value_len); if (len < 0) { ioa_dbg(ioa, "Failed to read %s attribute. %m\n", name); return -EIO; } return len; } /** * ipr_write_host_attr - * @ioa: ipr ioa struct * @name: attribute to be written * @value: new value * @value_len: length of value string * * Returns: * >= 0 if success / < 0 on failure **/ int ipr_write_host_attr(struct ipr_ioa *ioa, char *name, void *value, size_t value_len) { ssize_t len; char path[PATH_MAX]; sprintf(path, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_write_attr(path, name, value, value_len); if (len < 0) { ioa_dbg(ioa, "Failed to write %s attribute. %m\n", name); return -EIO; } return len; } /** * ipr_cmds_per_lun - * @ioa: ipr ioa struct * * Returns: * number of commands per lun if success / 6 on failure **/ int ipr_cmds_per_lun(struct ipr_ioa *ioa) { char value[100]; int rc; rc = ipr_read_host_attr(ioa, "cmd_per_lun", value, 100); if (rc) return 6; return strtoul(value, NULL, 10); } /** * ipr_scan - * @ioa: ipr ioa struct * @bus: bus number * @id: id number * @lun: lun number (RAID) level for new array * * Returns: * nothing **/ void ipr_scan(struct ipr_ioa *ioa, int bus, int id, int lun) { char buf[100]; char devpath[PATH_MAX]; if (lun < 0) { if (id < 0) sprintf(buf, "%d - -", bus); else sprintf(buf, "%d %d -", bus, id); } else { if (id < 0) sprintf(buf, "%d - %d", bus, lun); else sprintf(buf, "%d %d %d", bus, id, lun); } sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); sysfs_write_attr(devpath, "scan", buf, 100); } /** * __ipr_query_array_config - perform a query array configuration ioctl * @ioa: ipr ioa struct * @fd: file descriptor * @allow_rebuld_refresh: allow rebuild refresh flag * @set_array_id: set array id flag * @array_id: array id number * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int __ipr_query_array_config(struct ipr_ioa *ioa, int fd, bool allow_rebuild_refresh, bool set_array_id, bool vset_migrate_query, int array_id, struct ipr_array_query_data *buff) { int length = IPR_QAC_BUFFER_SIZE; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; if (!ioa->sis64) length = IPR_SIS32_QAC_BUFFER_SIZE; ioa->nr_ioa_microcode = 0; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_ARRAY_CONFIG; if (vset_migrate_query) { cdb[1] = 0x08; cdb[2] = array_id; if (ioa->sis64) cdb[3] = 0x80; } else if (set_array_id) { cdb[1] = 0x01; cdb[2] = array_id; if (ioa->sis64) cdb[3] = 0x80; } else if (allow_rebuild_refresh) cdb[1] = 0; else cdb[1] = 0x80; /* Prohibit Rebuild Candidate Refresh */ cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, buff->u.buf, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug) ioa_cmd_err(ioa, &sense_data, "Query Array Config", rc); else if (sense_data.sense_key == NOT_READY && sense_data.add_sense_code == 0x40 && sense_data.add_sense_code_qual == 0x85) ioa->nr_ioa_microcode = 1; } if (ioa->sis64) { buff->resp_len = ntohl(buff->u.buf64.resp_len); buff->num_records = ntohl(buff->u.buf64.num_records); buff->data = buff->u.buf64.data; buff->hdr_len = 8; } else { buff->resp_len = ntohs(buff->u.buf32.resp_len); buff->num_records = ntohs(buff->u.buf32.num_records); buff->data = buff->u.buf32.data; buff->hdr_len = 4; } return rc; } /** * ipr_query_array_config - entry point to query array configuration ioctl call * @ioa: ipr ioa struct * @allow_rebuld_refresh: allow rebuild refresh flag * @set_array_id: set array id flag * @array_id: array id number * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_array_config(struct ipr_ioa *ioa, bool allow_rebuild_refresh, bool set_array_id, bool vset_migrate_query, int array_id, struct ipr_array_query_data *buff) { int fd, rc; ioa->nr_ioa_microcode = 0; if (strlen(ioa->ioa.gen_name) == 0) { scsi_err((&ioa->ioa), "Adapter sg device does not exist\n"); return -ENOENT; } fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } rc = flock(fd, LOCK_EX); if (rc) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not lock %s. %m\n", ioa->ioa.gen_name); close(fd); return errno; } rc = __ipr_query_array_config(ioa, fd, allow_rebuild_refresh, set_array_id, vset_migrate_query, array_id, buff); close(fd); return rc; } /** * ipr_query_multi_ioa_status - * @ioa: ipr ioa struct * @buff: data buffer * @len: length * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_multi_ioa_status(struct ipr_ioa *ioa, void *buff, u32 len) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MAINTENANCE_IN; cdb[1] = IPR_QUERY_MULTI_ADAPTER_STATUS; cdb[6] = len >> 24; cdb[7] = (len >> 16) & 0xff; cdb[8] = (len >> 8) & 0xff; cdb[9] = len & 0xff; rc = sg_ioctl(fd, cdb, buff, len, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Multi Adapter Status", rc); else if (sense_data.sense_key == NOT_READY && sense_data.add_sense_code == 0x40 && sense_data.add_sense_code_qual == 0x85) ioa->nr_ioa_microcode = 1; } close(fd); return rc; } /** * ipr_start_array - Start array protection for an array * @ioa: ipr ioa struct * @cmd: char pointer * @stripe_size: stripe size for new array * @prot_level: protection (RAID) level for new array * @hot_spare: flag to indicate hot spare usage * * Returns: * 0 if success / non-zero on failure **/ static int ipr_start_array(struct ipr_ioa *ioa, char *cmd, int stripe_size, int prot_level, int hot_spare) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } ipr_update_qac_with_zeroed_devs(ioa); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_START_ARRAY_PROTECTION; if (hot_spare) cdb[1] = 0x01; cdb[4] = (u8)(stripe_size >> 8); cdb[5] = (u8)(stripe_size & 0xff); cdb[6] = prot_level; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Start Array Protection", rc); close(fd); return rc; } /** * ipr_start_array_protection - Start array protection for an array * @ioa: ipr ioa struct * @stripe_size: stripe size for new array * @prot_level: protection (RAID) level for new array * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_array_protection(struct ipr_ioa *ioa, int stripe_size, int prot_level) { return ipr_start_array(ioa, "Start Array Protection", stripe_size, prot_level, 0); } /** * ipr_migrate_array_protection - Migrate array protection for an array * @ioa: ipr ioa struct * @qac_data: struct ipr_array_query_data * @fd: file descriptor * @stripe_size: new or existing stripe size for array * @prot_level: new protection (RAID) level for array * * Returns: * 0 if success / non-zero on failure **/ int ipr_migrate_array_protection(struct ipr_ioa *ioa, struct ipr_array_query_data *qac_data, int fd, int stripe_size, int prot_level) { u8 cdb[IPR_CCB_CDB_LEN]; char *cmd = "migrate array protection"; struct sense_data_t sense_data; int rc; int length = qac_data->resp_len; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MIGRATE_ARRAY_PROTECTION; cdb[4] = (u8)(stripe_size >> 8); cdb[5] = (u8)(stripe_size & 0xff); cdb[6] = prot_level; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, cmd, rc); return rc; } /** * ipr_add_hot_spare - Adds a hot spare to an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_add_hot_spare(struct ipr_ioa *ioa) { return ipr_start_array(ioa, "Create hot spare", 0, 0, 1); } /** * ipr_stop_array - Stop array protection for an array * @ioa: ipr ioa struct * @cmd: command string * @hot_spare: flag to indicate hot spare usage * * Returns: * 0 if success / non-zero on failure **/ static int ipr_stop_array(struct ipr_ioa *ioa, char *cmd, int hot_spare) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_STOP_ARRAY_PROTECTION; if (hot_spare) cdb[1] = 0x01; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, cmd, rc); close(fd); return rc; } /** * ipr_stop_array_protection - Stop array protection for an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_stop_array_protection(struct ipr_ioa *ioa) { return ipr_stop_array(ioa, "Stop Array Protection", 0); } /** * ipr_remove_hot_spare - remove a hot spare from an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_remove_hot_spare(struct ipr_ioa *ioa) { return ipr_stop_array(ioa, "Delete hot spare", 1); } /** * ipr_rebuild_device_data - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_rebuild_device_data(struct ipr_ioa *ioa) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_REBUILD_DEVICE_DATA; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Rebuild Device Data", rc); close(fd); return rc; } /** * ipr_resync_array - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_resync_array(struct ipr_ioa *ioa) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RESYNC_ARRAY_PROTECTION; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Resync Array", rc); close(fd); return rc; } /** * ipr_add_array_device - * @ioa: ipr ioa struct * @fd: device/file descriptor * @qac_data: query array configuration info * * Returns: * 0 if success / non-zero on failure **/ int ipr_add_array_device(struct ipr_ioa *ioa, int fd, struct ipr_array_query_data *qac_data) { u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = qac_data->resp_len; ipr_update_qac_with_zeroed_devs(ioa); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_ADD_ARRAY_DEVICE; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Add Array Device", rc); return rc; } /** * ipr_query_command_status - * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_command_status(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_cmd_status); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_COMMAND_STATUS; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if ((rc != 0) && (errno != EINVAL || ipr_debug)) scsi_cmd_err(dev, &sense_data, "Query Command Status", rc); close(fd); return rc; } /** * ipr_query_resource_state * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_resource_state(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_query_res_state); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_RESOURCE_STATE; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query Resource State", rc); close(fd); return rc; } /** * ipr_mod_sense - issue a mode sense command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_mode_sense(struct ipr_dev *dev, u8 page, void *buff) { struct sense_data_t sense_data; int rc; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(dev, page, buff, &sense_data); /* post log if error unless error is device format in progress */ if ((rc != 0) && (rc != ENOENT) && (((sense_data.error_code & 0x7F) != 0x70) || ((sense_data.sense_key & 0x0F) != 0x02) || /* NOT READY */ (sense_data.add_sense_code != 0x04) || /* LOGICAL UNIT NOT READY */ (sense_data.add_sense_code_qual != 0x04))) /* FORMAT IN PROGRESS */ scsi_cmd_err(dev, &sense_data, "Mode Sense", rc); return rc; } /** * ipr_log_sense - issue a log sense command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_log_sense(struct ipr_dev *dev, u8 page, void *buff, u16 length) { struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; char *name = dev->gen_name; memset(&sense_data, 0, sizeof(sense_data)); if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { scsi_dbg(dev, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = LOG_SENSE; cdb[2] = 0x40 | page; cdb[7] = (length >> 8) & 0xff; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); scsi_cmd_dbg(dev, &sense_data, cdb, rc); close(fd); return rc; } /** * ipr_is_log_page_supported - is the give page supported? * @dev: ipr dev struct * @page: mode page * * Returns: * 1 if supported / 0 if not supported **/ int ipr_is_log_page_supported(struct ipr_dev *dev, u8 page) { struct ipr_supp_log_pages pages; int rc, i; memset(&pages, 0, sizeof(pages)); rc = ipr_log_sense(dev, 0, &pages, sizeof(pages)); if (rc) return -EIO; for (i = 0; i < ntohs(pages.length); i++) { if (pages.page[i] == page) return 1; } return 0; } /** * ipr_get_blk_size - return the block size for the given device * @dev: ipr dev struct * * Returns: * block size if success / non-zero on failure **/ int ipr_get_blk_size(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages; struct ipr_block_desc *block_desc; int rc; rc = ipr_mode_sense(dev, 0, &mode_pages); if (rc) return rc; if (mode_pages.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)mode_pages.data; return ((block_desc->block_length[1] << 8) | block_desc->block_length[2]); } return 0; } /** * ipr_mode_select - issue a mode select command * @dev: ipr dev struct * @buff: data buffer * @length: length of buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_mode_select(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SELECT; cdb[1] = 0x10; /* PF = 1, SP = 0 */ cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Mode Select", rc); close(fd); return rc; } /** * page0x0a_setup - Start array protection for an array * @dev: ipr dev struct * * Returns: * 1 if success / 0 on failure **/ int page0x0a_setup(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages; struct ipr_control_mode_page *page; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x0A, &mode_pages)) return -EIO; page = (struct ipr_control_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (page->queue_algorithm_modifier != 1) return 0; if (page->tst == 1 && page->qerr != 3) return 0; if (page->qerr != 1) return 0; if (page->dque != 0) return 0; return 1; } /** * ipr_setup_af_qdepth - * @dev: ipr dev struct * @qdepth: queue depth * * Returns: * 0 if success / non-zero on failure **/ static int ipr_setup_af_qdepth(struct ipr_dev *dev, int qdepth) { struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; int len; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); page->max_tcq_depth = qdepth; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (ipr_mode_select(dev, &mode_pages, len)) return -EIO; return 0; } /** * ipr_reset_device - issue a SG_SCSI_RESET to a device * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_reset_device(struct ipr_dev *dev) { int fd, rc, arg; if (strlen(dev->gen_name) == 0) return -ENOENT; fd = open(dev->gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } arg = SG_SCSI_RESET_DEVICE; rc = ioctl(fd, SG_SCSI_RESET, &arg); if (rc != 0) scsi_err(dev, "Reset Device failed. %m\n"); close(fd); return rc; } /** * ipr_re_read_partition - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_re_read_partition(struct ipr_dev *dev) { int fd, rc; if (strlen(dev->dev_name) == 0) return -ENOENT; fd = open(dev->dev_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } rc = ioctl(fd, BLKRRPART, NULL); if (rc != 0) scsi_err(dev, "Re-read partition table failed. %m\n"); close(fd); return rc; } /** * ipr_read_capacity - issue a read capacity command to a device * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_capacity(struct ipr_dev *dev, void *buff) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = sizeof(struct ipr_read_cap); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = READ_CAPACITY; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Read Capacity", rc); close(fd); return rc; } /** * ipr_read_capacity_16 - issue a read capacity 16 to a device * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_capacity_16(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_read_cap16); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SERVICE_ACTION_IN; cdb[1] = IPR_READ_CAPACITY_16; cdb[10] = length >> 24; cdb[11] = length>> 16 & 0xff; cdb[12] = length >> 8 & 0xff; cdb[13] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Read Capacity", rc); close(fd); return rc; } /** * ipr_reclaim_cache_store - * @ioa: ipr ioa struct * @action: action value * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_reclaim_cache_store(struct ipr_ioa *ioa, int action, void *buff) { char *file = ioa->ioa.gen_name; int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int timeout, rc; int length = sizeof(struct ipr_reclaim_query_data); if (strlen(file) == 0) return -ENOENT; fd = open(file, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n",file); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RECLAIM_CACHE_STORE; cdb[1] = (u8)action; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if ((action & IPR_RECLAIM_ACTION) == IPR_RECLAIM_PERFORM) timeout = IPR_CACHE_RECLAIM_TIMEOUT; else timeout = IPR_INTERNAL_DEV_TIMEOUT; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, timeout); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Reclaim Cache", rc); } else if ((action & IPR_RECLAIM_ACTION) == IPR_RECLAIM_PERFORM) ipr_reset_adapter(ioa); close(fd); return rc; } /** * ipr_start_stop - * @dev: ipr dev struct * @start: start or stop flag * @cmd: command buffer * * Returns: * 0 if success / non-zero on failure **/ static int ipr_start_stop(struct ipr_dev *dev, u8 start, char *cmd) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = START_STOP; cdb[4] = start; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, cmd, rc); close(fd); return rc; } /** * ipr_start_stop_start - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_stop_start(struct ipr_dev *dev) { return ipr_start_stop(dev, IPR_START_STOP_START, "Start Unit"); } /** * ipr_check_allow_restart - check the allow_restart flag for a device * @dev: ipr dev struct * * Return value: * none **/ int ipr_check_allow_restart(struct ipr_dev *dev) { char path[PATH_MAX]; char value[8]; ssize_t len; sprintf(path,"/sys/class/scsi_disk/%s", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "allow_restart", value, 8); if (len < 1) { syslog_dbg("Failed to open allow_restart parameter.\n"); return -1; } return atoi(value); } /** * ipr_allow_restart - set or clear the allow_restart flag for a device * @dev: ipr dev struct * @allow: value to set * * Return value: * none **/ void ipr_allow_restart(struct ipr_dev *dev, int allow) { char path[PATH_MAX]; char value_str[8]; ssize_t len; sprintf(path,"/sys/class/scsi_disk/%s", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "allow_restart", value_str, 8); if (len < 1) { syslog_dbg("Failed to open allow_restart parameter.\n"); return; } if (atoi(value_str) == allow) { return; } snprintf(value_str, 8, "%d", allow); if (sysfs_write_attr(path, "allow_restart", value_str, 1) < 0) syslog_dbg("Failed to write allow_restart parameter.\n"); } /** * ipr_start_stop_stop - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_stop_stop(struct ipr_dev *dev) { return ipr_start_stop(dev, IPR_START_STOP_STOP, "Stop Unit"); } /** * __ipr_test_unit_ready - issue a test unit ready command * @dev: ipr dev struct * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ int __ipr_test_unit_ready(struct ipr_dev *dev, struct sense_data_t *sense_data) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; int length = 0, allow_restart = 0; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = TEST_UNIT_READY; rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, sense_data, IPR_INTERNAL_DEV_TIMEOUT); close(fd); if (rc && sense_data->sense_key == NOT_READY && sense_data->add_sense_code == 0x4 && sense_data->add_sense_code_qual == 0x2) { allow_restart = ipr_check_allow_restart(dev); if (allow_restart) ipr_start_stop(dev, IPR_START_STOP_START, "Start Unit"); } return rc; } /** * ipr_test_unit_ready - issue a test unit ready command * @dev: ipr dev struct * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_test_unit_ready(struct ipr_dev *dev, struct sense_data_t *sense_data) { int rc = __ipr_test_unit_ready(dev, sense_data); if (rc != 0 && sense_data->sense_key != NOT_READY && sense_data->sense_key != UNIT_ATTENTION && (!strcmp(tool_name, "iprconfig") || ipr_debug)) scsi_cmd_err(dev, sense_data, "Test Unit Ready", rc); return rc; } static struct ipr_dev *find_multipath_jbod(struct ipr_dev *dev) { struct ipr_ioa *ioa; struct ipr_dev *multipath_dev; for_each_sas_ioa(ioa) { if (ioa == dev->ioa) continue; for_each_dev(ioa, multipath_dev) { if (dev->scsi_dev_data->device_id == multipath_dev->scsi_dev_data->device_id) return multipath_dev; } } return NULL; } /** * ipr_format_unit - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_format_unit(struct ipr_dev *dev) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u8 *defect_list_hdr; int length = IPR_DEFECT_LIST_HDR_LEN; char *name = dev->gen_name; char cmnd[1000]; struct ipr_dev *multipath_dev; if (strlen(dev->dev_name) && dev->scsi_dev_data->device_id) { sprintf(cmnd, "/sbin/multipathd -k\"del path %s\" > /dev/null 2>&1", strrchr(dev->dev_name, '/') + 1); system(cmnd); sprintf(cmnd, "/bin/rm -rf %s %s%s", dev->dev_name, dev->dev_name, "[0-9]*"); system(cmnd); multipath_dev = find_multipath_jbod(dev); if (multipath_dev) { sprintf(cmnd, "/sbin/multipathd -k\"del path %s\" > /dev/null 2>&1", strrchr(multipath_dev->dev_name, '/') + 1); system(cmnd); sprintf(cmnd, "/bin/rm -rf %s %s%s", multipath_dev->dev_name, multipath_dev->dev_name , "[0-9]*"); system(cmnd); } system("/sbin/multipath -F > /dev/null 2>&1"); } if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); defect_list_hdr = calloc(1, IPR_DEFECT_LIST_HDR_LEN); cdb[0] = FORMAT_UNIT; cdb[1] = IPR_FORMAT_DATA; /* lun = 0, fmtdata = 1, cmplst = 0, defect list format = 0 */ cdb[4] = 1; defect_list_hdr[1] = IPR_FORMAT_IMMED; /* FOV = 0, DPRY = 0, DCRT = 0, STPF = 0, IP = 0, DSP = 0, Immed = 1, VS = 0 */ rc = sg_ioctl(fd, cdb, defect_list_hdr, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); free(defect_list_hdr); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Format Unit", rc); close(fd); return rc; } /** * ipr_evaluate_device - * @dev: ipr dev struct * @res_handle: resource handle * * Returns: * 0 if success / non-zero on failure **/ int ipr_evaluate_device(struct ipr_dev *dev, u32 res_handle) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u32 resource_handle; int length = 0; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); resource_handle = ntohl(res_handle); cdb[0] = IPR_EVALUATE_DEVICE; cdb[2] = (u8)(resource_handle >> 24); cdb[3] = (u8)(resource_handle >> 16); cdb[4] = (u8)(resource_handle >> 8); cdb[5] = (u8)(resource_handle); rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_EVALUATE_DEVICE_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) scsi_cmd_err(dev, &sense_data, "Evaluate Device Capabilities", rc); close(fd); return rc; } /** * ipr_disrupt_device - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_disrupt_device(struct ipr_dev *dev) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u32 res_handle; char *name = dev->ioa->ioa.gen_name; if (strlen(name) == 0) return -ENOENT; if (!dev->dev_rcd) return -EINVAL; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); res_handle = ntohl(dev->resource_handle); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_DISRUPT_DEVICE; if (!ipr_force) cdb[2] = 0x40; cdb[6] = (u8)(res_handle >> 24); cdb[7] = (u8)(res_handle >> 16); cdb[8] = (u8)(res_handle >> 8); cdb[9] = (u8)(res_handle); scsi_info(dev, "Attempting to force device failure " "with disrupt device command\n"); rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) scsi_cmd_err(dev, &sense_data, "Disrupt Device", rc); close(fd); return rc; } /** * ipr_inquiry - issue an inquiry command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * @length buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_inquiry(struct ipr_dev *dev, u8 page, void *buff, u8 length) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = INQUIRY; if (page != IPR_STD_INQUIRY) { cdb[1] = 0x01; cdb[2] = page; } cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (ipr_debug || sense_data.sense_key != ILLEGAL_REQUEST)) scsi_cmd_err(dev, &sense_data, "Inquiry", rc); close(fd); return rc; } /** * __ipr_get_fw_version - get the firmware version * @dev: ipr dev struct * @pag3_ing: page 3 inquiry data * @release_level: location to copy version info into * * Returns: * nothing **/ static void __ipr_get_fw_version(struct ipr_dev *dev, struct ipr_dasd_inquiry_page3 *page3_inq, u8 release_level[4]) { if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) memcpy(release_level, page3_inq->load_id, 4); else memcpy(release_level, page3_inq->release_level, 4); } /** * ipr_get_fw_version - get the firmware version entry point * @dev: ipr dev struct * @release_level: location to copy version info into * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_fw_version(struct ipr_dev *dev, u8 release_level[4]) { struct ipr_dasd_inquiry_page3 page3_inq; int rc; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc) return rc; __ipr_get_fw_version(dev, &page3_inq, release_level); return 0; } /** * ipr_init_res_addr_aliases - * @aliases: ipr_res_addr_aliases struct * @res_addr: ipr_res_addr struct * * Returns: * nothing **/ static void ipr_init_res_addr_aliases(struct ipr_res_addr_aliases *aliases, struct ipr_res_addr *res_addr) { struct ipr_res_addr *ra; aliases->length = htonl(sizeof(*res_addr) * IPR_DEV_MAX_PATHS); for_each_ra_alias(ra, aliases) memcpy(ra, res_addr, sizeof(*ra)); } /** * ipr_query_res_addr_aliases - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @aliases: ipr_res_addr_aliases struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_addr_aliases(struct ipr_ioa *ioa, struct ipr_res_addr *res_addr, struct ipr_res_addr_aliases *aliases) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = ioa->ioa.gen_name; struct ipr_res_addr *ra; ipr_init_res_addr_aliases(aliases, res_addr); for_each_ra_alias(ra, aliases) ra->host = ioa->host_num; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_RES_ADDR_ALIASES; cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; cdb[10] = sizeof(*aliases) >> 24; cdb[11] = (sizeof(*aliases) >> 16) & 0xff; cdb[12] = (sizeof(*aliases) >> 8) & 0xff; cdb[13] = sizeof(*aliases) & 0xff; rc = sg_ioctl(fd, cdb, aliases, sizeof(*aliases), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc || (ntohl(aliases->length) <= 4)) ipr_init_res_addr_aliases(aliases, res_addr); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Resource Address Aliases", rc); if (rc && sense_data.sense_key != ILLEGAL_REQUEST) rc = 0; for_each_ra_alias(ra, aliases) ra->host = ioa->host_num; close(fd); return rc; } /** * ipr_init_res_path_aliases - * @aliases: ipr_res_path_aliases struct * @res_addr: ipr_res_path * * Returns: * nothing **/ static void ipr_init_res_path_aliases(struct ipr_res_path_aliases *aliases, struct ipr_res_path *res_path) { struct ipr_res_path *rp; aliases->length = htonl(sizeof(*res_path) * IPR_DEV_MAX_PATHS); for_each_rp_alias(rp, aliases) { memcpy(rp, res_path, sizeof(*rp)); } } /** * ipr_query_res_path_aliases - * @ioa: ipr ioa struct * @res_addr: ipr_res_path struct * @aliases: ipr_res_ipr_aliases struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_path_aliases(struct ipr_ioa *ioa, struct ipr_res_path *res_path, struct ipr_res_path_aliases *aliases) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = ioa->ioa.gen_name; ipr_init_res_path_aliases(aliases, res_path); if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_RES_PATH_ALIASES; memcpy(&cdb[2], res_path->res_path_bytes, sizeof(struct ipr_res_path)); cdb[10] = sizeof(*aliases) >> 24; cdb[11] = (sizeof(*aliases) >> 16) & 0xff; cdb[12] = (sizeof(*aliases) >> 8) & 0xff; cdb[13] = sizeof(*aliases) & 0xff; rc = sg_ioctl(fd, cdb, aliases, sizeof(*aliases), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc || (ntohl(aliases->length) <= 8)) ipr_init_res_path_aliases(aliases, res_path); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Resource Path Aliases", rc); if (rc && sense_data.sense_key != ILLEGAL_REQUEST) rc = 0; close(fd); return rc; } /** * ipr_query_sas_expander_info - * @ioa: ipr ioa struct * @info: ipr_query_sas_expander_info struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_sas_expander_info(struct ipr_ioa *ioa, struct ipr_query_sas_expander_info *info) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_SAS_EXPANDER_INFO; cdb[10] = sizeof(*info) >> 24; cdb[11] = (sizeof(*info) >> 16) & 0xff; cdb[12] = (sizeof(*info) >> 8) & 0xff; cdb[13] = sizeof(*info) & 0xff; rc = sg_ioctl(fd, cdb, info, sizeof(*info), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query SAS Expander Info", rc); close(fd); return rc; } /** * ipr_change_cache_parameters * @ioa: ipr ioa struct * @mode: caching mode to set * * Returns: * 0 if success / non-zero on failure **/ int ipr_change_cache_parameters(struct ipr_ioa *ioa, int mode) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_CHANGE_CACHE_PARAMETERS; cdb[2] = mode; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "change cache params failed", rc); close(fd); return rc; } /** * ipr_query_cache_parameters - * @ioa: ipr ioa struct * @buf: buffer for returned cache data * @len: length of buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_cache_parameters(struct ipr_ioa *ioa, void *buf, int len) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_CACHE_PARAMETERS; cdb[10] = len >> 24; cdb[11] = len >> 16 & 0xff; cdb[12] = len >> 8 & 0xff; cdb[13] = len & 0xff; rc = sg_ioctl(fd, cdb, buf, len, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "Query Cache Parameters", rc); close(fd); return rc; } /** * ipr_query_res_redundancy_info - * @dev: ipr dev struct * @info: ipr_res_redundancy_info struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_redundancy_info(struct ipr_dev *dev, struct ipr_res_redundancy_info *info) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; char *name = dev->ioa->ioa.gen_name; struct ipr_dev_record *dev_record; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; u32 res_handle; int fd, rc; if (scsi_dev_data) res_handle = ntohl(scsi_dev_data->handle); else if (ipr_is_af_dasd_device(dev)) { dev_record = dev->dev_rcd; if (dev_record->no_cfgte_dev) return -EINVAL; res_handle = ntohl(dev->resource_handle); } else return -EINVAL; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; if (dev->ioa->sis64) cdb[1] = IPR_QUERY_RES_REDUNDANCY_INFO64; else cdb[1] = IPR_QUERY_RES_REDUNDANCY_INFO; cdb[2] = (u8)(res_handle >> 24); cdb[3] = (u8)(res_handle >> 16); cdb[4] = (u8)(res_handle >> 8); cdb[5] = (u8)(res_handle); cdb[10] = sizeof(*info) >> 24; cdb[11] = (sizeof(*info) >> 16) & 0xff; cdb[12] = (sizeof(*info) >> 8) & 0xff; cdb[13] = sizeof(*info) & 0xff; rc = sg_ioctl(fd, cdb, info, sizeof(*info), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(dev->ioa, &sense_data, "Query Resource Redundancy Info", rc); close(fd); return rc; } /** * get_livedump_fname - * @ioa: ipr_ioa struct * @fname: string to return the filename * @max: max size of fname * * Returns: * negative on failure / otherwise length of the file name * **/ static int get_livedump_fname(struct ipr_ioa *ioa, char *fname, int max) { time_t cur_time, rc; struct tm *cur_tm; char time_str[100]; int len; fname[0] = '\0'; rc = time(&cur_time); if (rc == ((time_t)-1)) return -EIO; cur_tm = localtime(&cur_time); if (!cur_tm) return -EIO; strftime(time_str, sizeof(time_str), "%Y%m%d.%H%M", cur_tm); len = snprintf(fname, max, "ipr-%04X-%s-dump-%s", ioa->ccin, ioa->pci_address, time_str); return len; } #define IPR_MAX_LIVE_DUMP_SIZE (16 * 1024 * 1024) /** * get_scsi_max_xfer_len - * @fd: IOA sg file descriptor * * Returns: * -1 on failure / otherwise max scatter-gather transfer length * **/ static int get_scsi_max_xfer_len(int fd) { int rc, max_tablesize, max_scsi_len; char elem_str[64]; ssize_t len; len = sysfs_read_attr("/sys/module/sg/parameters", "scatter_elem_sz", elem_str, 64); if (len < 0) { syslog_dbg("Failed to read scatter_elem_sz parameter from sg module. %m\n"); return -1; } rc = ioctl(fd, SG_GET_SG_TABLESIZE, &max_tablesize); if (rc) { syslog_dbg("Unable to get device scatter-gather table size. %m\n"); return -1; } max_scsi_len = max_tablesize * atoi(elem_str); if (max_scsi_len < IPR_MAX_LIVE_DUMP_SIZE) return max_scsi_len; else return IPR_MAX_LIVE_DUMP_SIZE; } /** * ipr_get_live_dump - * @dev: ipr dev struct * @info: ipr_res_redundancy_info struct * * Returns: * 0 if success / non-zero on failure * **/ int ipr_get_live_dump(struct ipr_ioa *ioa) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN], *buf; char dump_file[100], dump_path[100]; int rc, fd, fd_dump, buff_len; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } buff_len = get_scsi_max_xfer_len(fd); if (buff_len <= 0) { rc = -EPERM; goto out; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_LIVE_DUMP; cdb[10] = buff_len >> 24; cdb[11] = (buff_len >> 16) & 0xff; cdb[12] = (buff_len >> 8) & 0xff; cdb[13] = buff_len & 0xff; buf = (u8*) malloc(buff_len); rc = sg_ioctl(fd, cdb, buf, buff_len, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc) { ioa_cmd_err(ioa, &sense_data, "Live Dump", rc); goto out_free; } rc = get_livedump_fname(ioa, dump_file, sizeof(dump_file)); if (rc < 0) { syslog_dbg("Failed to create a live dump file name.\n"); goto out_free; } snprintf(dump_path, sizeof(dump_path), "%s%s", IPRDUMP_DIR, dump_file); fd_dump = creat(dump_path, S_IRUSR); if (fd_dump < 0) { syslog(LOG_ERR, "Could not open %s. %m\n", dump_path); rc = fd_dump; goto out_free; } rc = write(fd_dump, buf, buff_len); if (rc != buff_len) syslog(LOG_ERR, "Could not write dump on file %s. %m\n", dump_path); close(fd_dump); out_free: free(buf); out: close(fd); return rc; } /** * ipr_set_array_asym_access - * @dev: ipr_dev struct * @qac: ipr_array_query_data struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_array_asym_access(struct ipr_ioa *ioa) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; int length = ioa->qac_data->resp_len; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SET_ARRAY_ASYMMETRIC_ACCESS; cdb[7] = (length >> 8) & 0xff; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "Set Array Asymmetric Access", rc); close(fd); return rc; } /** * get_write_buffer_dev - FIXME - probably need to remove this routine. * @dev: ipr dev struct * * Returns: * device generic name for now - see FIXME below **/ #if 0 static char *get_write_buffer_dev(struct ipr_dev *dev) { struct sysfs_class_device *class_device; struct sysfs_attribute *dev_attr; int rc, val; char path[SYSFS_PATH_MAX]; /* xxx FIXME */ return dev->gen_name; if (!strlen(dev->dev_name) || !dev->scsi_dev_data) return dev->gen_name; class_device = sysfs_open_class_device("block", &dev->dev_name[5]); if (!class_device) { scsi_dbg(dev, "Failed to open block class device for %s. %m\n", dev->dev_name); return dev->gen_name; } sprintf(path, "%s/queue/max_sectors_kb", class_device->path); dev_attr = sysfs_open_attribute(path); if (!dev_attr) { scsi_dbg(dev, "Failed to get max_sectors_kb attribute. %m\n"); goto fail; } rc = sysfs_read_attribute(dev_attr); if (rc) { sysfs_close_attribute(dev_attr); scsi_dbg(dev, "Failed to read max_sectors_kb attribute. %m\n"); goto fail; } sscanf(dev_attr->value, "%d", &val); sysfs_close_attribute(dev_attr); sysfs_close_class_device(class_device); if (val > 256) return dev->dev_name; return dev->gen_name; fail: sysfs_close_class_device(class_device); return dev->gen_name; } #endif /** * ipr_resume_device_bus - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_resume_device_bus(struct ipr_dev *dev, struct ipr_res_addr *res_addr) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = 0; struct ipr_ioa *ioa = dev->ioa; char *rp, *endptr; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RESUME_DEV_BUS; cdb[1] = IPR_RDB_UNQUIESCE_AND_REBALANCE; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; } else { cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; } rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_SUSPEND_DEV_BUS_TIMEOUT); close(fd); return rc; } /** * __ipr_write_buffer - Issue WRITE_BUFFER command. * In older kernels, max_sectors_kb is not big enough and * would return -EIO when using dev_name to issue * SG_IO ioctl(). In that case, we have to use gen_name. * Also, in case dev_name does not exist, we need to use * gen_name. Hence, we try dev_name first and switch to * gen_name if we get -ENOENT or -EIO. * @dev: ipr dev struct * @mode: mode value * @buff: data buffer * @length: buffer length * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ static int __ipr_write_buffer(struct ipr_dev *dev, u8 mode, void *buff, int length, struct sense_data_t *sense_data) { u32 direction = length ? SG_DXFER_TO_DEV : SG_DXFER_NONE; u8 cdb[IPR_CCB_CDB_LEN]; int rc; ENTER; if ((strlen(dev->dev_name) == 0) && (strlen(dev->gen_name) == 0)) return -ENOENT; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = WRITE_BUFFER; cdb[1] = mode; cdb[6] = (length & 0xff0000) >> 16; cdb[7] = (length & 0x00ff00) >> 8; cdb[8] = length & 0x0000ff; rc = sg_ioctl_by_name(dev->dev_name, cdb, buff, length, direction, sense_data, IPR_WRITE_BUFFER_TIMEOUT); if (rc != 0) { if ((rc == -ENOENT) || (rc == -EIO) || (rc == -EINVAL)) { syslog_dbg("Write buffer failed on sd device %s. %m\n", dev->dev_name); rc = sg_ioctl_by_name(dev->gen_name, cdb, buff, length, direction, sense_data, IPR_WRITE_BUFFER_TIMEOUT); if (rc != 0) { scsi_cmd_err(dev, sense_data, "Write buffer using sg device", rc); if (rc == -ENOMEM) syslog(LOG_ERR, "Cannot get enough memory to " "perform microcode download " "through %s. Reduce system " "memory usage and try again.\n", dev->gen_name); } } else scsi_cmd_err(dev, sense_data, "Write buffer using sd device", rc); } return rc; } /** * ipr_write_buffer - * @dev: ipr dev struct * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_write_buffer(struct ipr_dev *dev, void *buff, int length) { int rc, i; struct sense_data_t sense_data; struct ipr_disk_attr attr; int old_qdepth; int sas_ses = 0; int sas_ses_retries = 5; int mode5 = 1; ENTER; if ((rc = ipr_get_dev_attr(dev, &attr))) { scsi_dbg(dev, "Failed to get device attributes. rc=%d\n", rc); return rc; } if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) sas_ses = 1; if (sas_ses && !ipr_mode5_write_buffer) mode5 = 0; /* * Set the queue depth to 1 for the duration of the code download. * This prevents us from getting I/O errors during the code update */ old_qdepth = attr.queue_depth; attr.queue_depth = 1; if ((rc = ipr_set_dev_attr(dev, &attr, 0))) { scsi_dbg(dev, "Failed to set queue depth to 1. rc=%d\n", rc); return rc; } if (sas_ses) { if (!mode5) { for (i = 0; i < (sas_ses_retries + 1); i++) { rc = __ipr_write_buffer(dev, 0x0e, buff, length, &sense_data); if (!rc) { break; } else if (rc && sense_data.sense_key == UNIT_ATTENTION && sense_data.add_sense_code == 0x29 && sense_data.add_sense_code_qual == 0x00) { continue; } else if (rc && sense_data.sense_key == ILLEGAL_REQUEST) { mode5 = 1; break; } else if (rc) { goto out; } } } rc = ipr_suspend_device_bus(dev, &dev->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (rc) { scsi_dbg(dev, "Failed to quiesce the SAS port.\n"); goto out; } for (i = 0; i < (sas_ses_retries + 1); i++) { if (mode5) rc = __ipr_write_buffer(dev, 5, buff, length, &sense_data); else rc = __ipr_write_buffer(dev, 0x0f, NULL, 0, &sense_data); if (rc && sense_data.sense_key == UNIT_ATTENTION && sense_data.add_sense_code == 0x29 && sense_data.add_sense_code_qual == 0x00) { continue; } else { break; } } if (rc) { scsi_dbg(dev, "Write buffer %sfailed.\n", mode5 ? "" : "activate "); goto out_resume; } } else { if ((rc = __ipr_write_buffer(dev, 5, buff, length, &sense_data))) goto out; } scsi_dbg(dev, "Waiting for device to come ready after download.\n"); sleep(sas_ses ? 120 : 5); for (i = 0, rc = -1; rc && (i < 60); i++, sleep(1)) rc = __ipr_test_unit_ready(dev, &sense_data); out_resume: if (sas_ses) ipr_resume_device_bus(dev, &dev->res_addr[0]); out: attr.queue_depth = old_qdepth; ipr_set_dev_attr(dev, &attr, 0); LEAVE; return rc; } /** * ipr_suspend_device_bus - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @option: option value * * Returns: * 0 if success / non-zero on failure **/ int ipr_suspend_device_bus(struct ipr_dev *dev, struct ipr_res_addr *res_addr, u8 option) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = 0; struct ipr_ioa *ioa = dev->ioa; char *rp, *endptr; ENTER; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SUSPEND_DEV_BUS; cdb[1] = option; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; } else { cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; } rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_SUSPEND_DEV_BUS_TIMEOUT); close(fd); LEAVE; return rc; } /** * ipr_can_remove_device - indicate whether or not a device can be removed * @dev: ipr dev struct * * Returns: * 1 if device can be removed / 0 if device can not be removed **/ int ipr_can_remove_device(struct ipr_dev *dev) { struct ipr_res_addr *ra; for_each_ra(ra, dev) { if (ipr_suspend_device_bus(dev, ra, IPR_SDB_CHECK_ONLY)) return 0; } return 1; } /** * ipr_receive_diagnostics - * @dev: ipr dev struct * @page: page number * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_receive_diagnostics(struct ipr_dev *dev, u8 page, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = RECEIVE_DIAGNOSTIC; cdb[1] = 0x01; cdb[2] = page; cdb[3] = (length >> 8) & 0xff; cdb[4] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Receive diagnostics", rc); close(fd); return rc; } /** * ipr_send_diagnostics - * @dev: ipr dev struct * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_send_diagnostics(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SEND_DIAGNOSTIC; cdb[1] = 0x10; cdb[3] = (length >> 8) & 0xff; cdb[4] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Send diagnostics", rc); close(fd); return rc; } /** * set_supported_devs - * @dev: ipr dev struct * @std_inq: ipr_std_inq_data struct * * Returns: * 0 if success / non-zero on failure **/ static int set_supported_devs(struct ipr_dev *dev, struct ipr_std_inq_data *std_inq) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_supported_device supported_dev; fd = open(dev->ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->ioa->ioa.gen_name); return errno; } memset(&supported_dev, 0, sizeof(struct ipr_supported_device)); memcpy(&supported_dev.vpids, &std_inq->vpids, sizeof(std_inq->vpids)); supported_dev.num_records = 1; supported_dev.data_length = htons(sizeof(supported_dev)); supported_dev.reserved = 0; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SET_SUPPORTED_DEVICES; cdb[7] = (sizeof(supported_dev) >> 8) & 0xff; cdb[8] = sizeof(supported_dev) & 0xff; rc = sg_ioctl(fd, cdb, &supported_dev, sizeof(supported_dev), SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) ioa_cmd_err(dev->ioa, &sense_data, "Set supported devices", rc); close(fd); return rc; } /** * __device_supported - * @dev: ipr dev struct * @inq: ipr_std_inq_data * * Returns: * 0 if device is supported / 1 if device is not supported **/ static int __device_supported(struct ipr_dev *dev, struct ipr_std_inq_data *inq) { int i, j; const struct ses_table_entry *ste; if (!dev->scsi_dev_data) return 1; ste = get_ses_entry(dev->ioa, dev->scsi_dev_data->channel); if (!ste) return 1; if (!ste->block_15k_devices) return 1; for (i = 0; i < ARRAY_SIZE(unsupported_dasd); i++) { for (j = 0; j < IPR_VENDOR_ID_LEN; j++) { if (unsupported_dasd[i].compare_vendor_id_byte[j] && unsupported_dasd[i].vendor_id[j] != inq->vpids.vendor_id[j]) break; } if (j != IPR_VENDOR_ID_LEN) continue; for (j = 0; j < IPR_PROD_ID_LEN; j++) { if (unsupported_dasd[i].compare_product_id_byte[j] && unsupported_dasd[i].product_id[j] != inq->vpids.product_id[j]) break; } if (j != IPR_PROD_ID_LEN) continue; return 0; } return 1; } /** * device_supported - * @dev: ipr dev struct * * Returns: * 0 if device is supported / 1 if device is not supported **/ int device_supported(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq; if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq))) return -EIO; return __device_supported(dev, &std_inq); } /** * enable_af - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int enable_af(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq; if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq))) return -EIO; if (!__device_supported(dev, &std_inq)) { scsi_dbg(dev, "Unsupported device attached\n"); return -EIO; } if (set_supported_devs(dev, &std_inq)) return -EIO; return 0; } /** * get_blk_size - return the block size of the device * @dev: ipr dev struct * * Returns: * block size / non-zero on failure **/ static int get_blk_size(struct ipr_dev *dev) { struct ipr_mode_pages modes; struct ipr_block_desc *block_desc; int rc; memset(&modes, 0, sizeof(modes)); rc = ipr_mode_sense(dev, 0x0a, &modes); if (!rc) { if (modes.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)(modes.data); return ((block_desc->block_length[1] << 8) | block_desc->block_length[2]); } } return -EIO; } /** * format_req - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int format_req(struct ipr_dev *dev) { struct sense_data_t sense_data; int rc; rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) return 1; rc = get_blk_size(dev); if (rc < 0) return 0; if (ipr_is_gscsi(dev) && (rc != 512 || rc != 4096)) return 1; return 0; } /* * Scatter/gather list buffers are checked against the value returned * by queue_dma_alignment(), which defaults to 511 in Linux 2.6, * for alignment if a SG_IO ioctl request is sent through a /dev/sdX device. */ #define IPR_S_G_BUFF_ALIGNMENT 512 #define IPR_MAX_XFER 0x8000 const int cdb_size[] ={6, 10, 10, 0, 16, 12, 16, 16}; /** * _sg_ioctl - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * @retries number of retries * * Returns: * 0 if success / non-zero on failure **/ static int _sg_ioctl(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec, int retries) { int rc = 0; sg_io_hdr_t io_hdr_t; sg_iovec_t *iovec = NULL; int iovec_count = 0; int i; int buff_len, segment_size; void *dxferp; u8 *buf; /* check if scatter gather should be used */ if (xfer_len > IPR_MAX_XFER) { iovec_count = (xfer_len/IPR_MAX_XFER) + 1; iovec = malloc(iovec_count * sizeof(sg_iovec_t)); buff_len = xfer_len; segment_size = IPR_MAX_XFER; for (i = 0; (i < iovec_count) && (buff_len != 0); i++) { posix_memalign(&(iovec[i].iov_base), IPR_S_G_BUFF_ALIGNMENT, segment_size); if (data_direction == SG_DXFER_TO_DEV) memcpy(iovec[i].iov_base, data + (IPR_MAX_XFER * i), segment_size); iovec[i].iov_len = segment_size; buff_len -= segment_size; if (buff_len < segment_size) segment_size = buff_len; } iovec_count = i; dxferp = (void *)iovec; } else { iovec_count = 0; dxferp = data; } for (i = 0; i < (retries + 1); i++) { memset(&io_hdr_t, 0, sizeof(io_hdr_t)); memset(sense_data, 0, sizeof(struct sense_data_t)); io_hdr_t.interface_id = 'S'; io_hdr_t.cmd_len = cdb_size[(cdb[0] >> 5) & 0x7]; io_hdr_t.iovec_count = iovec_count; io_hdr_t.flags = 0; io_hdr_t.pack_id = 0; io_hdr_t.usr_ptr = 0; io_hdr_t.sbp = (unsigned char *)sense_data; io_hdr_t.mx_sb_len = sizeof(struct sense_data_t); io_hdr_t.timeout = timeout_in_sec * 1000; io_hdr_t.cmdp = cdb; io_hdr_t.dxfer_direction = data_direction; io_hdr_t.dxfer_len = xfer_len; io_hdr_t.dxferp = dxferp; rc = ioctl(fd, SG_IO, &io_hdr_t); if (rc == -1 && errno == EINVAL) { rc = -EINVAL; goto out; } if (rc == 0 && io_hdr_t.masked_status == CHECK_CONDITION) rc = CHECK_CONDITION; else if (rc == 0 && (io_hdr_t.host_status || io_hdr_t.driver_status)) rc = -EIO; if (rc == 0 || io_hdr_t.host_status == 1) break; } out: if (iovec_count) { for (i = 0, buf = (u8 *)data; i < iovec_count; i++) { if (data_direction == SG_DXFER_FROM_DEV) memcpy(buf, iovec[i].iov_base, iovec[i].iov_len); buf += iovec[i].iov_len; free(iovec[i].iov_base); } free(iovec); } return rc; }; /** * sg_ioctl - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ int sg_ioctl(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { return _sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec, 1); }; /** * sg_ioctl_noretry - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ int sg_ioctl_noretry(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { return _sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec, 0); }; /** * sg_ioctl_by_name - * @name: file name * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ static int sg_ioctl_by_name(char *name, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { int fd, rc; if (strlen(name) <= 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s.\n", name); return errno; } rc = sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec); close(fd); return rc; } /** * ipr_set_ha_mode - * @ioa: ipr ioa struct * @gscsi_only_ha: process gscsi only flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_ha_mode(struct ipr_ioa *ioa, int gscsi_only_ha) { int rc, len; struct sense_data_t sense_data; struct ipr_config_term_hdr *hdr; struct ipr_subsys_config_term *term; struct ipr_mode_pages pages; struct ipr_mode_page25 *page; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(&ioa->ioa, 0x25, &pages, &sense_data); if (!rc) { page = (struct ipr_mode_page25 *)(pages.data + pages.hdr.block_desc_len); for_each_page25_term(hdr, page) { if (hdr->term_id != IPR_SUBSYS_CONFIG_TERM_ID) continue; term = (struct ipr_subsys_config_term *)hdr; if (gscsi_only_ha) term->config = IPR_GSCSI_ONLY_HA_SUBSYS; else term->config = IPR_AFDASD_SUBSYS; len = pages.hdr.length + 1; pages.hdr.length = 0; return ipr_mode_select(&ioa->ioa, &pages, len); } } return rc; } /** * ipr_change_multi_adapter_assignment - set the preferred primary state and/or * the asymmetric access state * @ioa: ipr ioa struct * @preferred_primary: set preferred primary flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_change_multi_adapter_assignment(struct ipr_ioa *ioa, int preferred_primary, int asym_access) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_dev *dev = &ioa->ioa; if (strlen(dev->gen_name) == 0) return -ENOENT; fd = open(dev->gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MAINTENANCE_OUT; cdb[1] = IPR_CHANGE_MULTI_ADAPTER_ASSIGNMENT; if (asym_access) cdb[2] = IPR_PRESERVE_ASYMMETRIC_STATE; if (preferred_primary) cdb[3] = IPR_IOA_STATE_PRIMARY; else cdb[3] = IPR_IOA_STATE_NO_PREFERENCE; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) scsi_cmd_err(dev, &sense_data, "Change Multi Adapter Assignment", rc); close(fd); return rc; } static void ipr_save_ioa_attr(struct ipr_ioa *ioa, char *field, char *value, int update); /** * set_preferred_primary - set the preferred primary state * @ioa: ipr ioa struct * @preferred_primary: set preferred primary flag * * Returns: * 0 if success / non-zero on failure **/ int set_preferred_primary(struct ipr_ioa *ioa, int preferred_primary) { char temp[100]; sprintf(temp, "%d", preferred_primary); if (ipr_change_multi_adapter_assignment(ioa, preferred_primary, IPR_PRESERVE_ASYMMETRIC_STATE)) return -EIO; return 0; } /** * set_ha_mode - * @ioa: ipr ioa struct * @gscsi_only: gscsi only flag * * Returns: * 0 if success / non-zero on failure **/ int set_ha_mode(struct ipr_ioa *ioa, int gscsi_only) { char temp[100]; int reset_needed = (gscsi_only != ioa->in_gscsi_only_ha); sprintf(temp, "%d", gscsi_only); if (ipr_set_ha_mode(ioa, gscsi_only)) return -EIO; ipr_save_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp, 1); if (reset_needed) ipr_reset_adapter(ioa); return 0; } /** * ipr_set_active_active_mode - * @ioa: ipr ioa struct * @mode: enable or disable * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_active_active_mode(struct ipr_ioa *ioa) { int len, rc; struct ipr_mode_pages mode_pages; struct ipr_mode_page24 *page24; memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x24, &mode_pages); if (!rc) { page24 = (struct ipr_mode_page24 *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); len = mode_pages.hdr.length +1; mode_pages.hdr.length = 0; page24->dual_adapter_af = ENABLE_DUAL_IOA_ACTIVE_ACTIVE; return ipr_mode_select(&ioa->ioa, &mode_pages, len); } return rc; } /** * set_active_active_mode - * @ioa: ipr ioa struct * @mode: enable or disable * * Returns: * 0 if success / non-zero on failure **/ int set_active_active_mode(struct ipr_ioa *ioa, int mode) { struct ipr_ioa_attr attr; int rc; /* Get the current ioa attributes. */ rc = ipr_get_ioa_attr(ioa, &attr); if (rc) return rc; /* Get the saved ioa attributes from the config file. */ rc = ipr_modify_ioa_attr(ioa, &attr); if (rc) return rc; if (attr.active_active == mode) { /* We should never get here. */ ioa_dbg(ioa, "Saved and current asymmetric access mode are not the same.\n"); } attr.active_active = mode; return ipr_set_ioa_attr(ioa, &attr, 1); } int ipr_set_ses_mode(struct ipr_dev *dev, int mode) { int rc; struct sense_data_t sense_data; u8 buff = (u8)mode; rc = __ipr_write_buffer(dev, 0x1f, &buff, sizeof(buff), &sense_data); return rc; } /** * get_scsi_dev_data - get scsi device data * @scsi_dev_ref: scsi_dev_data struct * * Returns: * number of devices if success / 0 or -1 on failure **/ int get_scsi_dev_data(struct scsi_dev_data **scsi_dev_ref) { int num_devs = 0; DIR *dirfd; struct dirent *dent; struct scsi_dev_data *scsi_dev_data; struct scsi_dev_data *scsi_dev_base = *scsi_dev_ref; dirfd = opendir("/sys/class/scsi_device"); if (!dirfd) { syslog_dbg("Failed to open scsi_device class. %m\n"); return 0; } while ((dent = readdir(dirfd)) != NULL) { int host, channel, lun; signed char id; char devpath[PATH_MAX]; char buff[256]; ssize_t len; if (sscanf(dent->d_name,"%d:%d:%hhd:%d", &host, &channel, &id, &lun) != 4) continue; scsi_dev_base = realloc(scsi_dev_base, sizeof(struct scsi_dev_data) * (num_devs + 1)); scsi_dev_data = &scsi_dev_base[num_devs]; scsi_dev_data->host = host; scsi_dev_data->channel = channel; scsi_dev_data->id = id; scsi_dev_data->lun = lun; strcpy(scsi_dev_data->sysfs_device_name, dent->d_name); sprintf(devpath, "/sys/class/scsi_device/%s/device", dent->d_name); len = sysfs_read_attr(devpath, "type", buff, 256); if (len > 0) sscanf(buff, "%d", &scsi_dev_data->type); //FIXME WHERE TO GET OPENS!!! /* sysfs_attr = sysfs_get_device_attr(sysfs_device_device, "opens"); sscanf(sysfs_attr->value "%d", &scsi_dev_data->opens); */ scsi_dev_data->opens = 0; len = sysfs_read_attr(devpath, "state", buff, 256); if (len > 0 && strstr(buff, "offline")) scsi_dev_data->online = 0; else scsi_dev_data->online = 1; len = sysfs_read_attr(devpath, "queue_depth", buff, 256); if (len > 0) sscanf(buff, "%d", &scsi_dev_data->qdepth); len = sysfs_read_attr(devpath, "adapter_handle", buff, 256); if (len > 0) sscanf(buff, "%X", &scsi_dev_data->handle); len = sysfs_read_attr(devpath, "vendor", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->vendor_id, buff, IPR_VENDOR_ID_LEN); len = sysfs_read_attr(devpath, "model", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->product_id, buff, IPR_PROD_ID_LEN); len = sysfs_read_attr(devpath, "resource_path", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->res_path, buff, IPR_MAX_RES_PATH_LEN); len = sysfs_read_attr(devpath, "device_id", buff, 256); if (len > 0) sscanf(buff, "%lX", &scsi_dev_data->device_id); strcpy(scsi_dev_data->dev_name,""); strcpy(scsi_dev_data->gen_name,""); num_devs++; } closedir(dirfd); *scsi_dev_ref = scsi_dev_base; return num_devs; } /** * wait_for_dev - wait 20 seconds for the device to become available * @name: device name * * Returns: * -ETIMEDOUT **/ static int wait_for_dev(char *name) { int fd, delay; for (delay = 20, fd = 0; delay; delay--) { fd = open(name, O_RDWR); if (fd != -1) { close(fd); break; } syslog_dbg("Waiting for %s to show up\n", name); sleep(1); } if (fd == -1) syslog_dbg("Failed to open %s.\n", name); return -ETIMEDOUT; } /** * get_sg_names - waits for sg devices to become available * @num_devs: number of devices * * Returns: * nothing **/ static void get_sg_names(int num_devs) { int i; DIR *dirfd; struct dirent *dent; char devpath[PATH_MAX]; for (i = 0; i < num_devs; i++) { sprintf(devpath, "/sys/class/scsi_device/%s/device/scsi_generic", scsi_dev_table[i].sysfs_device_name); dirfd = opendir(devpath); if (!dirfd) continue; while((dent = readdir(dirfd)) != NULL) { if (dent->d_name[0] == '.') continue; if (strncmp(dent->d_name, "sg", 2)) continue; sprintf(scsi_dev_table[i].gen_name, "/dev/%s", dent->d_name); break; } closedir(dirfd); } } /** * get_sd_names - populate the scsi_dev_table dev_name entries * @num_devs: number of devices * * Returns: * nothing **/ static void get_sd_names(int num_devs) { int i; DIR *dirfd; struct dirent *dent; char devpath[PATH_MAX]; for (i = 0; i < num_devs; i++) { sprintf(devpath, "/sys/class/scsi_device/%s/device/block", scsi_dev_table[i].sysfs_device_name); dirfd = opendir(devpath); if (!dirfd) continue; while((dent = readdir(dirfd)) != NULL) { if (dent->d_name[0] == '.') continue; if (strncmp(dent->d_name, "sd", 2)) continue; sprintf(scsi_dev_table[i].dev_name, "/dev/%s", dent->d_name); break; } closedir(dirfd); } } /** * get_ioa_name - populate adapter information in the scsi_dev_table * @ioa: ipr ioa struct * @num_sg_devices: number of sg devices * * Returns: * nothing **/ static void get_ioa_name(struct ipr_ioa *ioa, int num_sg_devices) { int i; int host_no; int channel = IPR_IOAFP_VIRTUAL_BUS; int id = 0; int lun = 0; sscanf(ioa->host_name, "host%d", &host_no); if (!ioa->sis64) channel = id = lun = 255; for (i = 0; i < num_sg_devices; i++) { if (scsi_dev_table[i].host == host_no && scsi_dev_table[i].channel == channel && scsi_dev_table[i].id == id && scsi_dev_table[i].lun == lun && scsi_dev_table[i].type == IPR_TYPE_ADAPTER) { strcpy(ioa->ioa.dev_name, scsi_dev_table[i].dev_name); strcpy(ioa->ioa.gen_name, scsi_dev_table[i].gen_name); ioa->ioa.scsi_dev_data = &scsi_dev_table[i]; ioa->ioa.ioa = ioa; } } } struct ipr_dual_ioa_state { u8 state; char *desc; }; static struct ipr_dual_ioa_state dual_ioa_states [] = { {IPR_IOA_STATE_PRIMARY, "Primary"}, {IPR_IOA_STATE_SECONDARY, "Secondary"}, {IPR_IOA_STATE_NO_PREFERENCE, "No Preference"} }; /** * print_ioa_state - copy the ioa state into buf * @buf: data buffer * @state: state * * Returns: * nothing **/ static void print_ioa_state(char *buf, u8 state) { int i; for (i = 0; i < ARRAY_SIZE(dual_ioa_states); i++) { if (dual_ioa_states[i].state == state) { strcpy(buf, dual_ioa_states[i].desc); return; } } sprintf(buf, "Unknown (%d)", state); } /** * get_subsys_config - * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_subsys_config(struct ipr_ioa *ioa) { int rc; struct ipr_mode_pages pages; struct ipr_mode_page25 *page; struct ipr_config_term_hdr *hdr; struct ipr_subsys_config_term *term; struct sense_data_t sense_data; ioa->in_gscsi_only_ha = 0; if (!ioa->gscsi_only_ha) return; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(&ioa->ioa, 0x25, &pages, &sense_data); if (!rc) { page = (struct ipr_mode_page25 *)(pages.data + pages.hdr.block_desc_len); for_each_page25_term(hdr, page) { if (hdr->term_id != IPR_SUBSYS_CONFIG_TERM_ID) continue; term = (struct ipr_subsys_config_term *)hdr; if (term->config == IPR_GSCSI_ONLY_HA_SUBSYS) ioa->in_gscsi_only_ha = 1; return; } } } /** * get_dual_ioa_state - set ioa->is_secondary to indicate the dual ioa state * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_dual_ioa_state(struct ipr_ioa *ioa) { int rc; struct ipr_dual_ioa_entry *ioa_entry; sprintf(ioa->dual_state, "Primary"); sprintf(ioa->preferred_dual_state, "No Preference"); ioa->is_secondary = 0; if (!ioa->dual_raid_support) return; rc = ipr_query_multi_ioa_status(ioa, &ioa->ioa_status, sizeof(ioa->ioa_status)); if (rc) return; print_ioa_state(ioa->preferred_dual_state, ioa->ioa_status.cap.preferred_role); if (ntohl(ioa->ioa_status.num_entries)) { ioa_entry = (struct ipr_dual_ioa_entry *) (((unsigned long)&ioa->ioa_status.cap) + ntohl(ioa->ioa_status.cap.length)); print_ioa_state(ioa->dual_state, ioa_entry->cur_state); if (ioa_entry->cur_state == IPR_IOA_STATE_SECONDARY) ioa->is_secondary = 1; } } /** * get_af_block_size - return the af block size * @ioa_cap: ipr_inquiry_ioa_cap struct * * Returns: * the actual af_block_size or IPR_DEFAULT_AF_BLOCK_SIZE **/ static u16 get_af_block_size(struct ipr_inquiry_ioa_cap *ioa_cap) { int sz_off = offsetof(struct ipr_inquiry_ioa_cap, af_block_size); int len_off = offsetof(struct ipr_inquiry_ioa_cap, page_length); if (ioa_cap->page_length > (sz_off - len_off)) return ntohs(ioa_cap->af_block_size); return IPR_DEFAULT_AF_BLOCK_SIZE; } /** * get_ioa_cap - get the capability information for the ioa (inquiry page D0) * Also, if page 01 is supported, then there is cache on the card. * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_ioa_cap(struct ipr_ioa *ioa) { int rc, j; struct ipr_inquiry_page0 page0_inq; struct ipr_inquiry_ioa_cap ioa_cap; struct ipr_mode_page24 *page24; struct ipr_mode_pages mode_pages; ioa->af_block_size = IPR_DEFAULT_AF_BLOCK_SIZE; ioa->tcq_mode = ioa_get_tcq_mode(ioa); rc = ipr_inquiry(&ioa->ioa, 0, &page0_inq, sizeof(page0_inq)); if (rc) { ioa->ioa_dead = 1; return; } for (j = 0; j < page0_inq.page_length; j++) { if (page0_inq.supported_page_codes[j] == 0x01) { ioa->has_cache = 1; continue; } if (page0_inq.supported_page_codes[j] == 0xC4) { ioa->has_cache = 1; continue; } if (page0_inq.supported_page_codes[j] != 0xD0) continue; rc = ipr_inquiry(&ioa->ioa, 0xD0, &ioa_cap, sizeof(ioa_cap)); if (rc) break; ioa->af_block_size = get_af_block_size(&ioa_cap); ioa->support_4k = ioa_cap.af_4k_support; if (ioa_cap.is_aux_cache) ioa->is_aux_cache = 1; if (ioa_cap.can_attach_to_aux_cache && ioa_cap.is_dual_wide) ioa->protect_last_bus = 1; if (ioa_cap.gscsi_only_ha) ioa->gscsi_only_ha = 1; if (ioa_cap.sis_format == IPR_SIS64) ioa->sis64 = 1; else { if (ioa_cap.ra_id_encoding == IPR_2BIT_HOP) ioa->hop_count = IPR_2BIT_HOP; else ioa->hop_count = IPR_3BIT_HOP; } if (ioa_cap.dual_ioa_raid || ioa_cap.dual_ioa_asymmetric_access) { memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x24, &mode_pages); if (rc) break; page24 = (struct ipr_mode_page24 *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (ioa_cap.dual_ioa_raid && page24->dual_adapter_af == ENABLE_DUAL_IOA_AF) ioa->dual_raid_support = 1; if (ioa_cap.dual_ioa_asymmetric_access) { ioa->asymmetric_access = 1; if (page24->dual_adapter_af == ENABLE_DUAL_IOA_ACTIVE_ACTIVE) { ioa->dual_raid_support = 1; ioa->asymmetric_access_enabled = 1; } else { ioa->asymmetric_access_enabled = 0; } } } } } /** * get_prot_levels - populate the prot_level_str field for each array, for each * vset and each device in the vset * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_prot_levels(struct ipr_ioa *ioa) { struct ipr_dev *array, *vset, *dev; char *prot_level_str; for_each_array(ioa, array) { prot_level_str = get_prot_level_str(ioa->supported_arrays, array->raid_level); strncpy(array->prot_level_str, prot_level_str, 8); } for_each_vset(ioa, vset) { prot_level_str = get_prot_level_str(ioa->supported_arrays, vset->raid_level); strncpy(vset->prot_level_str, prot_level_str, 8); for_each_dev_in_vset(vset, dev) strncpy(dev->prot_level_str, prot_level_str, 8); } } void ipr_convert_res_path_to_bytes(struct ipr_dev *dev) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; int i; char *startptr, *endptr; if (scsi_dev_data) { startptr = dev->res_path_name; i = 0; do { dev->res_path[0].res_path_bytes[i++] = (u8)strtol(startptr, &endptr, 16); startptr = endptr + 1; } while (*endptr != '\0' && i < 8); while ( i < 8 ) dev->res_path[0].res_path_bytes[i++] = 0xff; } } /** * get_res_addr - * @dev: ipr dev struct * @res_addr: ipr_res_addr struct * * Returns: * 0 if success / -1 on failure **/ static int get_res_addr(struct ipr_dev *dev, struct ipr_res_addr *res_addr) { struct ipr_dev_record *dev_record = dev->dev_rcd; struct ipr_array_record *array_record = dev->array_rcd; if (dev->scsi_dev_data) { res_addr->host = dev->scsi_dev_data->host; res_addr->bus = dev->scsi_dev_data->channel; res_addr->target = dev->scsi_dev_data->id; res_addr->lun = dev->scsi_dev_data->lun; if (dev->ioa->sis64) { strncpy(dev->res_path_name, dev->scsi_dev_data->res_path, strlen(dev->scsi_dev_data->res_path)); ipr_convert_res_path_to_bytes(dev); } } else if (ipr_is_af_dasd_device(dev)) { if (dev_record && dev_record->no_cfgte_dev) { res_addr->host = dev->ioa->host_num; res_addr->bus = dev_record->type2.last_resource_addr.bus; res_addr->target = dev_record->type2.last_resource_addr.target; res_addr->lun = dev_record->type2.last_resource_addr.lun; } else if (dev_record) { res_addr->host = dev->ioa->host_num; res_addr->bus = dev_record->type2.resource_addr.bus; res_addr->target = dev_record->type2.resource_addr.target; res_addr->lun = dev_record->type2.resource_addr.lun; if (dev->ioa->sis64) { ipr_format_res_path(dev_record->type3.res_path, dev->res_path_name, IPR_MAX_RES_PATH_LEN); ipr_convert_res_path_to_bytes(dev); } } else return -1; } else if (ipr_is_volume_set(dev)) { if (array_record && array_record->no_config_entry) { res_addr->host = dev->ioa->host_num; res_addr->bus = array_record->type2.last_resource_addr.bus; res_addr->target = array_record->type2.last_resource_addr.target; res_addr->lun = array_record->type2.last_resource_addr.lun; } else if (array_record) { res_addr->host = dev->ioa->host_num; res_addr->bus = array_record->type2.resource_addr.bus; res_addr->target = array_record->type2.resource_addr.target; res_addr->lun = array_record->type2.resource_addr.lun; } else return -1; } else return -1; return 0; } /** * find_multipath_vset - return the other vset of a given multipath vset * @vset1: ipr dev struct * * Returns: * ipr_dev if success / NULL on failure **/ static struct ipr_dev *find_multipath_vset(struct ipr_dev *vset1) { struct ipr_ioa *ioa; struct ipr_dev *vset2; for_each_ioa(ioa) { if (ioa == vset1->ioa) continue; for_each_vset(ioa, vset2) { if (memcmp(vset1->vendor_id, vset2->vendor_id, IPR_VENDOR_ID_LEN)) continue; if (memcmp(vset1->product_id, vset2->product_id, IPR_PROD_ID_LEN)) continue; if (memcmp(vset1->serial_number, vset2->serial_number, IPR_SERIAL_NUM_LEN)) continue; return vset2; } } return NULL; } /** * link_multipath_vsets - set the alt_path entries for multipath vsets * * Returns: * nothing **/ static void link_multipath_vsets() { struct ipr_ioa *ioa; struct ipr_dev *vset1, *vset2; for_each_ioa(ioa) { for_each_vset(ioa, vset1) { vset2 = find_multipath_vset(vset1); if (!vset2) continue; vset1->alt_path = vset2; vset2->alt_path = vset1; } } } /** * ipr_format_res_path - Format the resource path into a string. * @res_path: resource path * @buf: buffer * @len: buffer length * * Return value: * none **/ void ipr_format_res_path(u8 *res_path, char *buffer, int len) { int i; char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) p += snprintf(p, buffer + len - p, "-%02X", res_path[i]); } /** * ipr_format_res_path_wo_hyphen - Format the resource path into a string. * @res_path: resource path * @buf: buffer * @len: buffer length * * Return value: * none **/ void ipr_format_res_path_wo_hyphen(u8 *res_path, char *buffer, int len) { int i; char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) p += snprintf(p, buffer + len - p, "%02X", res_path[i]); } /** * ipr_res_path_cmp - compare two resource paths * @dev_res_path * @scsi_res_path * * Returns: * 1 if the paths are the same, 0 otherwise **/ int ipr_res_path_cmp(u8 *dev_res_path, char *scsi_res_path) { char buffer[IPR_MAX_RES_PATH_LEN]; ipr_format_res_path(dev_res_path, buffer, IPR_MAX_RES_PATH_LEN); return !strncmp(buffer, scsi_res_path, IPR_MAX_RES_PATH_LEN); } /** * ipr_debug_dump_rcd - dump out a device record * @rcd: record structure * * Returns: * nothing */ void ipr_debug_dump_rcd(struct ipr_common_record *rcd) { int i; u8 *rcd_ptr = (u8 *)rcd; dprintf("===========\n"); dprintf("Record ID = %d, Length = %d\n", rcd->record_id, rcd->record_len); for (i=0; irecord_len; i++) { dprintf("%02x", rcd_ptr[i]); if ((i+1)%8 == 0) dprintf(" "); if ((i+1)%32 == 0) dprintf("\n"); } dprintf("\n"); } /** * ipr_get_logical_block_size - check the logical block size * @dev: ipr dev struct * * Return value: * none **/ int ipr_get_logical_block_size(struct ipr_dev *dev) { char path[PATH_MAX], *first_hyphen; char buff[16]; ssize_t len; int rc; first_hyphen = strchr(dev->dev_name, 's'); sprintf(path, "/sys/block/%s/queue", first_hyphen); len = sysfs_read_attr(path, "logical_block_size", buff, 16); if (len < 0) { syslog_dbg("Failed to open logical_block_size parameter.\n"); return -1; } rc = atoi(buff); return rc; } /** * check_current_config - populates the ioa configuration data * @allow_rebuild_refresh: allow_rebuild_refresh flag * * Returns: * nothing **/ void check_current_config(bool allow_rebuild_refresh) { struct scsi_dev_data *scsi_dev_data; int num_sg_devices, rc, device_count, j, k; struct ipr_ioa *ioa; struct ipr_array_query_data *qac_data; struct ipr_common_record *common_record; struct ipr_dev_record *device_record; struct ipr_array_record *array_record; struct ipr_std_inq_data std_inq_data; struct sense_data_t sense_data; struct ipr_res_addr res_addr, *ra; struct ipr_dev *dev; int *qac_entry_ref; struct ipr_dev_identify_vpd di_vpd; char *pchr; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; if (ipr_qac_data == NULL) { ipr_qac_data = (struct ipr_array_query_data *) calloc(num_ioas, sizeof(struct ipr_array_query_data)); } /* Get sg data via sysfs */ num_sg_devices = get_scsi_dev_data(&scsi_dev_table); get_sg_names(num_sg_devices); get_sd_names(num_sg_devices); for(ioa = ipr_ioa_head, qac_data = ipr_qac_data; ioa; ioa = ioa->next, qac_data++) { get_ioa_name(ioa, num_sg_devices); ioa->num_devices = 0; rc = ipr_inquiry(&ioa->ioa, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc) ioa->ioa_dead = 1; get_ioa_cap(ioa); get_dual_ioa_state(ioa); get_subsys_config(ioa); /* Get Query Array Config Data */ rc = ipr_query_array_config(ioa, allow_rebuild_refresh, 0, 0, 0, qac_data); if (rc != 0) { qac_data->num_records = 0; qac_data->resp_len = qac_data->hdr_len; } ioa->qac_data = qac_data; ioa->start_array_qac_entry = NULL; device_count = 0; memset(ioa->dev, 0, IPR_MAX_IOA_DEVICES * sizeof(struct ipr_dev)); qac_entry_ref = calloc(1, sizeof(int) * qac_data->num_records); /* now assemble data pertaining to each individual device */ for (j = 0, scsi_dev_data = scsi_dev_table; j < num_sg_devices; j++, scsi_dev_data++) { if (scsi_dev_data->host != ioa->host_num) continue; if (scsi_dev_data->type == TYPE_DISK || scsi_dev_data->type == IPR_TYPE_AF_DISK || scsi_dev_data->type == IPR_TYPE_ADAPTER || scsi_dev_data->type == TYPE_ENCLOSURE || scsi_dev_data->type == TYPE_ROM || scsi_dev_data->type == TYPE_TAPE || scsi_dev_data->type == TYPE_PROCESSOR) { ioa->dev[device_count].ioa = ioa; ioa->dev[device_count].scsi_dev_data = scsi_dev_data; ioa->dev[device_count].qac_entry = NULL; strcpy(ioa->dev[device_count].dev_name, scsi_dev_data->dev_name); strcpy(ioa->dev[device_count].gen_name, scsi_dev_data->gen_name); if (ioa->support_4k && scsi_dev_data->type == TYPE_DISK) { if (ipr_get_logical_block_size(&ioa->dev[device_count]) == IPR_JBOD_4K_BLOCK_SIZE) ioa->dev[device_count].block_dev_class |= IPR_BLK_DEV_CLASS_4K; } /* find array config data matching resource entry */ k = 0; for_each_qac_entry(common_record, qac_data) { if (common_record->record_id == IPR_RECORD_ID_DEVICE_RECORD) { device_record = (struct ipr_dev_record *)common_record; if (device_record->type2.resource_addr.bus == scsi_dev_data->channel && device_record->type2.resource_addr.target == scsi_dev_data->id && device_record->type2.resource_addr.lun == scsi_dev_data->lun) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD) { array_record = (struct ipr_array_record *)common_record; if (array_record->type2.resource_addr.bus == scsi_dev_data->channel && array_record->type2.resource_addr.target == scsi_dev_data->id && array_record->type2.resource_addr.lun == scsi_dev_data->lun) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_DEVICE_RECORD_3) { device_record = (struct ipr_dev_record *)common_record; if (ipr_res_path_cmp(device_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_VSET_RECORD_3) { array_record = (struct ipr_array_record *)common_record; if (ipr_res_path_cmp(array_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD_3) { array_record = (struct ipr_array_record *)common_record; if (ipr_res_path_cmp(array_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } k++; } /* Send Test Unit Ready to start device if its a volume set */ /* xxx TODO try to remove this */ if (!ipr_fast && ipr_is_volume_set(&ioa->dev[device_count])) __ipr_test_unit_ready(&ioa->dev[device_count], &sense_data); device_count++; } else if (scsi_dev_data->type == IPR_TYPE_ADAPTER) { ioa->ioa.ioa = ioa; ioa->ioa.scsi_dev_data = scsi_dev_data; } } /* now scan qac device and array entries to see which ones have not been referenced */ k = 0; for_each_qac_entry(common_record, qac_data) { if (qac_entry_ref[k] > 1) syslog(LOG_ERR, "Query Array Config entry referenced more than once\n"); if (common_record->record_id == IPR_RECORD_ID_SUPPORTED_ARRAYS) { ioa->supported_arrays = (struct ipr_supported_arrays *)common_record; } else if (!qac_entry_ref[k] && (ipr_is_device_record(common_record->record_id) || ipr_is_vset_record(common_record->record_id))) { // TODO - type 3 array records???? array_record = (struct ipr_array_record *)common_record; if (ipr_is_vset_record(common_record->record_id) && array_record->start_cand) { ioa->start_array_qac_entry = array_record; } else { /* add phantom qac entry to ioa device list */ ioa->dev[device_count].scsi_dev_data = NULL; ioa->dev[device_count].qac_entry = common_record; ioa->dev[device_count].ioa = ioa; strcpy(ioa->dev[device_count].dev_name, ""); strcpy(ioa->dev[device_count].gen_name, ""); device_count++; } } k++; } ioa->num_devices = device_count; free(qac_entry_ref); } for_each_ioa(ioa) { for_each_dev(ioa, dev) { get_res_addr(dev, &res_addr); for_each_ra(ra, dev) memcpy(ra, &res_addr, sizeof(*ra)); if (!dev || !dev->qac_entry) continue; if (dev->qac_entry->record_id == IPR_RECORD_ID_DEVICE_RECORD) { dev->vendor_id = dev->dev_rcd->type2.vendor_id; dev->product_id = dev->dev_rcd->type2.product_id; dev->serial_number = dev->dev_rcd->type2.serial_number; dev->array_id = dev->dev_rcd->type2.array_id; dev->resource_handle = dev->dev_rcd->type2.resource_handle; dev->block_dev_class = dev->dev_rcd->type2.block_dev_class; if (dev->block_dev_class & IPR_SSD) dev->read_intensive = dev->dev_rcd->type2.read_intensive; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_DEVICE_RECORD_3) { dev->vendor_id = dev->dev_rcd->type3.vendor_id; dev->product_id = dev->dev_rcd->type3.product_id; dev->serial_number = dev->dev_rcd->type3.serial_number; dev->array_id = dev->dev_rcd->type3.array_id; dev->resource_handle = dev->dev_rcd->type3.resource_handle; dev->block_dev_class = dev->dev_rcd->type3.block_dev_class; if (dev->block_dev_class & IPR_SSD) dev->read_intensive = dev->dev_rcd->type3.read_intensive; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_ARRAY_RECORD) { dev->vendor_id = dev->array_rcd->type2.vendor_id; dev->product_id = dev->array_rcd->type2.product_id; dev->serial_number = dev->array_rcd->type2.serial_number; dev->array_id = dev->array_rcd->type2.array_id; dev->raid_level = dev->array_rcd->type2.raid_level; dev->stripe_size = dev->array_rcd->type2.stripe_size; dev->resource_handle = dev->array_rcd->type2.resource_handle; dev->block_dev_class = dev->array_rcd->type2.block_dev_class; if (dev->block_dev_class & IPR_SSD) dev->read_intensive = dev->dev_rcd->type2.read_intensive; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_VSET_RECORD_3) { dev->vendor_id = dev->array_rcd->type3.vendor_id; dev->product_id = dev->array_rcd->type3.product_id; dev->serial_number = dev->array_rcd->type3.serial_number; dev->array_id = dev->array_rcd->type3.array_id; dev->raid_level = dev->array_rcd->type3.raid_level; dev->stripe_size = dev->array_rcd->type3.stripe_size; dev->resource_handle = dev->array_rcd->type3.resource_handle; dev->block_dev_class = dev->array_rcd->type3.block_dev_class; if (dev->block_dev_class & IPR_SSD) dev->read_intensive = dev->dev_rcd->type3.read_intensive; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_ARRAY_RECORD_3) { dev->vendor_id = dev->array_rcd->type3.vendor_id; dev->product_id = dev->array_rcd->type3.product_id; dev->serial_number = dev->array_rcd->type3.serial_number; dev->array_id = dev->array_rcd->type3.array_id; dev->raid_level = dev->array_rcd->type3.raid_level; dev->stripe_size = dev->array_rcd->type3.stripe_size; dev->resource_handle = dev->array_rcd->type3.resource_handle; dev->block_dev_class = dev->array_rcd->type3.block_dev_class; if (dev->block_dev_class & IPR_SSD) dev->read_intensive = dev->dev_rcd->type3.read_intensive; } } get_prot_levels(ioa); } for_each_ioa(ioa) { if (strlen((char *)ioa->yl_serial_num) == 0) { memset(&di_vpd, 0, sizeof(di_vpd)); rc = ipr_inquiry(&ioa->ioa, 0x83, &di_vpd, sizeof(di_vpd)); if (!rc && ntohs(di_vpd.add_page_len) > 120) { pchr = strstr((char *)&di_vpd.dev_identify_contxt[0],"SN"); if (pchr) strncpy((char *)ioa->yl_serial_num, (pchr + 3), YL_SERIAL_NUM_LEN); } } } set_devs_format_completed(); link_multipath_vsets(); ipr_cleanup_zeroed_devs(); resolve_old_config(); if (!first_time_check_zeroed_dev) { for_each_ioa(ioa) { for_each_dev(ioa, dev) { if (ipr_is_af_dasd_device(dev)) { memset(&mode_pages, 0, sizeof(mode_pages)); ipr_mode_sense(dev, 0x20, &mode_pages); page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (page->format_completed) ipr_add_zeroed_dev(dev); } } } first_time_check_zeroed_dev = 1; } } /** * num_devices_opens - return usage count (number of opens) for a given device * @host_num: host number * @channel: channel number * @id: id number * @lun: lun number * * Returns: * usage count (number of opens) for a given device **/ /* xxx TODO delete */ int num_device_opens(int host_num, int channel, int id, int lun) { struct scsi_dev_data *scsi_dev_base = NULL; int opens = 0; int k, num_sg_devices; /* Get sg data via sg proc file system */ num_sg_devices = get_scsi_dev_data(&scsi_dev_base); /* find usage counts in scsi_dev_data */ for (k = 0; k < num_sg_devices; k++) { if ((host_num == scsi_dev_base[k].host) && (channel == scsi_dev_base[k].channel) && (id == scsi_dev_base[k].id) && (lun == scsi_dev_base[k].lun)) { opens = scsi_dev_base[k].opens; break; } } free(scsi_dev_base); return opens; } /** * open_and_lock - Open and device file and lock it. * @file_name: the name of the device to open * * Returns: * file descriptor if success / -1 on failure **/ int open_and_lock(char *file_name) { int fd; int rc; fd = open(file_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", file_name); return fd; } rc = flock(fd, LOCK_EX); if (rc) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not lock %s. %m\n", file_name); close(fd); return rc; } return fd; } /** * exit_on_error - exits program or error after cleaning up * @s: string * @...: arguments * * Returns: * nothing - exits program on error **/ void exit_on_error(char *s, ...) { va_list args; char usr_str[256]; exit_func(); va_start(args, s); vsprintf(usr_str, s, args); va_end(args); closelog(); openlog(tool_name, LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR,"%s",usr_str); closelog(); exit(1); } /** * ipr_config_file_hdr - prints the header to the ipr config file * @file_name: file name * * Returns: * nothing **/ static void ipr_config_file_hdr(char *file_name) { FILE *fd; char cmd_str[64]; if (strlen(file_name) == 0) return; /* check if file currently exists */ fd = fopen(file_name, "r"); if (fd) { fclose(fd); return; } /* be sure directory is present */ sprintf(cmd_str,"install -d %s",IPR_CONFIG_DIR); system(cmd_str); /* otherwise, create new file */ fd = fopen(file_name, "w"); if (fd == NULL) { syslog(LOG_ERR, "Could not open %s. %m\n", file_name); return; } fprintf(fd,"# DO NOT EDIT! Software generated configuration file for\n"); fprintf(fd,"# ipr SCSI device subsystem\n\n"); fprintf(fd,"# Use iprconfig to configure\n"); fprintf(fd,"# See \"man iprconfig\" for more information\n\n"); fclose(fd); } /** * ipr_save_attr - * @ioa: ipr ioa struct * @category: * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_attr(struct ipr_ioa *ioa, char *category, char *field, char *value, int update) { char fname[64]; FILE *fd, *temp_fd; char temp_fname[64], line[64]; int bus_found = 0; int field_found = 0; sprintf(fname, "%s%x_%s", IPR_CONFIG_DIR, ioa->ccin, ioa->pci_address); ipr_config_file_hdr(fname); fd = fopen(fname, "r"); if (fd == NULL) return; sprintf(temp_fname, "%s.1", fname); temp_fd = fopen(temp_fname, "w"); if (temp_fd == NULL) { syslog(LOG_ERR, "Could not open %s. %m\n", temp_fname); return; } while (fgets(line, 64, fd)) { if (strstr(line, category) && line[0] != '#') { bus_found = 1; } else { if (bus_found) { if (line[0] == '[') { bus_found = 0; if (!field_found) { if (!update) fprintf(temp_fd, "# "); fprintf(temp_fd,"%s %s\n",field,value); } } if (strstr(line, field)) { if (update) sprintf(line,"%s %s\n",field,value); field_found = 1; } } } fputs(line, temp_fd); } if (!field_found) { if (!bus_found) fprintf(temp_fd,"\n%s\n", category); if (!update) fprintf(temp_fd, "# "); fprintf(temp_fd,"%s %s\n", field, value); } if (rename(temp_fname, fname)) syslog(LOG_ERR, "Could not save %s.\n", fname); fclose(fd); fclose(temp_fd); } /** * ipr_save_bus_attr - * @ioa: ipr ioa struct * @bus: bus * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_bus_attr(struct ipr_ioa *ioa, int bus, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s %d]", IPR_CATEGORY_BUS, bus); ipr_save_attr(ioa, category, field, value, update); } /** * ipr_save_dev_attr - * @dev: ipr dev struct * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_dev_attr(struct ipr_dev *dev, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s %d:%d:%d]", IPR_CATEGORY_DISK, dev->scsi_dev_data->channel, dev->scsi_dev_data->id, dev->scsi_dev_data->lun); ipr_save_attr(dev->ioa, category, field, value, update); } /** * ipr_save_ioa_attr - * @ioa: ipr ioa struct * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_ioa_attr(struct ipr_ioa *ioa, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s]", IPR_CATEGORY_IOA); ipr_save_attr(ioa, category, field, value, update); } /** * ipr_get_saved_attr - * @ioa: ipr ioa struct * @category: * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_attr(struct ipr_ioa *ioa, char *category, char *field, char *value) { FILE *fd; char fname[64], line[64], *str_ptr; int bus_found = 0; sprintf(fname, "%s%x_%s", IPR_CONFIG_DIR, ioa->ccin, ioa->pci_address); fd = fopen(fname, "r"); if (fd == NULL) return RC_FAILED; while (NULL != fgets(line, 64, fd)) { if (line[0] != '#') { if (strstr(line, category)) bus_found = 1; else if (bus_found) { if (line[0] == '[') bus_found = 0; if ((str_ptr = strstr(line, field))) { str_ptr += strlen(field); while (str_ptr[0] == ' ') str_ptr++; sscanf(str_ptr, "%s\n", value); fclose(fd); return RC_SUCCESS; } } } } fclose(fd); return RC_FAILED; } /** * ipr_get_saved_bus_attr - * @ioa: ipr ioa struct * @bus: * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_bus_attr(struct ipr_ioa *ioa, int bus, char *field, char *value) { char category[16]; sprintf(category,"[%s %d]", IPR_CATEGORY_BUS, bus); return ipr_get_saved_attr(ioa, category, field, value); } /** * ipr_get_saved_dev_attr - * @dev: ipr dev struct * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_dev_attr(struct ipr_dev *dev, char *field, char *value) { char category[30]; if (!dev->scsi_dev_data) return -ENXIO; sprintf(category,"[%s %d:%d:%d]", IPR_CATEGORY_DISK, dev->scsi_dev_data->channel, dev->scsi_dev_data->id, dev->scsi_dev_data->lun); return ipr_get_saved_attr(dev->ioa, category, field, value); } /** * ipr_get_saved_ioa_attr - * @ioa: ipr ioa struct * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_ioa_attr(struct ipr_ioa *ioa, char *field, char *value) { char category[16]; sprintf(category,"[%s]", IPR_CATEGORY_IOA); return ipr_get_saved_attr(ioa, category, field, value); } #define GSCSI_TCQ_DEPTH 3 #define GSCSI_SAS_TCQ_DEPTH 16 #define AS400_TCQ_DEPTH 16 #define DEFAULT_TCQ_DEPTH 64 /** * get_tcq_depth - return the proper queue depth for the given device * @dev: ipr dev struct * * Returns: * GSCSI_TCQ_DEPTH, GSCSI_SAS_TCQ_DEPTH, AS400_TCQ_DEPTH or DEFAULT_TCQ_DEPTH **/ static int get_tcq_depth(struct ipr_dev *dev) { if (ipr_is_gscsi(dev)) { if (ioa_is_spi(dev->ioa)) return GSCSI_TCQ_DEPTH; else return GSCSI_SAS_TCQ_DEPTH; } if (!dev->scsi_dev_data) return AS400_TCQ_DEPTH; if (!strncmp(dev->scsi_dev_data->vendor_id, "IBMAS400", 8)) return AS400_TCQ_DEPTH; return DEFAULT_TCQ_DEPTH; } /** * is_tagged - * @dev: ipr dev struct * * Returns: * **/ static int is_tagged(struct ipr_dev *dev) { char temp[100]; if (!ipr_read_dev_attr(dev, "tcq_enable", temp, 100)) return strtoul(temp, NULL, 10); else if (!ipr_read_dev_attr(dev, "queue_type", temp, 100)) return (strstr(temp, "none") ? 0 : 1); return 0; } /** * set_tagged - * @dev: ipr dev struct * @tcq_enabled: * * Returns: * 0 if success / non-zero on failure **/ static int set_tagged(struct ipr_dev *dev, int tcq_enabled) { char temp[100]; if (!ipr_read_dev_attr(dev, "tcq_enable", temp, 100)) { sprintf(temp, "%d", tcq_enabled); return ipr_write_dev_attr(dev, "tcq_enable", temp); } if (!ipr_read_dev_attr(dev, "queue_type", temp, 100)) { if (!tcq_enabled) return ipr_write_dev_attr(dev, "queue_type", "none"); if (page0x0a_setup(dev)) return ipr_write_dev_attr(dev, "queue_type", "ordered"); else return ipr_write_dev_attr(dev, "queue_type", "simple"); } return -EIO; } /** * get_format_timeout - * @dev: ipr dev struct * * Returns: * timeout value **/ static int get_format_timeout(struct ipr_dev *dev) { struct ipr_query_dasd_timeouts tos; int rc, i, records, timeout; char temp[100]; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_dasd_timeouts(dev, &tos); if (!rc) { records = (ntohl(tos.length) - sizeof(tos.length)) / sizeof(tos.record[0]); for (i = 0; i < records; i++) { if (tos.record[i].op_code != FORMAT_UNIT) continue; if (IPR_TIMEOUT_RADIX_IS_MINUTE(tos.record[i].timeout)) return ((tos.record[i].timeout & IPR_TIMEOUT_MASK) * 60); if (IPR_TIMEOUT_RADIX_IS_SECONDS(tos.record[i].timeout)) return tos.record[i].timeout & IPR_TIMEOUT_MASK; scsi_dbg(dev, "Unknown timeout radix: %X\n", (tos.record[i].timeout & IPR_TIMEOUT_RADIX_MASK)); break; } } } timeout = IPR_FORMAT_UNIT_TIMEOUT; rc = ipr_get_saved_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &timeout); return timeout; } static const struct ipr_dasd_timeout_record ipr_dasd_timeouts[] = { {READ_10, 0, __constant_cpu_to_be16(30)}, {WRITE_10, 0, __constant_cpu_to_be16(30)}, {WRITE_VERIFY, 0, __constant_cpu_to_be16(30)}, {SKIP_READ, 0, __constant_cpu_to_be16(30)}, {SKIP_WRITE, 0, __constant_cpu_to_be16(30)} }; struct ipr_dasd_timeouts { u32 length; struct ipr_dasd_timeout_record record[ARRAY_SIZE(ipr_dasd_timeouts) + 1]; }; /** * ipr_set_dasd_timeouts - * @dev: ipr dev struct * @format_timeout: * * Returns: * 0 if success / non-zero on failure **/ static int ipr_set_dasd_timeouts(struct ipr_dev *dev, int format_timeout) { int fd, rc, len; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_dasd_timeouts timeouts; struct ipr_disk_attr attr; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memcpy(timeouts.record, ipr_dasd_timeouts, sizeof(ipr_dasd_timeouts)); len = sizeof(timeouts) - sizeof(timeouts.record[0]); if (!ipr_get_dev_attr(dev, &attr)) { len = sizeof(timeouts); if (!format_timeout) format_timeout = attr.format_timeout; timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].op_code = FORMAT_UNIT; if (format_timeout >= IPR_TIMEOUT_MASK) { timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].timeout = (format_timeout / 60) | IPR_TIMEOUT_MINUTE_RADIX; } else { timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].timeout = format_timeout; } } timeouts.length = htonl(len); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SET_DASD_TIMEOUTS; cdb[7] = (len >> 8) & 0xff; cdb[8] = len & 0xff; rc = sg_ioctl(fd, cdb, &timeouts, len, SG_DXFER_TO_DEV, &sense_data, SET_DASD_TIMEOUTS_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Set DASD timeouts", rc); close(fd); return rc; } /** * ipr_get_dev_attr - * @ioa: ipr ioa struct * @attr: ipr_disk_attr struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr) { char temp[100]; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; if (ipr_read_dev_attr(dev, "queue_depth", temp, 100)) return -EIO; if (ipr_is_af_dasd_device(dev)) { memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); attr->queue_depth = page->max_tcq_depth; } else attr->queue_depth = strtoul(temp, NULL, 10); if (ipr_is_af_dasd_device(dev)) { if (attr->queue_depth < 2) attr->tcq_enabled = 0; else attr->tcq_enabled = 1; } else attr->tcq_enabled = is_tagged(dev); attr->format_timeout = get_format_timeout(dev); return 0; } int ipr_set_format_completed_bit(struct ipr_dev *dev) { int len; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); page->format_completed = 1; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (ipr_mode_select(dev, &mode_pages, len)) return -EIO; return 0; } /** * get_ioa_caching - * @ioa: ipr ioa struct * * Returns: * 0 **/ int get_ioa_caching(struct ipr_ioa *ioa) { int rc; int found = 0; struct ipr_query_ioa_caching_info info; struct ipr_global_cache_params_term *term; int term_size = sizeof(struct ipr_query_ioa_caching_info); memset(&info, 0, term_size); rc = ipr_query_cache_parameters(ioa, &info, term_size); if (rc) return IPR_IOA_REQUESTED_CACHING_DEFAULT; for_each_cache_term(term, &info) if (term && term->term_id == IPR_CACHE_PARAM_TERM_ID) { found = 1; break; } if (found == 1) if (term->enable_caching_dual_ioa_failure == IPR_IOA_CACHING_DUAL_FAILURE_ENABLED) if (term->disable_caching_requested == IPR_IOA_REQUESTED_CACHING_DISABLED) return IPR_IOA_CACHING_DISABLED_DUAL_ENABLED; else return IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED; else if (term->disable_caching_requested == IPR_IOA_REQUESTED_CACHING_DISABLED) return IPR_IOA_REQUESTED_CACHING_DISABLED; else return IPR_IOA_REQUESTED_CACHING_DEFAULT; else return IPR_IOA_REQUESTED_CACHING_DEFAULT; } /** * ipr_get_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * * Returns: * 0 **/ int ipr_get_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr) { attr->preferred_primary = 0; attr->gscsi_only_ha = ioa->in_gscsi_only_ha; attr->active_active = ioa->asymmetric_access_enabled; attr->caching = get_ioa_caching(ioa); if (!ioa->dual_raid_support) return 0; if (ioa->ioa_status.cap.preferred_role == IPR_IOA_STATE_PRIMARY) attr->preferred_primary = 1; return 0; } /** * ipr_modify_dev_attr - * @dev: ipr dev struct * @attr: ipr_disk_attr struct * * Returns: * 0 **/ int ipr_modify_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr) { char temp[100]; int rc; rc = ipr_get_saved_dev_attr(dev, IPR_QUEUE_DEPTH, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->queue_depth); rc = ipr_get_saved_dev_attr(dev, IPR_TCQ_ENABLED, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->tcq_enabled); rc = ipr_get_saved_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->format_timeout); return 0; } /** * ipr_modify_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * * Returns: * 0 **/ int ipr_modify_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr) { char temp[100]; int rc; rc = ipr_get_saved_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->gscsi_only_ha); rc = ipr_get_saved_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->active_active); return 0; } /** * ipr_set_dev_attr - * @dev: ipr dev struct * @attr: ipr_disk_attr struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr, int save) { struct ipr_disk_attr old_attr; char temp[100]; if (ipr_get_dev_attr(dev, &old_attr)) return -EIO; if (attr->queue_depth != old_attr.queue_depth) { sprintf(temp, "%d", attr->queue_depth); if (ipr_is_af_dasd_device(dev)) { if (ipr_setup_af_qdepth(dev, attr->queue_depth)) return -EIO; } else { if (ipr_write_dev_attr(dev, "queue_depth", temp)) return -EIO; } if (save) ipr_save_dev_attr(dev, IPR_QUEUE_DEPTH, temp, 1); } if (attr->format_timeout != old_attr.format_timeout) { if (ipr_is_af_dasd_device(dev)) { sprintf(temp, "%d", attr->format_timeout); if (ipr_set_dasd_timeouts(dev, attr->format_timeout)) return -EIO; if (save) ipr_save_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp, 1); } } if (attr->tcq_enabled != old_attr.tcq_enabled) { if (!ipr_is_af_dasd_device(dev)) { if (set_tagged(dev, attr->tcq_enabled)) return -EIO; sprintf(temp, "%d", attr->tcq_enabled); if (save) ipr_save_dev_attr(dev, IPR_TCQ_ENABLED, temp, 1); } } return 0; } /** * ipr_set_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr, int save) { struct ipr_ioa_attr old_attr; char temp[100]; int mode; if (ipr_get_ioa_attr(ioa, &old_attr)) return -EIO; /* FIXME - preferred_primary and active_active may change at the same * time. This code may need to change. */ if (attr->preferred_primary != old_attr.preferred_primary) if (ipr_change_multi_adapter_assignment(ioa, attr->preferred_primary, IPR_PRESERVE_ASYMMETRIC_STATE)) return -EIO; if (attr->gscsi_only_ha != old_attr.gscsi_only_ha) { sprintf(temp, "%d", attr->gscsi_only_ha); if (ipr_set_ha_mode(ioa, attr->gscsi_only_ha)) return -EIO; if (save) ipr_save_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp, 1); ipr_reset_adapter(ioa); } if (attr->active_active != old_attr.active_active && ioa->asymmetric_access) { sprintf(temp, "%d", attr->active_active); /* If setting active/active, use mode page 24. * If clearing, reset the adapter and then use * Change Multi Adapter Assignment. */ if (attr->active_active) { if (ipr_set_active_active_mode(ioa)) return -EIO; if (save) ipr_save_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp, 1); } else { if (save) ipr_save_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp, 1); ipr_reset_adapter(ioa); if (ipr_change_multi_adapter_assignment(ioa, attr->preferred_primary, attr->active_active)) return -EIO; } } if (attr->caching != old_attr.caching) { if (attr->caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) mode = IPR_IOA_SET_CACHING_DEFAULT; else mode = IPR_IOA_SET_CACHING_DISABLED; ipr_change_cache_parameters(ioa, mode); } get_dual_ioa_state(ioa); /* for preferred_primary */ get_subsys_config(ioa); /* for gscsi_only_ha */ return 0; } /** * ipr_query_dasd_timeouts - * @dev: ipr dev struct * @timeouts: ipr_query_dasd_timesouts struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_dasd_timeouts(struct ipr_dev *dev, struct ipr_query_dasd_timeouts *timeouts) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(timeouts, 0, sizeof(*timeouts)); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = QUERY_DASD_TIMEOUTS; cdb[7] = (sizeof(*timeouts) >> 8) & 0xff; cdb[8] = sizeof(*timeouts) & 0xff; rc = sg_ioctl(fd, cdb, timeouts, sizeof(*timeouts), SG_DXFER_FROM_DEV, &sense_data, SET_DASD_TIMEOUTS_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query DASD timeouts", rc); close(fd); return rc; } /** * ipr_get_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus) { struct ipr_mode_pages mode_pages; struct ipr_mode_page_28 *page_28; struct ipr_mode_page_28_scsi_dev_bus_attr *bus; int rc, i, busno; memset(&mode_pages, 0, sizeof(mode_pages)); memset(sbus, 0, sizeof(*sbus)); rc = ipr_mode_sense(&ioa->ioa, 0x28, &mode_pages); if (rc) return rc; page_28 = (struct ipr_mode_page_28 *)(mode_pages.data + mode_pages.hdr.block_desc_len); for_each_bus_attr(bus, page_28, i) { busno = bus->res_addr.bus; sbus->bus[busno].max_xfer_rate = ntohl(bus->max_xfer_rate); sbus->bus[busno].qas_capability = bus->qas_capability; sbus->bus[busno].scsi_id = bus->scsi_id; sbus->bus[busno].bus_width = bus->bus_width; sbus->bus[busno].extended_reset_delay = bus->extended_reset_delay; sbus->bus[busno].min_time_delay = bus->min_time_delay; sbus->num_buses++; } return 0; } /** * ipr_set_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus, int save) { struct ipr_mode_pages mode_pages; struct ipr_mode_page_28 *page_28; struct ipr_mode_page_28_scsi_dev_bus_attr *bus; struct ipr_scsi_buses old_settings; int rc, i, busno, len; int reset_needed = 0; char value_str[64]; memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x28, &mode_pages); if (rc) return rc; rc = ipr_get_bus_attr(ioa, &old_settings); if (rc) return rc; page_28 = (struct ipr_mode_page_28 *) (mode_pages.data + mode_pages.hdr.block_desc_len); for_each_bus_attr(bus, page_28, i) { busno = bus->res_addr.bus; bus->bus_width = sbus->bus[busno].bus_width; if (save && bus->bus_width != old_settings.bus[busno].bus_width) { sprintf(value_str, "%d", bus->bus_width); ipr_save_bus_attr(ioa, i, IPR_BUS_WIDTH, value_str, 1); } bus->max_xfer_rate = htonl(sbus->bus[busno].max_xfer_rate); if (save && bus->max_xfer_rate != old_settings.bus[busno].max_xfer_rate) { sprintf(value_str, "%d", IPR_BUS_XFER_RATE_TO_THRUPUT(sbus->bus[busno].max_xfer_rate, bus->bus_width)); ipr_save_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str, 1); } bus->qas_capability = sbus->bus[busno].qas_capability; if (save && bus->qas_capability != old_settings.bus[busno].qas_capability) { sprintf(value_str, "%d", bus->qas_capability); ipr_save_bus_attr(ioa, i, IPR_QAS_CAPABILITY, value_str, 1); } if (bus->scsi_id != sbus->bus[busno].scsi_id) reset_needed = 1; bus->scsi_id = sbus->bus[busno].scsi_id; if (save && bus->scsi_id != old_settings.bus[busno].scsi_id) { sprintf(value_str, "%d", bus->scsi_id); ipr_save_bus_attr(ioa, i, IPR_HOST_SCSI_ID, value_str, 1); } bus->min_time_delay = sbus->bus[busno].min_time_delay; if (save && bus->min_time_delay != old_settings.bus[busno].min_time_delay) { sprintf(value_str, "%d", bus->min_time_delay); ipr_save_bus_attr(ioa, i, IPR_MIN_TIME_DELAY, value_str, 1); } bus->extended_reset_delay = sbus->bus[busno].extended_reset_delay; } len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; rc = ipr_mode_select(&ioa->ioa, &mode_pages, len); if (reset_needed) ipr_reset_adapter(ioa); return rc; } /** * ipr_modify_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * * Returns: * nothing **/ void ipr_modify_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus) { int i, rc, saved_value, max_xfer_rate; char value_str[64]; for (i = 0; i < sbus->num_buses; i++) { rc = ipr_get_saved_bus_attr(ioa, i, IPR_QAS_CAPABILITY, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].qas_capability = saved_value; } if (ioa->scsi_id_changeable) { rc = ipr_get_saved_bus_attr(ioa, i, IPR_HOST_SCSI_ID, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].scsi_id = saved_value; } } rc = ipr_get_saved_bus_attr(ioa, i, IPR_BUS_WIDTH, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].bus_width = saved_value; } max_xfer_rate = get_max_bus_speed(ioa, i); rc = ipr_get_saved_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); if (saved_value <= max_xfer_rate) { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(saved_value, sbus->bus[i].bus_width); } else { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(max_xfer_rate, sbus->bus[i].bus_width); sprintf(value_str, "%d", max_xfer_rate); ipr_save_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str, 1); } } else { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(max_xfer_rate, sbus->bus[i].bus_width); } rc = ipr_get_saved_bus_attr(ioa, i, IPR_MIN_TIME_DELAY, value_str); if (rc == RC_SUCCESS) { sscanf(value_str,"%d", &saved_value); sbus->bus[i].min_time_delay = saved_value; } else { sbus->bus[i].min_time_delay = IPR_INIT_SPINUP_DELAY; } } } /** * get_unsupp_af - * @ing: ipr_std_ing_data struct * @page3: ipr_dasd_inquiry_page3 struct * * Returns: * unsupported_af_dasd struct **/ struct unsupported_af_dasd * get_unsupp_af(struct ipr_std_inq_data *inq, struct ipr_dasd_inquiry_page3 *page3) { int i, j; for (i = 0; i < ARRAY_SIZE(unsupported_af); i++) { for (j = 0; j < IPR_VENDOR_ID_LEN; j++) { if (unsupported_af[i].compare_vendor_id_byte[j] && unsupported_af[i].vendor_id[j] != inq->vpids.vendor_id[j]) break; } if (j != IPR_VENDOR_ID_LEN) continue; for (j = 0; j < IPR_PROD_ID_LEN; j++) { if (unsupported_af[i].compare_product_id_byte[j] && unsupported_af[i].product_id[j] != inq->vpids.product_id[j]) break; } if (j != IPR_PROD_ID_LEN) continue; for (j = 0; j < 4; j++) { if (unsupported_af[i].lid_mask[j] && unsupported_af[i].lid[j] != page3->load_id[j]) break; } if (j != 4) continue; return &unsupported_af[i]; } return NULL; } /** * disk_needs_msl - * @unsupp_af: unsupported_af_dasd struct * @inq: ipr_std_inq_data struct * * Returns: * true if needs msl / false otherwise **/ bool disk_needs_msl(struct unsupported_af_dasd *unsupp_af, struct ipr_std_inq_data *inq) { u32 ros_rcv_ram_rsvd, min_ucode_level; int j; if (unsupp_af->supported_with_min_ucode_level) { min_ucode_level = 0; ros_rcv_ram_rsvd = 0; for (j = 0; j < 4; j++) { if (unsupp_af->min_ucode_mask[j]) { min_ucode_level = (min_ucode_level << 8) | unsupp_af->min_ucode_level[j]; ros_rcv_ram_rsvd = (ros_rcv_ram_rsvd << 8) | inq->ros_rsvd_ram_rsvd[j]; } } if (min_ucode_level > ros_rcv_ram_rsvd) return true; } return false; } /** * is_af_blocked - * @dev: ipr dev struct * @silent: * * Returns: * true if blocked / false otherwise **/ bool is_af_blocked(struct ipr_dev *dev, int silent) { int rc; struct ipr_std_inq_data std_inq_data; struct ipr_dasd_inquiry_page3 dasd_page3_inq; struct unsupported_af_dasd *unsupp_af; /* Zero out inquiry data */ memset(&std_inq_data, 0, sizeof(std_inq_data)); rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc != 0) return false; /* Issue page 3 inquiry */ memset(&dasd_page3_inq, 0, sizeof(dasd_page3_inq)); rc = ipr_inquiry(dev, 0x03, &dasd_page3_inq, sizeof(dasd_page3_inq)); if (rc != 0) return false; unsupp_af = get_unsupp_af(&std_inq_data, &dasd_page3_inq); if (!unsupp_af) return false; /* If we make it this far, we have a match into the table. Now, determine if we need a certain level of microcode or if this disk is not supported all together. */ if (unsupp_af->supported_with_min_ucode_level) { if (disk_needs_msl(unsupp_af, &std_inq_data)) { if (ipr_force) { if (!silent) scsi_err(dev, "Disk %s needs updated microcode " "before transitioning to 522 bytes/sector " "format. IGNORING SINCE --force USED!", dev->gen_name); return false; } else { if (!silent) scsi_err(dev, "Disk %s needs updated microcode " "before transitioning to 522 bytes/sector " "format.", dev->gen_name); return true; } } } else {/* disk is not supported at all */ if (!silent) syslog(LOG_ERR,"Disk %s canot be formatted to " "522 bytes/sector.", dev->gen_name); return true; } return false; } /** * ipr_read_dev_attr - * @dev: ipr dev struct * @attr: * @value: * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_dev_attr(struct ipr_dev *dev, char *attr, char *value, size_t value_len) { char *sysfs_dev_name; char devpath[PATH_MAX]; ssize_t len; if (!dev->scsi_dev_data) { scsi_dbg(dev, "Cannot read dev attr %s. NULL scsi data\n", attr); return -ENOENT; } sysfs_dev_name = dev->scsi_dev_data->sysfs_device_name; sprintf(devpath, "/sys/class/scsi_device/%s/device", sysfs_dev_name); len = sysfs_read_attr(devpath, attr, value, value_len); if (len < 0) { scsi_dbg(dev, "Failed to read %s attribute. %m\n", attr); return -EIO; } return 0; } /** * ipr_write_dev_attr - * @dev: ipr dev struct * @attr: * @value: * * Returns: * 0 if success / non-zero on failure **/ int ipr_write_dev_attr(struct ipr_dev *dev, char *attr, char *value) { char *sysfs_dev_name; char devpath[PATH_MAX]; if (!dev->scsi_dev_data) return -ENOENT; sysfs_dev_name = dev->scsi_dev_data->sysfs_device_name; sprintf(devpath, "/sys/class/scsi_device/%s/device", sysfs_dev_name); if (sysfs_write_attr(devpath, attr, value, strlen(value)) < 0) { scsi_dbg(dev, "Failed to write attribute: %s\n", attr); return -EIO; } return 0; } /** * get_ucode_date - * @ucode_file: microcode file name * @ucode_date: microcode date string pointer * @max_size: max size for the date field * * Returns: * nothing **/ void get_ucode_date(char *ucode_file, char *ucode_date, int max_size) { struct stat st; struct tm *file_tm; ucode_date[0] = '\0'; if (stat(ucode_file, &st)) return; file_tm = localtime(&st.st_mtime); if (!file_tm) return; strftime(ucode_date, max_size, "%D", file_tm); } /** * get_ioa_ucode_version - * @ucode_file: microcode file name * * Returns: * 0 if success / non-zero on failure **/ u32 get_ioa_ucode_version(char *ucode_file) { int fd, rc; struct stat ucode_stats; struct ipr_ioa_ucode_header *image_hdr; fd = open(ucode_file, O_RDONLY); if (fd == -1) return 0; rc = fstat(fd, &ucode_stats); if (rc != 0) { close(fd); return 0; } image_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image_hdr == MAP_FAILED) { close(fd); return 0; } rc = ntohl(image_hdr->rev_level); munmap(image_hdr, ucode_stats.st_size); close(fd); return rc; } /** * fw_compare - compare two firmware images * @parm1: pointer to firmware image * @parm2: pointer to firmware image * * Returns: * 0 if the images are the same / non-zero otherwise **/ /* Sort in decending order */ static int fw_compare(const void *parm1, const void *parm2) { struct ipr_fw_images *first = (struct ipr_fw_images *)parm1; struct ipr_fw_images *second = (struct ipr_fw_images *)parm2; return memcmp(&second->version, &first->version, sizeof(second->version)); } /** * ipr_get_hotplug_dir - * * Returns: * 0 if success / non-zero on failure **/ static int ipr_get_hotplug_dir() { FILE *file; char buf[100]; char *loc, *end; file = fopen(FIRMWARE_HOTPLUG_CONFIG_FILE, "r"); if (!file) { hotplug_dir = realloc(hotplug_dir, strlen(FIRMWARE_HOTPLUG_DEFAULT_DIR) + 1); if (!hotplug_dir) return -ENOMEM; strcpy(hotplug_dir, FIRMWARE_HOTPLUG_DEFAULT_DIR); return 0; } clearerr(file); do { if (feof(file)) { syslog(LOG_ERR, "Failed parsing %s. Reached end of file.\n", FIRMWARE_HOTPLUG_CONFIG_FILE); return -EIO; } fgets(buf, 100, file); loc = strstr(buf, FIRMWARE_HOTPLUG_DIR_TAG); } while(!loc || buf[0] == '#'); loc = strchr(buf, '/'); fclose(file); if (!loc) { syslog(LOG_ERR, "Failed parsing %s.\n", FIRMWARE_HOTPLUG_CONFIG_FILE); return -EIO; } end = strchr(loc, ' '); if (!end) end = strchr(loc, '"'); if (end) *end = '\0'; hotplug_dir = realloc(hotplug_dir, strlen(loc) + 1); if (!hotplug_dir) return -ENOMEM; strcpy(hotplug_dir, loc); end = strchr(hotplug_dir, '\n'); if (end) *end = '\0'; return 0; } /** * get_dasd_ucode_version - * @ucode_file: file name of microcode file * @has_hdr: has header flag * * Returns: * 0 if success / non-zero on failure **/ u32 get_dasd_ucode_version(char *ucode_file, int has_hdr) { int fd; unsigned int len; struct stat ucode_stats; struct ipr_dasd_ucode_header *hdr; char *tmp; u32 rc; if (has_hdr) { fd = open(ucode_file, O_RDONLY); if (fd == -1) return 0; rc = fstat(fd, &ucode_stats); if (rc != 0) { fprintf(stderr, "Failed to stat %s\n", ucode_file); close(fd); return 0; } hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (hdr == MAP_FAILED) { fprintf(stderr, "mmap of %s failed\n", ucode_file); close(fd); return 0; } len = (hdr->length[0] << 16) | (hdr->length[1] << 8) | hdr->length[2]; if (len == ucode_stats.st_size) { rc = (hdr->modification_level[0] << 24) | (hdr->modification_level[1] << 16) | (hdr->modification_level[2] << 8) | hdr->modification_level[3]; munmap(hdr, ucode_stats.st_size); close(fd); return rc; } else { munmap(hdr, ucode_stats.st_size); close(fd); } } tmp = strrchr(ucode_file, '.'); if (!tmp) return 0; rc = strtoul(tmp+1, NULL, 16); return rc; } /** * get_ses_ucode_version - * @ucode_file: * * Returns: * ses microcode version / 0 if failure **/ u32 get_ses_ucode_version(char *ucode_file) { char *tmp = strrchr(ucode_file, '.'); if (!tmp) return 0; if (strlen(tmp+1) < 4) return 0; return (tmp[1] << 24) | (tmp[2] << 16) | (tmp[3] << 8) | tmp[4]; } /** * get_dev_fw_version - * @dev: ipr dev struct * * Returns: * device firmware version / 0 if failure **/ u32 get_dev_fw_version(struct ipr_dev *dev) { u8 release_level[4]; int rc; rc = ipr_get_fw_version(dev, release_level); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return 0; } rc = release_level[0] << 24 | release_level[1] << 16 | release_level[2] << 8 | release_level[3]; return rc; } /** * get_ioa_fw_version - * @ioa: ipr ioa struct * * Returns: * ioa firmware version **/ u32 get_ioa_fw_version(struct ipr_ioa *ioa) { char devpath[PATH_MAX]; char value[16]; ssize_t len; u32 fw_version; sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_read_attr(devpath, "fw_version", value, 16); if (len < 0) return -1; sscanf(value, "%8X", &fw_version); return fw_version; } /** * get_ioa_image_type - * @ioa: ipr ioa struct * * Returns: * ioa image type **/ static u8 get_ioa_image_type(struct ipr_ioa *ioa) { u32 fw_version = get_ioa_fw_version(ioa); return ((fw_version & 0x00ff0000) >> 16); } /** * get_ioa_fw_name - * @ioa: ipr ioa struct * @buf: data buffer * * Returns: * nothing **/ static void get_ioa_fw_name(struct ipr_ioa *ioa, char *buf) { const struct ioa_parms *ioa_parms = get_ioa_fw(ioa); if (ioa_parms) strcpy(buf, ioa_parms->fw_name); else sprintf(buf, "534953%02X", get_ioa_image_type(ioa)); } /** * get_linux_ioa_fw_name - * @ioa: ipr ioa struct * @buf: data buffer * * Returns: * nothing **/ static void get_linux_ioa_fw_name(struct ipr_ioa *ioa, char *buf) { sprintf(buf, "pci.%04x%04x.%02x", ioa->pci_vendor, ioa->pci_device, get_ioa_image_type(ioa)); } /** * get_linux_ioa_fw_name_capital - * @ioa: ipr ioa struct * @buf: data buffer * * Returns: * nothing **/ static void get_linux_ioa_fw_name_capital(struct ipr_ioa *ioa, char *buf) { sprintf(buf, "pci.%04X%04X.%02X", ioa->pci_vendor, ioa->pci_device, get_ioa_image_type(ioa)); } /** * init_ioa_ucode_entry - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_ioa_ucode_entry(struct ipr_fw_images *img) { img->version = get_ioa_ucode_version(img->file); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_disk_ucode_entry - * @img: ipr_fw_images * * Returns: * nothing **/ static void init_disk_ucode_entry(struct ipr_fw_images *img) { img->version = get_dasd_ucode_version(img->file, 1); img->has_header = 1; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_disk_ucode_entry_nohdr - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_disk_ucode_entry_nohdr(struct ipr_fw_images *img) { img->version = get_dasd_ucode_version(img->file, 0); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_ses_ucode_entry_nohdr - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_ses_ucode_entry_nohdr(struct ipr_fw_images *img) { img->version = get_ses_ucode_version(img->file); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * scan_fw_dir - * @path: * @name: * @list: * @len: * @init function pointer to initialization function * * Returns: * int len **/ static int scan_fw_dir(char *path, char *name, struct ipr_fw_images **list, int len, void (*init)(struct ipr_fw_images *)) { struct dirent **dirent; int rc, i; struct ipr_fw_images *ret = *list; rc = scandir(path, &dirent, NULL, alphasort); for (i = 0; i < rc && rc > 0; i++) { if (strstr(dirent[i]->d_name, name) == dirent[i]->d_name) { ret = realloc(ret, sizeof(*ret) * (len + 1)); sprintf(ret[len].file, "%s/%s", path, dirent[i]->d_name); init(&ret[len]); len++; } } for (i = 0; i < rc; i++) free(dirent[i]); if (rc > 0) free(dirent); *list = ret; return len; } /** * get_ioa_firmware_image_list - * @ioa: ipr ioa struct * @list: ipr_fw_images struct * * Returns: * length of list **/ int get_ioa_firmware_image_list(struct ipr_ioa *ioa, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; int len = 0; *list = NULL; get_ioa_fw_name(ioa, buf); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_ioa_ucode_entry); get_linux_ioa_fw_name(ioa, buf); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_ioa_ucode_entry); get_linux_ioa_fw_name_capital(ioa, buf); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_ioa_ucode_entry); sprintf(buf, "ibmsis%X.img", ioa->ccin); len = scan_fw_dir("/etc/microcode", buf, &ret, len, init_ioa_ucode_entry); sprintf(buf, "ibmsis%X.img", get_ioa_image_type(ioa)); len = scan_fw_dir("/etc/microcode", buf, &ret, len, init_ioa_ucode_entry); if (ret) qsort(ret, len, sizeof(*ret), fw_compare); *list = ret; return len; } /** * get_dasd_firmware_image_list- * @dev: ipr dev struct * @list: irp_fw_images struct * * Returns: * length value if success / non-zero on failure **/ int get_dasd_firmware_image_list(struct ipr_dev *dev, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; struct ipr_dasd_inquiry_page3 page3_inq; int rc; int len = 0; *list = NULL; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } sprintf(buf, "%.7s.%02X%02X%02X%02X", dev->scsi_dev_data->product_id, page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_disk_ucode_entry_nohdr); if (memcmp(dev->scsi_dev_data->vendor_id, "IBMAS400", 8) == 0) sprintf(buf, "ibmsis%02X%02X%02X%02X.img", page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir("/etc/microcode/device", buf, &ret, len, init_disk_ucode_entry); sprintf(buf, "IBM-%.7s.%02X%02X%02X%02X", dev->scsi_dev_data->product_id, page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_disk_ucode_entry_nohdr); if (len) qsort(ret, len, sizeof(*ret), fw_compare); else scsi_dbg(dev, "Could not find device firmware file\n"); *list = ret; return len; } /** * get_ses_load_id- * @dev: ipr dev struct * @load_id: * * Returns: * 0 if success / non-zero on failure **/ static int get_ses_load_id(struct ipr_dev *dev, u8 load_id[4]) { struct ipr_dasd_inquiry_page3 page3_inq; int rc; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc) return rc; if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) memcpy(load_id, page3_inq.release_level, 4); else memcpy(load_id, page3_inq.load_id, 4); return 0; } /** * get_ses_firmware_image_list- * @dev: ipr dev struct * @list: * * Returns: * length value if success / non-zero on failure **/ int get_ses_firmware_image_list(struct ipr_dev *dev, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; u8 load_id[4]; int rc; int len = 0; *list = NULL; rc = get_ses_load_id(dev, load_id); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } sprintf(buf, "%02X%02X%02X%02X", load_id[0], load_id[1], load_id[2], load_id[3]); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_ses_ucode_entry_nohdr); sprintf(buf, "IBM-%02X%02X%02X%02X", load_id[0], load_id[1], load_id[2], load_id[3]); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_ses_ucode_entry_nohdr); if (len) qsort(ret, len, sizeof(*ret), fw_compare); else scsi_dbg(dev, "Could not find device firmware file\n"); *list = ret; return len; } struct ipr_ioa_desc { u16 type; const char *desc; }; struct ipr_ioa_desc ioa_desc [] = { {0x5702, "PCI-X Dual Channel Ultra320 SCSI Adapter [5702]"}, {0x1974, "PCI-X Dual Channel Ultra320 SCSI Adapter [1974]"}, {0x5703, "PCI-X Dual Channel Ultra320 SCSI RAID Adapter [5703]"}, {0x1975, "PCI-X Dual Channel Ultra320 SCSI RAID Adapter [1975]"}, {0x2780, "PCI-X Quad Channel Ultra320 SCSI RAID Adapter [2780]"}, {0x5709, "SCSI RAID Enablement Card for PCI-X Dual Channel Ultra320 SCSI Integrated Controller [5709]"}, {0x1976, "SCSI RAID Enablement Card for PCI-X Dual Channel Ultra320 SCSI Integrated Controller [1976]"}, {0x570A, "PCI-X Dual Channel SCSI Integrated Controller (Adapter bus) [570A]"}, {0x570B, "PCI-X Dual Channel SCSI Integrated Controller (Adapter bus) [570B]"} }; /** * get_long_ioa_desc- * @type: * * Returns: * IOA description if success / NULL on failure **/ static const char *get_long_ioa_desc(u16 type) { int i; for (i = 0; i < sizeof(ioa_desc)/sizeof(ioa_desc[0]); i++) { if (type == ioa_desc[i].type) return ioa_desc[i].desc; } return NULL; } /** * ipr_log_ucode_error - log a microcode error * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_log_ucode_error(struct ipr_ioa *ioa) { const char *desc = get_long_ioa_desc(ioa->ccin); if (desc) { syslog(LOG_ERR, "Could not find required level of microcode for IBM '%s'. " "Please download the latest microcode from " "http://techsupport.services.ibm.com/server/mdownload/download.html. " "SCSI speeds will be limited to %d MB/s until updated microcode is downloaded.\n", desc, IPR_SAFE_XFER_RATE); } else { syslog(LOG_ERR, "Could not find required level of microcode for IBM %04X. " "Please download the latest microcode from " "http://techsupport.services.ibm.com/server/mdownload/download.html. " "SCSI speeds will be limited to %d MB/s until updated microcode is downloaded.\n", ioa->ccin, IPR_SAFE_XFER_RATE); } } /** * ipr_update_ioa_fw - * @ioa: ipr ioa struct * @image: pointer to fw image * @force: force flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_update_ioa_fw(struct ipr_ioa *ioa, struct ipr_fw_images *image, int force) { struct ipr_ioa_ucode_header *image_hdr; struct ipr_ioa_ucode_ext_header *ext_hdr; struct ipr_ioa_ucode_img_desc *img_desc; struct stat ucode_stats; u32 fw_version; int fd, rc; int ioafw = 1; int host_num = ioa->host_num; char *tmp; char ucode_file[200]; DIR *dir; char *img_file; char cwd[200]; char devpath[PATH_MAX]; ssize_t len; fw_version = get_ioa_fw_version(ioa); if (fw_version >= ioa->msl && !force) return 0; if (ipr_get_hotplug_dir()) return 0; fd = open(image->file, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "Could not open firmware file %s.\n", image->file); return fd; } rc = fstat(fd, &ucode_stats); if (rc != 0) { syslog(LOG_ERR, "Failed to stat IOA firmware file: %s.\n", image->file); close(fd); return rc; } image_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image_hdr == MAP_FAILED) { syslog(LOG_ERR, "Error mapping IOA firmware file: %s.\n", image->file); close(fd); return -EIO; } ext_hdr = (void *)image_hdr + ntohl(image_hdr->header_length); img_desc = (void *)ext_hdr + ntohl(ext_hdr->img_desc_offset); if (strncmp(img_desc->fw_type, IPR_IOAF_STR, 4)) ioafw = 0; if (ntohl(image_hdr->rev_level) > fw_version || force) { if (ioafw) ioa_info(ioa, "Updating microcode from %08X to %08X.\n", fw_version, ntohl(image_hdr->rev_level)); else ioa_info(ioa, "Updating microcode to %08X.\n", ntohl(image_hdr->rev_level)); /* Give the file name an absolute path if needed. */ if (image->file[0] != '/') { getcwd(cwd, sizeof(cwd)); strcat(cwd, "/"); img_file = strcat(cwd, image->file); } else img_file = image->file; tmp = strrchr(img_file, '/'); if (tmp) tmp++; else { syslog(LOG_ERR, "Failed to find image name in %s\n", img_file); return -EIO; } dir = opendir(hotplug_dir); if (!dir) mkdir(hotplug_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); dir = opendir(hotplug_dir); if (!dir) { syslog(LOG_ERR, "Failed to open %s. %m\n", hotplug_dir); munmap(image_hdr, ucode_stats.st_size); close(fd); return -EIO; } closedir(dir); sprintf(ucode_file, "%s/.%s", hotplug_dir, tmp); symlink(img_file, ucode_file); sprintf(ucode_file, ".%s\n", tmp); sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_write_attr(devpath, "update_fw", ucode_file, strlen(ucode_file)); sprintf(ucode_file, "%s/.%s", hotplug_dir, tmp); unlink(ucode_file); if (len < 0) ioa_err(ioa, "Microcode update failed. rc=%d\n", (int)len); check_current_config(false); for_each_ioa(ioa) { if (ioa->host_num != host_num) continue; ipr_init_ioa(ioa); break; } } else ipr_log_ucode_error(ioa); munmap(image_hdr, ucode_stats.st_size); close(fd); return rc; } /** * ipr_update_disk_fw - update disk fw * @dev: ipr dev struct * @image: pointer to fw image * @force: force flag * * Returns: * 0 if success / non-zero on failure **/ /* xxx TODO make a general routine to do write buffer that takes a struct ipr_fw_images as input */ int ipr_update_disk_fw(struct ipr_dev *dev, struct ipr_fw_images *image, int force) { int rc = 0; struct stat ucode_stats; int fd; struct ipr_dasd_ucode_header *img_hdr; struct ipr_dasd_inquiry_page3 page3_inq; struct ipr_std_inq_data std_inq_data; struct unsupported_af_dasd *unsupp_af; u32 level; u8 release_level[4]; memset(&std_inq_data, 0, sizeof(std_inq_data)); rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc != 0) return rc; rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return rc; } if (!force) { unsupp_af = get_unsupp_af(&std_inq_data, &page3_inq); if (!unsupp_af) return 0; if (!disk_needs_msl(unsupp_af, &std_inq_data)) return 0; } fd = open(image->file, O_RDONLY); if (fd < 0) { syslog_dbg("Could not open firmware file %s.\n", image->file); return fd; } rc = fstat(fd, &ucode_stats); if (rc != 0) { syslog(LOG_ERR, "Failed to stat firmware file: %s.\n", image->file); close(fd); return rc; } img_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (img_hdr == MAP_FAILED) { syslog(LOG_ERR, "Error reading firmware file: %s.\n", image->file); close(fd); return -EIO; } level = htonl(image->version); __ipr_get_fw_version(dev, &page3_inq, release_level); if (memcmp(&level, page3_inq.release_level, 4) > 0 || force) { scsi_info(dev, "Updating device microcode using %s " "from %02X%02X%02X%02X (%c%c%c%c) to %08X (%c%c%c%c)\n", image->file, release_level[0], release_level[1], release_level[2], release_level[3], release_level[0], release_level[1], release_level[2], release_level[3], image->version, image->version >> 24, (image->version >> 16) & 0xff, (image->version >> 8) & 0xff, image->version & 0xff); rc = ipr_write_buffer(dev, img_hdr, ucode_stats.st_size); ipr_init_dev(dev); } if (munmap(img_hdr, ucode_stats.st_size)) syslog(LOG_ERR, "munmap failed.\n"); close(fd); return rc; } /** * mode_select - issue a mode select command * @dev: ipr dev struct * @buff: data buffer * @length: * * Returns: * 0 if success / non-zero on failure **/ static int mode_select(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SELECT; cdb[1] = 0x11; cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc) scsi_cmd_err(dev, &sense_data, "Mode Select", rc); close(fd); return rc; } /** * setup_page0x00 - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x00(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_vendor_mode_page *page, *ch_page; int len; if (strncmp(dev->scsi_dev_data->vendor_id, "IBM", 3)) { scsi_dbg(dev, "Not setting up mode page 0x00 for unknown device.\n"); return 0; } memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x00, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x40, &ch_mode_pages)) return -EIO; page = (struct ipr_vendor_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_vendor_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->arhes, page->arhes, 1); IPR_SET_MODE(ch_page->cmdac, page->cmdac, 1); IPR_SET_MODE(ch_page->caen, page->caen, 1); /* Use a 3 second command aging timer - units are 50 ms */ IPR_SET_MODE(ch_page->cmd_aging_limit_hi, page->cmd_aging_limit_hi, 0); IPR_SET_MODE(ch_page->cmd_aging_limit_lo, page->cmd_aging_limit_lo, 60); len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { scsi_dbg(dev, "Failed to setup mode page 0x00\n"); return -EIO; } return 0; } /** * setup_page0x01 - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x01(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_rw_err_mode_page *page, *ch_page; int len; memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x01, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x41, &ch_mode_pages)) return -EIO; page = (struct ipr_rw_err_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_rw_err_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->awre, page->awre, 1); IPR_SET_MODE(ch_page->arre, page->arre, 1); if (page->awre != 1) goto error; if (page->arre != 1) goto error; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { error: scsi_err(dev, "Failed to setup mode page 0x01\n"); return -EIO; } return 0; } /** * setup_page0x0a - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x0a(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_control_mode_page *page, *ch_page; int len; int rc = 0; memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x0A, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x4A, &ch_mode_pages)) return -EIO; page = (struct ipr_control_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_control_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->queue_algorithm_modifier, page->queue_algorithm_modifier, 1); IPR_SET_MODE(ch_page->tst, page->tst, 1); IPR_SET_MODE(ch_page->tas, page->tas, 1); switch(dev->ioa->tcq_mode) { case IPR_TCQ_FROZEN: IPR_SET_MODE(ch_page->qerr, page->qerr, 3); if (page->qerr != 3) { IPR_SET_MODE(ch_page->qerr, page->qerr, 1); if (page->tst != 1) IPR_SET_MODE(ch_page->tst, page->tst, 0); } scsi_dbg(dev, "Using control mode settings: " "TST=%d, QERR=%d, TAS=%d\n", page->tst, page->qerr, page->tas); break; case IPR_TCQ_NACA: IPR_SET_MODE(ch_page->qerr, page->qerr, 0); break; case IPR_TCQ_DISABLE: default: rc = -EIO; break; }; IPR_SET_MODE(ch_page->dque, page->dque, 0); if (page->dque != 0) { scsi_dbg(dev, "Cannot set dque=0\n"); return -EIO; } if (page->queue_algorithm_modifier != 1) scsi_dbg(dev, "Cannot set QAM=1\n"); len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { scsi_err(dev, "Failed to setup mode page 0x0A\n"); return -EIO; } return rc; } /** * init_vset_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * VSETs: * 1. Adjust queue depth based on number of devices * */ static void init_vset_dev(struct ipr_dev *dev) { struct ipr_query_res_state res_state; char q_depth[100]; char cur_depth[100], saved_depth[100]; int depth, rc; memset(&res_state, 0, sizeof(res_state)); if (polling_mode && !dev->should_init) return; if (!ipr_query_resource_state(dev, &res_state)) { depth = res_state.vset.num_devices_in_vset * 4; snprintf(q_depth, sizeof(q_depth), "%d", depth); q_depth[sizeof(q_depth)-1] = '\0'; if (ipr_read_dev_attr(dev, "queue_depth", cur_depth, 100)) return; rc = ipr_get_saved_dev_attr(dev, IPR_QUEUE_DEPTH, saved_depth); if (rc == RC_SUCCESS) { if (!strcmp(saved_depth, q_depth)) return; depth = strtoul(saved_depth, NULL, 10); if (depth && depth <= 255) strcpy(q_depth, saved_depth); } if (!strcmp(cur_depth, q_depth)) return; if (ipr_write_dev_attr(dev, "queue_depth", q_depth)) return; } } /** * init_gpdd_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * GPDD DASD: * 1. Setup Mode Page 0x0A for TCQing. * 2. Enable TCQing * 3. Adjust queue depth * */ static void init_gpdd_dev(struct ipr_dev *dev) { struct ipr_disk_attr attr; struct sense_data_t sense_data; int rc; if (polling_mode && !dev->should_init) return; if (__ipr_test_unit_ready(dev, &sense_data)) { if ((sense_data.sense_key != UNIT_ATTENTION) || __ipr_test_unit_ready(dev, &sense_data)) return; } if (enable_af(dev)) return; if (ipr_get_dev_attr(dev, &attr)) return; if (setup_page0x00(dev)) return; if ((rc = setup_page0x0a(dev))) { if (rc != -EINVAL) { scsi_dbg(dev, "Failed to enable TCQing.\n"); return; } } else { attr.queue_depth = get_tcq_depth(dev); attr.tcq_enabled = 1; } if (ipr_modify_dev_attr(dev, &attr)) return; if (ipr_set_dev_attr(dev, &attr, 0)) return; } /** * init_af_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * AF DASD: * 1. Setup mode pages (pages 0x01, 0x0A, 0x20) * 2. Send set supported devices * 3. Set DASD timeouts */ static void init_af_dev(struct ipr_dev *dev) { struct ipr_disk_attr attr; int rc; if (ipr_set_dasd_timeouts(dev, 0)) return; if (polling_mode && (!dev->should_init && !memcmp(&attr, &dev->attr, sizeof(attr)))) return; if (polling_mode && !dev_init_allowed(dev)) return; if (setup_page0x00(dev)) return; if (setup_page0x01(dev)) return; if (enable_af(dev)) return; if (ipr_get_dev_attr(dev, &attr)) return; if ((rc = setup_page0x0a(dev))) { if (rc != -EINVAL) { scsi_dbg(dev, "Failed to enable TCQing.\n"); return; } } else attr.queue_depth = get_tcq_depth(dev); if (ipr_modify_dev_attr(dev, &attr)) return; memcpy(&dev->attr, &attr, sizeof(attr)); if (ipr_set_dev_attr(dev, &attr, 0)) return; } /** * init_ioa_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * IOA: * 1. Load saved adapter configuration */ static void init_ioa_dev(struct ipr_dev *dev) { struct ipr_scsi_buses buses; struct ipr_ioa_attr attr; if (!dev->ioa) return; if (polling_mode && !dev->ioa->should_init) return; if (ipr_get_ioa_attr(dev->ioa, &attr)) return; if (dev->ioa->asymmetric_access && dev->ioa->sis64) attr.active_active = 1; ipr_modify_ioa_attr(dev->ioa, &attr); if (ipr_set_ioa_attr(dev->ioa, &attr, 0)) return; if (ipr_get_bus_attr(dev->ioa, &buses)) return; ipr_modify_bus_attr(dev->ioa, &buses); if (ipr_set_bus_attr(dev->ioa, &buses, 0)) return; } /** * ipr_init_dev - * @dev: ipr dev struct * * Returns: * 0 **/ int ipr_init_dev(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return 0; switch (dev->scsi_dev_data->type) { case TYPE_DISK: if (ipr_is_volume_set(dev)) init_vset_dev(dev); else init_gpdd_dev(dev); break; case IPR_TYPE_AF_DISK: init_af_dev(dev); break; case IPR_TYPE_ADAPTER: if (&dev->ioa->ioa == dev) init_ioa_dev(dev); break; default: break; }; return 0; } /** * ipr_init_new_dev - * @dev: ipr dev struct * * Returns: * nothing **/ int ipr_init_new_dev(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return 0; switch (dev->scsi_dev_data->type) { case TYPE_DISK: if (!strlen(dev->dev_name)) return 1; wait_for_dev(dev->dev_name); break; case IPR_TYPE_ADAPTER: if (&dev->ioa->ioa != dev) break; case IPR_TYPE_AF_DISK: wait_for_dev(dev->gen_name); break; default: break; }; ipr_init_dev(dev); return 0; } /** * ipr_scan_ra - * @ioa: ipr ioa struct * @ra: ipr_res_addr struct * * Returns: * nothing **/ static void ipr_scan_ra(struct ipr_ioa *ioa, struct ipr_res_addr *ra) { ra_dbg(ra, "Scanning for new device\n"); ipr_scan(ioa, ra->bus, ra->target, ra->lun); } /** * ipr_for_each_unique_ra - * @dev: ipr dev struct * @func: function pointer * * Returns: * nothing **/ static void ipr_for_each_unique_ra(struct ipr_dev *dev, void (*func) (struct ipr_ioa *, struct ipr_res_addr *)) { signed int i, j; int dup; for (i = 0; i < ARRAY_SIZE(dev->res_addr); i++) { dup = 0; for (j = i - 1; j >= 0; j--) { if (!memcmp(&dev->res_addr[i], &dev->res_addr[j], sizeof(struct ipr_res_addr))) { dup = 1; break; } } if (!dup) func(dev->ioa, &dev->res_addr[i]); } } /** * fixup_improper_devs - * @ioa: ipr ioa struct * * Returns: * 0 if no improper devs / number of improper devs otherwise **/ static int fixup_improper_devs(struct ipr_ioa *ioa) { struct ipr_dev *dev; int improper = 0; for_each_dev(ioa, dev) { dev->local_flag = 0; if (ipr_improper_device_type(dev)) { improper++; dev->local_flag = 1; scsi_dbg(dev, "Deleting improper device\n"); ipr_write_dev_attr(dev, "delete", "1"); } } if (!improper) return 0; sleep(5); for_each_dev(ioa, dev) { if (!dev->local_flag) continue; dev->local_flag = 0; dev->rescan = 0; ipr_for_each_unique_ra(dev, ipr_scan_ra); } return improper; } /** * ipr_init_ioa - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_init_ioa(struct ipr_ioa *ioa) { struct ipr_dev *dev; if (ioa->ioa_dead) return 0; if (fixup_improper_devs(ioa)) return -EAGAIN; for_each_dev(ioa, dev) ipr_init_dev(dev); init_ioa_dev(&ioa->ioa); return 0; } /** * scsi_dev_kevent - * @buf: data buffer * @find_device: function pointer * @func: function pointer * * Returns: * nothing **/ void scsi_dev_kevent(char *buf, struct ipr_dev *(*find_device)(char *), int (*func)(struct ipr_dev *)) { struct ipr_dev *dev; char *name; int i, j, rc = -EAGAIN; name = strrchr(buf, '/'); if (!name) { syslog_dbg("Failed to handle %s kevent\n", buf); return; } name++; for (i = 0; i < 2 && rc == -EAGAIN; i++) { for (j = 0; j < 10; j++) { tool_init(1); check_current_config(false); dev = find_device(name); if (dev) break; sleep(2); } if (!dev) { syslog_dbg("Failed to find ipr dev %s\n", name); return; } rc = func(dev); } } /** * scsi_host_kevent - * @buf: data buffer * @func: funtion pointer * * Returns: * nothing **/ void scsi_host_kevent(char *buf, int (*func)(struct ipr_ioa *)) { struct ipr_ioa *ioa; char *c; int host; int i, j, rc = -EAGAIN; c = strrchr(buf, '/'); if (!c) { syslog_dbg("Failed to handle %s kevent\n", buf); return; } c += strlen("/host"); host = strtoul(c, NULL, 10); for (i = 0; i < 2 && rc == -EAGAIN; i++) { for (j = 0; j < 10; j++) { tool_init(1); check_current_config(false); ioa = find_ioa(host); if (ioa) break; sleep(2); } if (!ioa) { syslog_dbg("Failed to find ipr ioa %d\n", host); return; } rc = func(ioa); } } /** * get_dev_from_addr - * @res_addr: ipr_res_addr struct * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_addr(struct ipr_res_addr *res_addr) { struct ipr_ioa *ioa; int j; struct scsi_dev_data *scsi_dev_data; for_each_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data) continue; if (scsi_dev_data->host == res_addr->host && scsi_dev_data->channel == res_addr->bus && scsi_dev_data->id == res_addr->target && scsi_dev_data->lun == res_addr->lun) return &ioa->dev[j]; } } return NULL; } /** * get_dev_from_handle - * @res_handle: * @ioa: struct ipr_ioa * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_handle(struct ipr_ioa *ioa, u32 res_handle) { int j; for (j = 0; j < ioa->num_devices; j++) { if (!ioa->dev[j].qac_entry) continue; if (ipr_is_device_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; if (ipr_is_array_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; if (ipr_is_vset_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; } return NULL; } /** * ipr_daemonize - * * Returns: * nothing **/ void ipr_daemonize() { int rc = fork(); if (rc < 0) { syslog(LOG_ERR, "Failed to daemonize\n"); exit(1); } else if (rc) { exit(0); } close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); open(_PATH_DEVNULL,O_RDONLY); open(_PATH_DEVNULL,O_WRONLY); open(_PATH_DEVNULL,O_WRONLY); setsid(); } /** * ipr_disable_qerr - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_disable_qerr(struct ipr_dev *dev) { u8 ioctl_buffer[IOCTL_BUFFER_SIZE]; u8 ioctl_buffer2[IOCTL_BUFFER_SIZE]; struct ipr_control_mode_page *control_mode_page; struct ipr_control_mode_page *control_mode_page_changeable; struct ipr_mode_parm_hdr *mode_parm_hdr; int status; u8 length; /* Issue mode sense to get the control mode page */ status = ipr_mode_sense(dev, 0x0a, &ioctl_buffer); if (status) return -EIO; /* Issue mode sense to get the control mode page */ status = ipr_mode_sense(dev, 0x4a, &ioctl_buffer2); if (status) return -EIO; mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer2; control_mode_page_changeable = (struct ipr_control_mode_page *) (((u8 *)(mode_parm_hdr+1)) + mode_parm_hdr->block_desc_len); mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; control_mode_page = (struct ipr_control_mode_page *) (((u8 *)(mode_parm_hdr+1)) + mode_parm_hdr->block_desc_len); /* Turn off QERR since some drives do not like QERR and IMMED bit at the same time. */ IPR_SET_MODE(control_mode_page_changeable->qerr, control_mode_page->qerr, 0); /* Issue mode select to set page x0A */ length = mode_parm_hdr->length + 1; mode_parm_hdr->length = 0; control_mode_page->hdr.parms_saveable = 0; mode_parm_hdr->medium_type = 0; mode_parm_hdr->device_spec_parms = 0; status = ipr_mode_select(dev, &ioctl_buffer, length); if (status) return -EIO; return 0; } void ipr_set_manage_start_stop(struct ipr_dev *dev) { char path[PATH_MAX]; ssize_t len; char value_str[2]; int value; sprintf(path, "/sys/class/scsi_disk/%s", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "manage_start_stop", value_str, 2); if (len < 0) { syslog_dbg("Failed to open manage_start_stop parameter.\n"); return; } value = atoi(value_str); snprintf(value_str, 2, "%d", 1); if (sysfs_write_attr(path, "manage_start_stop", value_str, 1) < 0) syslog_dbg("Failed to write manage_start_stop parameter.\n"); len = sysfs_read_attr(path, "manage_start_stop", value_str, 2); value = atoi(value_str); } /** * ipr_query_ioa_device_port - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @option: option value * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_io_dev_port(struct ipr_dev *dev, struct ipr_query_io_port *io_port) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct ipr_ioa *ioa = dev->ioa; struct sense_data_t sense_data; char *rp, *endptr; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_IOA_DEV_PORT; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; cdb[10] = sizeof(*io_port) >> 24; cdb[11] = (sizeof(*io_port) >> 16) & 0xff; cdb[12] = (sizeof(*io_port) >> 8) & 0xff; cdb[13] = sizeof(*io_port) & 0xff; } else { close(fd); return -1; } rc = sg_ioctl(fd, cdb, io_port, sizeof(*io_port), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query IO port status", rc); close(fd); return rc; } iprutils/iprinit.c0000644000000000000000000000343712427235652013270 0ustar rootroot/** * IBM IPR adapter initialization utility * * (C) Copyright 2004, 2008 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprinit.c,v 1.22 2008/11/20 01:20:20 wboyer Exp $ */ #include #include #include #include #include #ifndef iprlib_h #include "iprlib.h" #endif #include char *tool_name = "iprinit"; static void init_all() { struct ipr_ioa *ioa; tool_init(1); check_current_config(false); for_each_ioa(ioa) ipr_init_ioa(ioa); } static void kevent_handler(char *buf) { polling_mode = 0; if (!strncmp(buf, "change@/class/scsi_host", 23) || (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host"))) scsi_host_kevent(buf, ipr_init_ioa); else if (!strncmp(buf, "add@/class/scsi_generic", 23) || (!strncmp(buf, "add@/devices/pci", 16) && strstr(buf, "scsi_generic"))) scsi_dev_kevent(buf, find_gen_dev, ipr_init_dev); else if (!strncmp(buf, "add@/block/sd", 13)) scsi_dev_kevent(buf, find_blk_dev, ipr_init_dev); } static void poll_ioas() { polling_mode = 1; init_all(); } int main(int argc, char *argv[]) { int i; ipr_sg_required = 1; openlog("iprinit", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else { printf("Usage: iprinit [options]\n"); printf(" Options: --version [--daemon] Print iprinit version\n"); return -EINVAL; } } init_all(); if (daemonize) { ipr_daemonize(); return handle_events(poll_ioas, 60, kevent_handler); } return 0; } iprutils/iprdump.c0000644000000000000000000001546212427235652013273 0ustar rootroot/** * IBM IPR adapter dump daemon * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprdump.c,v 1.19 2008/11/20 01:20:20 wboyer Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif #include #define IPRDUMP_SIZE (80 * 1024 * 1024) #define MAX_DUMP_FILES 4 #define TOOL_NAME "iprdump" #define DUMP_PREFIX TOOL_NAME"." char *tool_name = TOOL_NAME; struct dump_header { u32 eye_catcher; #define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2 u32 total_length; u32 num_elems; u32 first_entry_offset; u32 status; } *dump_header; struct dump_entry_header { u32 length; u32 id; #define IPR_DUMP_TEXT_ID 3 char data[0]; }; struct dump { struct dump_header hdr; u8 data[IPRDUMP_SIZE-sizeof(struct dump_header)]; }; static struct dump dump; static char usr_dir[100]; static char *enable = "1\n"; static char *disable = "0\n"; /** * enable_dump - * @ioa: ipr_ioa struct * * Returns: * nothing **/ static void enable_dump(struct ipr_ioa *ioa) { int rc; rc = ipr_write_host_attr(ioa, "dump", enable, strlen(enable)); if (rc != strlen(enable)) { ioa_err(ioa, "Failed to enable dump. rc=%d. %m\n", rc); return; } } /** * disable_dump - * @ioa: ipr_ioa struct * * Returns: * nothing **/ static void disable_dump(struct ipr_ioa *ioa) { int rc; rc = ipr_write_host_attr(ioa, "dump", disable, strlen(disable)); if (rc != strlen(disable)) { ioa_err(ioa, "Failed to disable dump. rc=%d. %m\n", rc); return; } } /** * read_dump - * @ioa: ipr_ioa struct * * Returns: * number of items read **/ static int read_dump(struct ipr_ioa *ioa) { int count = 0; char path[PATH_MAX]; FILE *file; sprintf(path, "/sys/class/scsi_host/%s/%s", ioa->host_name, "dump"); file = fopen(path, "r"); count = fread(&dump, 1 , sizeof(dump), file); fclose(file); return (count < 0) ? 0: count; } /** * select_dump_file - * @dirent: dirent struct * * Returns: * 1 if the string is found, else 0 **/ static int select_dump_file(const struct dirent *dirent) { if (strstr(dirent->d_name, DUMP_PREFIX)) return 1; return 0; } /** * dump_sort - * @a: void buffer * @b: void buffer * * Returns: * **/ static int dump_sort(const struct dirent **dumpa, const struct dirent **dumpb) { int numa, numb; sscanf((*dumpa)->d_name, DUMP_PREFIX"%d", &numa); sscanf((*dumpb)->d_name, DUMP_PREFIX"%d", &numb); if (numa < MAX_DUMP_FILES && numb >= (100 - MAX_DUMP_FILES)) return 1; if (numb < MAX_DUMP_FILES && numa >= (100 - MAX_DUMP_FILES)) return -1; return alphasort(dumpa, dumpb); } /** * cleanup_old_dumps - * * Returns: * Nothing **/ static void cleanup_old_dumps() { struct dirent **dirent; char fname[100]; int rc, i; rc = scandir(usr_dir, &dirent, select_dump_file, dump_sort); if (rc > 0) { for (i = 0 ; i < (rc - MAX_DUMP_FILES); i++) { sprintf(fname, "%s%s", usr_dir, dirent[i]->d_name); if (remove(fname)) { syslog(LOG_ERR, "Delete of %s%s failed. %m\n", usr_dir, dirent[i]->d_name); } } free(*dirent); } } /** * get_dump_fname - * @fname: file name * * Returns: * -EIO on error, else 0 **/ static int get_dump_fname(char *fname) { struct dirent **dirent; int rc; char *tmp; int dump_id = 0; cleanup_old_dumps(); rc = scandir(usr_dir, &dirent, select_dump_file, dump_sort); if (rc > 0) { rc--; tmp = strstr(dirent[rc]->d_name, DUMP_PREFIX); if (tmp) { tmp += strlen(DUMP_PREFIX); dump_id = strtoul(tmp, NULL, 10) + 1; if (dump_id > 99) dump_id = 0; } else { free(*dirent); fname[0] = '\0'; return -EIO; } free(*dirent); } sprintf(fname, "%s%02d", DUMP_PREFIX, dump_id); return 0; } /** * write_dump - * @ioa: ipr_ioa struct * @count: size to write * * Returns: * Nothing **/ static void write_dump(struct ipr_ioa *ioa, int count) { int f_dump; char dump_file[100], dump_path[100]; if (get_dump_fname(dump_file)) return; sprintf(dump_path, "%s%s", usr_dir, dump_file); f_dump = creat(dump_path, S_IRUSR); if (f_dump < 0) { syslog(LOG_ERR, "Cannot open %s. %m\n", dump_path); return; } write(f_dump, &dump, count); close(f_dump); ioa_err(ioa, "Dump of ipr IOA has completed to file: %s\n", dump_path); } /** * handle_signal - * @signal: signal value * * Returns: * Nothing **/ static void handle_signal(int signal) { struct ipr_ioa *ioa; tool_init(0); for_each_ioa(ioa) disable_dump(ioa); exit(0); } /** * select_dump_file - * @ioa: ipr_ioa struct * * Returns: * Nothing **/ static void dump_ioa(struct ipr_ioa *ioa) { int count; count = read_dump(ioa); if (count <= 0) return; write_dump(ioa, count); disable_dump(ioa); enable_dump(ioa); } /** * poll_for_dump - * * Returns: * Nothing **/ static void poll_for_dump() { struct ipr_ioa *ioa; tool_init(0); for_each_ioa(ioa) enable_dump(ioa); for_each_ioa(ioa) dump_ioa(ioa); } /** * kevent_handler - * @buf: character buffer * * Returns: * Nothing **/ static void kevent_handler(char *buf) { struct ipr_ioa *ioa; int host, i; char *index; if (!strncmp(buf, "change@/class/scsi_host", 23)) { host = strtoul(&buf[28], NULL, 10); } else if (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host")) { index = strrchr(buf, '/'); host = strtoul(index + 5, NULL, 10); } else return; for (i = 0; i < 10; i++) { tool_init(0); ioa = find_ioa(host); if (ioa) break; sleep(2); } if (!ioa) { syslog(LOG_ERR, "Failed to find ipr ioa %d for iprdump\n", host); return; } enable_dump(ioa); dump_ioa(ioa); } /** * main - * @argc: argument count * @argv: argument string * * Returns: * return value of handle_events() call **/ int main(int argc, char *argv[]) { struct ipr_ioa *ioa; int len, i; openlog(TOOL_NAME, LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); strcpy(usr_dir, IPRDUMP_DIR); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; if (strcmp(argv[i], "-d") == 0) { strcpy(usr_dir,argv[++i]); len = strlen(usr_dir); if (len < sizeof(usr_dir) && usr_dir[len] != '/') { usr_dir[len + 1] = '/'; usr_dir[len + 2] = '\0'; } } else { printf("Usage: "TOOL_NAME" [options]\n"); printf(" Options: --version Print iprdump version\n"); printf(" --daemon Run as a daemon\n"); printf(" --debug Print extra debugging information\n"); printf(" -d \n"); exit(1); } } if (daemonize) ipr_daemonize(); signal(SIGINT, handle_signal); signal(SIGQUIT, handle_signal); signal(SIGTERM, handle_signal); tool_init(0); for_each_ioa(ioa) enable_dump(ioa); return handle_events(poll_for_dump, 60, kevent_handler); } iprutils/iprdump.80000644000000000000000000000323012427235652013206 0ustar rootroot.\" (C) Copyright 2000, 2004 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRDUMP 8 "February 2005" .SH NAME iprdump - IBM Power RAID adapter dump utility .SH SYNOPSIS .B "iprdump [-d directory]" .sp .BI "iprdump --version --debug --use-polling --use-uevents" .SH DESCRIPTION .B iprdump is used to gather information in the event of an adapter failure. The dump data will by default be saved in the /var/log/ directory with the prefix iprdump.# where # will be the dump ID of the file. The ipr dump utility will make a log in the system error log when a dump is taken. The iprdump utility can be started at any time and will properly handle adapters being dynamically added and removed from the system. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprdump .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-d Directory where dump data is to be stored. Default is /var/log/. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Originally written by Michael Anderson . Rewritten for the 2.6 kernel by Brian King . iprutils/iprdbg.c0000644000000000000000000004320012427235652013051 0ustar rootroot/** * IBM IPR adapter debug utility * * (C) Copyright 2003 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprdbg.c,v 1.26 2006/09/08 16:26:01 brking Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif #include #include #include #include #include #include #include #include #define IPR_MAX_FLIT_ENTRIES 59 #define IPR_FLIT_TIMESTAMP_LEN 12 #define IPR_FLIT_FILENAME_LEN 184 #define IPRDBG_CONF "/etc/iprdbg.conf" #define IPRDBG_LOG "/var/log/iprdbg" #define SEPARATOR "--------------------------------------------------------------------------------" char *tool_name = "iprdbg"; enum iprdbg_cmd { IPRDBG_READ = IPR_IOA_DEBUG_READ_IOA_MEM, IPRDBG_WRITE = IPR_IOA_DEBUG_WRITE_IOA_MEM, IPRDBG_FLIT = IPR_IOA_DEBUG_READ_FLIT, IPRDBG_EDF = IPR_IOA_DEBUG_ENABLE_DBG_FUNC, IPRDBG_DDF = IPR_IOA_DEBUG_DISABLE_DBG_FUNC }; struct ipr_bus_speeds { u8 speed; char *description; }; struct ipr_bus_speeds bus_speeds[] = { {0, "Async"}, {1, "10MB/s"}, {2, "20MB/s"}, {3, "40MB/s"}, {4, "80MB/s"}, {5, "160MB/s (DT)"}, {13, "160MB/s (packetized)"}, {14, "320MB/s (packetized)"} }; struct ipr_flit_entry { u32 flags; u32 load_name; u32 text_ptr; u32 text_len; u32 data_ptr; u32 data_len; u32 bss_ptr; u32 bss_len; u32 prg_parms; u32 lid_flags; u8 timestamp[IPR_FLIT_TIMESTAMP_LEN]; u8 filename[IPR_FLIT_FILENAME_LEN]; }; struct ipr_flit { u32 ioa_ucode_img_rel_lvl; u32 num_entries; struct ipr_flit_entry flit_entry[IPR_MAX_FLIT_ENTRIES]; }; struct dbg_macro { char *cmd; }; static struct dbg_macro *macro; static int num_macros; static unsigned int last_adx; static unsigned int last_len; static unsigned int last_data; static char time_str[100]; static int getcurtime(char *buf, int max) { time_t cur_time, rc; struct tm *cur_tm; int len; buf[0] = '\0'; rc = time(&cur_time); if (rc == ((time_t)-1)) return -EIO; cur_tm = localtime(&cur_time); if (!cur_tm) return -EIO; len = strftime(buf, max, "%b %d %T", cur_tm); return len; } #define __logtofile(...) {if (outfile) {fprintf(outfile, __VA_ARGS__);}} #define logtofile(fmt, ...) \ do { \ if (outfile) { \ getcurtime(time_str, sizeof(time_str)); \ __logtofile("%s: "fmt, time_str, ##__VA_ARGS__); \ } \ } while (0) #define iprprint(...) {printf(__VA_ARGS__); logtofile(__VA_ARGS__);} #define __iprprint(...) {printf(__VA_ARGS__); __logtofile(__VA_ARGS__);} #define ipr_err(...) {fprintf(stderr, __VA_ARGS__); logtofile(__VA_ARGS__);} #define iprloginfo(...) {syslog(LOG_INFO, __VA_ARGS__); logtofile(__VA_ARGS__);} static FILE *outfile; static int debug_ioctl(struct ipr_ioa *ioa, enum iprdbg_cmd cmd, int ioa_adx, int mask, unsigned int *buffer, int len) { u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc, fd; u32 direction = SG_DXFER_NONE; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd < 0) { syslog(LOG_ERR, "Error opening %s. %m\n", ioa->ioa.gen_name); return fd; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_DEBUG; cdb[1] = cmd; cdb[2] = (ioa_adx >> 24) & 0xff; cdb[3] = (ioa_adx >> 16) & 0xff; cdb[4] = (ioa_adx >> 8) & 0xff; cdb[5] = ioa_adx & 0xff; cdb[6] = (mask >> 24) & 0xff; cdb[7] = (mask >> 16) & 0xff; cdb[8] = (mask >> 8) & 0xff; cdb[9] = mask & 0xff; cdb[10] = (len >> 24) & 0xff; cdb[11] = (len >> 16) & 0xff; cdb[12] = (len >> 8) & 0xff; cdb[13] = len & 0xff; if (cmd == IPRDBG_WRITE) direction = SG_DXFER_TO_DEV; else if (len > 0) direction = SG_DXFER_FROM_DEV; rc = sg_ioctl_noretry(fd, cdb, buffer, len, direction, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) ipr_err("Debug IOCTL failed. %d\n", errno); close(fd); return rc; } static int format_flit(struct ipr_flit *flit) { int num_entries = ntohl(flit->num_entries) - 1; struct ipr_flit_entry *flit_entry; signed int len; char filename[IPR_FLIT_FILENAME_LEN+1]; char time[IPR_FLIT_TIMESTAMP_LEN+1]; char buf1[IPR_FLIT_FILENAME_LEN+1]; char buf2[IPR_FLIT_FILENAME_LEN+1]; char lid_name[IPR_FLIT_FILENAME_LEN+1]; char path[IPR_FLIT_FILENAME_LEN+1]; int num_args, i; for (i = 0, flit_entry = flit->flit_entry; (i < num_entries) && (*flit_entry->timestamp); flit_entry++, i++) { snprintf(time, IPR_FLIT_TIMESTAMP_LEN, "%s", (char *)flit_entry->timestamp); time[IPR_FLIT_TIMESTAMP_LEN] = '\0'; snprintf(filename, IPR_FLIT_FILENAME_LEN, "%s", (char *)flit_entry->filename); filename[IPR_FLIT_FILENAME_LEN] = '\0'; num_args = sscanf(filename, "%184s " "%184s " "%184s " "%184s ", buf1, buf2, lid_name, path); if (num_args != 4) { ipr_err("Cannot parse flit\n"); return 1; } len = strlen(path); iprprint("LID Name: %s (%8X)\n", lid_name, ntohl(flit_entry->load_name)); iprprint("LID Path: %s\n", path); iprprint("Time Stamp: %s\n", time); iprprint("LID Path: %s\n", lid_name); iprprint("Text Segment: Address %08X, Length %8X\n", ntohl(flit_entry->text_ptr), ntohl(flit_entry->text_len)); iprprint("Data Segment: Address %08X, Length %8X\n", ntohl(flit_entry->data_ptr), ntohl(flit_entry->data_len)); iprprint("BSS Segment: Address %08X, Length %8X\n", ntohl(flit_entry->bss_ptr), ntohl(flit_entry->bss_len)); __iprprint("%s\n", SEPARATOR); } return 0; } static void ipr_fgets(char *buf, int size, FILE *stream) { int i, ch = 0; for (i = 0; ch != EOF && i < (size - 1); i++) { ch = fgetc(stream); if (ch == '\n') break; buf[i] = ch; } buf[i] = '\0'; strcat(buf, "\n"); } static void dump_data(unsigned int adx, unsigned int *buf, int len) { char ascii_buffer[17]; int i, k, j; for (i = 0; i < (len / 4);) { iprprint("%08X: ", adx+(i*4)); memset(ascii_buffer, '\0', sizeof(ascii_buffer)); for (j = 0; i < (len / 4) && j < 4; j++, i++) { __iprprint("%08X ", ntohl(buf[i])); memcpy(&ascii_buffer[j*4], &buf[i], 4); for (k = 0; k < 4; k++) { if (!isprint(ascii_buffer[(j*4)+k])) ascii_buffer[(j*4)+k] = '.'; } } for (;j < 4; j++) { __iprprint(" "); strncat(ascii_buffer, "....", sizeof(ascii_buffer)-1); } __iprprint(" |%s|\n", ascii_buffer); } } static int flit(struct ipr_ioa *ioa, int argc, char *argv[]) { struct ipr_flit flit; int rc; rc = debug_ioctl(ioa, IPRDBG_FLIT, 0, 0, (unsigned int *)&flit, sizeof(flit)); if (!rc) format_flit(&flit); return rc; } static int speeds(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int length = 64; unsigned int *buffer = calloc(length/4, 4); unsigned int adx, bus_speed; int num_buses, bus, i, j, rc; if (!buffer) { ipr_err("Memory allocation error\n"); return -ENOMEM; } if ((ioa->ccin == 0x2780) || (ioa->ccin == 0x2757)) num_buses = 4; else num_buses = 2; for (bus = 0, adx = 0xF0016380; bus < num_buses; bus++) { rc = debug_ioctl(ioa, IPRDBG_READ, adx, 0, buffer, length); if (!rc) { __iprprint("%s\n", SEPARATOR); iprprint("Bus %d speeds:\n", bus); for (i = 0; i < 16; i++) { bus_speed = ntohl(buffer[i]) & 0x0000000f; for (j = 0; j < sizeof(bus_speeds)/sizeof(struct ipr_bus_speeds); j++) { if (bus_speed == bus_speeds[j].speed) iprprint("Target %d: %s\n", i, bus_speeds[j].description); } } } switch(bus) { case 0: adx = 0xF001e380; break; case 1: adx = 0xF4016380; break; case 2: adx = 0xF401E380; break; }; } free(buffer); return 0; } static int eddf(struct ipr_ioa *ioa, enum iprdbg_cmd cmd, int argc, char *argv[]) { unsigned int parm1 = strtoul(argv[0], NULL, 16); unsigned int parm2 = 0; if (argc == 2) parm2 = strtoul(argv[1], NULL, 16); return debug_ioctl(ioa, cmd, parm1, parm2, NULL, 0); } static int edf(struct ipr_ioa *ioa, int argc, char *argv[]) { return eddf(ioa, IPRDBG_EDF, argc, argv); } static int ddf(struct ipr_ioa *ioa, int argc, char *argv[]) { return eddf(ioa, IPRDBG_DDF, argc, argv); } static int raw_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i, rc; unsigned int parm[4] = { 0 }; unsigned int *buf = NULL; for (i = 0; i < argc; i++) parm[i] = strtoul(argv[i], NULL, 16); if (argc == 4 && parm[3]) buf = calloc(parm[3]/4, 4); rc = debug_ioctl(ioa, parm[0], parm[1], parm[2], buf, parm[3]); if (!rc && buf) { dump_data(0, buf, parm[3]); last_data = ntohl(buf[0]); } free(buf); return rc; } static unsigned int get_adx(char *arg) { if (arg[0] == '@') { if (arg[1] == '+') return last_data + strtoul(&arg[2], NULL, 16); else if (arg[1] == '-') return last_data - strtoul(&arg[2], NULL, 16); else return last_data; } return strtoul(arg, NULL, 16); } static int bm4(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int adx; unsigned int write_buf = htonl(strtoul(argv[1], NULL, 16)); unsigned int mask = strtoul(argv[2], NULL, 16); adx = get_adx(argv[0]); if ((adx % 4) != 0) { ipr_err("Address must be 4 byte aligned\n"); return -EINVAL; } return debug_ioctl(ioa, IPRDBG_WRITE, adx, mask, &write_buf, 4); } static int mw4(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; unsigned int write_buf[16]; unsigned int adx; adx = get_adx(argv[0]); if ((adx % 4) != 0) { iprprint("Address must be 4 byte aligned\n"); return -EINVAL; } for (i = 0; i < (argc-1); i++) write_buf[i] = htonl(strtoul(argv[i+1], NULL, 16)); return debug_ioctl(ioa, IPRDBG_WRITE, adx, 0, write_buf, (argc-1)*4); } static int __mr4(struct ipr_ioa *ioa, unsigned int adx, int len) { unsigned int *buf; int rc; if ((adx % 4) != 0) { ipr_err("Address must be 4 byte aligned\n"); return -EINVAL; } if ((len % 4) != 0) { ipr_err("Length must be a 4 byte multiple\n"); return -EINVAL; } buf = calloc(len/4, 4); if (!buf) { ipr_err("Memory allocation error\n"); return -ENOMEM; } last_len = len; last_adx = adx; rc = debug_ioctl(ioa, IPRDBG_READ, adx, 0, buf, len); if (!rc) { dump_data(adx, buf, len); last_data = ntohl(buf[0]); } free(buf); return rc; } static int mr4(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int adx; int len = 4; if (argc == 0) { if (!last_len) { ipr_err("Not enough parameters specified\n"); return -EINVAL; } adx = last_adx + last_len; len = last_len; } else { adx = get_adx(argv[0]); if (argc == 2) len = strtoul(argv[1], NULL, 16); } return __mr4(ioa, adx, len); } static int help(struct ipr_ioa *ioa, int argc, char *argv[]) { printf("\n"); printf("mr4 address length " "- Read memory\n"); printf("mw4 address data " "- Write memory\n"); printf("bm4 address data preserve_mask " "- Modify bits set to 0 in preserve_mask\n"); printf("edf parm1 parm2 " "- Enable debug function\n"); printf("ddf parm1 parm2 " "- Disable debug function\n"); printf("raw cmd [parm1] [parm2] [length] " "- Manually execute IOA debug command\n"); printf("speeds " "- Current bus speeds for each device\n"); printf("flit " "- Format the flit\n"); printf("macros " "- List currently loaded macros\n"); printf("exit " "- Exit the program\n\n"); return 0; } static int quit(struct ipr_ioa *ioa, int argc, char *argv[]) { closelog(); openlog("iprdbg", LOG_PID, LOG_USER); iprloginfo("iprdbg on %s exited\n", ioa->pci_address); closelog(); exit(0); return 0; } static int macros(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; if (num_macros == 0) printf("No macros loaded. Place macros in "IPRDBG_CONF"\n"); for (i = 0; i < num_macros; i++) printf("%s\n", macro[i].cmd); return 0; } static const struct { char *cmd; int min_args; int max_args; int (*func)(struct ipr_ioa *,int argc, char *argv[]); } command [] = { { "mr4", 0, 2, mr4 }, { "mw4", 2, 17, mw4 }, { "bm4", 3, 3, bm4 }, { "edf", 1, 2, edf }, { "ddf", 1, 2, ddf }, { "raw", 1, 4, raw_cmd }, { "flit", 0, 0, flit }, { "speeds", 0, 0, speeds }, { "help", 0, 0, help }, { "?", 0, 0, help }, { "quit", 0, 0, quit }, { "q", 0, 0, quit }, { "exit", 0, 0, quit }, { "x", 0, 0, quit }, { "macros", 0, 0, macros }, }; static int last_cmd = -1; static int exec_cmd(struct ipr_ioa *ioa, int cmd, int argc, char *argv[]) { int num_args = argc - 1; if (num_args < command[cmd].min_args) { ipr_err("Not enough arguments specified.\n"); return -EINVAL; } if (num_args > command[cmd].max_args) { ipr_err("Too many arguments specified.\n"); return -EINVAL; } if (last_cmd != cmd) last_len = 0; last_cmd = cmd; return command[cmd].func(ioa, num_args, &argv[1]); } static int cmd_to_args(char *cmd, int *rargc, char ***rargv) { char *p; char **argv = NULL; int len = 0; int total_len = strlen(cmd); if (cmd[total_len-1] == '\n') { cmd[total_len-1] = '\0'; total_len--; } if (*cmd == '\0') return 0; for (p = strrchr(cmd, ' '); p; p = strrchr(cmd, ' ')) *p = '\0'; for (p = strrchr(cmd, '\t'); p; p = strrchr(cmd, '\t')) *p = '\0'; for (p = cmd, len = 0; p < cmd + total_len; p += (strlen(p) + 1), len++) { if (strlen(p) == 0) { len--; continue; } argv = realloc(argv, sizeof(char *)*(len+1)); argv[len] = p; } *rargv = argv; *rargc = len; return len; } static int exec_macro(struct ipr_ioa *ioa, char *cmd); static int __parse_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; for (i = 0; i < ARRAY_SIZE(command); i++) { if (strcasecmp(argv[0], command[i].cmd) != 0) continue; return exec_cmd(ioa, i, argc, argv); } for (i = 0; i < num_macros; i++) { if (strncasecmp(argv[0], macro[i].cmd, strlen(argv[0])) != 0) continue; return exec_macro(ioa, macro[i].cmd); } iprprint("Invalid command %s. Use \"help\" for a list " "of valid commands\n", argv[0]); return -EINVAL; } static int parse_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i, rc = 0; int index = 0; char *end; for (i = 0; i < argc; i++) { end = strchr(argv[i], ':'); if (!end) end = strchr(argv[i], ';'); if (!end) continue; if (strlen(argv[i]) == 1) { rc = __parse_cmd(ioa, i - index, &argv[index]); } else { *end = '\0'; rc = __parse_cmd(ioa, (i + 1) - index, &argv[index]); } index = i + 1; if (rc) return rc; } return __parse_cmd(ioa, i - index, &argv[index]); } static int exec_macro(struct ipr_ioa *ioa, char *cmd) { int argc; char **argv = NULL; char *copy = malloc(strlen(cmd) + 1); int rc = -EINVAL; if (!copy) return -ENOMEM; strcpy(copy, cmd); if (!cmd_to_args(copy, &argc, &argv)) goto out_free_argv; rc = parse_cmd(ioa, argc - 1, &argv[1]); free(copy); out_free_argv: free(argv); return rc; } static int main_cmd_line(int argc, char *argv[]) { struct ipr_dev *dev; if (argc < 3) { ipr_err("Invalid option\n"); return -EINVAL; } dev = find_dev(argv[argc-1]); if (!dev) { ipr_err("Invalid IOA specified: %s\n", argv[argc-1]); return -EINVAL; } return parse_cmd(dev->ioa, argc-2, &argv[1]); } static struct ipr_ioa *select_ioa() { int i, num_args, ioa_num = 0; struct ipr_ioa *ioa; char cmd_line[1000]; if (num_ioas > 1) { printf("\nSelect adapter to debug:\n"); i = 1; for_each_ioa(ioa) printf("%d. IBM %X: Location: %s %s\n", i++, ioa->ccin, ioa->pci_address, ioa->ioa.gen_name); printf("%d to quit\n", i); printf("\nSelection: "); fgets(cmd_line, 999, stdin); num_args = sscanf(cmd_line, "%d\n", &ioa_num); if (ioa_num == i) return NULL; if (num_args != 1 || ioa_num > num_ioas) return NULL; for (ioa = ipr_ioa_head, i = 1; i < ioa_num; ioa = ioa->next, i++) {} } else ioa = ipr_ioa_head; return ioa; } static int exec_shell_cmd(struct ipr_ioa *ioa, char *cmd) { char **argv = NULL; int argc, rc; if (!cmd_to_args(cmd, &argc, &argv)) return mr4(ioa, 0, NULL); rc = parse_cmd(ioa, argc, argv); free(argv); return rc; } static void add_new_macro(char *cmd) { char *buf = malloc(strlen(cmd) + 1); if (!buf) return; macro = realloc(macro, (num_macros + 1) * sizeof(struct dbg_macro)); if (!macro) { free(buf); return; } strcpy(buf, cmd); if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; macro[num_macros++].cmd = buf; } static void load_config() { char buf[2000]; FILE *config = fopen(IPRDBG_CONF, "r"); if (!config) return; while (!feof(config)) { if (!fgets(buf, sizeof(buf), config)) break; if (buf[0] == '#') continue; add_new_macro(buf); } fclose(config); } int main(int argc, char *argv[]) { char cmd_line[1000]; struct ipr_ioa *ioa; openlog("iprdbg", LOG_PERROR | /* Print error to stderr as well */ LOG_PID | /* Include the PID with each error */ LOG_CONS, /* Write to system console if there is an error sending to system logger */ LOG_USER); outfile = fopen(IPRDBG_LOG, "a"); if (!outfile) outfile = fopen(".iprdbglog", "a"); tool_init(0); check_current_config(false); load_config(); if (argc > 1) return main_cmd_line(argc, argv); ioa = select_ioa(); if (!ioa) return -ENXIO; closelog(); openlog("iprdbg", LOG_PID, LOG_USER); iprloginfo("iprdbg on %s started\n", ioa->pci_address); closelog(); openlog("iprdbg", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); printf("\nATTENTION: This utility is for adapter developers only! " "Use at your own risk!\n"); printf("\nUse \"quit\" to exit the program.\n\n"); while(1) { printf("IPRDB(%X)> ", ioa->ccin); ipr_fgets(cmd_line, 999, stdin); __logtofile("%s\n", SEPARATOR); logtofile("%s", cmd_line); exec_shell_cmd(ioa, cmd_line); } return 0; } iprutils/ipr.msg0000644000000000000000000005130412427235652012744 0ustar rootroot$quote " $set 0 Generic 1 "Select one of the following" 2 "Selection" 3 "Complete" 4 "to return to menu, current operations will continue" $set 101 main_menu 1 "Work with Disk Units" 2 "Display disk hardware status" 3 "Work with Disk Arrays" 4 "Work with disk unit recovery" 5 "Work with SCSI bus configuration" 6 "Work with driver configuration" 7 "Analyze log" $set 102 disk_status 1 "Display Disk Hardware Status" 2 "Type option, press Enter." 3 "Display hardware resource information details" 4 "No devices found" $set 104 device_details 1 "IOA Hardware Resource Information Details" 2 "Manufacturer" 3 "Machine Type and Model" 4 "Driver Version" 5 "Firmware Version" 6 "Serial Number" 7 "Part Number" 8 "Plant of Manufacturer" 9 "Cache Size" 10 "DRAM Size" 11 "Resource Name" 12 "Physical location" 13 "PCI Address" 14 "SCSI Host Number" 15 "Resource Path" 101 "Disk Array Information Details" 102 "RAID Level" 103 "Stripe Size" 104 "Capacity" 201 "Disk Unit Hardware Resource Information Details" 202 "Product ID" 203 "SCSI Host Number" 204 "SCSI Channel" 205 "SCSI Id" 206 "SCSI Lun" 207 "Level" 208 "Extended Details" 209 "FRU Number" 210 "EC Level" 211 "Device Specific (Z0)" 212 "Device Specific (Z1)" 213 "Device Specific (Z2)" 214 "Device Specific (Z3)" 215 "Device Specific (Z4)" 216 "Device Specific (Z5)" 217 "Device Specific (Z6)" $set 200 raid_screen 1 "Work with Disk Arrays" 2 "Display disk array status" 3 "Create a disk array" 4 "Delete a disk array" 5 "Add a device to a disk array" 6 "Format device for advanced function" 7 "Format device for JBOD function" 8 "Configure a hot spare device" 9 "Unconfigure a hot spare device" $set 201 raid_status 1 "Display Disk Array Status" 2 "Type option, press Enter." 3 "Display hardware resource information details" 4 "No devices found" $set 202 raid_start 1 "Create a Disk Array" 2 "Select the subsystems to create a disk array." 3 "Type choice, press Enter." 4 "create a disk array" $set 203 raid_stop 1 "Delete a Disk Array" 2 "Select the subsystems to delete a disk array." 3 "Type choice, press Enter." 4 "delete a disk array" $set 204 raid_stop_fail 1 "Delete a Disk Array Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no device parity protected units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 205 confirm_raid_stop 1 "Confirm Delete a Disk Array" 2 "ATTENTION: Disk units connected to these subsystems will not be\ protected after you confirm your choice." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." $set 207 raid_start_fail 1 "Create a Disk Array Failed" 2 "There are no disk units eligible for the selected operation due\ to one or more of the following reasons:" 3 "There are not enough device parity capable units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 208 configure_raid_start 1 "Select Disk Units for Parity Protection" 2 "Type option, press Enter." 3 "Select" $set 210 confirm_raid_start 1 "Confirm Start Device Parity Protection" 2 "ATTENTION: Data will not be preserved and may be lost on selected disk\ units when parity protection is enabled." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." $set 211 raid_start_complete 1 "Start Device Parity Protection Status" 2 "You selected to start device parity protection" $set 215 raid_include 1 "Add a Device to a Disk Array" 2 "Select the subsystem that the disk unit will be included." 3 "Type choice, press Enter." 4 "Select Parity Array to Include Disk Unit" $set 216 raid_include_fail 1 "Include Device Parity Protection Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no device parity protected units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 217 configure_raid_include 1 "Include Disk Units in Device Parity Protection" 2 "Select the units to be included in Device Parity Protection" 3 "Type choice, press Enter." 4 "Add a Device to a Disk Array" $set 218 configure_raid_include_fail 1 "Include Disk Unit Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are not enough disk units available to be included." 4 "The disk unit that needs to be included is not at the right\ location. Examine the 'message log' for the exposed unit\ and make sure that the replacement unit is at the correct\ location" 5 "Not all disk units attached to an IOA have reported to the\ system. Retry the operation." 6 "The disk unit to be included must be the same or greater\ capacity than the smallest device in the volume set and\ be formatted correctly" 7 "The type/model of the disk units is not supported for the\ requested operation" $set 219 confirm_raid_include 1 "Confirm Disk Units to be Included" 2 "ATTENTION: All listed device units will be formatted and zeroed before\ listed device is added to array." 3 "Press Enter to confirm your choice to have the system include\ the selected units in device parity protection" 4 "Cancel to return and change your choice." $set 221 dev_include_complete 1 "Include Disk Units in Device Parity Protection Status" 2 "The operation to include units in the device parity protection\ will be done in two phases. The device must first be formatted\ before device may be included into array set." 3 "You selected to include a device in a device parity set" $set 230 af_include_fail 1 "Format Device for Advanced Function Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 231 af_remove_fail 1 "Disable Device for Advanced Functions Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 232 add_hot_spare_fail 1 "Enable Device as Hot Spare Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 233 remove_hot_spare_fail 1 "Disable Device as Hot Spare Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 236 1 "Configure a hot spare device" 2 "Select the subsystems which disk units will be configured as hot spares" 3 "Type choice, press Enter." 4 "Select subsystem" 5 "Unconfigure a hot spare device" 6 "Select the subsystems which disk units will be unconfigured as hot spares" 7 "Type choice, press Enter." 8 "Select subsystem" $set 240 select_hot_spare 1 "Select Disk Units to Enable as Hot Spare" 2 "Type option, press Enter." 3 "Select" 4 "Select Disk Units to Disable as Hot Spare" 5 "Type option, press Enter." 6 "Select" $set 245 confirm_hot_spare 1 "Confirm Enable Hot Spare" 2 "ATTENTION: Existing data on these disk units will not be preserved." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." 5 "Confirm Disable Hot Spare" 6 "ATTENTION: Existing data on these disk units will not be preserved." 7 "Press Enter to continue." 8 "Cancel to return and change your choice." $set 300 disk_unit_recovery 1 "Work with Disk Unit Recovery" 2 "Concurrent Add Device" 3 "Concurrent Remove/Replace Device" 4 "Initialize and format disk unit" 5 "Reclaim IOA cache storage" 6 "Rebuild disk unit data" 7 "Work with resources containing cache battery packs" $set 301 concurrent_add_device 1 "Concurrent Device Add" 2 "Choose a single location for add operations" 3 "Select" $set 302 concurrent_remove_device 1 "Concurrent Device Remove" 2 "Choose a single location for remove operations" 3 "Select" 4 "Concurrent Device Add" 5 "Choose a single location for add operations" 6 "Select" $set 303 process_conc_maint 1 "Verify Device Concurrent Remove" 2 "Verify the selected device for concurrent remove operations" 3 "Press Enter when verification complete and ready to continue" 4 "Cancel to return and change your choice." 5 "Verify Device Concurrent Add" 6 "Verify the seleted device for concurrent add operations" 7 "Press Enter when verification complete and ready to continue" 8 "Cancel to return and change your choice." 9 "Complete Device Concurrent Remove" 10 "Remove selected device" 11 "Press Enter when selected device has been removed" 12 "Cancel to return and change your choice." 13 "Complete Device Concurrent Add" 14 "Add device to selected location" 15 "Press Enter when device has been installed" 16 "Cancel to return and change your choice." $set 312 configure_af_device | init_device 1 "Select Disk Units to format for Advanced Function" 2 "Select Disk Units to format for JBOD Function" 3 "Select Disk Units for Initialize and Format" 4 "Type option, press Enter." 5 "Select" $set 313 1 "Confirm Initialize and Format Disk Unit" 2 "Press 'c' to confirm your choice for 1=Initialize and format." 3 "Return to change your choice." $set 314 dev_init_complete 1 "Initialize and Format Status" 2 "You selected to initialize and format a disk unit" 4 "Please wait for the next screen." $set 315 reclaim_cache 1 "Reclaim IOA Cache Storage" 2 "Select the IOA to reclaim IOA cache storage." 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ Damaged objects may result on the system." 4 "Type choice, press Enter." 5 "Reclaim IOA cache storage." $set 316 confirm_reclaim 1 "Confirm Reclaim IOA Cache Storage" 2 "The disk units that may be affected by the function are displayed." 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ Filesystem corruption may result on the system." 4 "Press c=Confirm to reclaim cache storage." 5 "Cancel to return to change your choice." $set 317 confirm_reclaim_warning 2 "ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!" 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ By proceeding you will be allowing UNKNOWN data loss.\ This data loss may or may not be detected by the host operating system.\ Filesystem corruption may result on the system." 4 "Press 's' to continue." $set 319 reclaim_result 1 "Reclaim IOA Cache Storage Results" 2 "IOA cache storage reclamation has completed. Use the number\ of lost sectors to decide whether to restore data from the\ most recent save media or to continue with possible data loss." 3 "Number of lost sectors" 4 "IOA cache storage reclamation has completed. The number of\ lost sectors could not be determined." $set 321 raid_rebuild 1 "Rebuild Disk Unit Data" 2 "Select the units to be rebuilt" 3 "Type choice, press Enter." 4 "Rebuild" $set 322 raid_rebuild_fail 1 "Rebuild Disk Unit Data Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no disk units that need to be rebuilt." 4 "The disk unit that needs to be rebuild is not at the right\ location. Examine the 'message log' for the exposed unit\ and make sure that the replacement unit is at the correct\ location." 5 "Not all disk units attached to an IOA have reported to the\ system. Retry the operation." 6 "All the disk units in a parity set must be the same capacity\ with a minumum number of 4 disk units and a maximum of 10 units\ in the resulting parity set." 7 "The type/model of the disk units is not supported for the\ requested operation." $set 323 confirm_raid_rebuild 1 "Confirm Rebuild Disk Unit Data" 2 "Rebuilding the disk unit data may take several minutes for\ each unit selected." 3 "Press Enter to confirm having the data rebuilt." 4 "Cancel to return and change your choice." $set 330 battery_maint 1 "Work with Resources Containing Cache Battery Packs" 2 "Type options, press Enter" 3 "Force battery pack into error state" 4 "Display battery information" $set 341 confirm_force_battery_error 1 "Force Battery Packs Into Error State" 2 "ATTENTION: This service function should be run only under the direction\ of the IBM Hardware Service Support" 3 "You have selected to force a cache batter error on an IOA" 4 "You will have to replace the Cache Battery Pack in each selected IOA to\ resume normal operations." 5 "System performance could be significantly degraded until the cache battery\ packs are replaced on the selected IOAs." 6 "Continue to force the following battery packs into an error state" $set 332 show_battery_info 1 "Battery Information" 2 "Resource Name" 3 "Serial Number" 4 "Type" 5 "PCI Address" 6 "SCSI Host Number" 7 "Battery type" 8 "Battery state" 9 "Power on time (days)" 10 "Adjusted power on time (days)" 11 "Estimated time to warning (days)" 12 "Estimated time to error (days)" 13 "Resource Path" 21 "Nickel Cadmium (NiCd)" 22 "Nickel Metal Hydride (NiMH)" 23 "Lithium Ion (LiIon)" 24 "Unknown" 31 "No battery warning" 32 "Battery warning issued" 33 "Battery error issued" $set 401 bus_config 1 "Work with SCSI Bus Configuration" 2 "Select the subsystem to change scsi bus attribute." 3 "Type choice, press Enter." 4 "change scsi bus attribute" $set 402 bus_config_fail 1 "Work with SCSI Bus Configuration Failed" 2 "There are no SCSI Buses eligible for the selected operation.\ None of the installed adapters support the requested\ operation." $set 403 change_bus_attr 1 "Change SCSI Bus Configuration" 2 "Current Bus configurations are shown. To change\ setting hit 'c' for options menu. Hightlight desired\ option then hit Enter" 3 "Change Setting" 4 "BUS" 5 "QAS Capability" 6 "Host SCSI ID" 7 "Wide Enabled" 8 "Maximum Bus Throughput" 9 "Enable" 10 "Enabled" 11 "Disable" 12 "Disabled" 13 "Yes" 14 "No" 21 "Confirm Change SCSI Bus Configuration" 22 "Confirming this action will result in the following\ configuration to become active." 23 "Confirm Change SCSI Bus Configration" $set 458 driver_config 1 "Adapter Driver Configuration" 2 "Select subsystem to change driver configuration" 3 "Type choice, press Enter" 4 "change driver configuration" $set 459 change_driver_config 1 "Change Driver Configuration" 2 "Current Driver configurations are shown. To change\ setting, type in new value then hit Enter" 3 "Current Verbosity" 4 "New Verbosity" $set 500 log_menu 1 "Kernel Messages Log" 2 "View most recent IBM Storage error messages" 3 "View IBM Storage error messages" 4 "View all kernel error messages" 5 "View sisconfig error messages" 6 "Set root kernel message log directory" 7 "Set default editor" 8 "Restore defaults" 9 "View IBM boot time messages" $set 501 kernel_root 1 "Kernel Messages Log Root Directory" 2 "Enter new directory and press Enter" 3 "Current root directory:" 4 "New root directory:" $set 502 confirm_kernel_root 1 "Confirm Root Directory Change" 2 "Press 'c' to confirm your new kernel root directory choice." 3 "To return to the log menu without changing the root directory, choose 'q'." 4 "New root directory:" $set 503 set_default_editor 1 "Kernel Messages Editor" 2 "Enter new editor command and press Enter" 3 "Current editor:" 4 "New editor:" $set 504 confirm_set_default_editor 1 "Confirm Editor Change" 2 "Press 'c' to confirm your editor choice" 3 "To return to the log menu without changing the editor, choose 'q'." 4 "New editor:" $set 888 screen status strings 1 "" 2 "Invalid option specified" 3 "Screen paged up" 4 "Already at top" 5 "Screen paged down" 6 "Already at bottom" 10 "No devices to show details for" 15 "The selection is not valid" 16 "Error. More than one unit was selected." 17 "Invalid option. No devices selected." 18 "Start Parity Protection completed successfully" 19 "Start Parity Protection failed." 20 "Stop Parity Protection failed" 21 "Stop Parity Protection completed successfully." 25 "Error: number of devices selected must be a multiple of %d" 26 "Include failed" 27 "Include Unit completed successfully" 28 "Rebuild started, view Parity Status Window for rebuild progress" 29 "Rebuild failed" 30 "Device concurrent maintenance failed" 31 "Device concurrent maintenance failed, device in use" 32 "Device concurrent maintenance completed successfully" 33 "No units available for initialize and format" 34 "Initialize and format completed successfully" 35 "Initialize and format failed" 36 "Reclaim IOA Cache Storage completed successfully" 37 "Reclaim IOA Cache Storage failed" 38 "No Reclaim IOA Cache Storage is necessary" 39 "No Reclaim IOA Cache Storage performed" 40 "Rebuild started, view Parity Status Window for rebuild progress" 41 "Rebuild failed" 42 "The selected battery packs have successfully been placed into an error state." 43 "Attempting to force the selected battery packs into an error state failed." 44 "No configured resources contain a cache battery pack." 45 "Change SCSI Bus configurations completed successfully" 46 "Change SCSI Bus configurations failed" 47 "Change Device Driver Configurations completed successfully" 48 "Change Device Driver Configurations failed" 49 "No units available for initialize and format" 50 "Initialize and format completed successfully" 51 "Initialize and format failed" 52 "No devices available for the selected hot spare operation" 53 "Enable Device as Hot Spare Completed successfully" 54 "Disable Device as Hot Spare Completed successfully" 55 "Configure a hot spare device failed" 56 "Unconfigure a hot spare device failed" 57 "Change Device Driver Configurations completed successfully" 58 "Change Device Driver Configurations failed" 59 "Invalid directory" 60 "Root directory changed to %s" 61 "Root directory unchanged" 62 "Editor changed to %s" 63 "Editor unchanged" 64 "Default log values restored" 65 "Editor returned %d. Try setting the default editor." $set 999 empty set used for screens without displays iprutils/version.mk0000644000000000000000000000131012427235652013450 0ustar rootroot# (C) Copyright 2000, 2011 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. IPR_MAJOR_RELEASE=2 IPR_MINOR_RELEASE=4 IPR_FIX_LEVEL=5 IPR_RELEASE=1 IPR_FIX_DATE=(Nov 7, 2014) IPR_VERSION_STR=$(IPR_MAJOR_RELEASE).$(IPR_MINOR_RELEASE).$(IPR_FIX_LEVEL) $(IPR_FIX_DATE) IPR_DEFINES = -DIPR_MAJOR_RELEASE=$(IPR_MAJOR_RELEASE) \ -DIPR_MINOR_RELEASE=$(IPR_MINOR_RELEASE) \ -DIPR_FIX_LEVEL=$(IPR_FIX_LEVEL) \ -DIPR_FIX_DATE='"$(IPR_FIX_DATE)"' \ -DIPR_VERSION_STR='"$(IPR_VERSION_STR)"' \ -DIPR_RELEASE=$(IPR_RELEASE) iprutils/iprconfig.80000644000000000000000000006431712427235652013523 0ustar rootroot.\" (C) Copyright 2000, 2009 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRCONFIG 8 "August 2009" .SH NAME iprconfig \- IBM Power RAID storage adapter configuration/recovery utility .SH SYNOPSIS .BI "iprconfig [-e editor] [-k dir] [-c command]" .sp .BI "iprconfig --version --debug --force" .SH DESCRIPTION .B iprconfig is used to configure IBM Power RAID storage adapters, display information about them, and to perform adapter and disk unit recovery. The menu options are: .PP .B 1. Display hardware status. .br This option can be used to display various information regarding the IBM Power RAID adapters attached to the system and the disk units controlled by them. For each adapter and disk unit, their /dev name, physical location, description, vendor/product ID, and hardware status will be available. Beside each resource is an OPT field. By entering a 1 beside any resource, detailed information about that resource can be obtained. For an adapter resource, this will display the adapter firmware version and the physical location amongst other things. .PP .B 2. Work with Disk Arrays .br This option will present a second menu containing disk array related commands. .PP .B Display disk array status is used to display the status of disk arrays on the system. .PP .B Create a disk array is used to create a disk array. .PP .B Delete a disk array is used to delete disk arrays. Selecting this option will provide you with a list of disk arrays which can be deleted. .PP .B Add a device to a disk array is used to include devices of similar capacity into an existing disk array. This function is currently only supported for RAID 5 and RAID 6 disk arrays. .PP .B Format device for advanced function is used to format disks to 522 bytes/sector so that they may be used in a disk array. Only disks which are not formatted for advanced function or are formatted for advanced function but are not known to be zeroed will be available for selection for this function. .PP .B Format device for JBOD function (512) is used to format disks to 512 bytes/sector so that they may be used as standalone disks. Only disks which are not formatted for JBOD function or are formatted for JBOD function and are in the Format Required state will be available for this function. .PP .B Work with hot spares is used to create a hot spare which designates a device as a dedicated hot spare. It is also used to delete a hot spare which unconfigures a previously configured hot spare. .\".B Create a hot spare .\"is used to designate a device as a dedicated hot spare. .\".PP .\".B Delete a hot spare .\"is used to unconfigure a previously configured hot spare. .PP .B Work with asymmetric access is used to select which path of a disk array will be the primary path in a dual controller environment. Asymmetric Access must be enabled on the adapter first. Not all adapters support asymmetric access and adapters that do provide support may require updated microcode. .PP .B Force RAID Consistency Check is used to force a consistency check on a RAID array. All ipr adapters continually perform background consistency checking when idle. This option can be used to force a consistency check to be performed. .PP .B Migrate disk array protection is used to change the RAID protection level for an array to another supported level. In some cases, this will require adding more disks to the array. In other cases, disks will be freed. .PP .B 3. Work with disk unit recovery is used to perform the following disk unit recovery actions: .PP .B Concurrent add device is used to concurrently add a new disk to a running system. This feature is only supported with SES (SCSI Enclosure Services) packaging. .PP .B Concurrent remove device is used to concurrently remove a disk from a running system. This feature is only supported with SES (SCSI Enclosure Services) packaging. .PP .B Initialize and format disk unit is used to issue a SCSI format command to attached devices. A format unit command has special meaning to the adapter and is used as a service action for certain error conditions. Formatting a disk unit will lose all data on that drive. If the disk is attached to an ipr adapter that does not support RAID, the drive will be formatted to 512 bytes/sector. If the disk is attached to an ipr RAID adapter, the block size will not be changed. To change the block size, use the format menu options under the disk arrays menu. .PP .B Reclaim IOA cache storage is used to repair cache error conditions. .B ATTENTION: Use this option with care. This is used to discard data from the cache and may result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .PP .B Rebuild disk unit data is generally used following concurrent maintenance. Select this option after a failing array member device has been replaced to reconstruct the device as an active array member. .PP .B Work with resources containing cache battery packs is used to display information regarding rechargeable cache battery packs and to force rechargeable cache battery packs into an error state so that they can be replaced prior to failure. .B ATTENTION: Once an error has been forced on a rechargeable cache battery pack write caching will be disabled until the battery pack is replaced. .PP .B 4. Work with SCSI bus configuration is used to change configurable SCSI bus attributes, such as maximum SCSI bus speed, SCSI initiator ID, etc. .PP .B 5. Work with driver configuration is used to change driver configurable attributes, such as log_level. .PP .B 6. Work with disk configuration is used to change configurable disk attributes, such as queue depth. .PP .B 7. Work with adapter configuration is used to change configurable adapter attributes, such as dual adapter settings. Refer to the following command line options: primary, secondary, query-ha-mode, set-ha-mode, set-ioa-asymmetric-access and set-array-asymmetric-access for more information regarding these settings. .PP .B 8. Download microcode is used to download microcode to ipr adapters and attached SCSI disks. .PP .B 9. Analyze Log is an option available to analyze /var/log/messages* files. By default it will use vi as the editor to open the concatenated error log files. This can be changed by using option 6 on the .B Kernel Messages Log menu. Selecting option 1 on the Kernel Messages Log menu will display only the most recent errors logged by the ipr device driver and may be useful to filter out some of the clutter. Option 2 will display all recorded errors logged by the ipr device driver. Option 3 will display all kernel messages. Option 4 will display errors logged by the iprconfig utility. This may be useful for debugging problems. Option 5 can be used to change where the tool looks to find the kernel messages files. The default is to look in /var/log. .SH OPTIONS .TP .B \-e editor Default editor for viewing error logs. The default editor is vi, but can be changed with this parameter. .TP .B -k directory Kernel messages root directory. Root directory to look for kernel messages. Default is /var/log. .TP .B -c command Command line, non-interactive commands. Currently supported commands include: .RS .TP .B show-config .br Show ipr configuration. .TP .B show-alt-config .br Show alternate ipr configuration information. .TP .B show-ioas .br Show all ipr adapters. .TP .B show-arrays .br Show all ipr arrays. .TP .B show-battery-info [IOA] .br Show cache battery information for specified IOA. Example: .br .B iprconfig -c show-battery-info sg5 .TP .B show-details [device] .br Show device details for specified device. Example: .br .B iprconfig -c show-details sda .TP .B show-hot-spares .br Show all configured hot spares. .TP .B show-af-disks .br Show disks formatted for Advanced Function that are not configured in an array or as a hot spare. .TP .B show-all-af-disks .br Show all disks formatted for Advanced Function .TP .B show-jbod-disks .br Show all disks formatted for JBOD Function. .TP .B status [device] .br Show the status of the specified device. This is the same status as which shows up in the last column of the Display hardware status menu. Can specify either a /dev/sdX name or a /dev/sgX name. Example: .br .B iprconfig -c status /dev/sda .br .TP .B alt-status [device] .br Show the status of the specified device. This is the same status as above with the exception of when a long running command is executing to the device, in which case the percent complete for the long running command is printed. .br .TP .B query-raid-create [IOA] .br Show all devices attached to the specified IOA that are candidates for being used in a RAID array. Example: .br .B iprconfig -c query-raid-create sg5 .TP .B query-raid-delete [IOA] .br Show all RAID arrays attached to the specified IOA that can be deleted. .br .B iprconfig -c query-raid-delete sg5 .TP .B query-hot-spare-create [IOA] .br Show all devices attached to the specified IOA that are candidates for being hot spares. .TP .B query-hot-spare-delete [IOA] .br Show all hot spares attached to the specified IOA that can be deleted. .TP .B query-raid-consistency-check .br Show all RAID arrays that are candidates for a RAID consistency check. .TP .B query-format-for-jbod .br Show all disks that can be reformatted for JBOD function. .TP .B query-reclaim .br Show all IOAs that may need a reclaim cache storage. .TP .B query-arrays-raid-include .br Show all RAID arrays that can have disks included in them to increase their capacity. .TP .B query-devices-raid-include [array] .br Show all disks that can be added to the specified array to increase its capacity. .TP .B query-supported-raid-levels [IOA] .br Show all RAID levels supported by the specified adapter. .TP .B query-include-allowed [IOA] [raid level] .br Some RAID levels allow for adding additional disks to existing disk arrays to increase their capacity. Prints "yes" to stdout if the specified RAID level supports this function, else prints "no". .TP .B query-max-devices-in-array [IOA] [raid level] .br Print the maximum number of devices allowed in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-min-devices-in-array [IOA] [raid level] .br Print the minimum number of devices allowed in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-min-mult-in-array [IOA] [raid level] .br Print the minimum multiple of devices required in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-supp-stripe-sizes [IOA] [raid level] .br Print all supported stripe sizes supported for RAID arrays of the specified RAID level on the specified RAID adapter. Stripe sizes are printed in units of KB. .TP .B query-recommended-stripe-size [IOA] [raid level] .br Print the default/recommended stripe size for RAID arrays of the specified RAID level on the specified RAID adapter. Stripe size is in units of KB. .TP .B query-recovery-format .br Show all disks that can be formatted for error recovery purposes. .TP .B query-raid-rebuild .br Show all disks in RAID arrays that can be rebuilt. .TP .B query-format-for-raid .br Show all disks that can be formatted such that they can be used in a RAID array or as a hot spare. .TP .B query-ucode-level [device] .br Show the microcode level that is currently loaded on the specified device. Note: The device specified may be the sg device associated with an IOA, in which case the IOA's microcode level will be shown. .TP .B query-format-timeout [device] .br Show the current format timeout to be used when formatting the specified disk. This value is only applicable when the device is currently in Advanced Function format. .TP .B query-qdepth [device] .br Show the queue depth currently being used for the specified disk. .TP .B query-tcq-enable [device] .br Print 1 to stdout if tagged queuing is enabled for the specified device, else print 0 to stdout. .TP .B query-log-level [IOA] .br Print the current log level being used for the specified IOA. Can be a number from 0 to n. .TP .B query-add-device .br Show all empty disk slots that can have a disk concurrently added. .TP .B query-remove-device .br Show all disk slots which are either empty or have disks in them which can be concurrently removed from the running system. .TP .B query-initiator-id [IOA] [busno] .br Show the current SCSI initiator ID used by the IOA for the specified SCSI bus. .TP .B query-bus-speed [IOA] [busno] .br Show the current maximum SCSI bus speed allowed on the specified SCSI bus. .TP .B query-bus-width [IOA] [busno] .br Show the current SCSI bus width in units of bits for the specified SCSI bus. .TP .B query-path-status [IOA] .br Show the current dual path state for the SAS devices attached specified IOA. .TP .B query-path-details [device] .br Show the current dual path details for the specified SAS device. .TP .B query-arrays-raid-migrate .br Show the arrays that can be migrated to a different protection level. .TP .B query-devices-raid-migrate [array] .br Show the AF disks that are candidates to be used in a migration for a given array. .TP .B query-raid-levels-raid-migrate [array] .br Show the protection levels to which the given array can be migrated. .TP .B query-stripe-sizes-raid-migrate [array] [raid level] .br Given an array and a protection level, show the valid stripe sizes to which the array can be migrated. .TP .B query-devices-min-max-raid-migrate [array] [raid level] .br Show the number of devices that will be removed for a migration to a protection level that requires fewer devices. Or, show the minmum number of devices required, the maximum number of devices allowed and the multiple of the number of devices required for a migration that requires more devices. .TP .B query-ioas-asymmetric-access .br Show the IOAs that support asymmetric access. .TP .B query-arrays-asymmetric-access .br Show the disk arrays that are candidates for setting their asymmetric access mode to Optimized or Non-Optimized. .TP .B query-ioa-asymmetric-access-mode [IOA] .br Show the current asymmetric access mode for the given IOA. .TP .B query-array-asymmetric-access-mode [array] .br Show the current asymmetric access mode for the given disk array. .TP .B query-ioa-caching [IOA] .br Show whether or not the user requested caching mode for the given IOA is set to default or disabled. .TP .B query-array-label [label] .br Show the device name of the array with the specified label. Label must have been specified when creating the RAID array. See raid-create command. .TP .B query-array [location] .br Show the device name of the array of which one of the disks in the array has the specified platform location code. .TP .B query-device [location] .br Show the device name of the disk that has the specified platform location code. .TP .B query-location [device] .br The device specified can be either the device name of a disk or the device name of a single disk RAID 0 array. If the specified device name is a disk, the platform location code will be displayed. If the specified device name is a single device RAID 0 array, the platform location of the disk which is a member of the specified array will be displayed. .TP .B raid-create [-r raid_level] [-s stripe_size_in_kb] [-l label] [devices...] Create a RAID array. RAID level can be any supported RAID level for the given adapter, such as 0, 10, 5, 6. Currently supported stripe sizes in kb include 16, 64, and 256. If raid_level is not specified, it will default to RAID 5. If stripe size is not specified, it will default to the recommended stripe size for the selected RAID level. Devices are specified with their full name, either the /dev/sd name or the /dev/sg name is acceptable. On some RAID adapters, a label can also be specified. Example array creation: .br .B iprconfig -c raid-create -r 5 -s 64 /dev/sda /dev/sdb /dev/sdc .br This would create a RAID 5 array with a 64k stripe size using the specified devices. .TP .B raid-delete [RAID device] Delete the specified RAID array. Specify either the /dev/sd name or the /dev/sg name. Only 1 array can be deleted with a single command. Example: .br .B iprconfig -c raid-delete /dev/sda .br This would delete the disk array represented by /dev/sda .TP .B raid-include [array] [disk] ... [disk] .br Add the specified devices to the specified disk array to increase its capacity. Example: .br .B iprconfig -c raid-include sda sg6 sg7 .TP .B raid-migrate -r raid_level [-s stripe_size_in_kb] array [disk] ... [disk] .br Migrate an existing RAID array to a new RAID protection level. Optionally, a new stripe size can be given. In some cases one or more new disks must be added for the migration to succeed. Example: .br .B iprconfig -c raid-migrate -r 10 -s 64 sda sg5 sg6 .TP .B format-for-raid [disk] ... [disk] .br Format the specified disks for Advanced Function so they can be used in a RAID array or as a hot spare. .TP .B format-for-jbod [disk] ... [disk] .br Format the specified disks for JBOD Function so they can be used as standalone disks. .TP .B recovery-format [disk] ... [disk] .br Format the specified disks as directed by the reference guide for error recovery purposes. .TP .B hot-spare-create [disk] .br Create a hot spare using the specified Advanced Function disk. .TP .B hot-spare-delete [disk] .br Delete the specified hot spare. .TP .B disrupt-device [disk] .br Force the specified Advanced Function device failed. .TP .B reclaim-cache [IOA] .br Reclaim the specified IOA's write cache. .B ATTENTION: Use this option with care. This is used to discard data from the cache and may result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .TP .B reclaim-unknown-cache [IOA] .br Reclaim the specified IOA's write cache and allow unknown data loss. .B ATTENTION: Use this option with care. This is used to discard data from the cache and WILL result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .TP .B raid-consistency-check [array] .br Force a full RAID consistency check on the specified array. This command will return before the RAID consistency check has completed. Use the .B status command to check the status of the command. .TP .B raid-rebuild [disk] .br Following a disk replacement for a failed disk in a RAID array, use this command to rebuild the failed disk's data onto the new disk and return the disk array to the .B Active state. .TP .B update-ucode [device] [microcode file] .br Update the microcode on the specified device (IOA or disk) with the specified microcode file. .B ATTENTION: Limited checking of the microcode image is done. Make sure the specified microcode file is the correct file for the specified device. .TP .B set-format-timeout [disk] [timeout in hours] .br Set the format timeout to be used when formatting the specified disk. .TP .B set-qdepth [device] [queue depth] .br Set the queue depth for the specified device or disk array. .TP .B set-tcq-enable [device] [0 = disable, 1 = enable] .br Enable/disable tagged command queueing for the specified device. .TP .B set-log-level [IOA] [log level] .br Set the error logging verbosity to use for the specified IOA. Default is 2. .TP .B identify-disk [disk] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk identify LED for the specified disk. This function may or may not be available depending on the hardware packaging. .TP .B identify-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk identify LED for the specified location. This function may or may not be available depending on the hardware packaging. Example: .br .B iprconfig -c identify-slot 0000:d8:01.0/0:1:1: 1 .TP .B remove-disk [disk] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk remove identify LED for the specified device. When 1 is specified as the second argument, the specified disk is set to the remove state. When in this state, the disk may be removed. Once the disk has been physically removed, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically removes the disk from the host operating system. .TP .B remove-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk remove identify LED for the specified location. When 1 is specified as the second argument, the specified location is set to the remove state. When in this state, the disk may be removed. Once the disk has been physically removed, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically removes the disk from the host operating system. Example: .br .B iprconfig -c remove-slot 0000:d8:01.0/0:1:1: 1 .TP .B add-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk insert identify LED for the specified location. When 1 is specified as the second argument, the specified location is set to the insert state. When in this state, the disk may be inserted. Once the disk has been physically inserted, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically adds the disk to the host operating system. Example: .br .B iprconfig -c add-slot 0000:d8:01.0/0:1:1: 1 .TP .B set-initiator-id [IOA] [busno] [initiator id] .br Set the IOA's SCSI initiator ID for the specified bus. Must be a value between 0 and 7 and must not conflict with any other device on the SCSI bus. .TP .B set-bus-speed [IOA] [busno] [speed in MB/sec] .br Set the maximum SCSI bus speed allowed on the specified SCSI bus. .TP .B set-bus-width [IOA] [busno] [bus width in # bits] .br Set the SCSI bus width to use for the specified SCSI bus. Example: .br .B iprconfig -c set-bus-width sg5 0 16 .TP .B primary [IOA] .br Set the adapter as the .B preferred primary adapter. This is used in dual initiator RAID configurations to indicate which adapter should be the .B primary adapter. The .B primary adapter should be the adapter receiving the majority of the I/O. Example: .br .B iprconfig -c primary sg5 .TP .B secondary [IOA] .br Set the adapter to indicate it is not the .B preferred primary adapter. See the notes for the .B preferred primary for additional information. Example: .br .B iprconfig -c secondary sg5 .TP .B set-all-primary .br Set all attached ipr adapters as the .B preferred primary adapter. This can be used when running a dual initiator RAID HA configuration. This command can be run on the .B primary system to quickly enable the .B preferred primary mode for all attached adapters. Refer to /etc/ha.d/resource.d/iprha for an example of how this might be used. Example: .br .B iprconfig -c set-all-primary .TP .B set-all-secondary .br Set all attached ipr adapters to indicate they are not the .B preferred primary adapter. Example: .br .B iprconfig -c set-all-secondary .TP .B query-ha-mode [IOA] .br When an adapter is configured in a highly available dual adapter configuration, it may be able to be configured in one of two ways. The default mode is .B Normal. This mode is used for all SCSI adapters and many SAS adapters. Some SAS adapters also support a .B JBOD dual adapter configuration. This mode is to be used when the dual adapter configuration is to consist of JBOD disks rather than RAID arrays. If the adapter is .B NOT going to be used in a dual adapter configuration, this mode .B MUST be set to .B Normal. Example: .br .B iprconfig -c query-ha-mode sg5 .TP .B set-ha-mode [IOA] [Normal | RAID] Used to set the high-availability mode of the adapter. Refer to the .B query-ha-mode command for more information regarding these settings. Example: .br .B iprconfig -c set-ha-mode sg5 Normal .TP .B set-array-asymmetric-access-mode [array] [Optimized | Non-Optimized] Used to set the asymmetric access mode of the disk array. Example: .br .B iprconfig -c set-array-asymmetric-access-mode sda Optimized .TP .B set-ioa-asymmetric-access-mode [IOA] [Enabled | Disabled] Used to set the asymmetric access mode of the IOA. .br Example: .br .B iprconfig -c set-ioa-asymmetric-access-mode sg5 Enabled .TP .B set-ioa-caching [IOA] [Default | Disabled] Used to set the requested caching mode of the IOA. .br Example: .br .B iprconfig -c set-ioa-caching sg5 Disabled .TP .B get-live-dump [IOA] Dump the IOA's implementation unique critical information. The dump data will be saved in the /var/log/ directory with the pattern ipr-CCIN-PCI_ADDRESS-dump-TIMESTAMP. .br Example: .br .B iprconfig -c get-live-dump sg5 .RE .TP .B \-\-version Print version number of .B iprconfig .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-force Disable safety checks. Use this to disable safety checks in iprconfig. This will allow you to format devices that are not at the appropriate code levels. Only use this option if you really know what you are doing. .SH AUTHOR Brian King .SH NOTES .TP .B Notes on using under iSeries 5250 telnet .PP Only use this utility across 5250 telnet when there are no other options available to you. Since there may be occasions when 5250 telnet is your only option to access your iSeries Linux console, every attempt has been made to make this utility usable under 5250 telnet. By following a few guidelines, you can make your 5250 telnet experience more productive and much less frustrating. .PP 1. First of all, it will be helpful to know how the keys are mapped under 5250 telnet. From the 5250 telnet window, hit ESC. This will get you to the .B Send TELNET Control Functions menu. Take option 6 to display the keyboard map. Take note of how TAB, ESC, CTLC, and SENDWOCR are bound. They will be useful in the future. .PP 2. When selecting menu options, enter the menu number, followed by the enter key, same as usual. .PP 3. When typing single character commands (eg. r=Refresh), type the single character followed by the SENDWOCR key (F11 by default). .PP 4. When on a device/array/IOA selection screen (eg. Display Disk Unit Details), do NOT use the arrow keys to navigate. Instead use the TAB key (F7 by default) to navigate these screens. .PP 5. Beware of the backspace and delete keys. As a rule do NOT use them. .PP 6. When editing the root kernel message log directory or the default editor, you may use the arrow keys, but not the backspace and delete keys. Use the space bar to remove already typed characters. iprutils/systemd/0000755000000000000000000000000012427235652013127 5ustar rootrootiprutils/systemd/iprupdate.service0000644000000000000000000000022112427235652016501 0ustar rootroot[Unit] Description=IBM Power Raid update daemon [Service] Type=forking ExecStart=/sbin/iprupdate --daemon [Install] WantedBy=multi-user.target iprutils/systemd/iprinit.service0000644000000000000000000000021512427235652016165 0ustar rootroot[Unit] Description=IBM Power Raid init daemon [Service] Type=forking ExecStart=/sbin/iprinit --daemon [Install] WantedBy=multi-user.target iprutils/systemd/iprdump.service0000644000000000000000000000026612427235652016175 0ustar rootroot[Unit] Description=IBM Power Raid dump daemon Before=iprinit.service iprupdate.service [Service] Type=forking ExecStart=/sbin/iprdump --daemon [Install] WantedBy=multi-user.target iprutils/iprinit.80000644000000000000000000000311212427235652013203 0ustar rootroot.\" (C) Copyright 2000, 2005 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRINIT 8 "February 2005" .SH NAME iprinit - IBM Power RAID adapter/device initialization utility .SH SYNOPSIS .B iprinit .sp .BI "iprinit --debug --daemon --use-polling --use-uevents" .SH DESCRIPTION .B iprinit is used to setup IBM Power RAID adapters and devices for optimal performance. It will enable U320 scsi speeds if it is determined the disk enclosure supports it. It will also enable tagged command queuing. Any scsi bus attributes, such as bus width, or disk attributes, such as queue depth, that have been setup by iprconfig will be loaded as well. It is desired that this be called in the boot process, using the init.d script provided. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprinit .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-daemon Run as a daemon to handle adapters and devices being hotplugged. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Brian King (brking@us.ibm.com) iprutils/LICENSE0000644000000000000000000002661612427235652012457 0ustar rootrootCommon Public License Version 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. iprutils/iprupdate.80000644000000000000000000000364112427235652013531 0ustar rootroot.\" (C) Copyright 2000, 2004 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRUPDATE 8 "February 2005" .SH NAME iprupdate - IBM Power RAID adapter/device microcode update utility .SH SYNOPSIS .B iprupdate .sp .BI "iprupdate --debug --check_only --daemon --use-polling --use-uevents" .SH DESCRIPTION .B iprupdate is used to update the microcode on IBM storage adapters and the disk units attached to them to a minimum supported level. It can be run at any time while the ipr device driver is loaded. The utility will check the microcode revision level of each storage adapter/disk unit on the system and compare it to the microcode revision level that is installed in /etc/microcode, /usr/lib/microcode, and /lib/firmware. If the microcode level on the adapter/device is below the minimum supported level and the level of the latest image is newer, the microcode will be updated to the latest level available. It is desired that this be called in the boot process. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprupdate .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-check_only Check if any adapters or disk units are below their minimum supported level. .TP .B \-\-daemon Run as a daemon to handle adapters and devices being hotplugged. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Brian King (brking@us.ibm.com) iprutils/spec/0000755000000000000000000000000012427235652012371 5ustar rootrootiprutils/spec/iprutils.spec0000644000000000000000000006761612427235652015140 0ustar rootrootSummary: Utilities for the IBM Power Linux RAID adapters Name: iprutils Version: 2.4.5 Release: 1 License: CPL Group: System Environment/Base Vendor: IBM URL: http://sourceforge.net/projects/iprdd/ Source0: iprutils-%{version}-src.tgz BuildRoot: %{_tmppath}/%{name}-root %description Provides a suite of utilities to manage and configure SCSI devices supported by the ipr SCSI storage device driver. %prep %setup -q -n %{name} %build make %install if [ -d %{_unitdir} ]; then make INSTALL_MOD_PATH=$RPM_BUILD_ROOT install install -d $RPM_BUILD_ROOT/%{_unitdir} install -m 644 systemd/iprinit.service $RPM_BUILD_ROOT/%{_unitdir}/iprinit.service install -m 644 systemd/iprdump.service $RPM_BUILD_ROOT/%{_unitdir}/iprdump.service install -m 644 systemd/iprupdate.service $RPM_BUILD_ROOT/%{_unitdir}/iprupdate.service else make INSTALL_MOD_PATH=$RPM_BUILD_ROOT install install -d $RPM_BUILD_ROOT/%{_sysconfdir}/init.d install -m 755 init.d/iprinit $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprinit install -m 755 init.d/iprdump $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprdump install -m 755 init.d/iprupdate $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprupdate fi install -d $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d install -d $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d/resource.d install -m 755 init.d/iprha $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d/resource.d/iprha %ifarch ppc ppc64 %post # if the system is using systemd if [ -d %{_unitdir} ]; then if [ $1 = 2 ]; then echo "Restarting iprutils services" /usr/bin/systemctl restart iprinit.service > /dev/null 2>&1 /usr/bin/systemctl restart iprupdate.service > /dev/null 2>&1 /usr/bin/systemctl restart iprdump.service > /dev/null 2>&1 else /usr/bin/systemctl daemon-reload > /dev/null 2>&1 /usr/bin/systemctl enable iprinit.service > /dev/null 2>&1 /usr/bin/systemctl enable iprdump.service > /dev/null 2>&1 /usr/bin/systemctl enable iprupdate.service > /dev/null 2>&1 fi # if the system is not using systemd else if [ $1 = 2 ]; then echo "Restarting iprutils services" %{_sysconfdir}/init.d/iprdump restart > /dev/null 2>&1 %{_sysconfdir}/init.d/iprupdate restart > /dev/null 2>&1 %{_sysconfdir}/init.d/iprinit restart > /dev/null 2>&1 elif [ -f /sbin/chkconfig ]; then /sbin/chkconfig --add iprinit > /dev/null 2>&1 /sbin/chkconfig --add iprdump > /dev/null 2>&1 /sbin/chkconfig --add iprupdate > /dev/null 2>&1 /sbin/chkconfig iprinit on > /dev/null 2>&1 /sbin/chkconfig iprdump on > /dev/null 2>&1 /sbin/chkconfig iprupdate on > /dev/null 2>&1 else /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprinit /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprdump /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprupdate fi fi %endif %ifarch ppc ppc64 %preun # disable services if the system is using systemd if [ -d %{_unitdir} ]; then if [ $1 = 0 ]; then echo "Restarting iprutils services" /usr/bin/systemctl stop iprinit.service > /dev/null 2>&1 /usr/bin/systemctl stop iprupdate.service > /dev/null 2>&1 /usr/bin/systemctl stop iprdump.service > /dev/null 2>&1 else /usr/bin/systemctl disable iprinit.service > /dev/null 2>&1 /usr/bin/systemctl disable iprdump.service > /dev/null 2>&1 /usr/bin/systemctl disable iprupdate.service > /dev/null 2>&1 fi # disable services if the system is *not* using systemd elif [ $1 = 0 ]; then %{_sysconfdir}/init.d/iprdump stop > /dev/null 2>&1 %{_sysconfdir}/init.d/iprupdate stop > /dev/null 2>&1 %{_sysconfdir}/init.d/iprinit stop > /dev/null 2>&1 if [ -f /sbin/chkconfig ]; then /sbin/chkconfig --del iprinit > /dev/null 2>&1 /sbin/chkconfig --del iprdump > /dev/null 2>&1 /sbin/chkconfig --del iprupdate > /dev/null 2>&1 else /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprdump /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprupdate /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprinit fi fi %endif %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %if %{?_unitdir:1}%{!?_unitdir:0} %{_unitdir}/* %else %{_sysconfdir}/init.d/* %endif %doc README LICENSE /sbin/* %{_mandir}/man*/* %{_sysconfdir}/ha.d/resource.d/iprha %changelog * Tue Nov 7 2014 2014 Wen Xiong 2.4.5 - Raid migration fails on LE systems - Set known zeroed after format * Tue Sep 8 2014 2014 Wen Xiong 2.4.4 - Add support to catch cache hit - Change the filesnameof firmware to be case insentive. * Tue Sep 2 2014 2014 Wen Xiong 2.4.3 - On PowerNV, some of IOAs showed the wrong physical location - Read Intensive SDs are not showed up in iprconifg - Capacity is not reported for JBOD disk greater than 2TB - Add code for supporting DVD/Tape hotplug - allow systemd dameons to be activated laster during boot - add DVD/Tape support in dispaly hardware staut menu * Tue Jun 10 2014 2014 Wen Xiong 2.4.2 - Creation of systemd files for the ipr daemons and proper changes on the spec file - Adds three new CLI APIs: query-array/device/location - Doesn't show "Redusant Paths" in dual config - Couldn't create iprudmp file - allow iprconfig to load sg module - Segmentation fault when rebuild an array - Fix blan space on array member strings * Tue Apr 08 2014 Wen Xiong 2.4.1 - Release 2.4.1 - Avoid bashisms - remove libpci dependency - Failed to write attribute: delte when hot removing disks - segment fault when trying to show details of missing devices * Fri Feb 07 2014 Wen Xiong 2.4.0 - Release 2.4.0 - Eliminate libsysfs dependency * Fri Feb 07 2014 Wen Xiong 2.3.18 - Release 2.3.18 - array start enhancement patch - compliation fails on ununtu * Fri Feb 01 2014 Wen Xiong 2.3.17 - Release 2.3.17 - creating an array fails for T2 disks. - Several fixes for LE system. - Enable actie/active by default * Tue Nov 05 2013 Wen Xiong 2.3.16 - Release 2.3.16 - Fixes a bug for 4K bytes/sector disks in iprutils - Fixes a bug for disk hot swap in VSES on P8 - Release 2.3.15 - Add support for 4K bytes/sector disks in iprutils - Release 2.3.14 - Fixes stale information after hot plug a disk into an array - Segmentation fault when removing a disk with hot spare disk - Fxied sysfs error when updating microcode - Fixes the platform location issue for Tres drawer - Fix hop count defines - Release 2.3.13 - Fixes Platform Location for 32bit adapter. - Adds support for optical devices. - Fixes platform location for disks. - Changes %post and %preun sections of spec file. - Fixes release date on the spec file. * Fri Sep 05 2012 Wen Xiong 2.3.12 - Release 2.3.12 - Addes supporting suspend/resume utility for BlueHawk. - Fixes raid array delete error. * Tue Jun 12 2012 Kleber Sacilotto de Souza 2.3.11 - Release 2.3.11 - Fixes deleting an array logs I/O errors. - Fixes buffer overflow in disk details. - Fixes wrong status on an array for the secondary controller. - Fixes add_string_to_body() for non-interactive interface. - Fixes IOA asymmetric access mode change. - Gets location code when SES page 4 is unavailable. - Fixes physical location error if disk is vses disk. * Mon Apr 02 2012 Kleber Sacilotto de Souza 2.3.10 - Release 2.3.10 - Displays Hardware Location Code for disk/expander. - Changes concurrent maintenance GUI to 3 ways toggle. - Changes README for concurrent maintenance command line. - Adds structures and functions for query resource paths aliases. - Fixes a bug in suspend/resume device bus. - Supports concurrent maintenance for 64bit adapter. - Supports not physical remove disk. - Fixes header problem when query-remove/add-device. - Sometimes receive diag failed at first time after update adapter firmware. - If the disk in a RAID, iprutils itself has to delete sysfs name for secondary adapter. - Old drawers don't support query page 4 for physical location. - Adds display sas path detail for 64bit adapter. - Fixes GUI version of raid consistency check. - Adds new chip details entries. - Fixes SES microcode update failures. - Fixes SES microcode image version on CLI. - Adds a flag to raid create command for known zeroed drives. * Fri Dec 16 2011 Kleber Sacilotto de Souza 2.3.9 - Release 2.3.9 - Fixes disks and arrays status. - Uses C4 inquiry page for DRAM size. - Sets allow_restart to zero before raid delete. - Fixes raid consistency check for 64 bit adapters. - Fixes compile warning. - Updates queue depth values. * Thu Nov 24 2011 Kleber Sacilotto de Souza 2.3.8 - Release 2.3.8 - Adds support for FPGA updates. - Bumps up number of command status records. - Only uses primary adapter for initialize and format disk task. - Fixes check for hotspare devices. - Removes unnecessary call to find_dev(). * Thu Aug 18 2011 Kleber Sacilotto de Souza 2.3.7 - Release 2.3.7 - Adds support for vset device asymmetric config. - Fixes display details for array devices. - Increases buffer size for new adapter config data. - Fixes query command status functionality. * Thu Jul 14 2011 Kleber Sacilotto de Souza 2.3.6 - Release 2.3.6 - Fixes segmentation fault when qac is not available. - Fixes serial number comparison. * Thu Jun 16 2011 Kleber Sacilotto de Souza 2.3.5 - Release 2.3.5 - Fixes suspend and resume device bus commands. * Wed Apr 27 2011 Kleber Sacilotto de Souza 2.3.4 - Release 2.3.4 - Fixes data types for C4 page definition. - Fixes setting qac cdb[3] and rename ipr_is_array_record. - Fixes array migration funcionality. - Increases maximum dump size. * Mon Mar 07 2011 Wayne Boyer 2.3.3 - Release 2.3.3 - Adds initial support for sis64 asymmetric access changes. - Fixes segmentation faults when trying to display certain information. - Fixes some file permissions and a compilation warning. * Thu Nov 18 2010 Kleber Sacilotto de Souza 2.3.2 - Release 2.3.2 - Fixes code incompatibility with libsysfs version 1. - Changes get_scsi_max_xfer_len return value. * Fri Nov 12 2010 Kleber Sacilotto de Souza 2.3.1 - Release 2.3.1 - Fixes display of empty slots. - Fixes IOA description indentation. - Adds Resource Path display for SIS64 devices. - Adds support for the new live dump functionality. - Fixes mode sense page24 for little endian architectures. - Fixes battery info for little endian architectures. * Fri Aug 12 2010 Kleber Sacilotto de Souza 2.3.0 - Release 2.3.0 - Fixes type of pointers provided to scandir. - Fixes buffer overflow when the SCSI Host ID is equal or greater than 1000. - Fixes function names on comments in iprlib.c. - Fixes memory leak when ioctl() returns EINVAL. - Adds support to identify sis64 capability. - Adds support for new sysfs attributes. - Adds support for type 3 QAC command. - Adds support for type 3 records. * Mon May 10 2010 Kleber Sacilotto de Souza 2.2.21 - Release 2.2.21 - Fixes firmware image files left open after getting firmware level. - Changes IOA DRAM size display from hex to decimal base. - Handles SG_IO ioctl error with older distros which caused disk microcode download to fail with JBOD's. * Tue Apr 07 2010 Kleber Sacilotto de Souza 2.2.20 - Release 2.2.20 - Adds resource address parsing based on encoding format flag. * Fri Mar 19 2010 Kleber Sacilotto de Souza 2.2.19 - Release 2.2.19 - Fixes platform location display on blades. * Fri Oct 30 2009 Kleber Sacilotto de Souza 2.2.18 - Release 2.2.18 - Changes the default write buffer mode for writing to SES devices. - Fixes the CLI show-details command for SES devices. - Comments out the get_write_buffer_dev() routine to suppress compilation warnings as it is not being used at the moment. - Fixes the Platform Location Information display for hotplug adapters and displays the information for non-hotplug adapters. - Fixes the indentation problem when IOA host number is equal to or greater than 10. * Wed Sep 16 2009 Wayne Boyer 2.2.17 - Release 2.2.17 - Fixes a NULL pointer dereference which caused the daemons to silently fail. * Fri Aug 14 2009 Wayne Boyer 2.2.16 - Release 2.2.16 - Fixes a bug where CLI raid create is broken. - Fixes several potential memory leak problems. - Adds man page entries for the disable cache query and command. * Tue Jun 30 2009 Wayne Boyer 2.2.15 - Release 2.2.15 - Fixes a bug where cache reclaim can time out too soon. - Fixes some problems with the disable caching support. * Tue Apr 07 2009 Wayne Boyer 2.2.14 - Release 2.2.14 - Add support for disabling IOA caching. - Remove #include of as it is not used. - Allow giving a relative path for a microcode image file when doing an upgrade from the command line. - Add support for SSD drives. * Mon Nov 17 2008 Wayne Boyer 2.2.13 - Release 2.2.13 - Additional fixes for the active-active functionality. - Fixes for iprdump to recognize a /sys/devices path. * Thu Oct 23 2008 Wayne Boyer 2.2.12 - Release 2.2.12 - Additional GUI support and fixes for the active-active functionality. - Fixes for CLI RAID create and delete. * Fri Sep 26 2008 Wayne Boyer 2.2.11 - Release 2.2.11 - Add support for the active-active functionality. * Wed Aug 20 2008 Wayne Boyer - Fixed a compilation failure on small endian systems. * Wed Aug 13 2008 Wayne Boyer 2.2.10 - Release 2.2.10 - Add support for the array migration functionality. * Wed May 14 2008 Tseng-Hui Lin - Under heavy traffic, download microcode to a disk through /dev/sgX may get -ENOMEM SG_IO ioctl. Change to use /dev/sdX. Fall back to /dev/sgX if: (1) /dev/sdX does not exist or (2) max_sectors_kb is not set large enough for microcode download. * Thu Apr 09 2008 Tseng-Hui Lin 2.2.9 - Release 2.2.9 - Do not save preferred primary attribute to fix an infinite failover problem in HA two system RAID configuration. * Thu Sep 20 2007 Brian King 2.2.8 - Release 2.2.8 * Mon Aug 27 2007 Brian King - Add support for setting dual adapter HA mode. * Thu Jun 21 2007 Brian King 2.2.7 - Fixes for init.d scripts to fix problems installing them on some systems. * Wed Jun 13 2007 Brian King - rpm spec file updates * Tue May 1 2007 Brian King 2.2.6 - Fix iprinit dual initiator failover device rescanning code. * Wed Apr 25 2007 Brian King - Add iprha init.d script to enable/disable primary adapter mode for dual initiator configs. * Fri Apr 13 2007 Brian King - Add adapter config option in iprconfig for setting primary/secondary adapter in dual adapter environment. * Mon Mar 19 2007 Brian King - Add iprconfig option to display SAS dual pathing information. * Mon Mar 12 2007 Brian King - Increase time waiting for new devices to show up in iprconfig when doing concurrent add. * Fri Feb 9 2007 Brian King - Add filename date to microcode download screen. (Ryan Hong) - Fix to prevent unnecessarily writing sysfs attributes. (Ryan Hong) * Tue Feb 6 2007 Brian King - Return standard error code according to send_dev_init() return code. * Mon Feb 5 2007 Brian King - Fix incorrect memory free in analyze log menus. * Wed Jan 10 2007 Brian King - Fix send diagnostics buffer transfer length to be only what was received in the receive diagnostics. Fixes disk hotplug on some SAS disk enclosures. * Thu Jan 4 2007 Brian King - Sourceforge patch 1627673: iprutils fix to buffer overflow - Add checking to iprconfig command "set-qdepth" input queue depth value. If the given value is larger than 255, fail the command. - Fix a bug in which the iprconfig command "raid-create" may create an illegal queue depth value to the ipr config file. * Thu Jan 4 2007 Brian King - Sourceforge patch 1627672: iprutils fix to buffer overflow - Fix a bug in which the iprconfig command "raid-create" may create an illegal queue depth value to the ipr config file. * Wed Nov 29 2006 Brian King - Fixes for SES microcode download on SAS. * Mon Nov 20 2006 Brian King - Change to handle UA responses in the JBOD iprinit sequence. * Thu Nov 16 2006 Brian King - Fix segfault in iprconfig if /var/log does not exist. - Allow IOA microcode download to a secondary IOA. - Fix to prevent errors during SAS SES microcode download. * Tue Oct 10 2006 Brian King - Add a couple utility functions for SAS ipr_query_sas_expander_info ipr_query_res_redundancy_info * Wed Sep 27 2006 Brian King 2.2.3 - Fix SAS disk hotplug dual path bug. * Tue Sep 12 2006 Brian King - Change default QERR setting for SAS to 0. * Tue Sep 12 2006 Brian King 2.2.2 - Fix iprconfig set-format-timeout. * Mon Sep 11 2006 Brian King 2.2.1 * Fri Sep 8 2006 Brian King - Reduce default JBOD queue depth to 3. - Fix iprconfig -c set-bus-speed. * Wed Aug 23 2006 Brian King - Fix a race condition with hotplug events which could cause the ipr daemons to run before newly added devices are completed added to the system. * Wed Aug 9 2006 Brian King - Fix a segfault in iprdbg when using the macro function * Tue Jul 25 2006 Brian King 2.2.0 - Fix for command line SES microcode update. * Mon May 8 2006 Brian King - Use IOA's default format timeout for AF DASD instead of using a hard coded default. - Remove RAID support for some older drives that should never have been supported. * Mon May 1 2006 Brian King - Add support to iprinit for it to handle disks going from JBOD <-> AF format across an adapter reset. When this is detected, iprinit will now attempt to delete the disk and then rescan that slot. - Fixed an ncurses screen drawing bug which resulted in the screen getting paged down if the cursor was on the last item on the screen and 't' was pressed to toggle the display. - Added disk concurrent maintenance support for handling dual pathed SAS disks. * Fri Mar 17 2006 Brian King - Improve iprdbg's logging * Thu Mar 16 2006 Brian King - Print better status for devices when IOA is offline/dead. * Tue Mar 14 2006 Brian King - Fix to allow for compiling with libsysfs 2.0.0 * Tue Mar 14 2006 Brian King 2.1.4 - Concurrent maintenance fix for certain iSeries enclosures which would result in non existent drive slots being displayed in iprconfig. * Wed Mar 8 2006 Brian King - Remove some redundant code in disk hotplug path * Thu Mar 2 2006 Brian King - Fixup status of RAID 10 arrays to print a better status under multiple failure scenarios. * Thu Feb 24 2006 Brian King 2.1.3 - Prevent duplicate mode sense commands from being issued. - More uevent handling improvements. - Automatically create hotplug directory if it doesn't already exist so adapter microcode update works. * Thu Feb 9 2006 Brian King - Improve robustness of uevents failure handling. Fall back to polling method if needed. * Fri Feb 3 2006 Brian King - Auxiliary cache adapter fixes. * Fri Jan 27 2006 Brian King - Fix iprconfig -c update-ucode to properly report an error if the wrong microcode level is specified. * Mon Jan 23 2006 Brian King - Fixed a compiler issue. * Fri Jan 20 2006 Brian King - Fixed a bug in iprconfig query-raid-create that prevented JBOD candidates from being displayed if there were no AF candidates as well. * Thu Jan 5 2006 Brian King 2.1.2 - Make iprupdate return success/failure indication when invoked with --force. * Tue Jan 3 2006 Brian King - Concurrent maintenance fix for 7031-D24/T24. * Tue Dec 20 2005 Brian King 2.1.1 - Fix compile error in iprconfig * Thu Dec 18 2005 Brian King 2.1.0 - Updates for aux cache IOAs - Updates for SAS adapters - Misc fixes for new iprconfig command line options * Wed Dec 7 2005 Brian King - Add command line options to iprconfig to perform virtually every iprconfig function available in the ncurses interface. * Tue Nov 15 2005 Brian King 2.0.15.6 - Fix concurrent maintenance with disk drawers reporting multiple SES devices on the same SCSI bus. * Fri Oct 7 2005 Anton Blanchard - Fix string length calculation in ipr_get_hotplug_dir * Wed Aug 17 2005 Brian King 2.0.15.4 - Fix a couple of uninitialized variable compile errors * Wed Jul 27 2005 Brian King 2.0.15.3 - Fix: iprconfig: IOA microcode update would leave AF DASD (disks that are in disk arrays) in a state where they were no longer tagged queueing. Fix iprconfig to run iprinit on the adapter after a microcode download to ensure all attached devices are properly setup after a microcode download. - Fix iprinit: If an IOA was reset for some reason at runtime, this would cause AF DASD devices to get tagged queueing turned off and it would never get turned back on. Change iprinit to detect this and turn tagged queueing back on if this happens. - Changing the queue depth for a disk array was broken. Fix iprinit to properly restore the queue depth from the ipr configuration file. - Fix iprconfig to handle disk format failures better - Fix potential iprutils segfaults when iterating over disk arrays * Wed Jun 1 2005 Brian King 2.0.15.1 - Fix iprconfig Analyze Log options * Wed May 18 2005 Brian King 2.0.15 - Clarify format options - Setup mode page 0 for IBM drives to ensure command aging is enabled. This ensures commands are not starved on some drives. - Fix so that iprdump properly names dump files once 100 dumps have been made. - Make iprconfig handle failures of scsi disk formats better - Fix iprconfig Set root kernel message log directory menu - Properly display RAID level on all iprconfig screens - Don't disable init.d daemons on an rpm -U * Tue Apr 12 2005 Brian King 2.0.14.2 - Fixed bug preventing disk microcode update from working. * Mon Apr 4 2005 Brian King - Add ability to force RAID consistency check * Mon Mar 25 2005 Brian King 2.0.14.1 - Removed mention of primary/secondary adapters in some error screens since multi-initiator RAID is not supported and the messages will just cause confusion. * Mon Mar 24 2005 Brian King - iprconfig: During disk hotplug, wait for sd devices to show up. Fixes errors getting logged by iprconfig during hotplug. * Mon Mar 23 2005 Brian King - iprconfig: Fix cancel path on concurrent add/remove of disks - Don't display current bus width and speed for SAS disks * Mon Mar 21 2005 Brian King - Fix scoping bug caught by gcc 4.0. * Fri Mar 18 2005 Brian King - Stop iprupdate from continually logging errors for adapters with backlevel adapter firmware. * Mon Mar 7 2005 Brian King 2.0.14 - Add support for non-interactive array creation and deletion through iprconfig. - Use kobject_uevent notifications instead of polling if the kernel supports it. - Fix iprconfig to set the actual queue depth for advanced function disks - Allow user to force tagged queuing on to drives that do not support QERR=1. - Fix handling of medium format corrupt drives for drives - iprconfig: Download microcode. Fix blank screen when displaying lots of microcode images. - Fix iprinit to wait for scsi generic devices to show up in case we are racing with hotplug. Fixes the following error: 0:255:0:0: Mode Sense failed. rc=1, SK: 5 ASC: 24 ASCQ: 0 - Add "known to be zeroed" tracking to iprconfig to drastically reduce the time required to create a RAID array when starting with 512 formatted disks - Add ability to query multi-adapter status for dual initiator RAID configs - Add ability to set "preferred primary" adapter when running dual initiator RAID configs - Add iprconfig screen to display dual adapter status for dual initiator RAID configs - Prevent RAID configuration from occurring on "secondary" adapter in dual initiator RAID configs - Use /dev/sd for SG_IO instead of /dev/sg when possible - Set QERR=3 rather than 1 for multi-initiator configurations - Set TST=1 for multi-initiator configurations - Allow Format device for JBOD function to work for JBOD adapters - Fix handling of dead adapters in all of iprutils. - Fix iprconfig RAID start bug for systems with multiple RAID adapters. - Fix iprconfig RAID include bug for systems with multiple RAID adapters. - Fix failing array add device due to race condition with iprinit. * Tue Oct 5 2004 Brian King 2.0.13 - Improve iprupdate error logs to indicate where to download microcode from. - Set default tcq queue depth for AS400 disks to 16. - Don't log errors in iprdump if CONFIG_IPR_DUMP not enabled in the kernel - Fix sysfs parsing to handle new sdev target kernel change - Rescan JBOD devices following recovery format to make the device usable if it was originally in an unsupported sector size. - Display correct adapter serial number in iprconfig. - Support for microcode download to new adapters. - Support for iSeries disk microcode update using microcode images from the pSeries microcode website. * Fri Jun 11 2004 Brian King 2.0.12 - Fix bug preventing ucode download to iSeries disks from working * Thu Jun 10 2004 Brian King 2.0.11 - Fix segmentation fault in _sg_ioctl that was causing a silent failure of microcode update to disks. The microcode update would fail, but no error would be logged. The seg fault was in a child process, so the parent process kept running. * Thu May 23 2004 Brian King 2.0.10 - Don't let iprdbg sg ioctls be retried. - Add --force flag to iprconfig to allow user to workaround buggy drive firmware. - Don't initialize read/write protected disks - Fix some reclaim cache bugs - Don't setup Mode Page 0x0A if test unit ready fails * Thu May 2 2004 Brian King 2.0.9 - Add --debug option to all utilities - Make utilities behave better when ipr is not loaded - Fix dependencies in init.d scripts - Only enable init.d scripts on ppc - Don't log an error if ipr is not loaded * Mon Apr 28 2004 Brian King 2.0.8 - Fix to properly enable init.d scripts when the rpm is installed - Fix memory leak in code download path - Increase size of page 0 inquiry buffer so that extended vpd is displayed - Decrease write buffer timeout to 2 minutes * Wed Apr 16 2004 Brian King 2.0.7 - Load sg module in init.d scripts if not loaded - Load sg module in iprconfig if not loaded * Wed Apr 14 2004 Brian King 2.0.6 - Battery maintenance fixes. - Fix to properly display failed status for pulled physical disks. * Tue Apr 6 2004 Brian King 2.0.5 - Battery maintenance fixes. - Fix init.d scripts to work properly with yast runlevel editor. - Fix device details screen in iprconfig for Failed array members - Allow formatting devices even if qerr cannot be disabled * Tue Mar 29 2004 Brian King 2.0.4 - Fixed some sysfs calls that changed their calling interfaces * Tue Mar 17 2004 Brian King 2.0.2-3 - Fixed iprutils/README0000644000000000000000000001152312427235652012321 0ustar rootroot# (C) Copyright 2000, 2001 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. This directory contains user space tools required by the ipr device driver. To install them, type 'make' and then 'make install'. You will need pciutils-devel, ncurses, ncurses-devel, and sysfsutils packages installed to perform the 'make'. For more detailed information about ipr and iprutils, refer to the man pages and the Linux reference guide (SA23-1327), which is available here: http://publib16.boulder.ibm.com/pseries/en_US/infocenter/base/HW_scsi_adapters.htm iprdump - A user program meant to be run in the background to gather debug information should an IOA failure occur. Ideally, this program should be started during boot time after file systems are loaded when the ipr SCSI driver is used. An init.d script is provided to do this. iprupdate - IOA/disk unit microcode update utility This tool requires no arguments and should be run after each time the device driver is loaded. It will query each adapter/disk unit for the microcode version and update adapters/disk units that are not currently at the minimum supported levels. An init.d script is provided to run this at boot time. iprinit - IOA/disk unit initialization utility This tool requires no arguments and should be run after each time the device driver is loaded. It will setup all ipr adapters and attached disks to run with optimal performance and load any adapter, disk, or scsi bus configurations saved by iprconfig. It enables U320 scsi speeds and enables tagged command queuing. iprconfig - IOA Configuration and Recovery tool This is an ncurses based utility used to configure ipr adapters and devices. The basic functions of this tool are: CONFIGURATION 1. Display hardware status. This can be used to display all ipr SCSI disks attached to your system. Using this option it is possible to obtain information about each device on the system. 2. Work with SCSI Bus Configuration. This allows flexibility in setting some SCSI Bus parameters such is Max Bus Throughput and host SCSI ID. 3. Work with Driver Configuration. Currently allows adjustment to the devices drivers log level. 4. Work with disk configuration. Allows setting per device attributes, such as queue depth. 5. Download microcode. Allows microcode to be downloaded to adapters and SCSI disks. RAID 6. Create a disk array. Used to create a disk array. 7. Delete a disk array. Allows you to delete existing arrays. Data stored on the devices will not be preserved after issuing this command. 8. Add a device to a disk array. This option allows devices of similar capacity to be included into an existing RAID 5 disk array. 9. Format Device for advanced function/Format Device for JBOD (Just a Bunch Of DASD) function. Disk devices can either be formatted to 512 bytes/sector or 522 bytes/sector. Devices must be formatted to 522 bytes/sector, or advanced function format, in order to be used in a disk array or as a hot spare. Devices not in a disk array can be formatted to 512 bytes/sector so they can be used directly by the operating system. 10. Create a hot spare. Configure a disk to become a hot spare, capable of being used by the adapter to automatically replace a failed device RECOVERY 12. Concurrent add device. Concurrently add a SCSI disk to a running system. 13. Concurrent remove device. Concurrently remove a SCSI disk from a running system. 14. Initialize and format disk. This allows you to issue a SCSI format command to SCSI disks. Proceed with caution when using this option. 15. Reclaim IOA cache storage. This is to be used by IBM hardware service personnel only. Use of this function is potentially dangerous and may delete data from the non-volatile write cache on the adapter. 16. Rebuild disk unit data. This option is generally used following concurrent maintenance. Select this option after a failing array member device has been replaced to reconstruct device as an active array member. 17. Work with Resources Containing Cache Battery Packs. A set of displays are used to view the current status of the Cache Battery on resources containing battery packs and allow maintanence actions to be performed against those resources when necessary. 18. Analyze Log. This allows you to view the error messages logged by the ipr device driver. Changes Log: 1. Concurrent maintience commands line changes from PCI location to physical location(3/12/2012). #iprconfig -c identify-slot "U5886.001.P915059-P1-D1 1" #iprconfig -c remove-slot "U5886.001.P915059-P1-D1 1" #iprconfig -c add-slot "U5886.001.P915059-P1-D1 1" 2. Work with disk enclosures #iprconfig -c query-disk-enclosure-status #iprconfig -c suspend-disk-enclosure sg8 #iprconfig -c resume-disk-enclosure sg8 iprutils/iprlib.h0000644000000000000000000022213312427235652013074 0ustar rootroot#ifndef iprlib_h #define iprlib_h /** * IBM IPR adapter utility library * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprlib.h,v 1.107 2009/06/30 23:32:40 wboyer Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define IPR_DASD_UCODE_USRLIB 0 #define IPR_DASD_UCODE_ETC 1 #define IPR_DASD_UCODE_NOT_FOUND -1 #define IPR_DASD_UCODE_NO_HDR 1 #define IPR_DASD_UCODE_HDR 2 #define UCODE_BASE_DIR "/usr/lib/microcode" #define LINUX_UCODE_BASE_DIR "/lib/firmware" #define FIRMWARE_HOTPLUG_DIR_TAG "FIRMWARE_DIR" #define FIRMWARE_HOTPLUG_CONFIG_FILE "/etc/hotplug/firmware.agent" #define FIRMWARE_HOTPLUG_DEFAULT_DIR LINUX_UCODE_BASE_DIR #define IPRDUMP_DIR "/var/log/" #define IPR_JBOD_BLOCK_SIZE 512 #define IPR_DEFAULT_AF_BLOCK_SIZE 522 #define IPR_JBOD_4K_BLOCK_SIZE 4096 #define IPR_AF_4K_BLOCK_SIZE 4224 #define IOCTL_BUFFER_SIZE 512 #define IPR_MAX_NUM_BUSES 4 #define IPR_VENDOR_ID_LEN 8 #define IPR_PROD_ID_LEN 16 #define IPR_SERIAL_NUM_LEN 8 #define ESM_SERIAL_NUM_LEN 12 #define YL_SERIAL_NUM_LEN 12 #define IPR_DESCRIPTION_LEN 16 #define IPR_VPD_PLANT_CODE_LEN 4 #define IPR_VPD_CACHE_SIZE_LEN 3 #define IPR_VPD_DRAM_SIZE_LEN 3 #define IPR_VPD_PART_NUM_LEN 12 #define IPR_CCB_CDB_LEN 16 #define IPR_QAC_BUFFER_SIZE 200000 #define IPR_SIS32_QAC_BUFFER_SIZE 16000 #define IPR_INVALID_ARRAY_ID 0xFF #define IPR_IOA_RESOURCE_HANDLE 0xffffffff #define IPR_RECLAIM_NUM_BLOCKS_MULTIPLIER 256 #define IPR_SDB_CHECK_AND_QUIESCE 0x00 #define IPR_SDB_CHECK_ONLY 0x40 #define IPR_SDB_CHECK_AND_QUIESCE_ENC 0x0e #define IPR_RDB_UNQUIESCE_AND_REBALANCE 0x20 #define IPR_MAX_NUM_SUPP_INQ_PAGES 36 #define SES_MAX_NUM_SUPP_INQ_PAGES 7 #define IPR_DUMP_TRACE_ENTRY_SIZE 8192 #define IPR_MODE_SENSE_LENGTH 255 #define IPR_DEFECT_LIST_HDR_LEN 4 #define IPR_FORMAT_DATA 0x10 #define IPR_FORMAT_IMMED 2 #define IPR_ARRAY_CMD_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_INTERNAL_DEV_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_FORMAT_UNIT_TIMEOUT (3 * 60 * 60) /* 3 hours */ #define IPR_INTERNAL_TIMEOUT (30) /* 30 seconds */ #define IPR_SUSPEND_DEV_BUS_TIMEOUT (35) /* 35 seconds */ #define IPR_EVALUATE_DEVICE_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_WRITE_BUFFER_TIMEOUT (8 * 60) /* 8 minutes */ #define IPR_CACHE_RECLAIM_TIMEOUT (10 * 60) /* 10 minutes */ #define SET_DASD_TIMEOUTS_TIMEOUT (2 * 60) #define IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES 50 #define IPR_DRIVE_ELEM_STATUS_EMPTY 5 #define IPR_DRIVE_ELEM_STATUS_POPULATED 1 #define IPR_DRIVE_ELEM_STATUS_UNSUPP 0 #define IPR_DRIVE_ELEM_STATUS_NO_ACCESS 8 #define IPR_TIMEOUT_SECOND_RADIX 0x0000 #define IPR_TIMEOUT_MINUTE_RADIX 0x4000 #define IPR_TIMEOUT_RADIX_MASK 0xC000 #define IPR_TIMEOUT_RADIX_IS_MINUTE(to) \ (((to) & IPR_TIMEOUT_RADIX_MASK) == IPR_TIMEOUT_MINUTE_RADIX) #define IPR_TIMEOUT_RADIX_IS_SECONDS(to) \ (((to) & IPR_TIMEOUT_RADIX_MASK) == IPR_TIMEOUT_SECOND_RADIX) #define IPR_TIMEOUT_MASK 0x3FFF #define IPR_IOA_DEBUG 0xDDu #define IPR_IOA_DEBUG_READ_IOA_MEM 0x00u #define IPR_IOA_DEBUG_WRITE_IOA_MEM 0x01u #define IPR_IOA_DEBUG_READ_FLIT 0x03u #define IPR_IOA_DEBUG_ENABLE_DBG_FUNC 0x0Au #define IPR_IOA_DEBUG_DISABLE_DBG_FUNC 0x0Bu #define IPR_STD_INQ_Z0_TERM_LEN 8 #define IPR_STD_INQ_Z1_TERM_LEN 12 #define IPR_STD_INQ_Z2_TERM_LEN 4 #define IPR_STD_INQ_Z3_TERM_LEN 5 #define IPR_STD_INQ_Z4_TERM_LEN 4 #define IPR_STD_INQ_Z5_TERM_LEN 2 #define IPR_STD_INQ_Z6_TERM_LEN 10 #define IPR_STD_INQ_PART_NUM_LEN 12 #define IPR_STD_INQ_EC_LEVEL_LEN 10 #define IPR_STD_INQ_FRU_NUM_LEN 12 #define IPR_START_STOP_STOP 0x00 #define IPR_START_STOP_START 0x01 #define IPR_READ_CAPACITY_16 0x10 #define IPR_SERVICE_ACTION_IN 0x9E #define IPR_MAINTENANCE_IN 0xA3 #define IPR_QUERY_MULTI_ADAPTER_STATUS 0x01 #define IPR_MAINTENANCE_OUT 0xA4 #define IPR_CHANGE_MULTI_ADAPTER_ASSIGNMENT 0x02 #define IPR_QUERY_RESOURCE_STATE 0xC2 #define IPR_QUERY_COMMAND_STATUS 0xCB #define IPR_SUSPEND_DEV_BUS 0xC8 #define IPR_RESUME_DEV_BUS 0xC9 #define IPR_IOA_SERVICE_ACTION 0xD2 #define IPR_QUERY_RES_ADDR_ALIASES 0x10 #define IPR_QUERY_RES_PATH_ALIASES 0x20 #define IPR_DISRUPT_DEVICE 0x11 #define IPR_QUERY_SAS_EXPANDER_INFO 0x12 #define IPR_QUERY_RES_REDUNDANCY_INFO 0x13 #define IPR_QUERY_RES_REDUNDANCY_INFO64 0x23 #define IPR_CHANGE_CACHE_PARAMETERS 0x14 #define IPR_QUERY_CACHE_PARAMETERS 0x16 #define IPR_LIVE_DUMP 0x31 #define IPR_QUERY_IOA_DEV_PORT 0x32 #define IPR_EVALUATE_DEVICE 0xE4 #define SKIP_READ 0xE8 #define SKIP_WRITE 0xEA #define QUERY_DASD_TIMEOUTS 0xEB #define SET_DASD_TIMEOUTS 0xEC #define IPR_MIGRATE_ARRAY_PROTECTION 0xEF #define IPR_QUERY_ARRAY_CONFIG 0xF0 #define IPR_START_ARRAY_PROTECTION 0xF1 #define IPR_STOP_ARRAY_PROTECTION 0xF2 #define IPR_RESYNC_ARRAY_PROTECTION 0xF3 #define IPR_ADD_ARRAY_DEVICE 0xF4 #define IPR_REMOVE_ARRAY_DEVICE 0xF5 #define IPR_REBUILD_DEVICE_DATA 0xF6 #define IPR_RECLAIM_CACHE_STORE 0xF8 #define IPR_SET_ARRAY_ASYMMETRIC_ACCESS 0xFA #define IPR_RECLAIM_ACTION 0x68 #define IPR_RECLAIM_PERFORM 0x00 #define IPR_RECLAIM_RESET_BATTERY_ERROR 0x08 #define IPR_RECLAIM_EXTENDED_INFO 0x10 #define IPR_RECLAIM_QUERY 0x20 #define IPR_RECLAIM_RESET 0x40 #define IPR_RECLAIM_FORCE_BATTERY_ERROR 0x60 #define IPR_RECLAIM_UNKNOWN_PERM 0x80 #define SET_SUPPORTED_DEVICES 0xFB #define IPR_STD_INQUIRY 0xFF #ifndef REPORT_LUNS #define REPORT_LUNS 0xA0 #endif #define IPR_XLATE_DEV_FMT_RC(rc) ((((rc) & 127) == 51) ? -EIO : 0) #define IPR_TYPE_AF_DISK 0xC #define IPR_TYPE_ADAPTER 0x1f #define IPR_TYPE_EMPTY_SLOT 0xff #define IPR_ACTIVE_OPTIMIZED 0x0 #define IPR_ACTIVE_NON_OPTIMIZED 0x1 #define IPR_ACTIVE_STANDBY 0x2 #define IPR_CLEAR_ASYMMETRIC_STATE 0x0 #define IPR_PRESERVE_ASYMMETRIC_STATE 0x80 #define IPR_IOA_CACHING_DUAL_FAILURE_DISABLED 0x0 #define IPR_IOA_CACHING_DUAL_FAILURE_ENABLED 0x1 #define IPR_IOA_REQUESTED_CACHING_DEFAULT 0x0 #define IPR_IOA_REQUESTED_CACHING_DISABLED 0x1 #define IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED 0x2 #define IPR_IOA_CACHING_DISABLED_DUAL_ENABLED 0x3 #define IPR_IOA_SET_CACHING_DEFAULT 0x0 #define IPR_IOA_SET_CACHING_DISABLED 0x10 #define IPR_IOA_SET_CACHING_DUAL_DISABLED 0x20 #define IPR_IOA_SET_CACHING_DUAL_ENABLED 0x30 #define PHYSICAL_LOCATION_LENGTH 1024 #define IPR_HDD 0x0 #define IPR_SSD 0x1 #define IPR_BLK_DEV_CLASS_4K 0x4 #define IPR_RI 0x1 #define IPR_ARRAY_VIRTUAL_BUS 0x1 #define IPR_VSET_VIRTUAL_BUS 0x2 #define IPR_IOAFP_VIRTUAL_BUS 0x3 #define IPR_IS_DASD_DEVICE(std_inq_data) \ ((((std_inq_data).peri_dev_type) == TYPE_DISK) && !((std_inq_data).removeable_medium)) #define IPR_SET_MODE(change_mask, cur_val, new_val) \ { \ int mod_bits = (cur_val ^ new_val); \ if ((change_mask & mod_bits) == mod_bits) \ { \ cur_val = new_val; \ } \ } #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #define IPR_RECORD_ID_SUPPORTED_ARRAYS __constant_be16_to_cpu((u16)0) #define IPR_RECORD_ID_COMP_RECORD __constant_be16_to_cpu((u16)3) #define IPR_RECORD_ID_ARRAY_RECORD __constant_be16_to_cpu((u16)4) #define IPR_RECORD_ID_DEVICE_RECORD __constant_be16_to_cpu((u16)5) #define IPR_RECORD_ID_ARRAY_RECORD_3 __constant_be16_to_cpu((u16)6) #define IPR_RECORD_ID_VSET_RECORD_3 __constant_be16_to_cpu((u16)7) #define IPR_RECORD_ID_DEVICE_RECORD_3 __constant_be16_to_cpu((u16)8) extern struct ipr_array_query_data *ipr_array_query_data; extern int num_ioas; extern struct ipr_ioa *ipr_ioa_head; extern struct ipr_ioa *ipr_ioa_tail; extern int runtime; extern void (*exit_func) (void); extern int daemonize; extern int ipr_debug; extern int ipr_force; extern int ipr_sg_required; extern int polling_mode; extern int ipr_fast; extern int format_done; extern char *tool_name; extern struct sysfs_dev *head_zdev; extern struct sysfs_dev *tail_zdev; struct sysfs_dev { char sysfs_device_name[PATH_MAX]; struct sysfs_dev *next, *prev; }; struct ipr_phy { #if defined (__BIG_ENDIAN_BITFIELD) u8 box:3; u8 phy:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 phy:5; u8 box:3; #endif }; struct ipr_phy_2bit_hop { #if defined (__BIG_ENDIAN_BITFIELD) u8 box:2; u8 phy:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 phy:6; u8 box:2; #endif }; struct ipr_res_addr { u8 host; u8 bus; union { u8 target; struct ipr_phy phy; struct ipr_phy_2bit_hop phy_2bit_hop; }; u8 lun; #define IPR_GET_PHYSICAL_LOCATOR(res_addr) \ (((res_addr)->bus << 16) | ((res_addr)->target << 8) | (res_addr)->lun) }; struct ipr_res_path { u8 res_path_bytes[8]; }; struct ipr_std_inq_vpids { u8 vendor_id[IPR_VENDOR_ID_LEN]; /* Vendor ID */ u8 product_id[IPR_PROD_ID_LEN]; /* Product ID */ }; struct ipr_common_record { u16 record_id; u16 record_len; }; struct ipr_vset_res_state { u16 stripe_size; u8 prot_level; u8 num_devices_in_vset; u32 reserved6; u32 ilid; u32 failing_dev_ioasc; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; u8 prot_level_str[8]; }; struct ipr_dasd_res_state { u32 data_path_width; /* bits */ u32 data_xfer_rate; /* 100 KBytes/second */ u32 ilid; u32 failing_dev_ioasc; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; }; struct ipr_gscsi_res_state { u32 data_path_width; /* bits */ u32 data_xfer_rate; /* 100 KBytes/second */ }; struct ipr_supported_device { u16 data_length; u8 reserved; u8 num_records; struct ipr_std_inq_vpids vpids; u8 reserved2[16]; }; struct ipr_mode_page_hdr { #if defined (__BIG_ENDIAN_BITFIELD) u8 parms_saveable:1; u8 reserved1:1; u8 page_code:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 page_code:6; u8 reserved1:1; u8 parms_saveable:1; #endif u8 page_length; }; struct ipr_vendor_mode_page { /* Mode page 0x00 */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 qpe:1; u8 uqe:1; u8 dwd:1; u8 reserved1:4; u8 arhes:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 arhes:1; u8 reserved1:4; u8 dwd:1; u8 uqe:1; u8 qpe:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 asdpe:1; u8 reserved2:1; u8 cmdac:1; u8 rpfae:1; u8 dotf:1; u8 reserved3:1; u8 rrnde:1; u8 cpe:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 cpe:1; u8 rrnde:1; u8 reserved3:1; u8 dotf:1; u8 rpfae:1; u8 cmdac:1; u8 reserved2:1; u8 asdpe:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved4:6; u8 dwlro:1; u8 dlro:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 dlro:1; u8 dwlro:1; u8 reserved4:6; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved5:2; u8 dsn:1; u8 frdd:1; u8 dpsdp:1; u8 wpen:1; u8 caen:1; u8 ovple:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ovple:1; u8 caen:1; u8 wpen:1; u8 dpsdp:1; u8 frdd:1; u8 dsn:1; u8 reserved5:2; #endif u8 reserved7[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved8:1; u8 adc:1; u8 qemc:1; u8 drd:1; u8 led_mode:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 led_mode:4; u8 drd:1; u8 qemc:1; u8 adc:1; u8 reserved8:1; #endif u8 temp_threshold; u8 cmd_aging_limit_hi; u8 cmd_aging_limit_lo; u8 qpe_read_threshold; u8 reserved10; #if defined (__BIG_ENDIAN_BITFIELD) u8 drrt:1; u8 dnr:1; u8 reserved11:1; u8 rarr:1; u8 ffmt:1; u8 reserved12:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved12:3; u8 ffmt:1; u8 rarr:1; u8 reserved11:1; u8 dnr:1; u8 drrt:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 rtp:1; u8 rrc:1; u8 fcert:1; u8 reserved13:1; u8 drpdv:1; u8 dsf:1; u8 irt:1; u8 ivr:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ivr:1; u8 irt:1; u8 dsf:1; u8 drpdv:1; u8 reserved13:1; u8 fcert:1; u8 rrc:1; u8 rtp:1; #endif }; struct ipr_control_mode_page { /* Mode page 0x0A */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 tst:3; u8 reserved1:3; u8 gltsd:1; u8 rlec:1; u8 queue_algorithm_modifier:4; u8 reserved2:1; u8 qerr:2; u8 dque:1; u8 reserved3:1; u8 rac:1; u8 reserved4:2; u8 swp:1; u8 raerp:1; u8 uaaerp:1; u8 eaerp:1; u8 ato:1; u8 tas:1; u8 reserved5:3; u8 autoload_mode:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 rlec:1; u8 gltsd:1; u8 reserved1:3; u8 tst:3; u8 dque:1; u8 qerr:2; u8 reserved2:1; u8 queue_algorithm_modifier:4; u8 eaerp:1; u8 uaaerp:1; u8 raerp:1; u8 swp:1; u8 reserved4:2; u8 rac:1; u8 reserved3:1; u8 autoload_mode:3; u8 reserved5:3; u8 tas:1; u8 ato:1; #endif u16 ready_aen_holdoff_period; u16 busy_timeout_period; u16 reserved6; }; struct ipr_rw_err_mode_page { /* Page code 0x01 */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 awre:1; u8 arre:1; u8 tb:1; u8 rc:1; u8 eer:1; u8 per:1; u8 dte:1; u8 dcr:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 dcr:1; u8 dte:1; u8 per:1; u8 eer:1; u8 rc:1; u8 tb:1; u8 arre:1; u8 awre:1; #endif u8 read_retry_count; u8 correction_span; u8 head_offset_count; u8 data_strobe_offset_count; u8 reserved1; u8 write_retry_count; u8 reserved2; u16 recovery_time_limit; }; struct ipr_reclaim_query_data { u8 action_status; #define IPR_ACTION_SUCCESSFUL 0 #define IPR_ACTION_NOT_REQUIRED 1 #define IPR_ACTION_NOT_PERFORMED 2 #if defined (__BIG_ENDIAN_BITFIELD) u8 reclaim_known_needed:1; u8 reclaim_unknown_needed:1; u8 reserved2:2; u8 reclaim_known_performed:1; u8 reclaim_unknown_performed:1; u8 reserved3:1; u8 num_blocks_needs_multiplier:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 num_blocks_needs_multiplier:1; u8 reserved3:1; u8 reclaim_unknown_performed:1; u8 reclaim_known_performed:1; u8 reserved2:2; u8 reclaim_unknown_needed:1; u8 reclaim_known_needed:1; #endif u16 num_blocks; u8 rechargeable_battery_type; #define IPR_BATTERY_TYPE_NO_BATTERY 0 #define IPR_BATTERY_TYPE_NICD 1 #define IPR_BATTERY_TYPE_NIMH 2 #define IPR_BATTERY_TYPE_LIION 3 u8 rechargeable_battery_error_state; #define IPR_BATTERY_NO_ERROR_STATE 0 #define IPR_BATTERY_WARNING_STATE 1 #define IPR_BATTERY_ERROR_STATE 2 #if defined (__BIG_ENDIAN_BITFIELD) u8 conc_maint_battery:1; u8 battery_replace_allowed:1; u8 reserved4:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved4:6; u8 battery_replace_allowed:1; u8 conc_maint_battery:1; #endif u8 reserved5; u16 raw_power_on_time; u16 adjusted_power_on_time; u16 estimated_time_to_battery_warning; u16 estimated_time_to_battery_failure; u8 reserved6[240]; }; struct ipr_sdt_entry { u32 bar_str_offset; u32 end_offset; u8 entry_byte; u8 reserved[3]; #if defined (__BIG_ENDIAN_BITFIELD) u8 endian:1; u8 reserved1:1; u8 valid_entry:1; u8 reserved2:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:5; u8 valid_entry:1; u8 reserved1:1; u8 endian:1; #endif u8 resv; u16 priority; }; struct ipr_query_res_state { #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved1:1; u8 not_oper:1; u8 not_ready:1; u8 not_func:1; u8 reserved2:4; u8 read_write_prot:1; u8 reserved3:7; u8 prot_dev_failed:1; u8 prot_suspended:1; u8 prot_resuming:1; u8 degraded_oper:1; u8 service_req:1; u8 reserved4:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:4; u8 not_func:1; u8 not_ready:1; u8 not_oper:1; u8 reserved1:1; u8 reserved3:7; u8 read_write_prot:1; u8 reserved4:3; u8 service_req:1; u8 degraded_oper:1; u8 prot_resuming:1; u8 prot_suspended:1; u8 prot_dev_failed:1; #endif u8 reserved5; union { struct ipr_vset_res_state vset; struct ipr_dasd_res_state dasd; struct ipr_gscsi_res_state gscsi; }; }; struct ipr_query_io_port { u32 length; #define IOA_DEV_PORT_ACTIVE 0x0 #define IOA_DEV_PORT_SUSPEND 0x1 #define IOA_DEV_PORT_PARTIAL_SUSPEND 0x2 #define IOA_DEV_PORT_UNKNOWN 0xFF u8 port_state; u8 reserved1; u8 reserved2; u8 reserved3; }; struct ipr_res_addr_aliases { u32 length; struct ipr_res_addr res_addr[10]; }; #define for_each_ra_alias(ra, aliases) \ for (ra = (aliases)->res_addr; \ ra < ((aliases)->res_addr + (ntohl((aliases)->length) / sizeof(struct ipr_res_addr))) && \ ra < ((aliases)->res_addr + ARRAY_SIZE((aliases)->res_addr)); \ ra++) struct ipr_res_path_aliases { u32 length; u32 reserved1; struct ipr_res_path res_path[10]; }; #define for_each_rp_alias(rp, aliases) \ for (rp = (aliases)->res_path; \ rp < ((aliases)->res_path + ((ntohl((aliases)->length)- 4) / sizeof(struct ipr_res_path))) && \ rp < ((aliases)->res_path + ARRAY_SIZE((aliases)->res_path)); \ rp++) struct ipr_array_cap_entry { u8 prot_level; #define IPR_DEFAULT_RAID_LVL "5" #if defined (__BIG_ENDIAN_BITFIELD) u8 include_allowed:1; u8 reserved:5; u8 format_overlay_type:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 format_overlay_type:2; u8 reserved:5; u8 include_allowed:1; #endif #define IPR_FORMAT_ADD_DEVICES 1 #define IPR_FORMAT_REMOVE_DEVICES 2 u16 reserved2; u8 reserved3; u8 max_num_array_devices; u8 min_num_array_devices; u8 min_mult_array_devices; u16 reserved4; u16 supported_stripe_sizes; u16 reserved5; u16 recommended_stripe_size; u8 prot_level_str[8]; }; #define for_each_cap_entry(cap, supp) \ for (cap = (supp)->entry; \ (unsigned long)cap < ((unsigned long)((supp)->entry) + (ntohs((supp)->num_entries) * ntohs((supp)->entry_length))); \ cap = (struct ipr_array_cap_entry *)((unsigned long)cap + ntohs((supp)->entry_length))) struct ipr_array_record { struct ipr_common_record common; #if defined (__BIG_ENDIAN_BITFIELD) u8 issue_cmd:1; u8 known_zeroed:1; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 known_zeroed:1; u8 issue_cmd:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 saved_asym_access_state:4; u8 current_asym_access_state:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 current_asym_access_state:4; u8 saved_asym_access_state:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 established:1; u8 exposed:1; u8 non_func:1; u8 high_avail:1; u8 no_config_entry:1; u8 reserved3:3; u8 start_cand:1; u8 stop_cand:1; u8 resync_cand:1; u8 migrate_cand:1; u8 asym_access_cand:1; u8 reserved4:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved3:3; u8 no_config_entry:1; u8 high_avail:1; u8 non_func:1; u8 exposed:1; u8 established:1; u8 reserved4:3; u8 asym_access_cand:1; u8 migrate_cand:1; u8 resync_cand:1; u8 stop_cand:1; u8 start_cand:1; #endif union { struct { u16 stripe_size; u8 raid_level; u8 array_id; u32 resource_handle; struct ipr_res_addr resource_addr; struct ipr_res_addr last_resource_addr; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[8]; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved51:1; u8 read_intensive:1; u8 reserved5:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved5:3; u8 read_intensive:1; u8 reserved51:1; u8 block_dev_class:3; #endif u8 reserved6; u8 reserved7; u8 reserved8; }__attribute__((packed, aligned (4))) type2; struct { u8 reserved5; u8 raid_level; u16 stripe_size; u8 reserved6[4]; u8 reserved7[16]; u8 last_func_res_path[8]; u8 reserved8[2]; u8 array_id; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved91:1; u8 read_intensive:1; u8 reserved9:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved9:3; u8 read_intensive:1; u8 reserved91:1; u8 block_dev_class:3; #endif u32 resource_handle; u8 dev_id[8]; u8 lun[8]; u8 wwn[16]; u8 res_path[8]; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; u8 desc[IPR_DESCRIPTION_LEN]; u8 total_arr_size[8]; u8 total_size_inuse[8]; u8 max_size_to_use[8]; u8 total_size_enc[8]; u8 total_inuse_enc[8]; u8 max_size_enc[8]; }__attribute__((packed, aligned (4))) type3; }; }__attribute__((packed, aligned (4))); struct ipr_dev_record { struct ipr_common_record common; #if defined (__BIG_ENDIAN_BITFIELD) u8 issue_cmd:1; u8 known_zeroed:1; u8 reserved:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:6; u8 known_zeroed:1; u8 issue_cmd:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 saved_asym_access_state:4; u8 current_asym_access_state:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 current_asym_access_state:4; u8 saved_asym_access_state:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 array_member:1; u8 has_parity:1; u8 is_exposed_device:1; u8 is_hot_spare:1; u8 no_cfgte_vol:1; u8 no_cfgte_dev:1; u8 reserved2:2; u8 start_cand:1; u8 parity_cand:1; u8 stop_cand:1; u8 resync_cand:1; u8 include_cand:1; u8 exclude_cand:1; u8 rebuild_cand:1; u8 zero_cand:1; u8 add_hot_spare_cand:1; u8 rmv_hot_spare_cand:1; u8 migrate_array_prot_cand:1; u8 reserved3:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:2; u8 no_cfgte_dev:1; u8 no_cfgte_vol:1; u8 is_hot_spare:1; u8 is_exposed_device:1; u8 has_parity:1; u8 array_member:1; u8 zero_cand:1; u8 rebuild_cand:1; u8 exclude_cand:1; u8 include_cand:1; u8 resync_cand:1; u8 stop_cand:1; u8 parity_cand:1; u8 start_cand:1; u8 reserved3:5; u8 migrate_array_prot_cand:1; u8 rmv_hot_spare_cand:1; u8 add_hot_spare_cand:1; #endif union { struct { u8 reserved4[2]; u8 array_id; u32 resource_handle; struct ipr_res_addr resource_addr; struct ipr_res_addr last_resource_addr; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved51:1; u8 read_intensive:1; u8 reserved5:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved5:3; u8 read_intensive:1; u8 reserved51:1; u8 block_dev_class:3; #endif u8 reserved6; u8 reserved7; u8 reserved8; }__attribute__((packed, aligned (4))) type2; struct { u8 reserved4[3]; u8 reserved5[4]; u8 reserved6[16]; u8 last_func_res_path[8]; u8 reserved7[2]; u8 array_id; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved81:1; u8 read_intensive:1; u8 reserved8:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved8:3; u8 read_intensive:1; u8 reserved81:1; u8 block_dev_class:3; #endif u32 resource_handle; u8 dev_id[8]; u8 lun[8]; u8 wwn[16]; u8 res_path[8]; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; u8 desc[IPR_DESCRIPTION_LEN]; u8 reserved9[8]; u8 reserved10[2]; u8 reserved11[6]; u8 reserved12[8]; }__attribute__((packed, aligned (4))) type3; }; }__attribute__((packed, aligned (4))); #define __for_each_qac_entry(rcd, qac, type) \ if ((qac)->num_records) \ for (rcd = (type *)(qac)->data; \ ((unsigned long)rcd) < ((unsigned long)((qac)->u.buf + (qac)->resp_len)) && \ ((unsigned long)rcd) < ((unsigned long)((qac)->u.buf + IPR_QAC_BUFFER_SIZE)); \ rcd = (type *)((unsigned long)rcd + ntohs(((struct ipr_common_record *)rcd)->record_len))) #define for_each_supported_arrays_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_supported_arrays) \ if (rcd->common.record_id == IPR_RECORD_ID_SUPPORTED_ARRAYS) #define for_each_qac_entry(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_common_record) #define for_each_dev_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_dev_record) \ if (ipr_is_device_record(rcd->common.record_id)) #define for_each_array_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_array_record) \ if (ipr_is_array_record(rcd->common.record_id)) struct ipr_std_inq_data { #if defined (__BIG_ENDIAN_BITFIELD) u8 peri_qual:3; u8 peri_dev_type:5; u8 removeable_medium:1; u8 reserved1:7; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 peri_dev_type:5; u8 peri_qual:3; u8 reserved1:7; u8 removeable_medium:1; #endif u8 version; #if defined (__BIG_ENDIAN_BITFIELD) u8 aen:1; u8 obsolete1:1; u8 norm_aca:1; u8 hi_sup:1; u8 resp_data_fmt:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 resp_data_fmt:4; u8 hi_sup:1; u8 norm_aca:1; u8 obsolete1:1; u8 aen:1; #endif u8 additional_len; #if defined (__BIG_ENDIAN_BITFIELD) u8 sccs:1; u8 reserved2:7; u8 bque:1; u8 enc_serv:1; u8 vs:1; u8 multi_port:1; u8 mchngr:1; u8 obsolete2:2; u8 addr16:1; u8 rel_adr:1; u8 obsolete3:1; u8 wbus16:1; u8 sync:1; u8 linked:1; u8 trans_dis:1; u8 cmd_que:1; u8 vs2:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:7; u8 sccs:1; u8 addr16:1; u8 obsolete2:2; u8 mchngr:1; u8 multi_port:1; u8 vs:1; u8 enc_serv:1; u8 bque:1; u8 vs2:1; u8 cmd_que:1; u8 trans_dis:1; u8 linked:1; u8 sync:1; u8 wbus16:1; u8 obsolete3:1; u8 rel_adr:1; #endif struct ipr_std_inq_vpids vpids; u8 ros_rsvd_ram_rsvd[4]; u8 serial_num[IPR_SERIAL_NUM_LEN]; }; struct ipr_std_inq_data_long { struct ipr_std_inq_data std_inq_data; u8 z1_term[IPR_STD_INQ_Z1_TERM_LEN]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved:4; u8 clocking:2; u8 qas:1; u8 ius:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ius:1; u8 qas:1; u8 clocking:2; u8 reserved:4; #endif u8 reserved1[41]; u8 z2_term[IPR_STD_INQ_Z2_TERM_LEN]; u8 z3_term[IPR_STD_INQ_Z3_TERM_LEN]; u8 as400_service_level; u8 z4_term[IPR_STD_INQ_Z4_TERM_LEN]; u8 z5_term[IPR_STD_INQ_Z5_TERM_LEN]; u8 part_number[IPR_STD_INQ_PART_NUM_LEN]; u8 ec_level[IPR_STD_INQ_EC_LEVEL_LEN]; u8 fru_number[IPR_STD_INQ_FRU_NUM_LEN]; u8 z6_term[IPR_STD_INQ_Z6_TERM_LEN]; }; struct ipr_mode_page_28_scsi_dev_bus_attr { struct ipr_res_addr res_addr; #if defined (__BIG_ENDIAN_BITFIELD) u8 qas_capability:2; u8 enable_target_mode:1; u8 term_power_absent:1; u8 target_mode_supported:1; u8 lvd_to_se_transition_not_allowed:1; u8 reserved2:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:2; u8 lvd_to_se_transition_not_allowed:1; u8 target_mode_supported:1; u8 term_power_absent:1; u8 enable_target_mode:1; u8 qas_capability:2; #endif #define IPR_MODEPAGE28_QAS_CAPABILITY_NO_CHANGE 0 #define IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL 1 #define IPR_MODEPAGE28_QAS_CAPABILITY_ENABLE_ALL 2 /* NOTE: Due to current operation conditions QAS should never be enabled so the change mask will be set to 0 */ #define IPR_MODEPAGE28_QAS_CAPABILITY_CHANGE_MASK 0 u8 scsi_id; #define IPR_MODEPAGE28_SCSI_ID_NO_CHANGE 0x80u #define IPR_MODEPAGE28_SCSI_ID_NO_ID 0xFFu u8 bus_width; #define IPR_MODEPAGE28_BUS_WIDTH_NO_CHANGE 0 u8 extended_reset_delay; #define IPR_EXTENDED_RESET_DELAY 7 u32 max_xfer_rate; #define IPR_MODEPAGE28_MAX_XFR_RATE_NO_CHANGE 0 u8 min_time_delay; #define IPR_DEFAULT_SPINUP_DELAY 0xFFu #define IPR_INIT_SPINUP_DELAY 5 u8 reserved3; u16 reserved4; }; #define IPR_BUS_XFER_RATE_TO_THRUPUT(rate, width) \ (((rate) * ((width) / 8)) / 10) #define IPR_BUS_THRUPUT_TO_XFER_RATE(thruput, width) \ (((thruput) * 10) / ((width) / 8)) #define IPR_CONFIG_DIR "/etc/ipr/" #define IPR_CATEGORY_IOA "Adapter" #define IPR_GSCSI_HA_ONLY "JBOD_ONLY_HA" #define IPR_DUAL_ADAPTER_ACTIVE_ACTIVE "DUAL_ADAPTER_ACTIVE_ACTIVE" #define IPR_CATEGORY_BUS "Bus" #define IPR_QAS_CAPABILITY "QAS_CAPABILITY" #define IPR_HOST_SCSI_ID "HOST_SCSI_ID" #define IPR_BUS_WIDTH "BUS_WIDTH" #define IPR_MAX_XFER_RATE_STR "MAX_BUS_SPEED" #define IPR_MIN_TIME_DELAY "MIN_TIME_DELAY" #define IPR_CATEGORY_DISK "Disk" #define IPR_QUEUE_DEPTH "QUEUE_DEPTH" #define IPR_TCQ_ENABLED "TCQ_ENABLED" #define IPR_FORMAT_TIMEOUT "FORMAT_UNIT_TIMEOUT" #define IPR_MAX_XFER_RATE 320 #define IPR_SAFE_XFER_RATE 160 /* Internal return codes */ #define RC_SUCCESS 0 #define RC_FAILED -1 #define RC_NOOP -2 #define RC_NOT_PERFORMED -3 #define RC_IN_USE -4 #define RC_CANCEL 12 #define RC_REFRESH 1 #define RC_EXIT 3 struct scsi_dev_data { int host; int channel; union { u8 id; struct ipr_phy phy; struct ipr_phy_2bit_hop phy_2bit_hop; }; int lun; int type; int opens; int qdepth; int busy; int online; u32 handle; char vendor_id[IPR_VENDOR_ID_LEN + 1]; char product_id[IPR_PROD_ID_LEN + 1]; char sysfs_device_name[PATH_MAX]; char dev_name[64]; char gen_name[64]; #define IPR_MAX_RES_PATH_LEN 24 char res_path[IPR_MAX_RES_PATH_LEN]; u64 device_id; }; struct ipr_path_entry { u8 state; #define IPR_PATH_FUNCTIONAL 0 #define IPR_PATH_NOT_FUNCTIONAL 1 u8 local_bus_num; u8 remote_bus_num; u8 reserved; u8 local_connection_id[8]; u8 remote_connection_id[8]; }; struct ipr_path_entries { u32 num_path_entries; struct ipr_path_entry path[0]; /* variable length */ }; struct ipr_dual_ioa_entry { u32 length; u8 link_state; #define IPR_IOA_LINK_STATE_NOT_OPER 0 #define IPR_IOA_LINK_STATE_OPER 1 u8 cur_state; #define IPR_IOA_STATE_NO_CHANGE 0 #define IPR_IOA_STATE_PRIMARY 2 #define IPR_IOA_STATE_SECONDARY 3 #define IPR_IOA_STATE_NO_PREFERENCE 4 u8 fmt; u8 multi_adapter_type; #define IPR_IOA_MA_TYPE_UNDEFINED 0 #define IPR_IOA_MA_TYPE_DUAL_IOA 1 #define IPR_IOA_MA_TYPE_AUX_CACHE 2 u8 reserved[2]; u16 add_len; union { struct { u8 remote_vendor_id[IPR_VENDOR_ID_LEN]; u8 remote_prod_id[IPR_PROD_ID_LEN]; u8 remote_sn[IPR_SERIAL_NUM_LEN]; } fmt0; struct { u8 remote_vendor_id[IPR_VENDOR_ID_LEN]; u8 remote_prod_id[IPR_PROD_ID_LEN]; u8 remote_sn[IPR_SERIAL_NUM_LEN]; u8 wwid[8]; } fmt1; }; }; struct ipr_phys_bus_entry { u8 bus_num; u8 reserved[3]; u8 conn_id[8]; }; struct ipr_ioa_cap_entry { u32 length; u8 reserved; u8 preferred_role; u8 token; u8 reserved2[4]; u8 num_bus_entries; struct ipr_phys_bus_entry bus[0]; }; struct ipr_multi_ioa_status { u32 length; u32 num_entries; struct ipr_ioa_cap_entry cap; u8 buf[16 * 1024]; /* * struct ipr_dual_ioa_entry ioa[0]; */ }; struct ipr_disk_attr { int queue_depth; int tcq_enabled; int format_timeout; }; struct ipr_vset_attr { int queue_depth; }; struct ipr_ioa_attr { int preferred_primary; int gscsi_only_ha; int active_active; int caching; }; #define IPR_DEV_MAX_PATHS 2 struct ipr_dev { char dev_name[64]; char gen_name[64]; char prot_level_str[8]; u8 *vendor_id; u8 *product_id; u8 *serial_number; u8 array_id; u8 raid_level; u16 stripe_size; u32 resource_handle; u8 block_dev_class; u8 read_intensive; u32 is_reclaim_cand:1; u32 should_init:1; u32 init_not_allowed:1; u32 local_flag:1; u32 rescan:1; u8 active_suspend; u32 is_suspend_cand:1; u32 is_resume_cand:1; struct scsi_dev_data *scsi_dev_data; struct ipr_dev *ses[IPR_DEV_MAX_PATHS]; struct ipr_res_addr res_addr[IPR_DEV_MAX_PATHS]; struct ipr_res_path res_path[IPR_DEV_MAX_PATHS]; char res_path_name[IPR_MAX_RES_PATH_LEN]; struct ipr_disk_attr attr; char physical_location[PHYSICAL_LOCATION_LENGTH]; union { struct ipr_common_record *qac_entry; struct ipr_dev_record *dev_rcd; struct ipr_array_record *array_rcd; }; struct ipr_dev *alt_path; struct ipr_ioa *ioa; }; #define for_each_ra(ra, dev) \ for(ra = (dev)->res_addr; \ (ra - ((dev)->res_addr)) < IPR_DEV_MAX_PATHS; ra++) #define for_each_rp(rp, dev) \ for(rp = (dev)->res_path; \ (rp - ((dev)->res_path)) < IPR_DEV_MAX_PATHS; rp++) enum ipr_tcq_mode { IPR_TCQ_DISABLE = 0, IPR_TCQ_FROZEN = 1, IPR_TCQ_NACA = 2 }; #define IPR_MAX_IOA_DEVICES (IPR_MAX_NUM_BUSES * 45 * 2 + 1) struct ipr_ioa { struct ipr_dev ioa; u16 ccin; u16 af_block_size; u32 host_addr; u32 num_raid_cmds; u32 msl; u16 num_devices; u8 ioa_dead:1; u8 nr_ioa_microcode:1; u8 scsi_id_changeable:1; u8 dual_raid_support:1; u8 is_secondary:1; u8 should_init:1; u8 is_aux_cache:1; u8 protect_last_bus:1; u8 gscsi_only_ha:1; u8 in_gscsi_only_ha:1; u8 asymmetric_access:1; u8 asymmetric_access_enabled:1; u8 has_cache:1; u8 sis64:1; #define IPR_SIS32 0x00 #define IPR_SIS64 0x01 u8 support_4k:1; enum ipr_tcq_mode tcq_mode; u16 pci_vendor; u16 pci_device; u16 subsystem_vendor; u16 subsystem_device; u16 hop_count; #define IPR_NOHOP 0x00 #define IPR_2BIT_HOP 0x01 #define IPR_3BIT_HOP 0x02 char dual_state[16]; char preferred_dual_state[16]; int host_num; char pci_address[16]; char host_name[16]; char physical_location[1024]; u8 yl_serial_num[YL_SERIAL_NUM_LEN]; struct ipr_dev dev[IPR_MAX_IOA_DEVICES]; struct ipr_multi_ioa_status ioa_status; struct ipr_array_query_data *qac_data; struct ipr_array_record *start_array_qac_entry; struct ipr_supported_arrays *supported_arrays; struct ipr_reclaim_query_data *reclaim_data; struct ipr_ioa *next; struct ipr_ioa *cmd_next; }; #define __for_each_ioa(ioa, head) for (ioa = head; ioa; ioa = ioa->next) #define for_each_ioa(ioa) __for_each_ioa(ioa, ipr_ioa_head) #define for_each_primary_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (!ioa->is_secondary) #define for_each_secondary_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (ioa->is_secondary) #define for_each_sas_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (!__ioa_is_spi(ioa)) #define for_each_dev(i, d) for (d = (i)->dev; (d - (i)->dev) < (i)->num_devices; d++) #define for_each_hotplug_dev(i, d) \ for_each_dev(i, d) \ if (ipr_is_af_dasd_device(d) || ipr_is_gscsi(d)) #define for_each_af_dasd(i, d) \ for_each_dev(i, d) \ if (ipr_is_af_dasd_device(d)) #define for_each_dev_in_vset(v, d) \ for_each_af_dasd((v)->ioa, d) \ if (ipr_is_array_member(d) && d->array_id == (v)->array_id) #define for_each_vset(i, d) \ for_each_dev(i, d) \ if (ipr_is_volume_set(d) && !d->array_rcd->start_cand) #define for_each_array(i, d) \ for_each_dev(i, d) \ if (ipr_is_array(d) && !d->array_rcd->start_cand) #define for_each_dvd_tape(i, d) \ for_each_dev(i, d) \ if (d->scsi_dev_data && (d->scsi_dev_data->type == TYPE_ROM || \ d->scsi_dev_data->type == TYPE_TAPE)) #define for_each_ses(i, d) \ for_each_dev(i, d) \ if (d->scsi_dev_data && d->scsi_dev_data->type == TYPE_ENCLOSURE) #define for_each_hot_spare(i, d) \ for_each_dev(i, d) \ if (ipr_is_hot_spare(d)) #define for_each_disk(i, d) \ for_each_dev(i, d) \ if ((d)->scsi_dev_data && (ipr_is_af_dasd_device(d) || ipr_is_gscsi(d))) #define __for_each_disk(i, d) \ for_each_dev(i, d) \ if ((d)->scsi_dev_data && ((d)->scsi_dev_data->type == TYPE_DISK || \ (d)->scsi_dev_data->type == IPR_TYPE_AF_DISK)) #define for_each_standalone_disk(i, d) \ for_each_disk(i, d) \ if (!ipr_is_hot_spare(d) && !ipr_is_array_member(d)) #define __for_each_standalone_disk(i, d) \ __for_each_disk(i, d) \ if (!ipr_is_hot_spare(d) && !ipr_is_volume_set(d) && \ !ipr_is_array_member(d)) #define for_each_standalone_af_disk(i, d) \ for_each_standalone_disk(i, d) \ if (ipr_is_af_dasd_device(d)) #define for_each_jbod_disk(i, d) \ for_each_disk(i, d) \ if (ipr_is_gscsi(d)) struct ipr_dasd_inquiry_page3 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2[3]; u8 load_id[4]; u8 release_level[4]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_array_query32_data { u16 resp_len; u8 reserved; u8 num_records; u8 data[0]; }; struct ipr_array_query64_data { u32 resp_len; u32 num_records; u8 data[0]; }; struct ipr_array_query_data { u32 resp_len; u32 num_records; u32 hdr_len; u8 *data; union { u8 buf[IPR_QAC_BUFFER_SIZE]; struct ipr_array_query32_data buf32; struct ipr_array_query64_data buf64; } u; }; struct ipr_block_desc { u8 num_blocks[4]; u8 density_code; u8 block_length[3]; }; struct ipr_mode_parm_hdr { u8 length; u8 medium_type; u8 device_spec_parms; u8 block_desc_len; }; struct ipr_mode_pages { struct ipr_mode_parm_hdr hdr; u8 data[255 - sizeof(struct ipr_mode_parm_hdr)]; }; struct sense_data_t { u8 error_code; u8 segment_numb; u8 sense_key; u8 info[4]; u8 add_sense_len; u8 cmd_spec_info[4]; u8 add_sense_code; u8 add_sense_code_qual; u8 field_rep_unit_code; u8 sense_key_spec[3]; u8 add_sense_bytes[0]; }; struct ipr_ioa_mode_page { struct ipr_mode_page_hdr hdr; u8 reserved; u8 max_tcq_depth; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved1:1; u8 format_completed:1; u8 reserved2:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:6; u8 format_completed:1; u8 reserved1:1; #endif }; struct ipr_mode_page24 { struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 dual_adapter_af:2; u8 reserved:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:6; u8 dual_adapter_af:2; #endif #define DISABLE_DUAL_IOA_AF 0x00 #define ENABLE_DUAL_IOA_AF 0x02 #define ENABLE_DUAL_IOA_ACTIVE_ACTIVE 0x03 }; struct ipr_config_term_hdr { u8 term_id; u8 len; }; struct ipr_subsys_config_term { struct ipr_config_term_hdr hdr; #define IPR_SUBSYS_CONFIG_TERM_ID 0x02 u8 config; #define IPR_AFDASD_SUBSYS 0x00 #define IPR_GSCSI_ONLY_HA_SUBSYS 0x01 u8 reserved; }; struct ipr_mode_page25 { struct ipr_mode_page_hdr hdr; u8 reserved[2]; struct ipr_config_term_hdr term; u8 data[256]; }; #define for_each_page25_term(hdr, page) \ for (hdr = &(page)->term; \ (char *)hdr < ((char *)(page) + (page)->hdr.page_length + sizeof((page)->hdr)); \ hdr = (struct ipr_config_term_hdr *)((char *)hdr + (hdr)->len + sizeof(*hdr))) struct ipr_mode_page_28_header { struct ipr_mode_page_hdr header; u8 num_dev_entries; u8 dev_entry_length; }; struct ipr_mode_page_28 { struct ipr_mode_page_28_header page_hdr; struct ipr_mode_page_28_scsi_dev_bus_attr attr[0]; }; #define for_each_bus_attr(ptr, page28, i) \ for (i = 0, ptr = page28->attr; i < page_28->page_hdr.num_dev_entries; \ i++, ptr = (struct ipr_mode_page_28_scsi_dev_bus_attr *) \ ((u8 *)ptr + page_28->page_hdr.dev_entry_length)) struct ipr_scsi_bus_attr { u32 max_xfer_rate; u8 qas_capability; u8 scsi_id; u8 bus_width; u8 extended_reset_delay; u8 min_time_delay; }; struct ipr_scsi_buses { int num_buses; struct ipr_scsi_bus_attr bus[IPR_MAX_NUM_BUSES]; }; struct ipr_dasd_timeout_record { u8 op_code; u8 reserved; u16 timeout; }; struct ipr_query_dasd_timeouts { u32 length; struct ipr_dasd_timeout_record record[100]; }; struct ipr_supported_arrays { struct ipr_common_record common; u16 num_entries; u16 entry_length; struct ipr_array_cap_entry entry[0]; }; struct ipr_read_cap { u32 max_user_lba; u32 block_length; }; struct ipr_read_cap16 { u32 max_user_lba_hi; u32 max_user_lba_lo; u32 block_length; }; /* Struct for disks that are unsupported or require a minimum microcode level prior to formatting to 522-byte sectors. */ struct unsupported_af_dasd { char vendor_id[IPR_VENDOR_ID_LEN + 1]; char compare_vendor_id_byte[IPR_VENDOR_ID_LEN]; char product_id[IPR_PROD_ID_LEN + 1]; char compare_product_id_byte[IPR_PROD_ID_LEN]; char lid[5]; /* Load ID - Bytes 8-11 of Inquiry Page 3 */ char lid_mask[4]; /* Mask for certain bytes of the LID */ uint supported_with_min_ucode_level; char min_ucode_level[5]; char min_ucode_mask[4]; /* used to mask don't cares in the ucode level */ }; struct unsupported_dasd { char vendor_id[IPR_VENDOR_ID_LEN + 1]; char compare_vendor_id_byte[IPR_VENDOR_ID_LEN]; char product_id[IPR_PROD_ID_LEN + 1]; char compare_product_id_byte[IPR_PROD_ID_LEN]; }; struct ipr_cmd_status_record { u16 reserved1; u16 length; u8 array_id; u8 command_code; u8 status; #define IPR_CMD_STATUS_SUCCESSFUL 0 #define IPR_CMD_STATUS_IN_PROGRESS 2 #define IPR_CMD_STATUS_ATTRIB_CHANGE 3 #define IPR_CMD_STATUS_FAILED 4 #define IPR_CMD_STATUS_INSUFF_DATA_MOVED 5 #define IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS 6 #define IPR_CMD_STATUS_MIXED_LOG_BLK_SIZE 7 #define IPR_CMD_STATUS_UNSUPT_REQ_BLK_DEV_CLASS 8 u8 percent_complete; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; u32 failing_dev_ioasc; u32 ilid; u32 resource_handle; u8 vset_id; u8 flags; u16 reserved; u8 failing_dev_res_path[8]; }; struct ipr_cmd_status { u16 resp_len; u16 num_records; struct ipr_cmd_status_record record[250]; }; #define for_each_cmd_status(r, s) for (r = (s)->record; (unsigned long)(r) < ((unsigned long)((s)->record) + ntohs((s)->resp_len) - 4); r = (void *)(r) + ntohs((r)->length)) struct ipr_inquiry_page0 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 supported_page_codes[IPR_MAX_NUM_SUPP_INQ_PAGES]; }; struct ipr_inquiry_page3 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2[3]; u8 load_id[4]; u8 major_release; u8 card_type; u8 minor_release[2]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_inquiry_ioa_cap { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2; u8 sis_version[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 dual_ioa_raid:1; u8 dual_ioa_wcache:1; u8 dual_ioa_asymmetric_access:1; u8 reserved:5; u8 can_attach_to_aux_cache:1; u8 is_aux_cache:1; u8 is_dual_wide:1; u8 gscsi_only_ha:1; u8 reserved3:4; u8 reserved4:1; u8 af_4k_support:1; u8 reserved5:6; u8 reserved6:3; u8 ra_id_encoding:3; u8 sis_format:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:5; u8 dual_ioa_asymmetric_access:1; u8 dual_ioa_wcache:1; u8 dual_ioa_raid:1; u8 reserved3:4; u8 gscsi_only_ha:1; u8 is_dual_wide:1; u8 is_aux_cache:1; u8 can_attach_to_aux_cache:1; u8 reserved4:6; u8 af_4k_support:1; u8 reserved5:1; u8 sis_format:2; u8 ra_id_encoding:3; u8 reserved6:3; #endif u16 af_block_size; u16 af_ext_cap; u16 vset_ext_cap; u16 gscsi_ext_cap; }; struct ipr_dasd_ucode_header { u8 length[3]; u8 load_id[4]; u8 modification_level[4]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_ioa_ucode_img_desc { #define IPR_IOAF_STR "IOAF" #define IPR_FPGA_STR "FPGA" #define IPR_DRAM_STR "AGIG" char fw_type[4]; #define IPR_IOAF 0 #define IPR_FPGA 1 #define IPR_DRAM 2 u8 reserved[16]; }; struct ipr_ioa_ucode_ext_header { char eyecatcher[8]; /* EXTDLIMG */ u32 image_length; u32 flags; char fw_level[8]; u8 reserved[12]; u32 img_desc_offset; }; struct ipr_ioa_ucode_header { u32 header_length; u32 lid_table_offset; u32 rev_level; #define LINUX_HEADER_RESERVED_BYTES 20 u8 reserved[LINUX_HEADER_RESERVED_BYTES]; char eyecatcher[16]; /* IBMAS400 CCIN */ u32 num_lids; }; struct ipr_fw_images { char file[100]; u32 version; int has_header; char date[20]; }; struct ipr_ioa_vpd { struct ipr_std_inq_data std_inq_data; u8 ascii_part_num[IPR_VPD_PART_NUM_LEN]; u8 reserved[40]; u8 ascii_plant_code[IPR_VPD_PLANT_CODE_LEN]; }; struct ipr_cfc_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 cache_size[IPR_VPD_CACHE_SIZE_LEN]; struct ipr_std_inq_vpids vpids; u8 revision_level[4]; u8 serial_num[IPR_SERIAL_NUM_LEN]; u8 ascii_part_num[12]; u8 reserved3[40]; u8 ascii_plant_code[IPR_VPD_PLANT_CODE_LEN]; }; struct ipr_dram_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 dram_size[IPR_VPD_DRAM_SIZE_LEN]; }; struct ipr_cache_cap_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u32 cache_cap; u32 data_store_size; u32 write_cache_size; u32 comp_write_cache_size; u32 read_cache_size; u32 comp_read_cache_size; }; struct ipr_dev_identify_vpd { u8 peri_dev_type; u8 page_code; u16 add_page_len; u8 reserved[80]; u8 dev_identify_contxt[40]; }; #define IPR_IOA_MAX_SUPP_LOG_PAGES 64 struct ipr_supp_log_pages { u8 page_code; u8 reserved; u16 length; u8 page[IPR_IOA_MAX_SUPP_LOG_PAGES]; }; struct ipr_sas_phy_log_desc { u8 reserved; u8 phy; u8 reserved2[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reseved3:1; u8 attached_dev_type:3; u8 reserved4:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved4:4; u8 attached_dev_type:3; u8 reseved3:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved5:4; u8 link_rate:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 link_rate:4; u8 reserved5:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved6:4; u8 attached_ssp_initiator_port:1; u8 attached_stp_initiator_port:1; u8 attached_smp_initiator_port:1; u8 reserved7:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved7:1; u8 attached_smp_initiator_port:1; u8 attached_stp_initiator_port:1; u8 attached_ssp_initiator_port:1; u8 reserved6:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved8:4; u8 attached_ssp_target_port:1; u8 attached_stp_target_port:1; u8 attached_smp_target_port:1; u8 reserved9:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved9:1; u8 attached_smp_target_port:1; u8 attached_stp_target_port:1; u8 attached_ssp_target_port:1; u8 reserved8:4; #endif u8 sas_addr[8]; u8 attached_sas_addr[8]; u8 attached_phy_id; u8 reserved10[7]; u32 invalid_dword_count; u32 running_disparity_error_count; u32 loss_of_dword_sync_count; u32 phy_reset_problem_count; }; struct ipr_sas_port_log_desc { u16 port_num; #if defined (__BIG_ENDIAN_BITFIELD) u8 du:1; u8 ds:1; u8 tsd:1; u8 etc:1; u8 tmc:2; u8 lbin:1; u8 lp:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 lp:1; u8 lbin:1; u8 tmc:2; u8 etc:1; u8 tsd:1; u8 ds:1; u8 du:1; #endif u8 length; u8 proto_id; #define IPR_SAS_PROTOCOL_ID 0x06 u8 reserved[2]; u8 num_phys; #define IPR_IOA_MAX_PHYS 4 struct ipr_sas_phy_log_desc phy[IPR_IOA_MAX_PHYS]; }; #define IPR_SAS_LINK_RATE_NOT_PROGRAMMABLE 0 #define IPR_SAS_LINK_RATE_1_5_Gbps 8 #define IPR_SAS_LINK_RATE_3_0_Gbps 9 #define IPR_SAS_ROUTING_DIRECT 0 #define IPR_SAS_ROUTING_SUB_OR_DIRECT 1 #define IPR_SAS_ROUTING_TABLE_OR_DIRECT 2 struct ipr_sas_phy_info { u32 len; u8 bus; u8 cascade; u8 phy_num; u8 reserved; struct ipr_sas_phy_log_desc phy_log; #if defined (__BIG_ENDIAN_BITFIELD) u8 prog_min_link_rate:4; u8 hw_min_link_rate:4; u8 prog_max_link_rate:4; u8 hw_max_link_rate:4; u8 virtual_phy:1; u8 reserved2:7; u8 reserved3:4; u8 routing_attr:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 hw_min_link_rate:4; u8 prog_min_link_rate:4; u8 hw_max_link_rate:4; u8 prog_max_link_rate:4; u8 reserved2:7; u8 virtual_phy:1; u8 routing_attr:4; u8 reserved3:4; #endif }; struct ipr_sas_expander_info { u32 len; u8 bus; u8 cascade; u8 reserved; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved2:7; u8 sas_1_1_fmt:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 sas_1_1_fmt:1; u8 reserved2:7; #endif struct ipr_std_inq_vpids vpids; u8 prod_rev_level[4]; u8 component_vendor_id[8]; u8 component_id[2]; u8 component_rev_id; u8 reserved3; u8 vendor_spec[8]; u8 enclosure_logical_id[8]; u8 reserved4[3]; u8 num_phys; struct ipr_sas_phy_info phy[1]; }; struct ipr_query_sas_expander_info { u32 len; u8 reserved[3]; u8 num_expanders; struct ipr_sas_expander_info exp[1]; }; struct ipr_global_cache_params_term { #define IPR_CACHE_PARAM_TERM_ID 0x10 u8 term_id; u8 len; #if defined (__BIG_ENDIAN_BITFIELD) u8 enable_caching_dual_ioa_failure:1; u8 disable_caching_requested:1; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 disable_caching_requested:1; u8 enable_caching_dual_ioa_failure:1; #endif u8 reserved2; }; struct ipr_query_ioa_caching_info { u16 len; u8 reserved[2]; #define IPR_MAX_TERMS 512 struct ipr_global_cache_params_term term[IPR_MAX_TERMS]; }; #define for_each_cache_term(term, info) \ for (term = (info)->term; \ ((unsigned long)term) < ((unsigned long)((unsigned long)(info) + ntohs((info)->len)) + 2) && \ ((unsigned long)term + term->len + 2 - (unsigned long)(info)->term) < sizeof((info)->term); \ term = (struct ipr_global_cache_params_term *)(((unsigned long)term) + term->len + 2)) struct ipr_log_page18 { u8 page_code; u8 reserved; u16 length; #define IPR_IOA_MAX_PORTS 32 struct ipr_sas_port_log_desc port[IPR_IOA_MAX_PORTS]; }; #define for_each_port(port, log) \ for (port = (log)->port; \ (unsigned long)port < ((unsigned long)((log)->port) + ntohs(port->length)); \ port = (struct ipr_sas_port_log_desc *)((unsigned long)(&port->length) + port->length + 1)) #define for_each_phy(phy, port) \ for (phy = (port)->phy; \ (phy < ((port)->phy + (port)->num_phys)) && \ (phy < ((port)->phy + IPR_IOA_MAX_PORTS)); phy++) struct ipr_dasd_perf_counters_log_page30 { u8 page_code; u8 reserved1; u16 length; u8 reserved2[3]; u8 param_length; u16 dev_no_seeks; u16 dev_seeks_2_3; u16 dev_seeks_1_3; u16 dev_seeks_1_6; u16 dev_seeks_1_12; u16 dev_seeks_0; u32 reserved3; u16 dev_read_buf_overruns; u16 dev_write_buf_underruns; u32 dev_cache_read_hits; u32 dev_cache_partial_read_hits; u32 dev_cache_write_hits; u32 dev_cache_fast_write_hits; u32 reserved4; u32 reserved5; u32 ioa_dev_read_ops; u32 ioa_dev_write_ops; u32 ioa_cache_read_hits; u32 ioa_cache_partial_read_hits; u32 ioa_cache_write_hits; u32 ioa_cache_fast_write_hits; u32 ioa_cache_emu_read_hits; u32 ioa_idle_loop_count[2]; u32 ioa_idle_count_value; u8 ioa_idle_units; u8 reserved6[3]; }; struct ipr_fabric_config_element { u8 type_status; #define IPR_PATH_CFG_TYPE_MASK 0xF0 #define IPR_PATH_CFG_NOT_EXIST 0x00 #define IPR_PATH_CFG_IOA_PORT 0x10 #define IPR_PATH_CFG_EXP_PORT 0x20 #define IPR_PATH_CFG_DEVICE_PORT 0x30 #define IPR_PATH_CFG_DEVICE_LUN 0x40 #define IPR_PATH_CFG_STATUS_MASK 0x0F #define IPR_PATH_CFG_NO_PROB 0x00 #define IPR_PATH_CFG_DEGRADED 0x01 #define IPR_PATH_CFG_FAILED 0x02 #define IPR_PATH_CFG_SUSPECT 0x03 #define IPR_PATH_NOT_DETECTED 0x04 #define IPR_PATH_INCORRECT_CONN 0x05 u8 cascaded_expander; u8 phy; u8 link_rate; #define IPR_PHY_LINK_RATE_MASK 0x0F u32 wwid[2]; }; struct ipr_fabric_descriptor { u16 length; u8 ioa_port; u8 cascaded_expander; u8 phy; u8 path_state; #define IPR_PATH_ACTIVE_MASK 0xC0 #define IPR_PATH_NO_INFO 0x00 #define IPR_PATH_ACTIVE 0x40 #define IPR_PATH_NOT_ACTIVE 0x80 #define IPR_PATH_STATE_MASK 0x0F #define IPR_PATH_STATE_NO_INFO 0x00 #define IPR_PATH_HEALTHY 0x01 #define IPR_PATH_DEGRADED 0x02 #define IPR_PATH_FAILED 0x03 u16 num_entries; struct ipr_fabric_config_element elem[1]; }; struct ipr_fabric_config_element64 { u16 length; #if defined (__BIG_ENDIAN_BITFIELD) u8 cfg_elem_id:2; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 cfg_elem_id:2; #endif u8 reserved2; u8 type_status; u8 reserved3[2]; u8 link_rate; u8 res_path[8]; u32 wwid[4]; }; struct ipr_fabric_descriptor64 { u16 length; #if defined (__BIG_ENDIAN_BITFIELD) u8 fabric_id:2; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 fabric_id:2; #endif u8 reserved2[2]; u8 path_state; u8 reserved3; u8 res_path[8]; u32 reserved4; u8 reserved5[2]; u16 num_entries; struct ipr_fabric_config_element64 elem[1]; }; #define for_each_fabric_cfg(fabric, cfg) \ for (cfg = (fabric)->elem; \ cfg < ((fabric)->elem + ntohs((fabric)->num_entries)); \ cfg++) #define for_each_fabric_desc(fabric, info) \ for (fabric = (info)->u.fabric; \ ((unsigned long)fabric) < (((unsigned long)(info)) + ntohl((info)->len) + sizeof((info)->len)); \ fabric = (struct ipr_fabric_descriptor *)(((unsigned long)fabric) + ntohs(fabric->length))) #define for_each_fabric_desc64(fabric, info) \ for (fabric = (info)->u.fabric64; \ ((unsigned long)fabric) < (((unsigned long)(info)) + ntohl((info)->len) + sizeof((info)->len)); \ fabric = (struct ipr_fabric_descriptor64 *)(((unsigned long)fabric) + ntohs(fabric->length))) struct ipr_res_redundancy_info { u32 len; u8 possibly_redundant_paths; u8 healthy_paths; u8 degraded_paths; u8 failed_paths; u8 active_paths; u8 reserved[2]; u8 fabric_descriptors; union { struct ipr_fabric_descriptor fabric[1]; struct ipr_fabric_descriptor64 fabric64[1]; } u; u8 data[16384]; }; struct ses_inquiry_page0 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 supported_vpd_page[SES_MAX_NUM_SUPP_INQ_PAGES]; }; struct ses_serial_num_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 serial_num_head[3]; u8 feature_code[4]; u8 ascii_dash; u8 count[3]; u8 ascii_space; u8 ses_serial_num[7]; }; struct esm_serial_num_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 page_len; u8 ascii_len; u8 fru_part_number[3]; u8 frb_number[8]; u8 reserved2; u8 serial_num_head[3]; u8 esm_serial_num[12]; u8 reserved3; u8 model_num_head[3]; u8 model_num[4]; u8 reserved4; u8 frb_label_head[3]; u8 frb_label[5]; u8 reserved5; }; struct drive_elem_desc { u8 reserved1[2]; u8 desc_length[2]; u8 reserved2; #define DISK_PHY_LOC_LENGTH 9 u8 disk_physical_loc[DISK_PHY_LOC_LENGTH]; u8 reserved3[2]; u8 form_facter; u8 slot_height; u8 slot_bus; u8 phy_speed; }; struct drive_elem_desc_pg { u8 page_code; u8 reserved1; u16 page_length; u8 reserved2[6]; u16 desc_length; #define DEVICE_ELEM_NUMBER 50 struct drive_elem_desc dev_elem[DEVICE_ELEM_NUMBER]; }; struct ipr_ses_type_desc { u8 elem_type; #define IPR_SES_DEVICE_ELEM 0x01 #define IPR_SES_ESM_ELEM 0x07 #define IPR_SES_ENCLOSURE_ELEM 0x0E u8 num_elems; u8 subenclosure_id; u8 text_len; }; struct ipr_drive_elem_status { #if defined (__BIG_ENDIAN_BITFIELD) u8 select:1; u8 predictive_fault:1; u8 reserved:1; u8 swap:1; u8 status:4; u8 reserved2:1; u8 device_environment:2; u8 slot_id:5; u8 reserved3:4; u8 insert:1; u8 remove:1; u8 identify:1; u8 reserved4:1; u8 reserved5:1; u8 fault_requested:1; u8 fault_sensed:1; u8 reserved6:4; u8 disable_resets:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 status:4; u8 swap:1; u8 reserved:1; u8 predictive_fault:1; u8 select:1; u8 slot_id:5; u8 device_environment:2; u8 reserved2:1; u8 reserved4:1; u8 identify:1; u8 remove:1; u8 insert:1; u8 reserved3:4; u8 disable_resets:1; u8 reserved6:4; u8 fault_sensed:1; u8 fault_requested:1; u8 reserved5:1; #endif }; struct ipr_encl_status_ctl_pg { u8 page_code; u8 health_status; u16 byte_count; u8 reserved1[4]; struct ipr_drive_elem_status elem_status[IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES]; }; struct ipr_ses_config_pg { u8 buf[2048]; }; struct ipr_ses_inquiry_pageC3 { u8 perif_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_length; u8 mode_keyword[3]; u8 mode[4]; u8 reserved2; }; static inline int ipr_elem_offset(struct ipr_ses_config_pg *ses_cfg, u8 type) { int i, off; struct ipr_ses_type_desc *desc; u8 *diag = ses_cfg->buf; desc = (struct ipr_ses_type_desc *)&diag[12+diag[11]]; for (i = 0, off = 1; i < diag[10]; off += (desc->num_elems + 1), i++, desc++) if (desc->elem_type == type) return off; return 1; } static inline int ipr_dev_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_DEVICE_ELEM); } static inline int ipr_ses_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_ENCLOSURE_ELEM); } static inline int ipr_esm_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_ESM_ELEM); } static inline struct ipr_drive_elem_status * ipr_get_overall_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_dev_elem_offset(ses_cfg) - 1]); } static inline struct ipr_ses_type_desc *ipr_get_elem(struct ipr_ses_config_pg *ses_cfg, u8 type) { int i; struct ipr_ses_type_desc *desc; u8 *diag = ses_cfg->buf; desc = (struct ipr_ses_type_desc *)&diag[12+diag[11]]; for (i = 0; i < diag[10]; i++, desc++) if (desc->elem_type == type) return desc; return NULL; } static inline struct ipr_ses_type_desc *ipr_get_dev_elem(struct ipr_ses_config_pg *ses_cfg) { return ipr_get_elem(ses_cfg, IPR_SES_DEVICE_ELEM); } static inline struct ipr_drive_elem_status * ipr_get_enclosure_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_ses_elem_offset(ses_cfg) - 1]); } static inline struct ipr_drive_elem_status * ipr_get_esm_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_esm_elem_offset(ses_cfg) - 1]); } static inline int ipr_max_dev_elems(struct ipr_ses_config_pg *ses_cfg) { struct ipr_ses_type_desc *desc = ipr_get_dev_elem(ses_cfg); if (desc && desc->num_elems < IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES) return desc->num_elems; return IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES; } #define for_each_elem_status(elem, sd, sc) \ for (elem = &((sd)->elem_status[ipr_dev_elem_offset(sc)]); \ elem < &((sd)->elem_status[(ntohs((sd)->byte_count)-4)/sizeof((sd)->elem_status[0])]) && \ elem < &((sd)->elem_status[ipr_dev_elem_offset(sc) + ipr_max_dev_elems(sc)]) && \ elem < &((sd)->elem_status[IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES]); elem++) int sg_ioctl(int, u8 *, void *, u32, u32, struct sense_data_t *, u32); int sg_ioctl_noretry(int, u8 *, void *, u32, u32, struct sense_data_t *, u32); int ipr_change_multi_adapter_assignment(struct ipr_ioa *, int, int); int set_ha_mode(struct ipr_ioa *, int); int set_preferred_primary(struct ipr_ioa *, int); void check_current_config(bool); int num_device_opens(int, int, int, int); int open_and_lock(char *); void tool_init(int); void exit_on_error(char *, ...); bool is_af_blocked(struct ipr_dev *, int); int ipr_query_array_config(struct ipr_ioa *, bool, bool, bool, int, struct ipr_array_query_data *); int __ipr_query_array_config(struct ipr_ioa *, int, bool, bool, bool, int, struct ipr_array_query_data *); int ipr_query_command_status(struct ipr_dev *, void *); int ipr_query_io_dev_port(struct ipr_dev *, struct ipr_query_io_port *); int ipr_mode_sense(struct ipr_dev *, u8, void *); int ipr_mode_select(struct ipr_dev *, void *, int); int ipr_log_sense(struct ipr_dev *, u8, void *, u16); int ipr_is_log_page_supported(struct ipr_dev *, u8); int ipr_reset_device(struct ipr_dev *); int ipr_re_read_partition(struct ipr_dev *); int ipr_read_capacity(struct ipr_dev *, void *); int ipr_read_capacity_16(struct ipr_dev *, void *); int ipr_query_resource_state(struct ipr_dev *, void *); void ipr_allow_restart(struct ipr_dev *, int); int ipr_get_logical_block_size(struct ipr_dev *); bool ipr_is_af_blk_size(struct ipr_ioa *, int); void ipr_set_manage_start_stop(struct ipr_dev *); int ipr_start_stop_start(struct ipr_dev *); int ipr_start_stop_stop(struct ipr_dev *); int ipr_stop_array_protection(struct ipr_ioa *); int ipr_remove_hot_spare(struct ipr_ioa *); int ipr_start_array_protection(struct ipr_ioa *, int, int); int ipr_migrate_array_protection(struct ipr_ioa *, struct ipr_array_query_data *, int, int, int); int ipr_set_array_asym_access(struct ipr_ioa *); int ipr_add_hot_spare(struct ipr_ioa *); int ipr_rebuild_device_data(struct ipr_ioa *); int ipr_resync_array(struct ipr_ioa *); int ipr_test_unit_ready(struct ipr_dev *, struct sense_data_t *); int ipr_format_unit(struct ipr_dev *); void ipr_format_res_path(u8 *, char *, int); void ipr_format_res_path_wo_hyphen(u8 *, char *, int); int ipr_add_array_device(struct ipr_ioa *, int, struct ipr_array_query_data *); int ipr_reclaim_cache_store(struct ipr_ioa *, int, void *); int ipr_evaluate_device(struct ipr_dev *, u32); int ipr_query_res_redundancy_info(struct ipr_dev *, struct ipr_res_redundancy_info *); int ipr_query_sas_expander_info(struct ipr_ioa *, struct ipr_query_sas_expander_info *); int ipr_query_cache_parameters(struct ipr_ioa *, void *, int); int ipr_disrupt_device(struct ipr_dev *); int ipr_inquiry(struct ipr_dev *, u8, void *, u8); int ipr_query_res_addr_aliases(struct ipr_ioa *, struct ipr_res_addr *, struct ipr_res_addr_aliases *); int ipr_query_res_path_aliases(struct ipr_ioa *, struct ipr_res_path *, struct ipr_res_path_aliases *); void ipr_convert_res_path_to_bytes(struct ipr_dev *); void ipr_format_res_path(u8 *, char *, int); void ipr_reset_adapter(struct ipr_ioa *); void ipr_scan(struct ipr_ioa *, int, int, int); int ipr_read_host_attr(struct ipr_ioa *, char *, void *, size_t); int ipr_write_host_attr(struct ipr_ioa *, char *, void *, size_t); int ipr_read_dev_attr(struct ipr_dev *, char *, char *, size_t); int ipr_write_dev_attr(struct ipr_dev *, char *, char *); int ipr_suspend_device_bus(struct ipr_dev *, struct ipr_res_addr *, u8); int ipr_resume_device_bus(struct ipr_dev *, struct ipr_res_addr *); int ipr_can_remove_device(struct ipr_dev *); int ipr_receive_diagnostics(struct ipr_dev *, u8, void *, int); int ipr_send_diagnostics(struct ipr_dev *, void *, int); int ipr_get_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *); void ipr_modify_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *); int ipr_set_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *, int); int ipr_write_buffer(struct ipr_dev *, void *, int); int enable_af(struct ipr_dev *); int get_max_bus_speed(struct ipr_ioa *, int); int ipr_get_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *); int ipr_get_dev_attr(struct ipr_dev *, struct ipr_disk_attr *); int ipr_modify_dev_attr(struct ipr_dev *, struct ipr_disk_attr *); int ipr_set_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *, int); int ipr_modify_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *); int ipr_set_dev_attr(struct ipr_dev *, struct ipr_disk_attr *, int); int set_active_active_mode(struct ipr_ioa *, int); int ipr_set_format_completed_bit(struct ipr_dev *); int ipr_set_ses_mode(struct ipr_dev *, int); int get_ioa_caching(struct ipr_ioa *); int ipr_change_cache_parameters(struct ipr_ioa *, int); int ipr_query_dasd_timeouts(struct ipr_dev *, struct ipr_query_dasd_timeouts *); int get_ioa_firmware_image_list(struct ipr_ioa *, struct ipr_fw_images **); int get_dasd_firmware_image_list(struct ipr_dev *, struct ipr_fw_images **); int get_ses_firmware_image_list(struct ipr_dev *, struct ipr_fw_images **); int ipr_update_ioa_fw(struct ipr_ioa *, struct ipr_fw_images *, int); int ipr_update_disk_fw(struct ipr_dev *, struct ipr_fw_images *, int); int ipr_init_dev(struct ipr_dev *); int ipr_init_new_dev(struct ipr_dev *); int ipr_init_ioa(struct ipr_ioa *); int device_supported(struct ipr_dev *); struct ipr_dev *get_dev_from_addr(struct ipr_res_addr *res_addr); struct ipr_dev *get_dev_from_handle(struct ipr_ioa *ioa, u32 res_handle); void ipr_daemonize(); struct unsupported_af_dasd *get_unsupp_af(struct ipr_std_inq_data *, struct ipr_dasd_inquiry_page3 *); bool disk_needs_msl(struct unsupported_af_dasd *, struct ipr_std_inq_data *); void ipr_add_zeroed_dev(struct ipr_dev *); void ipr_update_qac_with_zeroed_devs(struct ipr_ioa *); void ipr_cleanup_zeroed_devs(); void ipr_del_zeroed_dev(struct ipr_dev *); int ipr_device_is_zeroed(struct ipr_dev *); struct ipr_array_cap_entry *get_raid_cap_entry(struct ipr_supported_arrays *, u8 ); char *get_prot_level_str(struct ipr_supported_arrays *, int); u32 get_dev_fw_version(struct ipr_dev *); u32 get_ioa_fw_version(struct ipr_ioa *); int ipr_disable_qerr(struct ipr_dev *); void ipr_log_ucode_error(struct ipr_ioa *); u32 get_dasd_ucode_version(char *, int); u32 get_ses_ucode_version(char *ucode_file); const char *get_bus_desc(struct ipr_ioa *); const char *get_ioa_desc(struct ipr_ioa *); int ioa_is_spi(struct ipr_ioa *); int __ioa_is_spi(struct ipr_ioa *); int page0x0a_setup(struct ipr_dev *); int handle_events(void (*) (void), int, void (*) (char *)); struct ipr_ioa *find_ioa(int); int parse_option(char *); struct ipr_dev *find_blk_dev(char *); struct ipr_dev *find_gen_dev(char *); struct ipr_dev *find_dev(char *); int ipr_cmds_per_lun(struct ipr_ioa *); void scsi_host_kevent(char *, int (*)(struct ipr_ioa *)); void scsi_dev_kevent(char *, struct ipr_dev *(*)(char *), int (*)(struct ipr_dev *)); int format_req(struct ipr_dev *); struct ipr_dev *get_vset_from_array(struct ipr_ioa *, struct ipr_dev *); struct ipr_dev *get_array_from_vset(struct ipr_ioa *, struct ipr_dev *); struct sysfs_dev * ipr_find_sysfs_dev(struct ipr_dev *, struct sysfs_dev *); void ipr_add_sysfs_dev(struct ipr_dev *, struct sysfs_dev **, struct sysfs_dev **); void ipr_del_sysfs_dev(struct ipr_dev *, struct sysfs_dev **, struct sysfs_dev **); struct ipr_dev *ipr_sysfs_dev_to_dev(struct sysfs_dev *); struct ipr_array_cap_entry *get_cap_entry(struct ipr_supported_arrays *, char *); int ipr_get_blk_size(struct ipr_dev *); u32 get_ioa_ucode_version(char *); int ipr_improper_device_type(struct ipr_dev *); int ipr_get_fw_version(struct ipr_dev *, u8 release_level[4]); int ipr_get_live_dump(struct ipr_ioa *); static inline u32 ipr_get_dev_res_handle(struct ipr_ioa *ioa, struct ipr_dev_record *dev_rcd) { if (ioa->sis64) return dev_rcd->type3.resource_handle; else return dev_rcd->type2.resource_handle; } static inline u32 ipr_get_arr_res_handle(struct ipr_ioa *ioa, struct ipr_array_record *array_rcd) { if (ioa->sis64) return array_rcd->type3.resource_handle; else return array_rcd->type2.resource_handle; } static inline int ipr_is_device_record(int record_id) { if ((record_id == IPR_RECORD_ID_DEVICE_RECORD) || (record_id == IPR_RECORD_ID_DEVICE_RECORD_3)) return 1; else return 0; } static inline int ipr_is_vset_record(int record_id) { if ((record_id == IPR_RECORD_ID_ARRAY_RECORD) || (record_id == IPR_RECORD_ID_VSET_RECORD_3)) return 1; else return 0; } static inline int ipr_is_array_record(int record_id) { if ((record_id == IPR_RECORD_ID_ARRAY_RECORD) || (record_id == IPR_RECORD_ID_ARRAY_RECORD_3)) return 1; else return 0; } static inline int ipr_is_af_dasd_device(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_device_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_remote_af_dasd_device(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_device_record(device->qac_entry->record_id) && device->dev_rcd->current_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED)) return 1; else return 0; } static inline int ipr_is_volume_set(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_vset_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_array(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_array_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_hidden(struct ipr_dev *device) { if (ipr_is_af_dasd_device(device) || ((device->scsi_dev_data) && (device->scsi_dev_data->type == 3))) //FIXME SES type. return 1; else return 0;} static inline int ipr_is_hot_spare(struct ipr_dev *device) { struct ipr_dev_record *dev_record = (struct ipr_dev_record *)device->qac_entry; if ((dev_record != NULL) && (ipr_is_device_record(dev_record->common.record_id)) && (dev_record->is_hot_spare)) return 1; else return 0; } static inline int ipr_is_array_member(struct ipr_dev *device) { struct ipr_dev_record *dev_record = (struct ipr_dev_record *)device->qac_entry; if ((dev_record != NULL) && (dev_record->array_member)) return 1; else return 0; } static inline int ipr_is_af(struct ipr_dev *device) { if (device->qac_entry != NULL) return 1; else return 0; } static inline int ipr_is_gscsi(struct ipr_dev *dev) { if (!dev->qac_entry && dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_DISK) return 1; else return 0; } static inline int ipr_is_ses(struct ipr_dev *dev) { if (dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_ENCLOSURE) return 1; else return 0; } static inline void ipr_strncpy_0(char *dest, char *source, int length) { memcpy(dest, source, length); dest[length] = '\0'; } static inline void ipr_strncpy_0n(char *dest, char *source, int length) { char *ch; memcpy(dest, source, length); dest[length] = '\0'; ch = strchr(dest, '\n'); if (ch) *ch = '\0'; } #define dprintf(...) \ do { \ if (ipr_debug) \ fprintf(stderr, __VA_ARGS__);\ } while(0) #define ENTER dprintf("Entering %s\n", __FUNCTION__); #define LEAVE dprintf("Leaving %s\n", __FUNCTION__); #define syslog_dbg(...) \ do { \ if (ipr_debug) \ syslog(LOG_ERR, __VA_ARGS__);\ } while(0) #define ra_dbg(ra, fmt, ...) \ syslog_dbg("%d:%d:%d:%d: " fmt, (ra)->host, (ra)->bus, \ (ra)->target, (ra)->lun, ##__VA_ARGS__); \ #define scsi_log(level, dev, fmt, ...) \ do { \ if (dev->scsi_dev_data && !dev->ioa->ioa_dead) { \ syslog(level, "%d:%d:%d:%d: " fmt, dev->ioa->host_num, \ dev->scsi_dev_data->channel, dev->scsi_dev_data->id, \ dev->scsi_dev_data->lun, ##__VA_ARGS__); \ } \ } while (0) #define scsi_dbg(dev, fmt, ...) \ do { \ if ((dev)->scsi_dev_data) { \ syslog_dbg("%d:%d:%d:%d: " fmt, (dev)->ioa->host_num, \ (dev)->scsi_dev_data->channel, (dev)->scsi_dev_data->id, \ (dev)->scsi_dev_data->lun, ##__VA_ARGS__); \ } \ } while (0) #define scsi_info(dev, fmt, ...) \ scsi_log(LOG_NOTICE, dev, fmt, ##__VA_ARGS__) #define scsi_err(dev, fmt, ...) \ scsi_log(LOG_ERR, dev, fmt, ##__VA_ARGS__) #define scsi_warn(dev, fmt, ...) \ scsi_log(LOG_WARNING, dev, fmt, ##__VA_ARGS__) #define scsi_cmd_err(dev, sense, cmd, rc) \ do { \ if ((((sense)->error_code & 0x7F) != 0x70) || \ (((sense)->sense_key & 0x0F) != 0x05) || ipr_debug) { \ scsi_err(dev, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } while (0) #define scsi_cmd_dbg(dev, sense, cmd, rc) \ do { \ if (rc && ipr_debug) { \ scsi_err(dev, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } while (0) #define ioa_log(level, ioa, fmt, ...) \ syslog(level, "%s: " fmt, ioa->pci_address, ##__VA_ARGS__) #define ioa_info(ioa, fmt, ...) \ ioa_log(LOG_NOTICE, ioa, fmt, ##__VA_ARGS__) #define ioa_err(ioa, fmt, ...) \ ioa_log(LOG_ERR, ioa, fmt, ##__VA_ARGS__) #define ioa_dbg(ioa, fmt, ...) \ syslog_dbg("%s: " fmt, ioa->pci_address, ##__VA_ARGS__) #define ioa_cmd_err(ioa, sense, cmd, rc) \ do { \ if (!ioa->ioa_dead) { \ if ((((sense)->error_code & 0x7F) != 0x70) || \ (((sense)->sense_key & 0x0F) != 0x05) || ipr_debug) { \ ioa_err(ioa, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } \ } while (0) #endif /* iprlib_h */ iprutils/iprconfig.h0000644000000000000000000016406212427235652013601 0ustar rootroot#ifndef iprconfig_h #define iprconfig_h /** * IBM IPR adapter configuration utility * * (C) Copyright 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * **/ #include #define _(string) gettext(string) #define __(string) (string) #define EXIT_FLAG 0x8000 /* stops at given screen on exit call */ #define CANCEL_FLAG 0x4000 /* stops at given screen on quit call */ #define REFRESH_FLAG 0x2000 /* refreshes screen on quit or exit */ #define TOGGLE_FLAG 0x1000 #define FWD_FLAG 0x0800 #define CONFIRM_FLAG 0x0400 #define CONFIRM_REC_FLAG 0x0200 #define MENU_FLAG 0x0100 #define ENTER_FLAG 0x0080 #define NUM_OPTS(x) (sizeof(x)/sizeof(struct screen_opts)) #define REFRESH_SCREEN -13 #define TOGGLE_SCREEN -14 #define INVALID_OPTION_STATUS 2 #define PGUP_STATUS 3 #define TOP_STATUS 4 #define PGDN_STATUS 5 #define BTM_STATUS 6 #define MAX_FIELD_SIZE 39 typedef struct info_container i_container; struct info_container { i_container *next_item; /* reference to next info_container */ char field_data[MAX_FIELD_SIZE + 1]; /* stores characters entered into a user-entry field */ void *data; /* stores a field pointer */ int y; /* cursor y position of user selection */ int x; /* cursor x position of user selection */ }; #define for_each_icon(icon) for (icon = i_con_head; icon; icon = icon->next_item) struct screen_output { int rc; i_container *i_con; }; typedef struct function_output fn_out; struct function_output { fn_out *next; /* reference to next fn_out if >1 */ int index; /* used to plop text into # spots on text.msg */ int cat_offset; /* varies text set used in text.msg -> esp dif platforms */ char *text; /* holds "special" data to be passed back to the screen */ }; int main_menu(i_container * i_con); int disk_status(i_container * i_con); int device_details(i_container * i_con); int path_details(i_container * i_con); int raid_screen(i_container * i_con); int raid_status(i_container * i_con); int raid_stop(i_container * i_con); int confirm_raid_stop(i_container * i_con); int do_confirm_raid_stop(i_container * i_con); int raid_start(i_container * i_con); int raid_start_loop(i_container * i_con); int configure_raid_start(i_container * i_con); int configure_raid_parameters(i_container * i_con); int confirm_raid_start(i_container * i_con); int raid_start_complete(); int raid_include(i_container * i_con); int configure_raid_include(i_container * i_con); int confirm_raid_include(i_container * i_con); int confirm_raid_include_device(i_container * i_con); int disk_unit_recovery(i_container * i_con); int concurrent_add_device(i_container *i_con); int concurrent_remove_device(i_container *i_con); int verify_conc_maint(i_container * i_con); int path_status(i_container * i_con); int init_device(i_container * i_con); int confirm_init_device(i_container * i_con); int send_dev_inits(i_container * i_con); int reclaim_cache(i_container * i_con); int confirm_reclaim(i_container * i_con); int reclaim_warning(i_container * i_con); int reclaim_result(i_container * i_con); int af_include(i_container * i_con); int af_remove(i_container * i_con); int hot_spare_screen(i_container *i_con); int add_hot_spare(i_container * i_con); int remove_hot_spare(i_container * i_con); int hot_spare(i_container * i_con, int action); int select_hot_spare(i_container * i_con, int action); int confirm_hot_spare(int action); int hot_spare_complete(int action); int raid_migrate(i_container * i_con); int asym_access(i_container *i_con); int asym_access_menu(i_container * i_con); int raid_rebuild(i_container * i_con); int confirm_raid_rebuild(i_container * i_con); int raid_resync(i_container * i_con); int confirm_raid_resync(i_container * i_con); int battery_maint(i_container * i_con); int enclosures_maint(i_container * i_con); int battery_fork(i_container * i_con); int enclosures_fork(i_container * i_con); int force_battery_error(i_container *i_con); int enable_battery(i_container *i_con); int ipr_suspend_disk_enclosure(i_container *i_con); int ipr_resume_disk_enclosure(i_container *i_con); int bus_config(i_container * i_con); int change_bus_attr(i_container * i_con); int confirm_change_bus_attr(i_container *i_con); int driver_config(i_container * i_con); int change_driver_config(i_container *i_con); int disk_config(i_container * i_con); int ioa_config(i_container * i_con); int change_disk_config(i_container *); int change_ioa_config(i_container *); int download_ucode(i_container *); int choose_ucode(i_container *); int log_menu(i_container *); int ibm_storage_log_tail(i_container *); int ibm_storage_log(i_container *); int kernel_log(i_container *); int iprconfig_log(i_container *); int kernel_root(i_container *); int confirm_kernel_root(i_container *); int confirm_kernel_root_change(i_container *); int confirm_kernel_root_change2(i_container *); int set_default_editor(i_container *); int confirm_set_default_editor(i_container *); int confirm_set_default_editor_change(i_container *); int confirm_set_default_editor_change2(i_container *); int restore_log_defaults(i_container *); int ibm_boot_log(i_container *); int exit_confirmed(i_container *); static int raid_create_check_num_devs(struct ipr_array_cap_entry *, int, int); /* constant strings */ const char *no_dev_found = __("No devices found"); const char *wait_for_next_screen = __("Please wait for the next screen."); /* others */ int menu_test(i_container * i_con); ITEM **menu_test_menu(int field_index); typedef struct screen_node s_node; struct screen_opts { int (*screen_function) (i_container *); char *key; char *list_str; }; struct screen_node { int rc_flags; int f_flags; int num_opts; struct screen_opts *options; char *title; char *body; char *header[10]; }; struct screen_opts null_opt[] = { {NULL, "\n"} }; struct screen_opts main_menu_opt[] = { {disk_status, "1", __("Display hardware status")}, {raid_screen, "2", __("Work with disk arrays")}, {disk_unit_recovery, "3", __("Work with disk unit recovery")}, {bus_config, "4", __("Work with SCSI bus configuration")}, {driver_config, "5", __("Work with driver configuration")}, {disk_config, "6", __("Work with disk configuration")}, {ioa_config, "7", __("Work with adapter configuration")}, {download_ucode, "8", __("Download microcode")}, {log_menu, "9", __("Analyze log")}, }; s_node n_main_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG), .num_opts = NUM_OPTS(main_menu_opt), .options = &main_menu_opt[0], .title = __("IBM Power RAID Configuration Utility") }; struct screen_opts exit_confirm_opt[] = { {main_menu, "1", __("Return to main menu")}, {exit_confirmed, "2", __("Exit iprconfig")}, }; s_node n_exit_confirm = { .num_opts = NUM_OPTS(exit_confirm_opt), .options = &exit_confirm_opt[0], .title = __("Confirm Exit"), .header = { __("One or more disks is currently known to be zeroed. Exiting now " "will cause this state to be lost, resulting in longer array creation times\n\n"), "" } }; struct screen_opts disk_status_opt[] = { {device_details, "\n"} }; s_node n_disk_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_status_opt), .options = &disk_status_opt[0], .title = __("Display Hardware Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display hardware resource information details\n\n"), "" } }; s_node n_adapter_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("IOA Hardware Resource Information Details") }; s_node n_device_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Unit Hardware Resource Information Details") }; s_node n_vset_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Array Information Details") }; s_node n_ses_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Enclosure Information Details") }; struct screen_opts raid_screen_opt[] = { {raid_status, "1", __("Display disk array status")}, {raid_start, "2", __("Create a disk array")}, {raid_stop, "3", __("Delete a disk array")}, {raid_include, "4", __("Add a device to a disk array")}, {af_include, "5", __("Format device for RAID function")}, {af_remove, "6", __("Format device for JBOD function")}, {hot_spare_screen, "7", __("Work with hot spares")}, {asym_access, "8", __("Work with asymmetric access")}, {raid_resync, "9", __("Force RAID Consistency Check")}, {raid_migrate, "0", __("Migrate disk array protection")}, }; struct screen_opts hot_spare_opt[] = { {add_hot_spare, "1", __("Create a hot spare")}, {remove_hot_spare, "2", __("Delete a hot spare")}, }; s_node n_asym_access = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Array Asymmetric Access"), .header = { __("Select the disk array path.\n\n"), __("Type choice, press Enter.\n"), __(" 1=change asymmetric access for a disk array\n\n"), "" } }; s_node n_asym_access_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Setting Array Asymmetric Access Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o Active/Active mode is not enabled on the IOAs.\n"), __("o An IOA needs updated microcode in order to support " "active/active configurations.\n"), __("o None of the disk arrays in the system are capable of " "changing asymmetric access attributes.\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), "" } }; struct screen_opts change_array_asym_access_opt[] = { {asym_access_menu, "c"}, {NULL, "\n"} }; s_node n_change_array_asym_access = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_array_asym_access_opt), .options = &change_array_asym_access_opt[0], .title = __("Change Asymmetric Access Configuration of Array"), .header = { __("Current array asymmetric access configuration is shown. To " "change setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; s_node n_raid_migrate_complete = { .title = __("Migrate Disk Array Status"), .body = __("You selected to migrate a disk array") }; s_node n_confirm_raid_migrate = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Migrate a Disk Array"), .header = { __("ATTENTION: Disk array will be migrated.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_raid_migrate = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG ), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Migrate Disk Array Protection"), .header = { __("Select only one disk array for migration.\n\n"), __("Type choice, press Enter.\n"), __(" 1=migrate protection for a disk array\n\n"), "" } }; s_node n_raid_migrate_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Disk Array Migration Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o There are not enough unused AF disks for the migration.\n"), "" } }; s_node n_raid_migrate_add_disks = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disk Units for Migration"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_hot_spare_screen = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(hot_spare_opt), .options = &hot_spare_opt[0], .title = __("Work with Hot Spares") }; s_node n_raid_screen = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(raid_screen_opt), .options = &raid_screen_opt[0], .title = __("Work with Disk Arrays") }; s_node n_raid_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_status_opt), .options = &disk_status_opt[0], .title = __("Display Disk Array Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display hardware resource information details\n\n"), "" } }; struct screen_opts raid_stop_opt[] = { {confirm_raid_stop, "\n"} }; s_node n_raid_stop = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG ), .num_opts = NUM_OPTS(raid_stop_opt), .options = &raid_stop_opt[0], .title = __("Delete a Disk Array"), .header = { __("Select the disk array(s) to delete.\n\n"), __("Type choice, press Enter.\n"), __(" 1=delete a disk array\n\n"), "" } }; s_node n_raid_stop_fail = { .title = __("Delete a Disk Array Failed"), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; struct screen_opts confirm_raid_stop_opt[] = { {do_confirm_raid_stop, "\n"} }; s_node n_confirm_raid_stop = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_raid_stop_opt), .options = &confirm_raid_stop_opt[0], .title = __("Confirm Delete a Disk Array"), .header = { __("ATTENTION: Disk array will be deleted.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts raid_start_opt[] = { {raid_start_loop, "\n"} }; s_node n_raid_start = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_start_opt), .options = &raid_start_opt[0], .title = __("Create a Disk Array"), .header = { __("Select the adapter.\n\n"), __("Type choice, press Enter.\n"), __(" 1=create a disk array\n\n"), "" } }; s_node n_raid_start_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Create a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation due " "to one or more of the following reasons:\n\n"), __("o There are not enough advanced function disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_configure_raid_start = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disk Units for Disk Array"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_confirm_raid_start = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Create Disk Array"), .header = { __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_raid_start_complete = { .title = __("Create Disk Array Status"), .body = __("You selected to create a disk array") }; struct screen_opts raid_include_opt[] = { {configure_raid_include, "\n"} }; s_node n_raid_include = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_include_opt), .options = &raid_include_opt[0], .title = __("Add Devices to a Disk Array"), .header = { __("Select the disk array.\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select disk array\n\n"), "" } }; s_node n_raid_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Add Devices to a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have" "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_configure_raid_include = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Add Devices to a Disk Array"), .header = { __("Select the devices to be included in the disk array\n\n"), __("Type choice, press Enter.\n"), __(" 1=Add a Device to a Disk Array\n\n"), "" } }; s_node n_configure_raid_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Add Devices to a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are not enough disks available to be included.\n"), __("o Not all disks attached to an IOA have reported to the " "system. Retry the operation.\n"), __("o The disk to be included must be the same or greater " "capacity than the smallest device in the disk array and " "be formatted correctly\n"), __("o The disk is not supported for the requested operation\n"), "" } }; s_node n_confirm_raid_include = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Devices to Add to a Disk Array"), .header = { __("All listed disks will be added to the disk array.\n"), __("ATTENTION: Disks that have not been zeroed with be formatted first.\n"), __("Press Enter to confirm your choice to have the system " "include the selected disks in the disk array\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_dev_include_complete = { .title = __("Add Devices to a Disk Array Status"), .header = { __("The operation to add disks to the disk array " "will be done in two phases. The disks will first be " "formatted, then added to the disk array."), __("You selected to add disks to a disk array"), "" } }; s_node n_af_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Format Device for RAID Function Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o All disks are already formatted for RAID Function.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_af_remove_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Format Device for JBOD Function (512) Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o All disks are already formatted for JBOD function.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_add_hot_spare_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Create Hot Spare Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_remove_hot_spare_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Delete Hot Spare Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_add_hot_spare = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Create a Hot Spare Device"), .header = { __("Select the adapter on which disks will be " "configured as hot spares\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select adapter\n\n"), "" }, }; s_node n_remove_hot_spare = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Delete a Hot Spare Device"), .header = { __("Select the adapter on which hot spares will be deleted\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select adapter\n\n"), "" }, };/* xxx search for subsystem and disk unit and replace all */ s_node n_select_add_hot_spare = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disks to Create Hot Spares"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_select_remove_hot_spare = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Hot Spares to Delete"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_confirm_add_hot_spare = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Create Hot Spare"), .header = { __("ATTENTION: Existing data on these disks " "will not be preserved.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; s_node n_confirm_remove_hot_spare = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Delete Hot Spare"), .header = { __("ATTENTION: Selected disks will no longer be available " "as hot spare devices.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts disk_unit_recovery_opt[] = { {concurrent_add_device, "1", __("Concurrent add device")}, {concurrent_remove_device, "2", __("Concurrent remove device")}, {init_device, "3", __("Initialize and format disk")}, {reclaim_cache, "4", __("Reclaim IOA cache storage")}, {raid_rebuild, "5", __("Rebuild disk unit data")}, {raid_resync, "6", __("Force RAID Consistency Check")}, {battery_maint, "7", __("Work with resources containing cache battery packs")}, {path_status, "8", __("Display SAS path status")}, {enclosures_maint, "9", __("Work with disk enclosures")} }; s_node n_disk_unit_recovery = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(disk_unit_recovery_opt), .options = &disk_unit_recovery_opt[0], .title = __("Work with Disk Unit Recovery") }; #define IPR_CONC_REMOVE 1 #define IPR_CONC_ADD 2 #define IPR_VERIFY_CONC_REMOVE 3 #define IPR_VERIFY_CONC_ADD 4 #define IPR_WAIT_CONC_REMOVE 5 #define IPR_WAIT_CONC_ADD 6 #define IPR_CONC_IDENTIFY 7 s_node n_concurrent_remove_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Concurrent Device Remove"), .header = { __("Choose a single location for remove operations\n"), __(" 1=Select\n\n"), "" } }; s_node n_concurrent_add_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Concurrent Device Add"), .header = { __("Choose a single location for add operations\n"), __(" 1=Select\n\n"), "" } }; s_node n_verify_conc_remove = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Verify Device Concurrent Remove"), .header = { __("Verify the selected device for concurrent remove operations\n\n"), __("Press Enter when verification complete and ready to continue\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_verify_conc_add = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Verify Device Concurrent Add"), .header = { __("Verify the selected device for concurrent add operations\n\n"), __("Press Enter when verification complete and ready to continue\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_wait_conc_remove = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Complete Device Concurrent Remove"), .header = { __("Remove selected device\n\n"), __("Press Enter when selected device has been removed\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_wait_conc_add = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Complete Device Concurrent Add"), .header = { __("Add selected device\n\n"), __("Press Enter when selected device has been installed\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts init_device_opt[] = { {confirm_init_device, "\n"} }; s_node n_af_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks to format for RAID Function"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_jbod_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks to format for JBOD Function (512)"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks for Initialize and Format"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; struct screen_opts confirm_inits_opt[] = { {send_dev_inits, "c"} }; s_node n_confirm_init_device = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_inits_opt), .options = &confirm_inits_opt[0], .title = __("Confirm Initialize and Format Disks"), .header = { __("Press 'c' to confirm your choice for 1=Initialize and format.\n"), __(" q=Return to change your choice.\n\n"), "" } }; s_node n_dev_init_complete = { .title = __("Initialize and Format Status"), .body = __("You selected to initialize and format a disk") }; struct screen_opts reclaim_cache_opts[] = { {confirm_reclaim, "\n"} }; s_node n_reclaim_cache = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(reclaim_cache_opts), .options = &reclaim_cache_opts[0], .title = __("Reclaim IOA Cache Storage"), .header = { __("Select the IOA to reclaim IOA cache storage.\n"), __("ATTENTION: Proceed with this function only if directed " "to from a service procedure. Data in the IOA cache " "will be discarded.\n\n"), __("Type choice, press Enter.\n"), __(" 1=Reclaim IOA cache storage.\n\n"), "" } }; struct screen_opts confirm_reclaim_opt[] = { {reclaim_warning, "c"} }; s_node n_confirm_reclaim = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_reclaim_opt), .options = &confirm_reclaim_opt[0], .title = __("Confirm Reclaim IOA Cache Storage"), .header = { __("The disks that may be affected by the function are displayed.\n"), __("ATTENTION: Proceed with this function only if directed to from a " "service procedure. Data in the IOA cache will be discarded. " "Filesystem corruption may result on the system.\n\n"), __("Press c=Confirm to reclaim cache storage.\n"), __(" q=Cancel to return to change your choice.\n\n"), "" } }; struct screen_opts confirm_reclaim_warning_opt[] = { {reclaim_result, "s"} }; s_node n_confirm_reclaim_warning = { .f_flags = (CONFIRM_REC_FLAG | CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_reclaim_warning_opt), .options = &confirm_reclaim_warning_opt[0], .title = __("Confirm Reclaim IOA Cache Storage"), .header = { __("ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!\n"), __("ATTENTION: Proceed with this function only if directed to from a " "service procedure. Data in the IOA cache will be discarded. " "This data loss may or may not be detected by the host operating " "system. Filesystem corruption may result on the system.\n\n"), __("Press 's' to continue.\n\n"), "" } }; s_node n_reclaim_result = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Reclaim IOA Cache Storage Results") }; struct screen_opts raid_rebuild_opt[] = { {confirm_raid_rebuild, "\n"} }; s_node n_raid_rebuild = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_rebuild_opt), .options = &raid_rebuild_opt[0], .title = __("Rebuild Disk Unit Data"), .header = { __("Select the disks to be rebuilt\n\n"), __("Type choice, press Enter.\n"), __(" 1=Rebuild\n\n"), "" } }; s_node n_raid_rebuild_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Rebuild Disk Unit Data Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disks that need to be rebuilt.\n"), __("o The disk that needs to be rebuilt is not at the right " "location. Examine the 'message log' for the exposed unit " "and make sure that the replacement unit is at the correct " "location.\n"), __("o Not all disks attached to an IOA have reported to the " "system. Retry the operation.\n"), __("o The disk is not supported for the requested operation.\n"), "" } }; s_node n_confirm_raid_rebuild = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Rebuild Disk Unit Data"), .header = { __("Rebuilding the disk unit data may take several " "minutes for each disk selected.\n\n"), __("Press Enter to confirm having the data rebuilt.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; s_node n_raid_resync_complete = { .title = __("Force RAID Consistency Check Status"), .body = __("You selected to force a RAID consistency check") }; struct screen_opts raid_resync_opt[] = { {confirm_raid_resync, "\n"} }; s_node n_raid_resync = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_resync_opt), .options = &raid_resync_opt[0], .title = __("Force RAID Consistency Check"), .header = { __("Select the arrays to be checked\n\n"), __("Type choice, press Enter.\n"), __(" 1=Check\n\n"), "" } }; s_node n_raid_resync_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Force RAID Consistency Check Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_confirm_raid_resync = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Force RAID Consistency Check"), .header = { __("Forcing a consistency check on a RAID array may take " "a long time, depending on current system activity.\n\n"), __("Press Enter to confirm.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; struct screen_opts configure_af_device_opt[] = { {confirm_init_device, "\n"} }; struct screen_opts battery_maint_opt[] = { {battery_fork, "\n"} }; s_node n_battery_maint = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(battery_maint_opt), .options = &battery_maint_opt[0], .title = __("Work with Resources Containing Cache Battery Packs"), .header = { __("Type options, press Enter\n"), __(" 1=Display battery information\n"), __(" 2=Force battery pack into error state\n"), __(" 3=Start IOA cache\n\n"), "" } }; struct screen_opts confirm_force_battery_error_opt[] = { {force_battery_error, "c"} }; s_node n_confirm_force_battery_error = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_force_battery_error_opt), .options = &confirm_force_battery_error_opt[0], .title = __("Force Battery Packs Into Error State"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __("You have selected to force a cache batter error on an IOA\n\n"), __("You will have to replace the Cache Battery Pack in each selected " "IOA to resume normal operations.\n\n"), __("System performance could be significantly degraded until the cache " "battery packs are replaced on the selected IOAs.\n\n"), __(" c=Continue to force the following battery packs into an error state\n\n"), "" }, }; struct screen_opts confirm_start_cache_opt[] = { {enable_battery, "c"} }; s_node n_confirm_start_cache = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_start_cache_opt), .options = &confirm_start_cache_opt[0], .title = __("Start IOA cache after concurrently replacing battery pack"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __("You have selected to start the IOA cache after concurrently replacing the battery pack\n\n"), __(" c=Continue to start IOA cache\n\n"), "" }, }; s_node n_show_battery_info = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Battery Information") }; struct screen_opts enclosures_maint_opt[] = { {enclosures_fork, "\n"} }; s_node n_enclosures_maint = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(enclosures_maint_opt), .options = &enclosures_maint_opt[0], .title = __("Work with disk enclosures"), .header = { __("Type options, press Enter\n"), __(" 1=Display disk enclosure details\n"), __(" 2=Suspend disk enclosure path\n"), __(" 3=Resume disk enclosure path\n\n"), "" } }; struct screen_opts confirm_suspend_disk_enclosure_opt[] = { {ipr_suspend_disk_enclosure, "c"} }; s_node n_confirm_suspend_disk_enclosure = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_suspend_disk_enclosure_opt), .options = &confirm_suspend_disk_enclosure_opt[0], .title = __("Suspend Disk Enclosure"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __(" c=Continue to issue suspend enclosure command\n\n"), "" }, }; struct screen_opts confirm_resume_disk_enclosure_opt[] = { {ipr_resume_disk_enclosure, "c"} }; s_node n_confirm_resume_disk_enclosure = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_resume_disk_enclosure_opt), .options = &confirm_resume_disk_enclosure_opt[0], .title = __("Resume Disk Enclosure"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __(" c=Continue to issue resume enclosure command\n\n"), "" }, }; s_node n_show_enclosure_info = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Hardware detail Information") }; struct screen_opts path_status_opt[] = { {path_details, "\n"} }; s_node n_path_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(path_status_opt), .options = &path_status_opt[0], .title = __("Display SAS Path Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display SAS Path routing details\n\n"), "" } }; s_node n_path_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Display SAS Path Details"), .header = {__("\n\n"), ""} }; struct screen_opts bus_config_opt[] = { {change_bus_attr, "\n"} }; s_node n_bus_config = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(bus_config_opt), .options = &bus_config_opt[0], .title = __("Work with SCSI Bus Configuration"), .header = { __("Select the adapter to change scsi bus attribute.\n\n"), __("Type choice, press Enter.\n"), __(" 1=change scsi bus attribute\n\n"), "" } }; s_node n_bus_config_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Work with SCSI Bus Configuration Failed"), .header = { __("There are no SCSI Buses eligible for the selected operation. " "None of the installed adapters support the requested " "operation.\n"), "" } }; struct screen_opts change_bus_attr_opt[] = { {NULL, "c"}, {confirm_change_bus_attr, "\n"} }; s_node n_change_bus_attr = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_bus_attr_opt), .options = &change_bus_attr_opt[0], .title = __("Change SCSI Bus Configuration"), .header = { __("Current bus configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts confirm_change_bus_attr_opt[] = { {NULL, "c"} }; s_node n_confirm_change_bus_attr = { .f_flags = (CONFIRM_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_change_bus_attr_opt), .options = &confirm_change_bus_attr_opt[0], .title = __("Confirm Change SCSI Bus Configuration"), .header = { __("Confirming this action will result in the following" "configuration to become active.\n"), __(" c=Confirm Change SCSI Bus Configration\n\n"), "" } }; struct screen_opts driver_config_opt[] = { {change_driver_config, "\n"} }; s_node n_driver_config = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(driver_config_opt), .options = &driver_config_opt[0], .title = __("Adapter Driver Configuration"), .header = { __("Select adapter to change driver configuration\n\n"), __("Type choice, press Enter\n"), __(" 1=change driver configuration\n\n"), "" } }; s_node n_change_driver_config = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Change Driver Configuration"), .header = { __("Current Driver configurations " "are shown. To change setting, type in new " "value then hit Enter\n\n"), "" } }; struct screen_opts disk_config_opt[] = { {change_disk_config, "\n"} }; s_node n_disk_config = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_config_opt), .options = &disk_config_opt[0], .title = __("Change Disk Configuration"), .header = { __("Type option, press Enter.\n"), __(" 1=Change Disk Configuration\n\n"), "" } }; struct screen_opts change_disk_attr_opt[] = { {NULL, "c"}, {NULL, "\n"} }; s_node n_change_disk_config = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_disk_attr_opt), .options = &change_disk_attr_opt[0], .title = __("Change Configuration of Disk"), .header = { __("Current Disk configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts ioa_config_opt[] = { {change_ioa_config, "\n"} }; s_node n_ioa_config = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(ioa_config_opt), .options = &ioa_config_opt[0], .title = __("Change Adapter Configuration"), .header = { __("Type option, press Enter.\n"), __(" 1=Change Adapter Configuration\n\n"), "" } }; struct screen_opts change_ioa_attr_opt[] = { {NULL, "c"}, {NULL, "\n"} }; s_node n_change_ioa_config = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_ioa_attr_opt), .options = &change_ioa_attr_opt[0], .title = __("Change Configuration of Adapter"), .header = { __("Current Adapter configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts download_ucode_opt[] = { {choose_ucode, "\n"} }; s_node n_download_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | FWD_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(download_ucode_opt), .options = &download_ucode_opt[0], .title = __("Download Microcode"), .header = { __("Select the device(s) to download microcode\n\n"), __("Type choice, press Enter.\n"), __(" 1=device to download microcode\n\n"), "" } }; s_node n_choose_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Choose Microcode Image"), .header = { __("Select the microcode image to download\n\n"), __("Type choice, press Enter.\n"), __(" 1=download microcode\n\n"), "OPT Version Date Image File\n", "--- --------------- -------- --------------------------\n", "" } }; s_node n_confirm_download_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Microcode Download"), .header = { __("ATTENTION: System performance may be affected during " "the microcode download process\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "OPT Version Date Image File\n", "--- --------------- -------- --------------------------\n", "" } }; s_node n_download_ucode_in_progress = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Microcode Download In Progress"), .header = { __("Please wait for the microcode update to complete\n"), __("This process may take several minutes\n"), "" } }; struct screen_opts log_menu_opt[] = { {ibm_storage_log_tail, "1", __("View most recent ipr error messages")}, {ibm_storage_log, "2", __("View ipr error messages")}, {kernel_log, "3", __("View all kernel error messages")}, {iprconfig_log, "4", __("View iprconfig error messages")}, {kernel_root, "5", __("Set root kernel message log directory")}, {set_default_editor, "6", __("Set default editor")}, {restore_log_defaults, "7", __("Restore defaults")}, {ibm_boot_log, "8", __("View ipr boot time messages")} }; s_node n_log_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(log_menu_opt), .options = &log_menu_opt[0], .title = __("Kernel Messages Log"), .header = { __("ATTENTION: Devices listed below are currently known to be zeroed. " "By exiting now, this information will be lost, resulting in potentially " "longer array creation times.\n\n"), __(" q=Cancel to return to iprconfig.\n"), __(" e=Exit iprconfig\n") } }; s_node n_exit_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(log_menu_opt), .options = &null_opt[0], .title = __("Confirm Exit") }; struct screen_opts kernel_root_opt[] = { {confirm_kernel_root, "\n"} }; s_node n_kernel_root = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(kernel_root_opt), .options = &kernel_root_opt[0], .title = __("Kernel Messages Log Root Directory"), .header = { __("Enter new directory and press Enter\n\n"), "" } }; struct screen_opts confirm_kernel_root_opt[] = { {NULL, "c"}, }; s_node n_confirm_kernel_root = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(confirm_kernel_root_opt), .options = &confirm_kernel_root_opt[0], .title = __("Confirm Root Directory Change"), .header = { __("Press 'c' to confirm your new " "kernel root directory choice.\n\n"), __("To return to the log menu without " "changing the root directory, choose 'q'.\n\n"), "" } }; struct screen_opts set_default_editor_opt[] = { {confirm_set_default_editor, "\n"} }; s_node n_set_default_editor = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(set_default_editor_opt), .options = &set_default_editor_opt[0], .title = __("Kernel Messages Editor"), .header = { __("Enter new editor command and press Enter\n\n"), "" } }; struct screen_opts confirm_set_default_editor_opt[] = { {NULL, "c"} }; s_node n_confirm_set_default_editor = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(confirm_set_default_editor_opt), .options = &confirm_set_default_editor_opt[0], .title = __("Confirm Editor Change"), .header = { __("Press 'c' to confirm your editor choice\n\n"), __("To return to the log menu without " "changing the editor, choose 'q'.\n\n"), "" } }; const char *screen_status[] = { /* 0 */ "", /* 1 */ "", /* 2 */ __("Invalid option specified"), /* 3 */ __("Screen paged up"), /* 4 */ __("Already at top"), /* 5 */ __("Screen paged down"), /* 6 */ __("Already at bottom"), /* 7 */ "", /* 8 */ "", /* 9 */ "", /* 10 */ __("No devices to show details for"), /* 11 */ "", /* 12 */ "", /* 13 */ "", /* 14 */ "", /* 15 */ __("The selection is not valid."), /* 16 */ __("Error. More than one device was selected."), /* 17 */ __("Invalid option. No devices selected."), /* 18 */ __("Disk array successfully created."), /* 19 */ __("Create disk array failed."), /* 20 */ __("Delete disk array failed."), /* 21 */ __("Disk array successfully deleted."), /* 22 */ __("Create disk array failed - can not mix SSDs and HDDs."), /* 23 */ "", /* 24 */ "", /* 25 */ __("Error: number of devices selected must be a multiple of %d"), /* 26 */ __("Add device failed"), /* 27 */ __("Add device completed successfully"), /* 28 */ __("Rebuild started, view Disk Array Status Window for rebuild progress"), /* 29 */ __("Rebuild failed"), /* 30 */ __("Device concurrent maintenance failed"), /* 31 */ __("Device concurrent maintenance failed, device in use"), /* 32 */ __("Device concurrent maintenance completed successfully"), /* 33 */ __("No units available for initialize and format"), /* 34 */ __("Initialize and format completed successfully"), /* 35 */ __("Initialize and format failed"), /* 36 */ __("Reclaim IOA Cache Storage completed successfully"), /* 37 */ __("Reclaim IOA Cache Storage failed"), /* 38 */ __("No Reclaim IOA Cache Storage is necessary"), /* 39 */ __("No Reclaim IOA Cache Storage performed"), /* 40 */ __("Rebuild started, view Disk Array Status Window for rebuild progress"), /* 41 */ __("Rebuild failed"), /* 42 */ __("The selected battery packs have successfully been placed into an error state."), /* 43 */ __("Failed to force the selected battery packs into an error state."), /* 44 */ __("No configured resources contain a cache battery pack."), /* 45 */ __("Change SCSI Bus configurations completed successfully"), /* 46 */ __("Change SCSI Bus configurations failed"), /* 47 */ __("Change device driver configurations completed successfully"), /* 48 */ __("Change device driver configurations failed"), /* 49 */ __("No units available for initialize and format"), /* 50 */ __("Initialize and format completed successfully"), /* 51 */ __("Initialize and format failed"), /* 52 */ __("No devices available for the selected hot spare operation"), /* 53 */ __("Hot spare successfully created"), /* 54 */ __("Hot spare successfully deleted"), /* 55 */ __("Failed to create hot spare"), /* 56 */ __("Failed to delete hot spare"), /* 57 */ __("Successfully changed device driver configuration"), /* 58 */ __("Failed to change device driver configuration"), /* 59 */ __("Invalid directory"), /* 60 */ __("Root directory changed to %s"), /* 61 */ __("Root directory unchanged"), /* 62 */ __("Editor changed to %s"), /* 63 */ __("Editor unchanged"), /* 64 */ __("Default log values restored"), /* 65 */ __("Editor returned %d. Try setting the default editor."), /* 66 */ __("Failed to change disk configuration."), /* 67 */ __("Microcode Download failed."), /* 68 */ __("Failed to enable IOA cache."), /* 69 */ __("Invalid number of devices selected."), /* 70 */ __("Failed to start IOA cache."), /* 71 */ __("IOA cache started successfully."), /* 72 */ __("Selected battery packs successfully forced into an error state."), /* 73 */ __("Force RAID Consistency check failed"), /* 74 */ __("RAID Consistency check successful"), /* 75 */ __("Failed to read error log. Try setting root kernel message log directory."), /* 76 */ __("No SAS disks available"), /* 77 */ __("Too many disks were selected. The maximum is %d."), /* 78 */ __("Too few disks were selected. The minimum is %d."), /* 79 */ __("Migrate Array Protection completed successfully."), /* 80 */ __("Migrate Array Protection failed."), /* 81 */ __("Selected disk enclosure was suspended successfully."), /* 82 */ __("Failed to suspend disk enclosure"), /* 83 */ __("Disk enclosure is already in suspend state"), /* 84 */ __("Disk enclosure is already in active state"), /* 85 */ __("Selected disk enclosure was resumed successfully."), /* 86 */ __("Failed to resume disk enclosure"), /* 87 */ __("Can not suspend an expander from the RAID controller with the same serial number"), /* 88 */ __("Incorrect device type specified. Please specify a valid disk enclosure to suspend"), /* 89 */ __("Incorrect device type specified. Please specify a valid disk enclosure to resume"), /* 90 */ __("Selected disk enclosure is in Unknown state. Please check your hardware support"), /* 91 */ __("Create disk array failed - can not mix 5XX and 4K disks."), /* 92 */ __("Create disk array failed - can not build with read intensive disks only."), /* NOTE: 127 maximum limit */ }; /* TODO - replace constants in iprconfig.c with the following enums/defines */ enum { RC_0_Success = 0, RC_1_Blank, RC_2_Invalid_Option, RC_3_Screen_Up, RC_4_At_Top, RC_5_Screen_Down, RC_6_At_Bottom, RC_7_Blank, RC_8_Blank, RC_9_Blank, RC_10_No_Devices, RC_11_Blank, RC_12_Blank, RC_13_Blank, RC_14_Blank, RC_15_Invalid_Selection, RC_16_More_Than_One_Dev, RC_17_Invalid_Option, RC_18_Array_Created, RC_19_Create_Fail, RC_20_Delete_Fail, RC_21_Delete_Success, RC_22_Mixed_Block_Dev_Classes, RC_23_Blank, RC_24_Blank, RC_25_Devices_Multiple, RC_26_Include_Fail, RC_27_Include_Success, RC_28_Rebuild_Started, RC_29_Rebuild_Fail, RC_30_Maint_Fail, RC_31_Maint_Fail_In_Use, RC_32_Maint_Success, RC_33_No_Units, RC_34_Init_Format_Success, RC_35_Init_Format_Fail, RC_36_Reclaim_Cache_Success, RC_37_Reclaim_Cache_Fail, RC_38_No_Reclaim_Needed, RC_39_No_Reclaim_Performed, RC_40_Rebuild_Started, RC_41_Rebuild_Failed, RC_42_Battery_Err_State_Success, RC_43_Battery_Err_State_Fail, RC_44_No_Battery_Pack, RC_45_Change_Bus_Conf_Success, RC_46_Change_Bus_Conf_Fail, RC_47_Change_Driver_Conf_Success, RC_48_Change_Driver_Conf_Fail, RC_49_No_Units_Available, RC_50_Init_Format_Success, RC_51_Init_Format_Fail, RC_52_No_Devices_Available, RC_53_Hot_Spare_Created, RC_54_Hot_Spare_Deleted, RC_55_Failed_Create, RC_56_Failed_Delete, RC_57_Change_Driver_Conf_Success, RC_58_Change_Driver_Conf_Fail, RC_59_Invalid_Dir, RC_60_Root_Changed, RC_61_Root_Unchanged, RC_62_Editor_Changed, RC_63_Editor_Unchanged, RC_64_Log_Restored, RC_65_Set_Default_Editor, RC_66_Disk_Conf_Fail, RC_67_uCode_Download_Fail, RC_68_Cache_Enable_Fail, RC_69_Invalid_Number_Devs, RC_70_Cache_Start_Fail, RC_71_Cache_Start_Success, RC_72_Battery_Err_State, RC_73_Force_Check_Fail, RC_74_Check_Success, RC_75_Failed_Read_Err_Log, RC_76_No_SAS_Disks, RC_77_Too_Many_Disks, RC_78_Too_Few_Disks, RC_79_Migrate_Prot_Success, RC_80_Migrate_Prot_Fail, RC_81_Suspended_Success, RC_82_Suspended_Fail, RC_83_Enclosure_Is_Suspend, RC_84_Enclosure_Is_Active, RC_85_Enclosure_Resume_Success, RC_86_Enclosure_Resume_Fail, RC_87_No_Suspend_Same_Seri_Num, RC_88_Invalid_Dev_For_Suspend, RC_89_Invalid_Dev_For_Resume, RC_90_Enclosure_Is_Unknown, RC_91_Mixed_Logical_Blk_Size, RC_92_UNSUPT_REQ_BLK_DEV_CLASS, /* NOTE: 127 maximum limit */ }; #endif /* iprconfig_h */ iprutils/init.d/0000755000000000000000000000000012427235652012624 5ustar rootrootiprutils/init.d/iprupdate0000644000000000000000000000360012427235652014543 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprupdate # # System startup script for the ipr microcode update facility # ### BEGIN INIT INFO # Provides: iprupdate # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the iprupdate utility # Description: Start the iprupdate utility ### END INIT INFO IPRUPDATE=/sbin/iprupdate test -x $IPRUPDATE || exit 5 . /lib/lsb/init-functions start() { echo "Checking ipr microcode levels" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRUPDATE --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -n "Completed ipr microcode updates" log_success_msg " " else echo -n "Completed ipr microcode updates" log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr update daemon" killproc $IPRUPDATE RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { $0 stop $0 start return $RETVAL } status() { echo -n "Checking for ipr update daemon: " pidofproc $IPRUPDATE > /dev/null 2>&1 RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/init.d/iprdump0000644000000000000000000000350712427235652014234 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprdump # # System startup script for the ipr dump facility # ### BEGIN INIT INFO # Provides: iprdump # Required-Start: $local_fs iprinit # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog iprinit # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the ipr dump daemon # Description: Start the ipr dump daemon ### END INIT INFO IPRDUMP=/sbin/iprdump test -x $IPRDUMP || exit 5 . /lib/lsb/init-functions start() { echo -n "Starting ipr dump daemon" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRDUMP --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr dump daemon" killproc $IPRDUMP RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { echo -n "Reload ipr dump daemon" $0 stop && $0 start return $RETVAL } status() { echo -n "Checking for iprdump daemon: " pidofproc $IPRDUMP > /dev/null 2>&1 RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/init.d/iprha0000644000000000000000000000275512427235652013663 0ustar rootroot#!/bin/sh # Copyright (C) 2007 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprha # # System startup script for the ipr HA facility # ### BEGIN INIT INFO # Provides: iprha # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Set all ipr adapters to be preferred primary adapter # Description: Set all ipr adapters to be preferred primary adapter. # This script should be started on the "primary" system in a high # availability configuration for improved ipr disk performance. ### END INIT INFO IPRCONFIG=/sbin/iprconfig test -x $IPRCONFIG || exit 5 . /lib/lsb/init-functions start() { echo -n "Enabling ipr primary adapter mode" $IPRCONFIG -c set-all-primary RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Disabling ipr primary adapter mode" $IPRCONFIG -c set-all-secondary RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; *) echo "Usage: $0 {start|stop}" exit 1 ;; esac iprutils/init.d/iprinit0000644000000000000000000000355612427235652014236 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprinit # # System startup script for the ipr init facility # ### BEGIN INIT INFO # Provides: iprinit # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the ipr init daemon # Description: Start the ipr initialization daemon ### END INIT INFO IPRINIT=/sbin/iprinit test -x $IPRINIT || exit 5 . /lib/lsb/init-functions start() { echo -n "Starting ipr initialization daemon" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRINIT --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr initialization daemon" killproc $IPRINIT RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { echo -n "Reload ipr initialization daemon" $0 stop && $0 start return $RETVAL } status() { echo -n "Checking for ipr initialization daemon: " pidofproc $IPRINIT > /dev/null 2>&1 RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/Makefile0000644000000000000000000000547612427235652013113 0ustar rootroot# (C) Copyright 2000, 2001 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. INCLUDEDIR = -I. -I/usr/include/ncurses CC = gcc include version.mk CFLAGS += -g -Wall $(IPR_DEFINES) UTILS_VER = $(IPR_MAJOR_RELEASE).$(IPR_MINOR_RELEASE).$(IPR_FIX_LEVEL) TAR = cd .. && tar -zcpf iprutils-$(UTILS_VER)-src.tgz --exclude .git --exclude CVS --exclude applied-patches --exclude series --exclude txt --exclude pc --exclude patches --exclude debug --exclude *~* iprutils all: iprconfig iprupdate iprdump iprinit iprdbg docs iprconfig: iprconfig.c iprlib.o iprconfig.h $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprconfig iprconfig.c iprlib.o $(LDFLAGS) -lform -lpanel -lncurses -lmenu -lm iprupdate: iprupdate.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprupdate iprlib.o iprupdate.c $(LDFLAGS) iprdump:iprdump.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprdump iprlib.o iprdump.c $(LDFLAGS) iprinit:iprinit.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprinit iprlib.o iprinit.c $(LDFLAGS) iprdbg:iprdbg.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprdbg iprlib.o iprdbg.c $(LDFLAGS) iprucode:iprucode.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprucode iprlib.o iprucode.c $(LDFLAGS) iprlib.o: iprlib.c iprlib.h $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprlib.o -c iprlib.c %.8.gz : %.8 gzip -f -c $< > $<.gz %.nroff : %.8 nroff -Tascii -man $< > $@ %.ps : %.nroff a2ps -B --columns=1 -R -o $@ $< %.pdf : %.ps ps2pdf $< $@ %.html : %.8 groff -Thtml -man $< > $@ docs : $(patsubst %.8,%.8.gz,$(wildcard *.8)) pdfs : $(patsubst %.8,%.pdf,$(wildcard *.8)) htmldocs : $(patsubst %.8,%.html,$(wildcard *.8)) utils: ./*.c ./*.h cd .. $(TAR) clean: rm -f iprupdate iprconfig iprdump iprinit iprdbg *.o rm -f *.ps *.pdf *.nroff *.gz *.tgz *.rpm *.html install: all install -d $(INSTALL_MOD_PATH)/sbin install --mode=755 iprconfig $(INSTALL_MOD_PATH)/sbin/iprconfig install --mode=755 iprupdate $(INSTALL_MOD_PATH)/sbin/iprupdate install --mode=755 iprdump $(INSTALL_MOD_PATH)/sbin/iprdump install --mode=755 iprinit $(INSTALL_MOD_PATH)/sbin/iprinit install --mode=700 iprdbg $(INSTALL_MOD_PATH)/sbin/iprdbg install -d $(INSTALL_MOD_PATH)/usr/share/man/man8 install iprconfig.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprconfig.8.gz install iprupdate.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprupdate.8.gz install iprdump.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprdump.8.gz install iprinit.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprinit.8.gz rpm: *.c *.h *.8 -make clean cd .. $(TAR) mv ../iprutils-$(UTILS_VER)-src.tgz . rpmbuild --nodeps -ts iprutils-$(UTILS_VER)-src.tgz cp `rpm --eval '%{_srcrpmdir}'`/iprutils-$(UTILS_VER)-$(IPR_RELEASE).src.rpm .