Changes in 4.9.190 usb: usbfs: fix double-free of usb memory upon submiturb error usb: iowarrior: fix deadlock on disconnect sound: fix a memory leak bug x86/mm: Check for pfn instead of page in vmalloc_sync_one() x86/mm: Sync also unmappings in vmalloc_sync_all() mm/vmalloc: Sync unmappings in __purge_vmap_area_lazy() perf record: Fix wrong size in perf_record_mmap for last kernel module perf db-export: Fix thread__exec_comm() perf record: Fix module size on s390 usb: yurex: Fix use-after-free in yurex_delete can: peak_usb: fix potential double kfree_skb() netfilter: nfnetlink: avoid deadlock due to synchronous request_module iscsi_ibft: make ISCSI_IBFT dependson ACPI instead of ISCSI_IBFT_FIND mac80211: don't warn about CW params when not using them hwmon: (nct6775) Fix register address and added missed tolerance for nct6106 cpufreq/pasemi: fix use-after-free in pas_cpufreq_cpu_init() s390/qdio: add sanity checks to the fast-requeue path ALSA: compress: Fix regression on compressed capture streams ALSA: compress: Prevent bypasses of set_params ALSA: compress: Don't allow paritial drain operations on capture streams ALSA: compress: Be more restrictive about when a drain is allowed perf probe: Avoid calling freeing routine multiple times for same pointer drbd: dynamically allocate shash descriptor ACPI/IORT: Fix off-by-one check in iort_dev_find_its_id() ARM: davinci: fix sleep.S build error on ARMv4 scsi: megaraid_sas: fix panic on loading firmware crashdump scsi: ibmvfc: fix WARN_ON during event pool release scsi: scsi_dh_alua: always use a 2 second delay before retrying RTPG tty/ldsem, locking/rwsem: Add missing ACQUIRE to read_failed sleep loop perf/core: Fix creating kernel counters for PMUs that override event->cpu can: peak_usb: pcan_usb_pro: Fix info-leaks to USB devices can: peak_usb: pcan_usb_fd: Fix info-leaks to USB devices hwmon: (nct7802) Fix wrong detection of in4 presence ALSA: firewire: fix a memory leak bug ALSA: hda - Don't override global PCM hw info flag mac80211: don't WARN on short WMM parameters from AP SMB3: Fix deadlock in validate negotiate hits reconnect smb3: send CAP_DFS capability during session setup mwifiex: fix 802.11n/WPA detection iwlwifi: don't unmap as page memory that was mapped as single scsi: mpt3sas: Use 63-bit DMA addressing on SAS35 HBA sh: kernel: hw_breakpoint: Fix missing break in switch statement mm/usercopy: use memory range to be accessed for wraparound check mm/memcontrol.c: fix use after free in mem_cgroup_iter() bpf: get rid of pure_initcall dependency to enable jits bpf: restrict access to core bpf sysctls bpf: add bpf_jit_limit knob to restrict unpriv allocations vhost-net: set packet weight of tx polling to 2 * vq size vhost_net: use packet weight for rx handler, too vhost_net: introduce vhost_exceeds_weight() vhost: introduce vhost_exceeds_weight() vhost_net: fix possible infinite loop vhost: scsi: add weight support siphash: add cryptographically secure PRF siphash: implement HalfSipHash1-3 for hash tables inet: switch IP ID generator to siphash netfilter: ctnetlink: don't use conntrack/expect object addresses as id xtensa: add missing isync to the cpu_reset TLB code ALSA: hda - Fix a memory leak bug ALSA: hda - Add a generic reboot_notify ALSA: hda - Let all conexant codec enter D3 when rebooting HID: holtek: test for sanity of intfdata HID: hiddev: avoid opening a disconnected device HID: hiddev: do cleanup in failure of opening a device Input: kbtab - sanity check for endpoint type Input: iforce - add sanity checks net: usb: pegasus: fix improper read if get_registers() fail xen/pciback: remove set but not used variable 'old_state' irqchip/irq-imx-gpcv2: Forward irq type to parent perf header: Fix divide by zero error if f_header.attr_size==0 perf header: Fix use of unitialized value warning libata: zpodd: Fix small read overflow in zpodd_get_mech_type() scsi: hpsa: correct scsi command status issue after reset ata: libahci: do not complain in case of deferred probe kbuild: modpost: handle KBUILD_EXTRA_SYMBOLS only for external modules arm64/efi: fix variable 'si' set but not used arm64/mm: fix variable 'pud' set but not used IB/core: Add mitigation for Spectre V1 IB/mad: Fix use-after-free in ib mad completion handling ocfs2: remove set but not used variable 'last_hash' staging: comedi: dt3000: Fix signed integer overflow 'divider * base' staging: comedi: dt3000: Fix rounding up of timer divisor USB: core: Fix races in character device registration and deregistraion usb: cdc-acm: make sure a refcount is taken early enough USB: CDC: fix sanity checks in CDC union parser USB: serial: option: add D-Link DWM-222 device ID USB: serial: option: Add support for ZTE MF871A USB: serial: option: add the BroadMobi BM818 card USB: serial: option: Add Motorola modem UARTs asm-generic: fix -Wtype-limits compiler warnings bpf: fix bpf_jit_limit knob for PAGE_SIZE >= 64K arm64: compat: Allow single-byte watchpoints on all addresses netfilter: conntrack: Use consistent ct id hash calculation Input: psmouse - fix build error of multiple definition iommu/amd: Move iommu_init_pci() to .init section bnx2x: Fix VF's VLAN reconfiguration in reload. net/packet: fix race in tpacket_snd() sctp: fix the transport error_count check xen/netback: Reset nr_frags before freeing skb net/mlx5e: Only support tx/rx pause setting for port owner net/mlx5e: Use flow keys dissector to parse packets for ARFS team: Add vlan tx offload to hw_enc_features bonding: Add vlan tx offload to hw_enc_features Linux 4.9.190 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
298 lines
7.3 KiB
C
298 lines
7.3 KiB
C
#include <linux/libata.h>
|
|
#include <linux/cdrom.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pm_qos.h>
|
|
#include <scsi/scsi_device.h>
|
|
|
|
#include "libata.h"
|
|
|
|
static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */
|
|
module_param(zpodd_poweroff_delay, int, 0644);
|
|
MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds");
|
|
|
|
enum odd_mech_type {
|
|
ODD_MECH_TYPE_SLOT,
|
|
ODD_MECH_TYPE_DRAWER,
|
|
ODD_MECH_TYPE_UNSUPPORTED,
|
|
};
|
|
|
|
struct zpodd {
|
|
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
|
|
struct ata_device *dev;
|
|
|
|
/* The following fields are synchronized by PM core. */
|
|
bool from_notify; /* resumed as a result of
|
|
* acpi wake notification */
|
|
bool zp_ready; /* ZP ready state */
|
|
unsigned long last_ready; /* last ZP ready timestamp */
|
|
bool zp_sampled; /* ZP ready state sampled */
|
|
bool powered_off; /* ODD is powered off
|
|
* during suspend */
|
|
};
|
|
|
|
static int eject_tray(struct ata_device *dev)
|
|
{
|
|
struct ata_taskfile tf;
|
|
static const char cdb[ATAPI_CDB_LEN] = { GPCMD_START_STOP_UNIT,
|
|
0, 0, 0,
|
|
0x02, /* LoEj */
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
ata_tf_init(dev, &tf);
|
|
tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
|
|
tf.command = ATA_CMD_PACKET;
|
|
tf.protocol = ATAPI_PROT_NODATA;
|
|
|
|
return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
|
|
}
|
|
|
|
/* Per the spec, only slot type and drawer type ODD can be supported */
|
|
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
|
|
{
|
|
char *buf;
|
|
unsigned int ret;
|
|
struct rm_feature_desc *desc;
|
|
struct ata_taskfile tf;
|
|
static const char cdb[ATAPI_CDB_LEN] = { GPCMD_GET_CONFIGURATION,
|
|
2, /* only 1 feature descriptor requested */
|
|
0, 3, /* 3, removable medium feature */
|
|
0, 0, 0,/* reserved */
|
|
0, 16,
|
|
0, 0, 0,
|
|
};
|
|
|
|
buf = kzalloc(16, GFP_KERNEL);
|
|
if (!buf)
|
|
return ODD_MECH_TYPE_UNSUPPORTED;
|
|
desc = (void *)(buf + 8);
|
|
|
|
ata_tf_init(dev, &tf);
|
|
tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
|
|
tf.command = ATA_CMD_PACKET;
|
|
tf.protocol = ATAPI_PROT_PIO;
|
|
tf.lbam = 16;
|
|
|
|
ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
|
|
buf, 16, 0);
|
|
if (ret) {
|
|
kfree(buf);
|
|
return ODD_MECH_TYPE_UNSUPPORTED;
|
|
}
|
|
|
|
if (be16_to_cpu(desc->feature_code) != 3) {
|
|
kfree(buf);
|
|
return ODD_MECH_TYPE_UNSUPPORTED;
|
|
}
|
|
|
|
if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) {
|
|
kfree(buf);
|
|
return ODD_MECH_TYPE_SLOT;
|
|
} else if (desc->mech_type == 1 && desc->load == 0 &&
|
|
desc->eject == 1) {
|
|
kfree(buf);
|
|
return ODD_MECH_TYPE_DRAWER;
|
|
} else {
|
|
kfree(buf);
|
|
return ODD_MECH_TYPE_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
/* Test if ODD is zero power ready by sense code */
|
|
static bool zpready(struct ata_device *dev)
|
|
{
|
|
u8 sense_key, *sense_buf;
|
|
unsigned int ret, asc, ascq, add_len;
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
|
|
ret = atapi_eh_tur(dev, &sense_key);
|
|
|
|
if (!ret || sense_key != NOT_READY)
|
|
return false;
|
|
|
|
sense_buf = dev->link->ap->sector_buf;
|
|
ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
|
|
if (ret)
|
|
return false;
|
|
|
|
/* sense valid */
|
|
if ((sense_buf[0] & 0x7f) != 0x70)
|
|
return false;
|
|
|
|
add_len = sense_buf[7];
|
|
/* has asc and ascq */
|
|
if (add_len < 6)
|
|
return false;
|
|
|
|
asc = sense_buf[12];
|
|
ascq = sense_buf[13];
|
|
|
|
if (zpodd->mech_type == ODD_MECH_TYPE_SLOT)
|
|
/* no media inside */
|
|
return asc == 0x3a;
|
|
else
|
|
/* no media inside and door closed */
|
|
return asc == 0x3a && ascq == 0x01;
|
|
}
|
|
|
|
/*
|
|
* Update the zpodd->zp_ready field. This field will only be set
|
|
* if the ODD has stayed in ZP ready state for zpodd_poweroff_delay
|
|
* time, and will be used to decide if power off is allowed. If it
|
|
* is set, it will be cleared during resume from powered off state.
|
|
*/
|
|
void zpodd_on_suspend(struct ata_device *dev)
|
|
{
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
unsigned long expires;
|
|
|
|
if (!zpready(dev)) {
|
|
zpodd->zp_sampled = false;
|
|
zpodd->zp_ready = false;
|
|
return;
|
|
}
|
|
|
|
if (!zpodd->zp_sampled) {
|
|
zpodd->zp_sampled = true;
|
|
zpodd->last_ready = jiffies;
|
|
return;
|
|
}
|
|
|
|
expires = zpodd->last_ready +
|
|
msecs_to_jiffies(zpodd_poweroff_delay * 1000);
|
|
if (time_before(jiffies, expires))
|
|
return;
|
|
|
|
zpodd->zp_ready = true;
|
|
}
|
|
|
|
bool zpodd_zpready(struct ata_device *dev)
|
|
{
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
return zpodd->zp_ready;
|
|
}
|
|
|
|
/*
|
|
* Enable runtime wake capability through ACPI and set the powered_off flag,
|
|
* this flag will be used during resume to decide what operations are needed
|
|
* to take.
|
|
*
|
|
* Also, media poll needs to be silenced, so that it doesn't bring the ODD
|
|
* back to full power state every few seconds.
|
|
*/
|
|
void zpodd_enable_run_wake(struct ata_device *dev)
|
|
{
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
|
|
sdev_disable_disk_events(dev->sdev);
|
|
|
|
zpodd->powered_off = true;
|
|
device_set_run_wake(&dev->tdev, true);
|
|
acpi_pm_device_run_wake(&dev->tdev, true);
|
|
}
|
|
|
|
/* Disable runtime wake capability if it is enabled */
|
|
void zpodd_disable_run_wake(struct ata_device *dev)
|
|
{
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
|
|
if (zpodd->powered_off) {
|
|
acpi_pm_device_run_wake(&dev->tdev, false);
|
|
device_set_run_wake(&dev->tdev, false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Post power on processing after the ODD has been recovered. If the
|
|
* ODD wasn't powered off during suspend, it doesn't do anything.
|
|
*
|
|
* For drawer type ODD, if it is powered on due to user pressed the
|
|
* eject button, the tray needs to be ejected. This can only be done
|
|
* after the ODD has been recovered, i.e. link is initialized and
|
|
* device is able to process NON_DATA PIO command, as eject needs to
|
|
* send command for the ODD to process.
|
|
*
|
|
* The from_notify flag set in wake notification handler function
|
|
* zpodd_wake_dev represents if power on is due to user's action.
|
|
*
|
|
* For both types of ODD, several fields need to be reset.
|
|
*/
|
|
void zpodd_post_poweron(struct ata_device *dev)
|
|
{
|
|
struct zpodd *zpodd = dev->zpodd;
|
|
|
|
if (!zpodd->powered_off)
|
|
return;
|
|
|
|
zpodd->powered_off = false;
|
|
|
|
if (zpodd->from_notify) {
|
|
zpodd->from_notify = false;
|
|
if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
|
|
eject_tray(dev);
|
|
}
|
|
|
|
zpodd->zp_sampled = false;
|
|
zpodd->zp_ready = false;
|
|
|
|
sdev_enable_disk_events(dev->sdev);
|
|
}
|
|
|
|
static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
|
|
{
|
|
struct ata_device *ata_dev = context;
|
|
struct zpodd *zpodd = ata_dev->zpodd;
|
|
struct device *dev = &ata_dev->sdev->sdev_gendev;
|
|
|
|
if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
|
|
zpodd->from_notify = true;
|
|
pm_runtime_resume(dev);
|
|
}
|
|
}
|
|
|
|
static void ata_acpi_add_pm_notifier(struct ata_device *dev)
|
|
{
|
|
acpi_handle handle = ata_dev_acpi_handle(dev);
|
|
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
|
zpodd_wake_dev, dev);
|
|
}
|
|
|
|
static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
|
|
{
|
|
acpi_handle handle = ata_dev_acpi_handle(dev);
|
|
acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev);
|
|
}
|
|
|
|
void zpodd_init(struct ata_device *dev)
|
|
{
|
|
struct acpi_device *adev = ACPI_COMPANION(&dev->tdev);
|
|
enum odd_mech_type mech_type;
|
|
struct zpodd *zpodd;
|
|
|
|
if (dev->zpodd || !adev || !acpi_device_can_poweroff(adev))
|
|
return;
|
|
|
|
mech_type = zpodd_get_mech_type(dev);
|
|
if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
|
|
return;
|
|
|
|
zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
|
|
if (!zpodd)
|
|
return;
|
|
|
|
zpodd->mech_type = mech_type;
|
|
|
|
ata_acpi_add_pm_notifier(dev);
|
|
zpodd->dev = dev;
|
|
dev->zpodd = zpodd;
|
|
dev_pm_qos_expose_flags(&dev->tdev, 0);
|
|
}
|
|
|
|
void zpodd_exit(struct ata_device *dev)
|
|
{
|
|
ata_acpi_remove_pm_notifier(dev);
|
|
kfree(dev->zpodd);
|
|
dev->zpodd = NULL;
|
|
}
|