[PATCH 01/10] mtip32xx: Abort secure erase when drive is mounted

From: Asai Thambi SP
Date: Mon Feb 22 2016 - 23:59:44 EST



To avoid erasing a device with a mounted filesystem, try to get exclusive
access to the blkdev object corresponding to the device.

Signed-off-by: Selvan Mani <smani@xxxxxxxxxx>
Signed-off-by: Rajesh Kumar Sambandam <rsambandam@xxxxxxxxxx>
Signed-off-by: Asai Thambi S P <asamymuthupa@xxxxxxxxxx>
---
drivers/block/mtip32xx/mtip32xx.c | 113 ++++++++++++++++++++++++++++++-------
drivers/block/mtip32xx/mtip32xx.h | 2 +
2 files changed, 94 insertions(+), 21 deletions(-)

diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index 9b180db..37690ab 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -982,6 +982,28 @@ static void mtip_issue_non_ncq_command(struct mtip_port *port, int tag)
port->cmd_issue[MTIP_TAG_INDEX(tag)]);
}

+static inline int mtip_bdev_claim(struct driver_data *dd)
+{
+ if (dd->bdev_claimed == false) {
+ igrab(dd->bdev->bd_inode);
+ if (blkdev_get(dd->bdev, FMODE_EXCL, dd->bdev) < 0) {
+ dev_warn(&dd->pdev->dev, "Drive erase aborted due to non-zero refcount (%d)\n",
+ dd->bdev->bd_holders);
+ return -ERESTARTSYS;
+ }
+ dd->bdev_claimed = true;
+ }
+ return 0;
+}
+
+static inline void mtip_bdev_unclaim(struct driver_data *dd)
+{
+ if (dd->bdev_claimed) {
+ blkdev_put(dd->bdev, FMODE_EXCL);
+ dd->bdev_claimed = false;
+ }
+}
+
static bool mtip_pause_ncq(struct mtip_port *port,
struct host_to_dev_fis *fis)
{
@@ -991,10 +1013,21 @@ static bool mtip_pause_ncq(struct mtip_port *port,
reply = port->rxfis + RX_FIS_D2H_REG;
task_file_data = readl(port->mmio+PORT_TFDATA);

- if ((task_file_data & 1))
+ if ((task_file_data & 1)) {
+ if ((fis->command == ATA_CMD_SEC_SET_PASS) ||
+ (fis->command == ATA_CMD_SEC_ERASE_PREP) ||
+ (fis->command == ATA_CMD_SEC_ERASE_UNIT) ||
+ ((fis->command == 0xFC) &&
+ (fis->features == 0x27 || fis->features == 0x72 ||
+ fis->features == 0x62 || fis->features == 0x26 ||
+ fis->features == 0x12)))
+ mtip_bdev_unclaim(port->dd);
return false;
+ }

- if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
+ if (fis->command == ATA_CMD_SEC_SET_PASS) {
+ mtip_bdev_unclaim(port->dd);
+ } else if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
port->ic_pause_timer = jiffies;
return true;
} else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1005,8 +1038,10 @@ static bool mtip_pause_ncq(struct mtip_port *port,
} else if ((fis->command == ATA_CMD_SEC_ERASE_UNIT) ||
((fis->command == 0xFC) &&
(fis->features == 0x27 || fis->features == 0x72 ||
- fis->features == 0x62 || fis->features == 0x26))) {
+ fis->features == 0x62 || fis->features == 0x26 ||
+ fis->features == 0x12))) {
clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+ mtip_bdev_unclaim(port->dd);
/* Com reset after secure erase or lowlevel format */
mtip_restart_port(port);
clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
@@ -1110,8 +1145,23 @@ static int mtip_exec_internal_command(struct mtip_port *port,

set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);

+ /* Check for secure erase while fs mounted */
+ if ((fis->command == ATA_CMD_SEC_SET_PASS) ||
+ (fis->command == ATA_CMD_SEC_ERASE_PREP) ||
+ (fis->command == ATA_CMD_SEC_ERASE_UNIT) ||
+ ((fis->command == 0xFC) &&
+ (fis->features == 0x27 || fis->features == 0x72 ||
+ fis->features == 0x62 || fis->features == 0x26 ||
+ fis->features == 0x12))) {
+ rv = mtip_bdev_claim(dd);
+ if (rv)
+ goto exec_ic_exit;
+ }
+
if (fis->command == ATA_CMD_SEC_ERASE_PREP)
set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ else if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
+ dd->port->ic_pause_timer = 0;

clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags);

@@ -1122,6 +1172,8 @@ static int mtip_exec_internal_command(struct mtip_port *port,
MTIP_QUIESCE_IO_TIMEOUT_MS) < 0) {
dev_warn(&dd->pdev->dev,
"Failed to quiesce IO\n");
+
+ mtip_bdev_unclaim(dd);
mtip_put_int_command(dd, int_cmd);
clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
wake_up_interruptible(&port->svc_wait);
@@ -1197,6 +1249,8 @@ static int mtip_exec_internal_command(struct mtip_port *port,
mtip_device_reset(dd); /* recover from timeout issue */
rv = -EAGAIN;
goto exec_ic_exit;
+ } else {
+ rv = 0;
}
} else {
u32 hba_stat, port_stat;
@@ -1247,6 +1301,10 @@ static int mtip_exec_internal_command(struct mtip_port *port,
}
}
exec_ic_exit:
+ if (rv < 0) {
+ clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ mtip_bdev_unclaim(dd);
+ }
/* Clear the allocated and active bits for the internal command. */
mtip_put_int_command(dd, int_cmd);
clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags);
@@ -1783,6 +1841,7 @@ static int exec_drive_task(struct mtip_port *port, u8 *command)
struct host_to_dev_fis fis;
struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG);
unsigned int to;
+ int rv;

/* Build the FIS. */
memset(&fis, 0, sizeof(struct host_to_dev_fis));
@@ -1809,14 +1868,17 @@ static int exec_drive_task(struct mtip_port *port, u8 *command)
command[6]);

/* Execute the command. */
- if (mtip_exec_internal_command(port,
+ rv = mtip_exec_internal_command(port,
&fis,
5,
0,
0,
0,
GFP_KERNEL,
- to) < 0) {
+ to);
+ if (rv < 0) {
+ if (rv == -ERESTARTSYS)
+ return rv;
return -1;
}

@@ -1905,16 +1967,17 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
command[3]);

/* Execute the command. */
- if (mtip_exec_internal_command(port,
+ rv = mtip_exec_internal_command(port,
&fis,
5,
(xfer_sz ? dma_addr : 0),
(xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0),
0,
GFP_KERNEL,
- to)
- < 0) {
- rv = -EFAULT;
+ to);
+ if (rv < 0) {
+ if (rv != -ERESTARTSYS)
+ rv = -EFAULT;
goto exit_drive_command;
}

@@ -2152,15 +2215,17 @@ static int exec_drive_taskfile(struct driver_data *dd,
transfer_size = ATA_SECT_SIZE * fis.sect_count;

/* Execute the command.*/
- if (mtip_exec_internal_command(dd->port,
+ err = mtip_exec_internal_command(dd->port,
&fis,
5,
dma_buffer,
transfer_size,
0,
GFP_KERNEL,
- timeout) < 0) {
- err = -EIO;
+ timeout);
+ if (err < 0) {
+ if (err != -ERESTARTSYS)
+ err = -EIO;
goto abort;
}

@@ -2258,6 +2323,8 @@ abort:
static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
unsigned long arg)
{
+ int ret = 0;
+
switch (cmd) {
case HDIO_GET_IDENTITY:
{
@@ -2277,10 +2344,11 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
return -EFAULT;

/* Execute the drive command. */
- if (exec_drive_command(dd->port,
+ ret = exec_drive_command(dd->port,
drive_command,
- (void __user *) (arg+4)))
- return -EIO;
+ (void __user *) (arg+4));
+ if (ret < 0)
+ return ret;

/* Copy the status back to the users buffer. */
if (copy_to_user((void __user *) arg,
@@ -2301,8 +2369,9 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
return -EFAULT;

/* Execute the drive command. */
- if (exec_drive_task(dd->port, drive_command))
- return -EIO;
+ ret = exec_drive_task(dd->port, drive_command);
+ if (ret < 0)
+ return ret;

/* Copy the status back to the users buffer. */
if (copy_to_user((void __user *) arg,
@@ -2314,7 +2383,7 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
}
case HDIO_DRIVE_TASKFILE: {
ide_task_request_t req_task;
- int ret, outtotal;
+ int outtotal;

if (copy_from_user(&req_task, (void __user *) arg,
sizeof(req_task)))
@@ -2329,13 +2398,13 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
sizeof(req_task)))
return -EFAULT;

- return ret;
+ break;
}

default:
return -EINVAL;
}
- return 0;
+ return ret;
}

/*
@@ -3620,7 +3689,7 @@ static inline bool is_se_active(struct driver_data *dd)
if (time_after(jiffies, to)) {
clear_bit(MTIP_PF_SE_ACTIVE_BIT,
&dd->port->flags);
- clear_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag);
+ mtip_bdev_unclaim(dd);
dd->port->ic_pause_timer = 0;
wake_up_interruptible(&dd->port->svc_wait);
return false;
@@ -3934,6 +4003,7 @@ skip_create_disk:
add_disk(dd->disk);

dd->bdev = bdget_disk(dd->disk, 0);
+ dd->bdev_claimed = false;
/*
* Now that the disk is active, initialize any sysfs attributes
* managed by the protocol layer.
@@ -4008,6 +4078,7 @@ static int mtip_block_remove(struct driver_data *dd)
{
struct kobject *kobj;

+ mtip_bdev_unclaim(dd);
mtip_hw_debugfs_exit(dd);

if (dd->mtip_svc_handler) {
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index 3274784..29357fd 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -491,6 +491,8 @@ struct driver_data {

int isr_binding;

+ bool bdev_claimed; /* set when bdev is claimed exclusively */
+
struct block_device *bdev;

struct list_head online_list; /* linkage for online list */
--
1.7.1