1
0
Files
Greg Kroah-Hartman dc9f99e1b7 Merge 4.9.318 into android-4.9-q
Changes in 4.9.318
	USB: new quirk for Dell Gen 2 devices
	ptrace/xtensa: Replace PT_SINGLESTEP with TIF_SINGLESTEP
	ptrace: Reimplement PTRACE_KILL by always sending SIGKILL
	btrfs: add "0x" prefix for unsupported optional features
	drm/virtio: fix NULL pointer dereference in virtio_gpu_conn_get_modes
	mwifiex: add mutex lock for call in mwifiex_dfs_chan_sw_work_queue
	b43legacy: Fix assigning negative value to unsigned variable
	b43: Fix assigning negative value to unsigned variable
	ipw2x00: Fix potential NULL dereference in libipw_xmit()
	ACPICA: Avoid cache flush inside virtual machines
	ALSA: jack: Access input_dev under mutex
	drm/amd/pm: fix double free in si_parse_power_table()
	ath9k: fix QCA9561 PA bias level
	media: cx25821: Fix the warning when removing the module
	scsi: megaraid: Fix error check return value of register_chrdev()
	drm/amd/pm: fix the compile warning
	ipv6: Don't send rs packets to the interface of ARPHRD_TUNNEL
	ASoC: dapm: Don't fold register value changes into notifications
	dma-debug: change allocation mode from GFP_NOWAIT to GFP_ATIOMIC
	ipmi:ssif: Check for NULL msg when handling events and messages
	openrisc: start CPU timer early in boot
	nvme-pci: fix a NULL pointer dereference in nvme_alloc_admin_tags
	ASoC: rt5645: Fix errorenous cleanup order
	media: exynos4-is: Fix compile warning
	rxrpc: Return an error to sendmsg if call failed
	eth: tg3: silence the GCC 12 array-bounds warning
	fs: jfs: fix possible NULL pointer dereference in dbFree()
	ARM: OMAP1: clock: Fix UART rate reporting algorithm
	fat: add ratelimit to fat*_ent_bread()
	ARM: versatile: Add missing of_node_put in dcscb_init
	ARM: dts: exynos: add atmel,24c128 fallback to Samsung EEPROM
	ARM: hisi: Add missing of_node_put after of_find_compatible_node
	PCI: Avoid pci_dev_lock() AB/BA deadlock with sriov_numvfs_store()
	powerpc/xics: fix refcount leak in icp_opal_init()
	macintosh/via-pmu: Fix build failure when CONFIG_INPUT is disabled
	drm: fix EDID struct for old ARM OABI format
	ASoC: mediatek: Fix error handling in mt8173_max98090_dev_probe
	x86/delay: Fix the wrong asm constraint in delay_loop()
	drm/mediatek: Fix mtk_cec_mask()
	spi: spi-ti-qspi: Fix return value handling of wait_for_completion_timeout
	NFC: NULL out the dev->rfkill to prevent UAF
	HID: hid-led: fix maximum brightness for Dream Cheeky
	spi: img-spfi: Fix pm_runtime_get_sync() error checking
	ath9k_htc: fix potential out of bounds access with invalid rxstatus->rs_keyix
	inotify: show inotify mask flags in proc fdinfo
	x86/pm: Fix false positive kmemleak report in msr_build_context()
	drm/msm/dsi: fix error checks and return values for DSI xmit functions
	drm/msm/hdmi: check return value after calling platform_get_resource_byname()
	drm/rockchip: vop: fix possible null-ptr-deref in vop_bind()
	x86/mm: Cleanup the control_va_addr_alignment() __setup handler
	drm/msm: return an error pointer in msm_gem_prime_get_sg_table()
	media: uvcvideo: Fix missing check to determine if element is found in list
	ASoC: mxs-saif: Fix refcount leak in mxs_saif_probe
	regulator: pfuze100: Fix refcount leak in pfuze_parse_regulators_dt
	media: exynos4-is: Change clk_disable to clk_disable_unprepare
	media: pvrusb2: fix array-index-out-of-bounds in pvr2_i2c_core_init
	Bluetooth: fix dangling sco_conn and use-after-free in sco_sock_timeout
	m68k: math-emu: Fix dependencies of math emulation support
	sctp: read sk->sk_bound_dev_if once in sctp_rcv()
	ASoC: wm2000: fix missing clk_disable_unprepare() on error in wm2000_anc_transition()
	rxrpc: Fix listen() setting the bar too high for the prealloc rings
	rxrpc: Don't try to resend the request if we're receiving the reply
	soc: qcom: smp2p: Fix missing of_node_put() in smp2p_parse_ipc
	soc: qcom: smsm: Fix missing of_node_put() in smsm_parse_ipc
	mfd: ipaq-micro: Fix error check return value of platform_get_irq()
	scsi: fcoe: Fix Wstringop-overflow warnings in fcoe_wwn_from_mac()
	drivers/base/node.c: fix compaction sysfs file leak
	powerpc/8xx: export 'cpm_setbrg' for modules
	powerpc/idle: Fix return value of __setup() handler
	powerpc/4xx/cpm: Fix return value of __setup() handler
	tty: fix deadlock caused by calling printk() under tty_port->lock
	Input: sparcspkr - fix refcount leak in bbc_beep_probe
	video: fbdev: clcdfb: Fix refcount leak in clcdfb_of_vram_setup
	iommu/amd: Increase timeout waiting for GA log enablement
	wifi: mac80211: fix use-after-free in chanctx code
	iwlwifi: mvm: fix assert 1F04 upon reconfig
	fs-writeback: writeback_sb_inodes:Recalculate 'wrote' according skipped pages
	ext4: fix use-after-free in ext4_rename_dir_prepare
	ext4: fix bug_on in ext4_writepages
	ext4: verify dir block before splitting it
	dlm: fix plock invalid read
	dlm: fix missing lkb refcount handling
	ocfs2: dlmfs: fix error handling of user_dlm_destroy_lock
	scsi: dc395x: Fix a missing check on list iterator
	scsi: ufs: qcom: Add a readl() to make sure ref_clk gets enabled
	drm/amdgpu/cs: make commands with 0 chunks illegal behaviour.
	drm/bridge: analogix_dp: Grab runtime PM reference for DP-AUX
	md: fix an incorrect NULL check in does_sb_need_changing
	md: fix an incorrect NULL check in md_reload_sb
	RDMA/hfi1: Fix potential integer multiplication overflow errors
	irqchip/armada-370-xp: Do not touch Performance Counter Overflow on A375, A38x, A39x
	irqchip: irq-xtensa-mx: fix initial IRQ affinity
	mac80211: upgrade passive scan to active scan on DFS channels after beacon rx
	um: chan_user: Fix winch_tramp() return value
	um: Fix out-of-bounds read in LDT setup
	iommu/msm: Fix an incorrect NULL check on list iterator
	nodemask.h: fix compilation error with GCC12
	hugetlb: fix huge_pmd_unshare address update
	rtl818x: Prevent using not initialized queues
	ASoC: rt5514: Fix event generation for "DSP Voice Wake Up" control
	carl9170: tx: fix an incorrect use of list iterator
	gma500: fix an incorrect NULL check on list iterator
	docs/conf.py: Cope with removal of language=None in Sphinx 5.0.0
	dt-bindings: gpio: altera: correct interrupt-cells
	RDMA/rxe: Generate a completion for unsupported/invalid opcode
	MIPS: IP27: Remove incorrect `cpu_has_fpu' override
	netfilter: nf_tables: disallow non-stateful expression in sets earlier
	pcmcia: db1xxx_ss: restrict to MIPS_DB1XXX boards
	staging: greybus: codecs: fix type confusion of list iterator variable
	usb: usbip: fix a refcount leak in stub_probe()
	usb: usbip: add missing device lock on tweak configuration cmd
	USB: storage: karma: fix rio_karma_init return
	pwm: lp3943: Fix duty calculation in case period was clamped
	rpmsg: qcom_smd: Fix irq_of_parse_and_map() return value
	rtc: mt6397: check return value after calling platform_get_resource()
	serial: meson: acquire port->lock in startup()
	serial: digicolor-usart: Don't allow CS5-6
	serial: txx9: Don't allow CS5-6
	serial: sh-sci: Don't allow CS5-6
	serial: st-asc: Sanitize CSIZE and correct PARENB for CS7
	firmware: dmi-sysfs: Fix memory leak in dmi_sysfs_register_handle
	clocksource/drivers/oxnas-rps: Fix irq_of_parse_and_map() return value
	net: ethernet: mtk_eth_soc: out of bounds read in mtk_hwlro_get_fdir_entry()
	modpost: fix removing numeric suffixes
	jffs2: fix memory leak in jffs2_do_fill_super
	tcp: tcp_rtx_synack() can be called from process context
	tracing: Avoid adding tracer option before update_tracer_options
	i2c: cadence: Increase timeout per message if necessary
	m68knommu: set ZERO_PAGE() to the allocated zeroed page
	m68knommu: fix undefined reference to `_init_sp'
	video: fbdev: pxa3xx-gcu: release the resources correctly in pxa3xx_gcu_probe/remove()
	net: fix nla_strcmp to handle more then one trailing null character
	ata: pata_octeon_cf: Fix refcount leak in octeon_cf_probe
	net/mlx4_en: Fix wrong return value on ioctl EEPROM query failure
	SUNRPC: Fix the calculation of xdr->end in xdr_get_next_encode_buffer()
	net: xfrm: unexport __init-annotated xfrm4_protocol_init()
	net: altera: Fix refcount leak in altera_tse_mdio_create
	iio: dummy: iio_simple_dummy: check the return value of kstrdup()
	lkdtm/usercopy: Expand size of "out of frame" object
	tty: synclink_gt: Fix null-pointer-dereference in slgt_clean()
	tty: Fix a possible resource leak in icom_probe
	drivers: staging: rtl8192e: Fix deadlock in rtllib_beacons_stop()
	USB: host: isp116x: check return value after calling platform_get_resource()
	drivers: tty: serial: Fix deadlock in sa1100_set_termios()
	drivers: usb: host: Fix deadlock in oxu_bus_suspend()
	USB: hcd-pci: Fully suspend across freeze/thaw cycle
	usb: dwc2: gadget: don't reset gadget's driver->bus
	misc: rtsx: set NULL intfdata when probe fails
	clocksource/drivers/sp804: Avoid error on multiple instances
	staging: rtl8712: fix uninit-value in r871xu_drv_init()
	serial: msm_serial: disable interrupts in __msm_console_write()
	md: protect md_unregister_thread from reentrancy
	Revert "net: af_key: add check for pfkey_broadcast in function pfkey_process"
	drm/radeon: fix a possible null pointer dereference
	modpost: fix undefined behavior of is_arm_mapping_symbol()
	nodemask: Fix return values to be unsigned
	vringh: Fix loop descriptors check in the indirect cases
	ALSA: hda/conexant - Fix loopback issue with CX20632
	cifs: return errors during session setup during reconnects
	ata: libata-transport: fix {dma|pio|xfer}_mode sysfs files
	nfc: st21nfca: fix incorrect validating logic in EVT_TRANSACTION
	nfc: st21nfca: fix memory leaks in EVT_TRANSACTION handling
	Input: bcm5974 - set missing URB_NO_TRANSFER_DMA_MAP urb flag
	powerpc/32: Fix overread/overwrite of thread_struct via ptrace
	mtd: cfi_cmdset_0002: Move and rename chip_check/chip_ready/chip_good_for_write
	mtd: cfi_cmdset_0002: Use chip_ready() for write on S29GL064N
	PCI: qcom: Fix unbalanced PHY init on probe errors
	tcp: fix tcp_mtup_probe_success vs wrong snd_cwnd
	Linux 4.9.318

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I2028f3dcd422886bf997ec7e1d2ae86515f1bfb9
2022-06-20 23:23:32 +03:00

809 lines
20 KiB
C

/*
* Copyright 2008 ioogle, Inc. All rights reserved.
* Released under GPL v2.
*
* Libata transport class.
*
* The ATA transport class contains common code to deal with ATA HBAs,
* an approximated representation of ATA topologies in the driver model,
* and various sysfs attributes to expose these topologies and management
* interfaces to user-space.
*
* There are 3 objects defined in in this class:
* - ata_port
* - ata_link
* - ata_device
* Each port has a link object. Each link can have up to two devices for PATA
* and generally one for SATA.
* If there is SATA port multiplier [PMP], 15 additional ata_link object are
* created.
*
* These objects are created when the ata host is initialized and when a PMP is
* found. They are removed only when the HBA is removed, cleaned before the
* error handler runs.
*/
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <scsi/scsi_transport.h>
#include <linux/libata.h>
#include <linux/hdreg.h>
#include <linux/uaccess.h>
#include <linux/pm_runtime.h>
#include "libata.h"
#include "libata-transport.h"
#define ATA_PORT_ATTRS 3
#define ATA_LINK_ATTRS 3
#define ATA_DEV_ATTRS 9
struct scsi_transport_template;
struct scsi_transport_template *ata_scsi_transport_template;
struct ata_internal {
struct scsi_transport_template t;
struct device_attribute private_port_attrs[ATA_PORT_ATTRS];
struct device_attribute private_link_attrs[ATA_LINK_ATTRS];
struct device_attribute private_dev_attrs[ATA_DEV_ATTRS];
struct transport_container link_attr_cont;
struct transport_container dev_attr_cont;
/*
* The array of null terminated pointers to attributes
* needed by scsi_sysfs.c
*/
struct device_attribute *link_attrs[ATA_LINK_ATTRS + 1];
struct device_attribute *port_attrs[ATA_PORT_ATTRS + 1];
struct device_attribute *dev_attrs[ATA_DEV_ATTRS + 1];
};
#define to_ata_internal(tmpl) container_of(tmpl, struct ata_internal, t)
#define tdev_to_device(d) \
container_of((d), struct ata_device, tdev)
#define transport_class_to_dev(dev) \
tdev_to_device((dev)->parent)
#define tdev_to_link(d) \
container_of((d), struct ata_link, tdev)
#define transport_class_to_link(dev) \
tdev_to_link((dev)->parent)
#define tdev_to_port(d) \
container_of((d), struct ata_port, tdev)
#define transport_class_to_port(dev) \
tdev_to_port((dev)->parent)
/* Device objects are always created whit link objects */
static int ata_tdev_add(struct ata_device *dev);
static void ata_tdev_delete(struct ata_device *dev);
/*
* Hack to allow attributes of the same name in different objects.
*/
#define ATA_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
struct device_attribute device_attr_##_prefix##_##_name = \
__ATTR(_name,_mode,_show,_store)
#define ata_bitfield_name_match(title, table) \
static ssize_t \
get_ata_##title##_names(u32 table_key, char *buf) \
{ \
char *prefix = ""; \
ssize_t len = 0; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(table); i++) { \
if (table[i].value & table_key) { \
len += sprintf(buf + len, "%s%s", \
prefix, table[i].name); \
prefix = ", "; \
} \
} \
len += sprintf(buf + len, "\n"); \
return len; \
}
#define ata_bitfield_name_search(title, table) \
static ssize_t \
get_ata_##title##_names(u32 table_key, char *buf) \
{ \
ssize_t len = 0; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(table); i++) { \
if (table[i].value == table_key) { \
len += sprintf(buf + len, "%s", \
table[i].name); \
break; \
} \
} \
len += sprintf(buf + len, "\n"); \
return len; \
}
static struct {
u32 value;
char *name;
} ata_class_names[] = {
{ ATA_DEV_UNKNOWN, "unknown" },
{ ATA_DEV_ATA, "ata" },
{ ATA_DEV_ATA_UNSUP, "ata" },
{ ATA_DEV_ATAPI, "atapi" },
{ ATA_DEV_ATAPI_UNSUP, "atapi" },
{ ATA_DEV_PMP, "pmp" },
{ ATA_DEV_PMP_UNSUP, "pmp" },
{ ATA_DEV_SEMB, "semb" },
{ ATA_DEV_SEMB_UNSUP, "semb" },
{ ATA_DEV_ZAC, "zac" },
{ ATA_DEV_NONE, "none" }
};
ata_bitfield_name_search(class, ata_class_names)
static struct {
u32 value;
char *name;
} ata_err_names[] = {
{ AC_ERR_DEV, "DeviceError" },
{ AC_ERR_HSM, "HostStateMachineError" },
{ AC_ERR_TIMEOUT, "Timeout" },
{ AC_ERR_MEDIA, "MediaError" },
{ AC_ERR_ATA_BUS, "BusError" },
{ AC_ERR_HOST_BUS, "HostBusError" },
{ AC_ERR_SYSTEM, "SystemError" },
{ AC_ERR_INVALID, "InvalidArg" },
{ AC_ERR_OTHER, "Unknown" },
{ AC_ERR_NODEV_HINT, "NoDeviceHint" },
{ AC_ERR_NCQ, "NCQError" }
};
ata_bitfield_name_match(err, ata_err_names)
static struct {
u32 value;
char *name;
} ata_xfer_names[] = {
{ XFER_UDMA_7, "XFER_UDMA_7" },
{ XFER_UDMA_6, "XFER_UDMA_6" },
{ XFER_UDMA_5, "XFER_UDMA_5" },
{ XFER_UDMA_4, "XFER_UDMA_4" },
{ XFER_UDMA_3, "XFER_UDMA_3" },
{ XFER_UDMA_2, "XFER_UDMA_2" },
{ XFER_UDMA_1, "XFER_UDMA_1" },
{ XFER_UDMA_0, "XFER_UDMA_0" },
{ XFER_MW_DMA_4, "XFER_MW_DMA_4" },
{ XFER_MW_DMA_3, "XFER_MW_DMA_3" },
{ XFER_MW_DMA_2, "XFER_MW_DMA_2" },
{ XFER_MW_DMA_1, "XFER_MW_DMA_1" },
{ XFER_MW_DMA_0, "XFER_MW_DMA_0" },
{ XFER_SW_DMA_2, "XFER_SW_DMA_2" },
{ XFER_SW_DMA_1, "XFER_SW_DMA_1" },
{ XFER_SW_DMA_0, "XFER_SW_DMA_0" },
{ XFER_PIO_6, "XFER_PIO_6" },
{ XFER_PIO_5, "XFER_PIO_5" },
{ XFER_PIO_4, "XFER_PIO_4" },
{ XFER_PIO_3, "XFER_PIO_3" },
{ XFER_PIO_2, "XFER_PIO_2" },
{ XFER_PIO_1, "XFER_PIO_1" },
{ XFER_PIO_0, "XFER_PIO_0" },
{ XFER_PIO_SLOW, "XFER_PIO_SLOW" }
};
ata_bitfield_name_search(xfer, ata_xfer_names)
/*
* ATA Port attributes
*/
#define ata_port_show_simple(field, name, format_string, cast) \
static ssize_t \
show_ata_port_##name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_port *ap = transport_class_to_port(dev); \
\
return snprintf(buf, 20, format_string, cast ap->field); \
}
#define ata_port_simple_attr(field, name, format_string, type) \
ata_port_show_simple(field, name, format_string, (type)) \
static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL)
ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int);
ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long);
ata_port_simple_attr(local_port_no, port_no, "%u\n", unsigned int);
static DECLARE_TRANSPORT_CLASS(ata_port_class,
"ata_port", NULL, NULL, NULL);
static void ata_tport_release(struct device *dev)
{
}
/**
* ata_is_port -- check if a struct device represents a ATA port
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA Port, %0 else
*/
static int ata_is_port(const struct device *dev)
{
return dev->release == ata_tport_release;
}
static int ata_tport_match(struct attribute_container *cont,
struct device *dev)
{
if (!ata_is_port(dev))
return 0;
return &ata_scsi_transport_template->host_attrs.ac == cont;
}
/**
* ata_tport_delete -- remove ATA PORT
* @port: ATA PORT to remove
*
* Removes the specified ATA PORT. Remove the associated link as well.
*/
void ata_tport_delete(struct ata_port *ap)
{
struct device *dev = &ap->tdev;
ata_tlink_delete(&ap->link);
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
put_device(dev);
}
/** ata_tport_add - initialize a transport ATA port structure
*
* @parent: parent device
* @ap: existing ata_port structure
*
* Initialize a ATA port structure for sysfs. It will be added to the device
* tree below the device specified by @parent which could be a PCI device.
*
* Returns %0 on success
*/
int ata_tport_add(struct device *parent,
struct ata_port *ap)
{
int error;
struct device *dev = &ap->tdev;
device_initialize(dev);
dev->type = &ata_port_type;
dev->parent = parent;
dev->release = ata_tport_release;
dev_set_name(dev, "ata%d", ap->print_id);
transport_setup_device(dev);
ata_acpi_bind_port(ap);
error = device_add(dev);
if (error) {
goto tport_err;
}
device_enable_async_suspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_forbid(dev);
transport_add_device(dev);
transport_configure_device(dev);
error = ata_tlink_add(&ap->link);
if (error) {
goto tport_link_err;
}
return 0;
tport_link_err:
transport_remove_device(dev);
device_del(dev);
tport_err:
transport_destroy_device(dev);
put_device(dev);
return error;
}
/*
* ATA link attributes
*/
static int noop(int x) { return x; }
#define ata_link_show_linkspeed(field, format) \
static ssize_t \
show_ata_link_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_link *link = transport_class_to_link(dev); \
\
return sprintf(buf, "%s\n", sata_spd_string(format(link->field))); \
}
#define ata_link_linkspeed_attr(field, format) \
ata_link_show_linkspeed(field, format) \
static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL)
ata_link_linkspeed_attr(hw_sata_spd_limit, fls);
ata_link_linkspeed_attr(sata_spd_limit, fls);
ata_link_linkspeed_attr(sata_spd, noop);
static DECLARE_TRANSPORT_CLASS(ata_link_class,
"ata_link", NULL, NULL, NULL);
static void ata_tlink_release(struct device *dev)
{
}
/**
* ata_is_link -- check if a struct device represents a ATA link
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA link, %0 else
*/
static int ata_is_link(const struct device *dev)
{
return dev->release == ata_tlink_release;
}
static int ata_tlink_match(struct attribute_container *cont,
struct device *dev)
{
struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
if (!ata_is_link(dev))
return 0;
return &i->link_attr_cont.ac == cont;
}
/**
* ata_tlink_delete -- remove ATA LINK
* @port: ATA LINK to remove
*
* Removes the specified ATA LINK. remove associated ATA device(s) as well.
*/
void ata_tlink_delete(struct ata_link *link)
{
struct device *dev = &link->tdev;
struct ata_device *ata_dev;
ata_for_each_dev(ata_dev, link, ALL) {
ata_tdev_delete(ata_dev);
}
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
put_device(dev);
}
/**
* ata_tlink_add -- initialize a transport ATA link structure
* @link: allocated ata_link structure.
*
* Initialize an ATA LINK structure for sysfs. It will be added in the
* device tree below the ATA PORT it belongs to.
*
* Returns %0 on success
*/
int ata_tlink_add(struct ata_link *link)
{
struct device *dev = &link->tdev;
struct ata_port *ap = link->ap;
struct ata_device *ata_dev;
int error;
device_initialize(dev);
dev->parent = &ap->tdev;
dev->release = ata_tlink_release;
if (ata_is_host_link(link))
dev_set_name(dev, "link%d", ap->print_id);
else
dev_set_name(dev, "link%d.%d", ap->print_id, link->pmp);
transport_setup_device(dev);
error = device_add(dev);
if (error) {
goto tlink_err;
}
transport_add_device(dev);
transport_configure_device(dev);
ata_for_each_dev(ata_dev, link, ALL) {
error = ata_tdev_add(ata_dev);
if (error) {
goto tlink_dev_err;
}
}
return 0;
tlink_dev_err:
while (--ata_dev >= link->device) {
ata_tdev_delete(ata_dev);
}
transport_remove_device(dev);
device_del(dev);
tlink_err:
transport_destroy_device(dev);
put_device(dev);
return error;
}
/*
* ATA device attributes
*/
#define ata_dev_show_class(title, field) \
static ssize_t \
show_ata_dev_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_device *ata_dev = transport_class_to_dev(dev); \
\
return get_ata_##title##_names(ata_dev->field, buf); \
}
#define ata_dev_attr(title, field) \
ata_dev_show_class(title, field) \
static DEVICE_ATTR(field, S_IRUGO, show_ata_dev_##field, NULL)
ata_dev_attr(class, class);
ata_dev_attr(xfer, pio_mode);
ata_dev_attr(xfer, dma_mode);
ata_dev_attr(xfer, xfer_mode);
#define ata_dev_show_simple(field, format_string, cast) \
static ssize_t \
show_ata_dev_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_device *ata_dev = transport_class_to_dev(dev); \
\
return snprintf(buf, 20, format_string, cast ata_dev->field); \
}
#define ata_dev_simple_attr(field, format_string, type) \
ata_dev_show_simple(field, format_string, (type)) \
static DEVICE_ATTR(field, S_IRUGO, \
show_ata_dev_##field, NULL)
ata_dev_simple_attr(spdn_cnt, "%d\n", int);
struct ata_show_ering_arg {
char* buf;
int written;
};
static int ata_show_ering(struct ata_ering_entry *ent, void *void_arg)
{
struct ata_show_ering_arg* arg = void_arg;
u64 seconds;
u32 rem;
seconds = div_u64_rem(ent->timestamp, HZ, &rem);
arg->written += sprintf(arg->buf + arg->written,
"[%5llu.%09lu]", seconds,
rem * NSEC_PER_SEC / HZ);
arg->written += get_ata_err_names(ent->err_mask,
arg->buf + arg->written);
return 0;
}
static ssize_t
show_ata_dev_ering(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
struct ata_show_ering_arg arg = { buf, 0 };
ata_ering_map(&ata_dev->ering, ata_show_ering, &arg);
return arg.written;
}
static DEVICE_ATTR(ering, S_IRUGO, show_ata_dev_ering, NULL);
static ssize_t
show_ata_dev_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
int written = 0, i = 0;
if (ata_dev->class == ATA_DEV_PMP)
return 0;
for(i=0;i<ATA_ID_WORDS;i++) {
written += snprintf(buf+written, 20, "%04x%c",
ata_dev->id[i],
((i+1) & 7) ? ' ' : '\n');
}
return written;
}
static DEVICE_ATTR(id, S_IRUGO, show_ata_dev_id, NULL);
static ssize_t
show_ata_dev_gscr(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
int written = 0, i = 0;
if (ata_dev->class != ATA_DEV_PMP)
return 0;
for(i=0;i<SATA_PMP_GSCR_DWORDS;i++) {
written += snprintf(buf+written, 20, "%08x%c",
ata_dev->gscr[i],
((i+1) & 3) ? ' ' : '\n');
}
if (SATA_PMP_GSCR_DWORDS & 3)
buf[written-1] = '\n';
return written;
}
static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL);
static ssize_t
show_ata_dev_trim(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
unsigned char *mode;
if (!ata_id_has_trim(ata_dev->id))
mode = "unsupported";
else if (ata_dev->horkage & ATA_HORKAGE_NOTRIM)
mode = "forced_unsupported";
else if (ata_dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM)
mode = "forced_unqueued";
else if (ata_fpdma_dsm_supported(ata_dev))
mode = "queued";
else
mode = "unqueued";
return snprintf(buf, 20, "%s\n", mode);
}
static DEVICE_ATTR(trim, S_IRUGO, show_ata_dev_trim, NULL);
static DECLARE_TRANSPORT_CLASS(ata_dev_class,
"ata_device", NULL, NULL, NULL);
static void ata_tdev_release(struct device *dev)
{
}
/**
* ata_is_ata_dev -- check if a struct device represents a ATA device
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA device, %0 else
*/
static int ata_is_ata_dev(const struct device *dev)
{
return dev->release == ata_tdev_release;
}
static int ata_tdev_match(struct attribute_container *cont,
struct device *dev)
{
struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
if (!ata_is_ata_dev(dev))
return 0;
return &i->dev_attr_cont.ac == cont;
}
/**
* ata_tdev_free -- free a ATA LINK
* @dev: ATA PHY to free
*
* Frees the specified ATA PHY.
*
* Note:
* This function must only be called on a PHY that has not
* successfully been added using ata_tdev_add().
*/
static void ata_tdev_free(struct ata_device *dev)
{
transport_destroy_device(&dev->tdev);
put_device(&dev->tdev);
}
/**
* ata_tdev_delete -- remove ATA device
* @port: ATA PORT to remove
*
* Removes the specified ATA device.
*/
static void ata_tdev_delete(struct ata_device *ata_dev)
{
struct device *dev = &ata_dev->tdev;
transport_remove_device(dev);
device_del(dev);
ata_tdev_free(ata_dev);
}
/**
* ata_tdev_add -- initialize a transport ATA device structure.
* @ata_dev: ata_dev structure.
*
* Initialize an ATA device structure for sysfs. It will be added in the
* device tree below the ATA LINK device it belongs to.
*
* Returns %0 on success
*/
static int ata_tdev_add(struct ata_device *ata_dev)
{
struct device *dev = &ata_dev->tdev;
struct ata_link *link = ata_dev->link;
struct ata_port *ap = link->ap;
int error;
device_initialize(dev);
dev->parent = &link->tdev;
dev->release = ata_tdev_release;
if (ata_is_host_link(link))
dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno);
else
dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp);
transport_setup_device(dev);
ata_acpi_bind_dev(ata_dev);
error = device_add(dev);
if (error) {
ata_tdev_free(ata_dev);
return error;
}
transport_add_device(dev);
transport_configure_device(dev);
return 0;
}
/*
* Setup / Teardown code
*/
#define SETUP_TEMPLATE(attrb, field, perm, test) \
i->private_##attrb[count] = dev_attr_##field; \
i->private_##attrb[count].attr.mode = perm; \
i->attrb[count] = &i->private_##attrb[count]; \
if (test) \
count++
#define SETUP_LINK_ATTRIBUTE(field) \
SETUP_TEMPLATE(link_attrs, field, S_IRUGO, 1)
#define SETUP_PORT_ATTRIBUTE(field) \
SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1)
#define SETUP_DEV_ATTRIBUTE(field) \
SETUP_TEMPLATE(dev_attrs, field, S_IRUGO, 1)
/**
* ata_attach_transport -- instantiate ATA transport template
*/
struct scsi_transport_template *ata_attach_transport(void)
{
struct ata_internal *i;
int count;
i = kzalloc(sizeof(struct ata_internal), GFP_KERNEL);
if (!i)
return NULL;
i->t.eh_strategy_handler = ata_scsi_error;
i->t.eh_timed_out = ata_scsi_timed_out;
i->t.user_scan = ata_scsi_user_scan;
i->t.host_attrs.ac.attrs = &i->port_attrs[0];
i->t.host_attrs.ac.class = &ata_port_class.class;
i->t.host_attrs.ac.match = ata_tport_match;
transport_container_register(&i->t.host_attrs);
i->link_attr_cont.ac.class = &ata_link_class.class;
i->link_attr_cont.ac.attrs = &i->link_attrs[0];
i->link_attr_cont.ac.match = ata_tlink_match;
transport_container_register(&i->link_attr_cont);
i->dev_attr_cont.ac.class = &ata_dev_class.class;
i->dev_attr_cont.ac.attrs = &i->dev_attrs[0];
i->dev_attr_cont.ac.match = ata_tdev_match;
transport_container_register(&i->dev_attr_cont);
count = 0;
SETUP_PORT_ATTRIBUTE(nr_pmp_links);
SETUP_PORT_ATTRIBUTE(idle_irq);
SETUP_PORT_ATTRIBUTE(port_no);
BUG_ON(count > ATA_PORT_ATTRS);
i->port_attrs[count] = NULL;
count = 0;
SETUP_LINK_ATTRIBUTE(hw_sata_spd_limit);
SETUP_LINK_ATTRIBUTE(sata_spd_limit);
SETUP_LINK_ATTRIBUTE(sata_spd);
BUG_ON(count > ATA_LINK_ATTRS);
i->link_attrs[count] = NULL;
count = 0;
SETUP_DEV_ATTRIBUTE(class);
SETUP_DEV_ATTRIBUTE(pio_mode);
SETUP_DEV_ATTRIBUTE(dma_mode);
SETUP_DEV_ATTRIBUTE(xfer_mode);
SETUP_DEV_ATTRIBUTE(spdn_cnt);
SETUP_DEV_ATTRIBUTE(ering);
SETUP_DEV_ATTRIBUTE(id);
SETUP_DEV_ATTRIBUTE(gscr);
SETUP_DEV_ATTRIBUTE(trim);
BUG_ON(count > ATA_DEV_ATTRS);
i->dev_attrs[count] = NULL;
return &i->t;
}
/**
* ata_release_transport -- release ATA transport template instance
* @t: transport template instance
*/
void ata_release_transport(struct scsi_transport_template *t)
{
struct ata_internal *i = to_ata_internal(t);
transport_container_unregister(&i->t.host_attrs);
transport_container_unregister(&i->link_attr_cont);
transport_container_unregister(&i->dev_attr_cont);
kfree(i);
}
__init int libata_transport_init(void)
{
int error;
error = transport_class_register(&ata_link_class);
if (error)
goto out_unregister_transport;
error = transport_class_register(&ata_port_class);
if (error)
goto out_unregister_link;
error = transport_class_register(&ata_dev_class);
if (error)
goto out_unregister_port;
return 0;
out_unregister_port:
transport_class_unregister(&ata_port_class);
out_unregister_link:
transport_class_unregister(&ata_link_class);
out_unregister_transport:
return error;
}
void __exit libata_transport_exit(void)
{
transport_class_unregister(&ata_link_class);
transport_class_unregister(&ata_port_class);
transport_class_unregister(&ata_dev_class);
}