summaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorPreetham Chandru R <pchandru@nvidia.com>2016-12-08 09:02:08 -0500
committerPreetham Chandru R <pchandru@nvidia.com>2017-08-21 05:16:34 -0400
commit74d2eb8486d78767a6b0fa84d43cd85c83b43706 (patch)
tree5482f2a346ae12044eede099a6d8340c2857a71b /drivers/ata
parentec1a95bdfc6277ca2f74048dbc9dcbaac2398b7f (diff)
ata: ahci_tegra: Change to handle bad sectors
Scanning the entire SATA disk for bad block takes a lot of time. Also there is an issue in tegra controller with bad block handling case so a WAR is merged to handle this issue. Since the driver gets to know the bad block sector in this WAR we can take advantage and update the filesystem about bad block info quickly. The kernel invokes a user space script with bad block sector which in turn updates the filesystem about bad sectors in the dri Bug 200156170 Change-Id: I70e64cac6356aea3d898d4fa6de62ad7131bd57a Signed-off-by: Preetham Chandru R <pchandru@nvidia.com> Reviewed-on: http://git-master/r/1278669 (cherry picked from commit 3c0fdb862e3986e9535f8843aa0c5f99d17d9fa9) Reviewed-on: https://git-master.nvidia.com/r/1505549 GVS: Gerrit_Virtual_Submit Reviewed-by: Venu Byravarasu <vbyravarasu@nvidia.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/tegra/ahci_tegra.c113
-rw-r--r--drivers/ata/tegra/ahci_tegra.h27
2 files changed, 139 insertions, 1 deletions
diff --git a/drivers/ata/tegra/ahci_tegra.c b/drivers/ata/tegra/ahci_tegra.c
index d88b2c6b4..39ae34a71 100644
--- a/drivers/ata/tegra/ahci_tegra.c
+++ b/drivers/ata/tegra/ahci_tegra.c
@@ -313,6 +313,59 @@ static int tegra_ahci_softreset(struct ata_link *link, unsigned int *class,
313 313
314} 314}
315 315
316static int tegra_ahci_umh(unsigned long long block, char *block_dev)
317{
318 char *envp[] = {
319 "HOME=/",
320 "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
321 NULL };
322 char *argv[TEGRA_BADBLK_MAX_ARGUMENTS + 1];
323 char buf1[TEGRA_BADBLK_STRING_LENGTH] = { };
324 char buf2[TEGRA_BADBLK_STRING_LENGTH] = { };
325 int ret = 0;
326
327 snprintf(buf1, sizeof(buf1), "%llu", block);
328 snprintf(buf2, sizeof(buf2), "%s", block_dev);
329 argv[TEGRA_BADBLK_COMMAND] = "/system/bin/badblk.sh";
330 argv[TEGRA_BADBLK_COMMAND_PARAM1] = buf1;
331 argv[TEGRA_BADBLK_COMMAND_PARAM2] = buf2;
332 argv[TEGRA_BADBLK_MAX_ARGUMENTS] = NULL;
333
334 ret = call_usermodehelper(argv[TEGRA_BADBLK_COMMAND],
335 argv, envp, UMH_WAIT_PROC);
336 if (ret == -ENOENT) {
337 argv[TEGRA_BADBLK_COMMAND] = "/home/ubuntu/badblk.sh";
338 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
339 ret = call_usermodehelper(argv[TEGRA_BADBLK_COMMAND],
340 argv, envp, UMH_WAIT_PROC);
341 }
342 return ret;
343}
344
345static void tegra_ahci_badblk_manage(struct work_struct *work)
346{
347 struct tegra_ahci_badblk_priv *badblk =
348 container_of(work, struct tegra_ahci_badblk_priv, badblk_work);
349 struct tegra_ahci_priv *tegra =
350 container_of(badblk, struct tegra_ahci_priv, badblk);
351 struct tegra_ahci_badblk_info *temp = NULL;
352 struct platform_device *pdev = tegra->pdev;
353 int ret = 0;
354
355 spin_lock(&tegra->badblk.badblk_lock);
356 temp = tegra->badblk.head;
357 if (!temp) {
358 spin_unlock(&tegra->badblk.badblk_lock);
359 return;
360 }
361 tegra->badblk.head = temp->next;
362
363 spin_unlock(&tegra->badblk.badblk_lock);
364
365 ret = tegra_ahci_umh(temp->block, temp->block_dev);
366 devm_kfree(&pdev->dev, temp);
367}
368
316static void tegra_ahci_unbind(struct work_struct *work) 369static void tegra_ahci_unbind(struct work_struct *work)
317{ 370{
318 struct tegra_ahci_priv *tegra = 371 struct tegra_ahci_priv *tegra =
@@ -326,8 +379,65 @@ static void tegra_ahci_unbind(struct work_struct *work)
326 pdev->dev.driver->shutdown = NULL; 379 pdev->dev.driver->shutdown = NULL;
327} 380}
328 381
382static char *tegra_ahci_get_disk_name(struct scsi_cmnd *scsicmd)
383{
384 if (scsicmd && scsicmd->request && scsicmd->request->rq_disk)
385 return scsicmd->request->rq_disk->disk_name;
386 else
387 return NULL;
388}
389
390static void tegra_ahci_schedule_badblk_work(struct ata_queued_cmd *qc,
391 struct ata_device *dev)
392{
393
394 unsigned long long sector;
395 unsigned long long block;
396 char *disk_name = NULL;
397 struct tegra_ahci_badblk_info *temp = NULL;
398 struct ata_host *host = qc->ap->host;
399 struct ahci_host_priv *hpriv = host->private_data;
400 struct tegra_ahci_priv *tegra = hpriv->plat_data;
401 struct platform_device *pdev = tegra->pdev;
402
403 disk_name = tegra_ahci_get_disk_name(qc->scsicmd);
404 sector = ata_tf_read_block(&qc->tf, dev);
405 block = (sector * 512) / 4096;
406 if (spin_trylock(&tegra->badblk.badblk_lock)) {
407 temp = devm_kzalloc(&pdev->dev,
408 sizeof(struct tegra_ahci_badblk_info),
409 GFP_ATOMIC);
410 if (temp) {
411 temp->block = block;
412 if (disk_name)
413 strncpy(temp->block_dev, disk_name, 99);
414 temp->next = tegra->badblk.head;
415 tegra->badblk.head = temp;
416 schedule_work(&tegra->badblk.badblk_work);
417 }
418 spin_unlock(&tegra->badblk.badblk_lock);
419 }
420}
421
329static void tegra_ahci_error_handler(struct ata_port *ap) 422static void tegra_ahci_error_handler(struct ata_port *ap)
330{ 423{
424 int tag;
425
426 for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
427 struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
428
429 if ((qc->flags & ATA_QCFLAG_FAILED)
430 && (qc->result_tf.feature & (ATA_UNC | ATA_AMNF))) {
431 struct ata_link *link;
432 struct ata_device *dev;
433
434 ata_for_each_link(link, qc->ap, PMP_FIRST)
435 ata_for_each_dev(dev, link, ENABLED)
436 tegra_ahci_schedule_badblk_work(qc,
437 dev);
438 }
439 }
440
331 ahci_ops.error_handler(ap); 441 ahci_ops.error_handler(ap);
332 if (!ata_dev_enabled(ap->link.device)) { 442 if (!ata_dev_enabled(ap->link.device)) {
333 if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) { 443 if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) {
@@ -1430,6 +1540,9 @@ static int tegra_ahci_probe(struct platform_device *pdev)
1430 if (ret) 1540 if (ret)
1431 goto poweroff_controller; 1541 goto poweroff_controller;
1432 1542
1543 INIT_WORK(&tegra->badblk.badblk_work, tegra_ahci_badblk_manage);
1544 tegra->badblk.head = NULL;
1545 spin_lock_init(&tegra->badblk.badblk_lock);
1433 1546
1434 ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, 1547 ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info,
1435 &ahci_platform_sht); 1548 &ahci_platform_sht);
diff --git a/drivers/ata/tegra/ahci_tegra.h b/drivers/ata/tegra/ahci_tegra.h
index 49801a73f..eaf10bedd 100644
--- a/drivers/ata/tegra/ahci_tegra.h
+++ b/drivers/ata/tegra/ahci_tegra.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * drivers/ata/ahci_tegra.h 2 * drivers/ata/ahci_tegra.h
3 * 3 *
4 * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. 4 * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved.
5 * 5 *
6 * This software is licensed under the terms of the GNU General Public 6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and 7 * License version 2, as published by the Free Software Foundation, and
@@ -173,6 +173,16 @@
173 173
174#define TEGRA_AHCI_READ_LOG_EXT_NOENTRY 0x80 174#define TEGRA_AHCI_READ_LOG_EXT_NOENTRY 0x80
175 175
176/* Badblock Management */
177#define TEGRA_BADBLK_STRING_LENGTH 100
178
179enum tegra_badblk_arguments {
180 TEGRA_BADBLK_COMMAND,
181 TEGRA_BADBLK_COMMAND_PARAM1,
182 TEGRA_BADBLK_COMMAND_PARAM2,
183 TEGRA_BADBLK_MAX_ARGUMENTS,
184};
185
176enum tegra_sata_bars { 186enum tegra_sata_bars {
177 TEGRA_SATA_IPFS = 0, 187 TEGRA_SATA_IPFS = 0,
178 TEGRA_SATA_CONFIG, 188 TEGRA_SATA_CONFIG,
@@ -188,6 +198,18 @@ enum tegra_ahci_port_runtime_status {
188 TEGRA_AHCI_PORT_RUNTIME_DEVSLP = 8, 198 TEGRA_AHCI_PORT_RUNTIME_DEVSLP = 8,
189}; 199};
190 200
201struct tegra_ahci_badblk_info {
202 unsigned long long block;
203 char block_dev[TEGRA_BADBLK_STRING_LENGTH];
204 struct tegra_ahci_badblk_info *next;
205};
206
207struct tegra_ahci_badblk_priv {
208 struct work_struct badblk_work;
209 struct tegra_ahci_badblk_info *head;
210 spinlock_t badblk_lock;
211};
212
191struct tegra_ahci_priv { 213struct tegra_ahci_priv {
192 struct platform_device *pdev; 214 struct platform_device *pdev;
193 void __iomem *base_list[TEGRA_SATA_BARS_MAX]; 215 void __iomem *base_list[TEGRA_SATA_BARS_MAX];
@@ -207,6 +229,7 @@ struct tegra_ahci_priv {
207 struct pinctrl_state *devslp_pullup; 229 struct pinctrl_state *devslp_pullup;
208 struct tegra_prod *prod_list; 230 struct tegra_prod *prod_list;
209 struct work_struct work; 231 struct work_struct work;
232 struct tegra_ahci_badblk_priv badblk;
210 int devslp_gpio; 233 int devslp_gpio;
211 bool devslp_override; 234 bool devslp_override;
212 bool devslp_pinmux_override; 235 bool devslp_pinmux_override;
@@ -395,4 +418,6 @@ static inline void tegra_ahci_aux_update(struct ahci_host_priv *hpriv, u32 val,
395 writel(rval, tegra->base_list[TEGRA_SATA_AUX] + offset); 418 writel(rval, tegra->base_list[TEGRA_SATA_AUX] + offset);
396 rval = readl(tegra->base_list[TEGRA_SATA_AUX] + offset); 419 rval = readl(tegra->base_list[TEGRA_SATA_AUX] + offset);
397} 420}
421
422u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev);
398#endif 423#endif