pm8001: fix pm8001_store_update_fw

The current implementation may mix the negative value returned from
pm8001_set_nvmd with count. -(-ENOMEM) could be interpreted as bytes
programmed, this patch fixes it.

Signed-off-by: Tomas Henzl <thenzl@redhat.com>
Signed-off-by: Suresh Thiagarajan <Suresh.Thiagarajan@pmcs.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index e9b0921..7abbf28 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -526,18 +526,19 @@
 {
 	struct pm8001_ioctl_payload	*payload;
 	DECLARE_COMPLETION_ONSTACK(completion);
-	u8		*ioctlbuffer = NULL;
-	u32		length = 0;
-	u32		ret = 0;
+	u8		*ioctlbuffer;
+	u32		ret;
+	u32		length = 1024 * 5 + sizeof(*payload) - 1;
 
-	length = 1024 * 5 + sizeof(*payload) - 1;
+	if (pm8001_ha->fw_image->size > 4096) {
+		pm8001_ha->fw_status = FAIL_FILE_SIZE;
+		return -EFAULT;
+	}
+
 	ioctlbuffer = kzalloc(length, GFP_KERNEL);
-	if (!ioctlbuffer)
+	if (!ioctlbuffer) {
+		pm8001_ha->fw_status = FAIL_OUT_MEMORY;
 		return -ENOMEM;
-	if ((pm8001_ha->fw_image->size <= 0) ||
-	    (pm8001_ha->fw_image->size > 4096)) {
-		ret = FAIL_FILE_SIZE;
-		goto out;
 	}
 	payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
 	memcpy((u8 *)&payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
@@ -547,6 +548,10 @@
 	payload->minor_function = 0x1;
 	pm8001_ha->nvmd_completion = &completion;
 	ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
+	if (ret) {
+		pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+		goto out;
+	}
 	wait_for_completion(&completion);
 out:
 	kfree(ioctlbuffer);
@@ -557,26 +562,25 @@
 {
 	struct pm8001_ioctl_payload	*payload;
 	DECLARE_COMPLETION_ONSTACK(completion);
-	u8		*ioctlbuffer = NULL;
-	u32		length = 0;
+	u8		*ioctlbuffer;
 	struct fw_control_info	*fwControl;
-	u32		loopNumber, loopcount = 0;
-	u32		sizeRead = 0;
 	u32		partitionSize, partitionSizeTmp;
-	u32		ret = 0;
-	u32		partitionNumber = 0;
+	u32		loopNumber, loopcount;
 	struct pm8001_fw_image_header *image_hdr;
+	u32		sizeRead = 0;
+	u32		ret = 0;
+	u32		length = 1024 * 16 + sizeof(*payload) - 1;
 
-	length = 1024 * 16 + sizeof(*payload) - 1;
-	ioctlbuffer = kzalloc(length, GFP_KERNEL);
-	image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
-	if (!ioctlbuffer)
-		return -ENOMEM;
 	if (pm8001_ha->fw_image->size < 28) {
-		ret = FAIL_FILE_SIZE;
-		goto out;
+		pm8001_ha->fw_status = FAIL_FILE_SIZE;
+		return -EFAULT;
 	}
-
+	ioctlbuffer = kzalloc(length, GFP_KERNEL);
+	if (!ioctlbuffer) {
+		pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+		return -ENOMEM;
+	}
+	image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
 	while (sizeRead < pm8001_ha->fw_image->size) {
 		partitionSizeTmp =
 			*(u32 *)((u8 *)&image_hdr->image_length + sizeRead);
@@ -614,18 +618,18 @@
 
 		pm8001_ha->nvmd_completion = &completion;
 		ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
-		if (ret)
-			break;
+		if (ret) {
+			pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+			goto out;
+		}
 		wait_for_completion(&completion);
 		if (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS) {
-			ret = fwControl->retcode;
-			break;
+			pm8001_ha->fw_status = fwControl->retcode;
+			ret = -EFAULT;
+			goto out;
+		}
 		}
 	}
-	if (ret)
-		break;
-	partitionNumber++;
-}
 out:
 	kfree(ioctlbuffer);
 	return ret;
@@ -640,22 +644,29 @@
 	char *cmd_ptr, *filename_ptr;
 	int res, i;
 	int flash_command = FLASH_CMD_NONE;
-	int err = 0;
+	int ret;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
-	cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+	/* this test protects us from running two flash processes at once,
+	 * so we should start with this test */
+	if (pm8001_ha->fw_status == FLASH_IN_PROGRESS)
+		return -EINPROGRESS;
+	pm8001_ha->fw_status = FLASH_IN_PROGRESS;
 
+	cmd_ptr = kzalloc(count*2, GFP_KERNEL);
 	if (!cmd_ptr) {
-		err = FAIL_OUT_MEMORY;
-		goto out;
+		pm8001_ha->fw_status = FAIL_OUT_MEMORY;
+		return -ENOMEM;
 	}
 
 	filename_ptr = cmd_ptr + count;
 	res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
 	if (res != 2) {
-		err = FAIL_PARAMETERS;
-		goto out1;
+		pm8001_ha->fw_status = FAIL_PARAMETERS;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
@@ -666,50 +677,38 @@
 		}
 	}
 	if (flash_command == FLASH_CMD_NONE) {
-		err = FAIL_PARAMETERS;
-		goto out1;
+		pm8001_ha->fw_status = FAIL_PARAMETERS;
+		ret = -EINVAL;
+		goto out;
 	}
 
-	if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) {
-		err = FLASH_IN_PROGRESS;
-		goto out1;
-	}
-	err = request_firmware(&pm8001_ha->fw_image,
+	ret = request_firmware(&pm8001_ha->fw_image,
 			       filename_ptr,
 			       pm8001_ha->dev);
 
-	if (err) {
+	if (ret) {
 		PM8001_FAIL_DBG(pm8001_ha,
-			pm8001_printk("Failed to load firmware image file %s,"
-			" error %d\n", filename_ptr, err));
-		err = FAIL_OPEN_BIOS_FILE;
-		goto out1;
+			pm8001_printk(
+			"Failed to load firmware image file %s,	error %d\n",
+			filename_ptr, ret));
+		pm8001_ha->fw_status = FAIL_OPEN_BIOS_FILE;
+		goto out;
 	}
 
-	switch (flash_command) {
-	case FLASH_CMD_UPDATE:
-		pm8001_ha->fw_status = FLASH_IN_PROGRESS;
-		err = pm8001_update_flash(pm8001_ha);
-		break;
-	case FLASH_CMD_SET_NVMD:
-		pm8001_ha->fw_status = FLASH_IN_PROGRESS;
-		err = pm8001_set_nvmd(pm8001_ha);
-		break;
-	default:
-		pm8001_ha->fw_status = FAIL_PARAMETERS;
-		err = FAIL_PARAMETERS;
-		break;
-	}
-	release_firmware(pm8001_ha->fw_image);
-out1:
-	kfree(cmd_ptr);
-out:
-	pm8001_ha->fw_status = err;
-
-	if (!err)
-		return count;
+	if (FLASH_CMD_UPDATE == flash_command)
+		ret = pm8001_update_flash(pm8001_ha);
 	else
-		return -err;
+		ret = pm8001_set_nvmd(pm8001_ha);
+
+	release_firmware(pm8001_ha->fw_image);
+out:
+	kfree(cmd_ptr);
+
+	if (ret)
+		return ret;
+
+	pm8001_ha->fw_status = FLASH_OK;
+	return count;
 }
 
 static ssize_t pm8001_show_update_fw(struct device *cdev,
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 1738310..dd12c6f 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -4824,7 +4824,7 @@
 	rc = pm8001_tag_alloc(pm8001_ha, &tag);
 	if (rc) {
 		kfree(fw_control_context);
-		return rc;
+		return -EBUSY;
 	}
 	ccb = &pm8001_ha->ccb_info[tag];
 	ccb->fw_control_context = fw_control_context;
@@ -4946,7 +4946,7 @@
 	rc = pm8001_tag_alloc(pm8001_ha, &tag);
 	if (rc) {
 		kfree(fw_control_context);
-		return rc;
+		return -EBUSY;
 	}
 	ccb = &pm8001_ha->ccb_info[tag];
 	ccb->fw_control_context = fw_control_context;