power: udev: Set DLPM based on power source and device

Disable DLPM [max_performance] when plugged in or when a device does not
handle DLPM well [transcend MTS400].

Error reported by user swapping SSD:
http://preston4tw.blogspot.com/2015/02/transcend-mts400-ssd-power-saving.html
https://wiki.archlinux.org/index.php/Solid_state_drive#Resolving_SATA_power_management_related_errors
https://superuser.com/questions/887916/transcend-mts400-ssd-crashes-my-acer-c720-chromebook-how-to-disable-sata-power

BUG=b:115398975,b:122781871
TEST=Check Fizz is running as max_performance
     Check script manually with sh -x.
CQ-DEPEND=CL:1475865

Change-Id: I72979409f37b67f845ca32f955ad1bb78c863b28
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1454431
Reviewed-by: Mike Frysinger <vapier@chromium.org>
(cherry picked from commit 33b32a5d3992fe35547fdb9539d5b7632405ba71)
Reviewed-on: https://chromium-review.googlesource.com/c/1478018
Reviewed-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
Commit-Queue: Sarthak Kukreti <sarthakkukreti@chromium.org>
Tested-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
diff --git a/power_manager/udev/optional/98-powerknobs.rules b/power_manager/udev/optional/98-powerknobs.rules
index b286519..6bb90b3 100644
--- a/power_manager/udev/optional/98-powerknobs.rules
+++ b/power_manager/udev/optional/98-powerknobs.rules
@@ -5,11 +5,7 @@
 ACTION=="add", SUBSYSTEM=="module", DEVPATH=="/module/snd_hda_intel", \
   ATTR{parameters/power_save}="0", ATTR{parameters/power_save_controller}="N"
 
-# Minimize link power for SATA drives
-ACTION=="add|change", SUBSYSTEM=="scsi_host", \
-  TEST=="link_power_management_policy", \
-  ATTR{link_power_management_policy}="min_power"
-
 # Configure SSD/HDD power management setting on power plug/unplug
 ACTION=="change", SUBSYSTEM=="power_supply", ATTR{type}!="Battery", \
-  RUN+="/usr/bin/set_blkdev_pm"
+  RUN+="/usr/bin/set_blkdev_pm" \
+  RUN+="/usr/bin/set_sata_link_pm"
diff --git a/power_manager/udev/optional/set_sata_link_pm b/power_manager/udev/optional/set_sata_link_pm
new file mode 100755
index 0000000..a85e6a0
--- /dev/null
+++ b/power_manager/udev/optional/set_sata_link_pm
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script configures link power management for sata devices. It is executed
+# at boot time and whenever power is plugged or unplugged (through udev).
+#
+# When battery is plugged in and the device supports it, set link to min_power.
+# On charger, use max_power, it improves latencies.
+
+. /usr/share/misc/chromeos-common.sh
+. /usr/sbin/write_gpt.sh
+
+load_base_vars
+
+if dump_power_status | grep -qF 'line_power_connected 1'; then
+  on_ac="1"
+else
+  on_ac="0"
+fi
+
+drive="$(get_fixed_dst_drive)"
+
+if [ -z "${drive}" ]; then
+  # Root device could not be located."
+  exit
+fi
+
+dev_sysfs="/sys/block/${drive##*/}"
+dev_sysfs_full_path="/sys/block/$(readlink "${dev_sysfs}")"
+link_pm_path_glob="$(echo "${dev_sysfs_full_path}" | \
+       sed 's|target.*|scsi_host/host*/link_power_management_policy|')"
+# No "" inside, link_pm_path_glob has wildcard.
+link_pm_path="$(echo ${link_pm_path_glob})"
+
+if [ ! -f "${link_pm_path}" ]; then
+  # Older kernel, or the device is not a SATA device.
+  exit
+fi
+
+if [ "${on_ac}" = "1" ]; then
+  link_pm_policy="max_performance"
+else
+  # Transcend SSD have issues with DLPM: b:115398975
+  if grep -q "TS.*MTS400" "${dev_sysfs}/device/model"; then
+    link_pm_policy="max_performance"
+  else
+    link_pm_policy="min_power"
+  fi
+fi
+
+echo "${link_pm_policy}" > "${link_pm_path}"