diff options
author | Preetham Chandru R <pchandru@nvidia.com> | 2016-12-08 09:02:08 -0500 |
---|---|---|
committer | Preetham Chandru R <pchandru@nvidia.com> | 2017-08-21 05:16:34 -0400 |
commit | 74d2eb8486d78767a6b0fa84d43cd85c83b43706 (patch) | |
tree | 5482f2a346ae12044eede099a6d8340c2857a71b /drivers/ata | |
parent | ec1a95bdfc6277ca2f74048dbc9dcbaac2398b7f (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.c | 113 | ||||
-rw-r--r-- | drivers/ata/tegra/ahci_tegra.h | 27 |
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 | ||
316 | static 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 | |||
345 | static 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 | |||
316 | static void tegra_ahci_unbind(struct work_struct *work) | 369 | static 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 | ||
382 | static 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 | |||
390 | static 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 | |||
329 | static void tegra_ahci_error_handler(struct ata_port *ap) | 422 | static 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 | |||
179 | enum tegra_badblk_arguments { | ||
180 | TEGRA_BADBLK_COMMAND, | ||
181 | TEGRA_BADBLK_COMMAND_PARAM1, | ||
182 | TEGRA_BADBLK_COMMAND_PARAM2, | ||
183 | TEGRA_BADBLK_MAX_ARGUMENTS, | ||
184 | }; | ||
185 | |||
176 | enum tegra_sata_bars { | 186 | enum 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 | ||
201 | struct 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 | |||
207 | struct tegra_ahci_badblk_priv { | ||
208 | struct work_struct badblk_work; | ||
209 | struct tegra_ahci_badblk_info *head; | ||
210 | spinlock_t badblk_lock; | ||
211 | }; | ||
212 | |||
191 | struct tegra_ahci_priv { | 213 | struct 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 | |||
422 | u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev); | ||
398 | #endif | 423 | #endif |