Provide a common interface in iprlib for code looking for the microcode version of an ipr_dev. This also make the functions get_ioa_fw_version and get_dev_fw_version internal to iprlib.c and update callers to use the generic get_fw_version function. Signed-off-by: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com> Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
237 lines
4.7 KiB
C
237 lines
4.7 KiB
C
/**
|
|
* 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 <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <scsi/sg.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef iprlib_h
|
|
#include "iprlib.h"
|
|
#endif
|
|
|
|
#include <sys/mman.h>
|
|
|
|
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_fw_version(&ioa->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);
|
|
}
|