diff options
author | Laura Abbott <labbott@fedoraproject.org> | 2017-02-09 07:40:12 -0800 |
---|---|---|
committer | Laura Abbott <labbott@fedoraproject.org> | 2017-02-09 15:49:46 -0800 |
commit | 1bc62ddc806f05d6ce37097ab27629489e5308a6 (patch) | |
tree | 136074d7c86e68c9c93b75d5252ec2e15a34b412 | |
parent | 63440a0b28e1d96d33716be2784098dd473dfb3d (diff) | |
download | kernel-1bc62ddc806f05d6ce37097ab27629489e5308a6.tar.gz kernel-1bc62ddc806f05d6ce37097ab27629489e5308a6.tar.xz kernel-1bc62ddc806f05d6ce37097ab27629489e5308a6.zip |
Linux v4.9.9
Fix DMA on stack from 1-wire driver (rhbz 1415397)
-rw-r--r-- | 0001-x86-efi-always-map-first-physical-page-into-EFI-page.patch | 64 | ||||
-rw-r--r-- | PCI-ASPM-Handle-PCI-to-PCIe-bridges-as-roots-of-PCIe-hierarchies.patch | 90 | ||||
-rw-r--r-- | kernel.spec | 17 | ||||
-rw-r--r-- | sources | 2 | ||||
-rw-r--r-- | w1-ds2490-USB-transfer-buffers-need-to-be-DMAable.patch | 360 |
5 files changed, 370 insertions, 163 deletions
diff --git a/0001-x86-efi-always-map-first-physical-page-into-EFI-page.patch b/0001-x86-efi-always-map-first-physical-page-into-EFI-page.patch deleted file mode 100644 index 55baf9b52..000000000 --- a/0001-x86-efi-always-map-first-physical-page-into-EFI-page.patch +++ /dev/null @@ -1,64 +0,0 @@ -From fb1d9d3f95654f00c4156129f3cd90d3efe32d26 Mon Sep 17 00:00:00 2001 -From: Jiri Kosina <jkosina@suse.cz> -Date: Wed, 25 Jan 2017 20:52:33 +0100 -Subject: [PATCH] x86/efi: always map first physical page into EFI pagetables - -Commit 129766708 ("x86/efi: Only map RAM into EFI page tables if in -mixed-mode") stopped creating 1:1 mapping for all RAM in case of running -in native 64bit mode. - -It turns out though that there are 64bit EFI implementations in the wild -(this particular problem has been reported on Lenovo Yoga 710-11IKB) which -still make use of first physical page for their own private use (which is -what legacy BIOS used to do, but EFI specification doesn't grant any such -right to EFI BIOS ... oh well). - -In case there is no mapping for this particular frame in EFI pagetables, -as soon as firmware tries to make use of it, triple fault occurs and the -system reboots (in case of Yoga 710-11IKB this is very early during boot). - -Fix that by always mapping the first page of physical memory into EFI -pagetables. - -Note: just reverting 129766708 is not enough on v4.9-rc1+ to fix the -regression on affected hardware, as commit ab72a27da ("x86/efi: -Consolidate region mapping logic") later made the first physical frame not -to be mapped anyway. - -Fixes: 129766708 ("x86/efi: Only map RAM into EFI page tables if in mixed-mode") -Cc: stable@kernel.org # v4.8+ -Cc: Waiman Long <waiman.long@hpe.com> -Cc: Borislav Petkov <bp@suse.de> -Cc: Laura Abbott <labbott@redhat.com> -Cc: Vojtech Pavlik <vojtech@ucw.cz> -Reported-by: Hanka Pavlikova <hanka@ucw.cz> -Signed-off-by: Jiri Kosina <jkosina@suse.cz> ---- - arch/x86/platform/efi/efi_64.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c -index 319148bd4b05..02ae2abe8b8e 100644 ---- a/arch/x86/platform/efi/efi_64.c -+++ b/arch/x86/platform/efi/efi_64.c -@@ -269,6 +269,17 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) - efi_scratch.use_pgd = true; - - /* -+ * Certain firmware versions are way too sentimental and still believe -+ * they are exclusive and unquestionable owners of first physical page. -+ * Create 1:1 mapping for this page to avoid triple faults during early -+ * boot with such firmware. -+ */ -+ if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, _PAGE_RW)) { -+ pr_err("Failed to create 1:1 mapping of first page\n"); -+ return 1; -+ } -+ -+ /* - * When making calls to the firmware everything needs to be 1:1 - * mapped and addressable with 32-bit pointers. Map the kernel - * text and allocate a new stack because we can't rely on the --- -2.11.0 - diff --git a/PCI-ASPM-Handle-PCI-to-PCIe-bridges-as-roots-of-PCIe-hierarchies.patch b/PCI-ASPM-Handle-PCI-to-PCIe-bridges-as-roots-of-PCIe-hierarchies.patch deleted file mode 100644 index a76639e6a..000000000 --- a/PCI-ASPM-Handle-PCI-to-PCIe-bridges-as-roots-of-PCIe-hierarchies.patch +++ /dev/null @@ -1,90 +0,0 @@ -From patchwork Mon Jan 30 15:23:24 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: PCI/ASPM: Handle PCI-to-PCIe bridges as roots of PCIe hierarchies -From: Bjorn Helgaas <bhelgaas@google.com> -X-Patchwork-Id: 9545603 -Message-Id: <20170130152324.32437.37400.stgit@bhelgaas-glaptop.roam.corp.google.com> -To: linux-pci@vger.kernel.org -Cc: Jao Ching Chen <jcchen@pericom.com>, lists@ssl-mail.com, - linux-kernel@vger.kernel.org, Blake Moore <blake.moore@men.de>, - Takashi Iwai <tiwai@suse.com> -Date: Mon, 30 Jan 2017 09:23:24 -0600 - -In a struct pcie_link_state, link->root points to the pcie_link_state of -the root of the PCIe hierarchy. For the topmost link, this points to -itself (link->root = link). For others, we copy the pointer from the -parent (link->root = link->parent->root). - -Previously we recognized that Root Ports originated PCIe hierarchies, but -we treated PCI/PCI-X to PCIe Bridges as being in the middle of the -hierarchy, and when we tried to copy the pointer from link->parent->root, -there was no parent, and we dereferenced a NULL pointer: - - BUG: unable to handle kernel NULL pointer dereference at 0000000000000090 - IP: [<ffffffff9e424350>] pcie_aspm_init_link_state+0x170/0x820 - -Recognize that PCI/PCI-X to PCIe Bridges originate PCIe hierarchies just -like Root Ports do, so link->root for these devices should also point to -itself. - -Fixes: 51ebfc92b72b ("PCI: Enumerate switches below PCI-to-PCIe bridges") -Link: https://bugzilla.kernel.org/show_bug.cgi?id=193411 -Link: https://bugzilla.opensuse.org/show_bug.cgi?id=1022181 -Tested-by: lists@ssl-mail.com -Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> -CC: stable@vger.kernel.org # v4.2+ ---- - drivers/pci/pcie/aspm.c | 19 +++++++++++++------ - 1 file changed, 13 insertions(+), 6 deletions(-) - - --- -To unsubscribe from this list: send the line "unsubscribe linux-pci" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html - -diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c -index 17ac1dce3286..3dd8bcbb3011 100644 ---- a/drivers/pci/pcie/aspm.c -+++ b/drivers/pci/pcie/aspm.c -@@ -532,25 +532,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) - link = kzalloc(sizeof(*link), GFP_KERNEL); - if (!link) - return NULL; -+ - INIT_LIST_HEAD(&link->sibling); - INIT_LIST_HEAD(&link->children); - INIT_LIST_HEAD(&link->link); - link->pdev = pdev; -- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) { -+ -+ /* -+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe -+ * hierarchies. -+ */ -+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || -+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) { -+ link->root = link; -+ } else { - struct pcie_link_state *parent; -+ - parent = pdev->bus->parent->self->link_state; - if (!parent) { - kfree(link); - return NULL; - } -+ - link->parent = parent; -+ link->root = link->parent->root; - list_add(&link->link, &parent->children); - } -- /* Setup a pointer to the root port link */ -- if (!link->parent) -- link->root = link; -- else -- link->root = link->parent->root; - - list_add(&link->sibling, &link_list); - pdev->link_state = link; diff --git a/kernel.spec b/kernel.spec index 7722f60ca..74dccdf90 100644 --- a/kernel.spec +++ b/kernel.spec @@ -42,7 +42,7 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 101 +%global baserelease 100 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -54,7 +54,7 @@ Summary: The Linux kernel %if 0%{?released_kernel} # Do we have a -stable update to apply? -%define stable_update 8 +%define stable_update 9 # Set rpm version accordingly %if 0%{?stable_update} %define stablerev %{stable_update} @@ -631,15 +631,9 @@ Patch851: selinux-namespace-fix.patch #rhbz 1390308 Patch852: nouveau-add-maxwell-to-backlight-init.patch -#The saddest EFI firmware bug -Patch854: 0001-x86-efi-always-map-first-physical-page-into-EFI-page.patch - #CVE-2017-2596 rhbz 1417812 1417813 Patch855: kvm-fix-page-struct-leak-in-handle_vmon.patch -# rhbz 1418858 -Patch856: PCI-ASPM-Handle-PCI-to-PCIe-bridges-as-roots-of-PCIe-hierarchies.patch - #CVE-2017-5897 rhbz 1419848 1419851 Patch857: ip6_gre-fix-ip6gre_err-invalid-reads.patch @@ -650,6 +644,9 @@ Patch859: 2-2-media-dvb-usb-firmware-don-t-do-DMA-on-stack.patch #rhbz 1420276 Patch860: 0001-sctp-avoid-BUG_ON-on-sctp_wait_for_sndbuf.patch +#rhbz 1415397 +Patch861: w1-ds2490-USB-transfer-buffers-need-to-be-DMAable.patch + # END OF PATCH DEFINITIONS %endif @@ -2179,6 +2176,10 @@ fi # # %changelog +* Thu Feb 09 2017 Laura Abbott <labbott@fedoraproject.org> - 4.9.9-100 +- Linux v4.9.9 +- Fix DMA on stack from 1-wire driver (rhbz 1415397) + * Thu Feb 9 2017 Justin M. Forbes <jforbes@fedoraproject.org> - sctp: avoid BUG_ON on sctp_wait_for_sndbuf (rhbz 1420276) @@ -1,3 +1,3 @@ SHA512 (linux-4.9.tar.xz) = bf67ff812cc3cb7e5059e82cc5db0d9a7c5637f7ed9a42e4730c715bf7047c81ed3a571225f92a33ef0b6d65f35595bc32d773356646df2627da55e9bc7f1f1a SHA512 (perf-man-4.9.tar.gz) = d23bb3da1eadd6623fddbf4696948de7675f3dcf57c711a7427dd7ae111394f58d8f42752938bbea7cd219f1e7f6f116fc67a1c74f769711063940a065f37b99 -SHA512 (patch-4.9.8.xz) = 98400f66cdb4536dfefc8e88a60d9c84dd60e999e1f8547dd25c1e1476099fa76c4f6a0b6b45f91ddc6003ec6233a3e1b6e44b8172c8e89b3e9aca7ffde7b15e +SHA512 (patch-4.9.9.xz) = a7a2d44b83b00b20f1424d12af0f42e1c576d3053feacd13491ef185661fb1c789b9265c500b62f5ede39f57b72f358820000fa6c852a5f035e566ee1dfcd5d9 diff --git a/w1-ds2490-USB-transfer-buffers-need-to-be-DMAable.patch b/w1-ds2490-USB-transfer-buffers-need-to-be-DMAable.patch new file mode 100644 index 000000000..7e902b100 --- /dev/null +++ b/w1-ds2490-USB-transfer-buffers-need-to-be-DMAable.patch @@ -0,0 +1,360 @@ +From patchwork Wed Jan 18 20:31:11 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: w1: ds2490: USB transfer buffers need to be DMAable +From: "Maciej S. Szmigiero" <mail@maciej.szmigiero.name> +X-Patchwork-Id: 9524693 +Message-Id: <5ba98814-d0b0-fbd4-d631-eda3472f4017@maciej.szmigiero.name> +To: Evgeniy Polyakov <zbr@ioremap.net> +Cc: linux-kernel <linux-kernel@vger.kernel.org> +Date: Wed, 18 Jan 2017 21:31:11 +0100 + +ds2490 driver was doing USB transfers from / to buffers on a stack. +This is not permitted and made the driver non-working with vmapped stacks. + +Since all these transfers are done under the same bus_mutex lock we can +simply use shared buffers in a device private structure for two most common +of them. + +While we are at it, let's also fix a comparison between int and size_t in +ds9490r_search() which made the driver spin in this function if state +register get requests were failing. + +Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name> +Cc: stable@vger.kernel.org +--- + drivers/w1/masters/ds2490.c | 142 ++++++++++++++++++++++++++------------------ + 1 file changed, 84 insertions(+), 58 deletions(-) + +diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c +index 049a884a756f..59d74d1b47a8 100644 +--- a/drivers/w1/masters/ds2490.c ++++ b/drivers/w1/masters/ds2490.c +@@ -153,6 +153,9 @@ struct ds_device + */ + u16 spu_bit; + ++ u8 st_buf[ST_SIZE]; ++ u8 byte_buf; ++ + struct w1_bus_master master; + }; + +@@ -174,7 +177,6 @@ struct ds_status + u8 data_in_buffer_status; + u8 reserved1; + u8 reserved2; +- + }; + + static struct usb_device_id ds_id_table [] = { +@@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) + return err; + } + +-static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, +- unsigned char *buf, int size) +-{ +- int count, err; +- +- memset(st, 0, sizeof(*st)); +- +- count = 0; +- err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, +- dev->ep[EP_STATUS]), buf, size, &count, 1000); +- if (err < 0) { +- pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", +- dev->ep[EP_STATUS], err); +- return err; +- } +- +- if (count >= sizeof(*st)) +- memcpy(st, buf, sizeof(*st)); +- +- return count; +-} +- + static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) + { + pr_info("%45s: %8x\n", str, buf[off]); +@@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) + } + } + ++static int ds_recv_status(struct ds_device *dev, struct ds_status *st, ++ bool dump) ++{ ++ int count, err; ++ ++ if (st) ++ memset(st, 0, sizeof(*st)); ++ ++ count = 0; ++ err = usb_interrupt_msg(dev->udev, ++ usb_rcvintpipe(dev->udev, ++ dev->ep[EP_STATUS]), ++ dev->st_buf, sizeof(dev->st_buf), ++ &count, 1000); ++ if (err < 0) { ++ pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", ++ dev->ep[EP_STATUS], err); ++ return err; ++ } ++ ++ if (dump) ++ ds_dump_status(dev, dev->st_buf, count); ++ ++ if (st && count >= sizeof(*st)) ++ memcpy(st, dev->st_buf, sizeof(*st)); ++ ++ return count; ++} ++ + static void ds_reset_device(struct ds_device *dev) + { + ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0); +@@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev) + static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) + { + int count, err; +- struct ds_status st; + + /* Careful on size. If size is less than what is available in + * the input buffer, the device fails the bulk transfer and +@@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) + err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), + buf, size, &count, 1000); + if (err < 0) { +- u8 buf[ST_SIZE]; +- int count; +- + pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); + usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); +- +- count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); +- ds_dump_status(dev, buf, count); ++ ds_recv_status(dev, NULL, true); + return err; + } + +@@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit) + { + struct ds_status st; + int count = 0, err = 0; +- u8 buf[ST_SIZE]; + + do { + err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); +@@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) + err = ds_send_control(dev, CTL_RESUME_EXE, 0); + if (err) + break; +- err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); ++ err = ds_recv_status(dev, &st, false); + if (err) + break; + +@@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) + + static int ds_wait_status(struct ds_device *dev, struct ds_status *st) + { +- u8 buf[ST_SIZE]; + int err, count = 0; + + do { + st->status = 0; +- err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); ++ err = ds_recv_status(dev, st, false); + #if 0 + if (err >= 0) { + int i; + printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err); + for (i=0; i<err; ++i) +- printk("%02x ", buf[i]); ++ printk("%02x ", dev->st_buf[i]); + printk("\n"); + } + #endif +@@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) + * can do something with it). + */ + if (err > 16 || count >= 100 || err < 0) +- ds_dump_status(dev, buf, err); ++ ds_dump_status(dev, dev->st_buf, err); + + /* Extended data isn't an error. Well, a short is, but the dump + * would have already told the user that and we can't do anything +@@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) + { + int err; + struct ds_status st; +- u8 rbyte; + + err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte); + if (err) +@@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte) + if (err) + return err; + +- err = ds_recv_data(dev, &rbyte, sizeof(rbyte)); ++ err = ds_recv_data(dev, &dev->byte_buf, 1); + if (err < 0) + return err; + +- return !(byte == rbyte); ++ return !(byte == dev->byte_buf); + } + + static int ds_read_byte(struct ds_device *dev, u8 *byte) +@@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master, + int err; + u16 value, index; + struct ds_status st; +- u8 st_buf[ST_SIZE]; + int search_limit; + int found = 0; + int i; +@@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master, + /* FIFO 128 bytes, bulk packet size 64, read a multiple of the + * packet size. + */ +- u64 buf[2*64/8]; ++ const size_t bufsize = 2 * 64; ++ u64 *buf; ++ ++ buf = kmalloc(bufsize, GFP_KERNEL); ++ if (!buf) ++ return; + + mutex_lock(&master->bus_mutex); + +@@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master, + do { + schedule_timeout(jtime); + +- if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < +- sizeof(st)) { ++ err = ds_recv_status(dev, &st, false); ++ if (err < 0 || err < sizeof(st)) + break; +- } + + if (st.data_in_buffer_status) { + /* Bulk in can receive partial ids, but when it does +@@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master, + * bulk without first checking if status says there + * is data to read. + */ +- err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); ++ err = ds_recv_data(dev, (u8 *)buf, bufsize); + if (err < 0) + break; + for (i = 0; i < err/8; ++i) { +@@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master, + } + search_out: + mutex_unlock(&master->bus_mutex); ++ kfree(buf); + } + + #if 0 ++/* ++ * FIXME: if this disabled code is ever used in the future all ds_send_data() ++ * calls must be changed to use a DMAable buffer. ++ */ + static int ds_match_access(struct ds_device *dev, u64 init) + { + int err; +@@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init) + + static u8 ds9490r_touch_bit(void *data, u8 bit) + { +- u8 ret; + struct ds_device *dev = data; + +- if (ds_touch_bit(dev, bit, &ret)) ++ if (ds_touch_bit(dev, bit, &dev->byte_buf)) + return 0; + +- return ret; ++ return dev->byte_buf; + } + + #if 0 +@@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data) + { + struct ds_device *dev = data; + int err; +- u8 bit = 0; + +- err = ds_touch_bit(dev, 1, &bit); ++ err = ds_touch_bit(dev, 1, &dev->byte_buf); + if (err) + return 0; + +- return bit & 1; ++ return dev->byte_buf & 1; + } + #endif + +@@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data) + { + struct ds_device *dev = data; + int err; +- u8 byte = 0; + +- err = ds_read_byte(dev, &byte); ++ err = ds_read_byte(dev, &dev->byte_buf); + if (err) + return 0; + +- return byte; ++ return dev->byte_buf; + } + + static void ds9490r_write_block(void *data, const u8 *buf, int len) + { + struct ds_device *dev = data; ++ u8 *tbuf; ++ ++ if (len <= 0) ++ return; ++ ++ tbuf = kmalloc(len, GFP_KERNEL); ++ if (!tbuf) ++ return; + +- ds_write_block(dev, (u8 *)buf, len); ++ memcpy(tbuf, buf, len); ++ ds_write_block(dev, tbuf, len); ++ ++ kfree(tbuf); + } + + static u8 ds9490r_read_block(void *data, u8 *buf, int len) + { + struct ds_device *dev = data; + int err; ++ u8 *tbuf; + +- err = ds_read_block(dev, buf, len); +- if (err < 0) ++ if (len <= 0) ++ return 0; ++ ++ tbuf = kmalloc(len, GFP_KERNEL); ++ if (!tbuf) + return 0; + +- return len; ++ err = ds_read_block(dev, tbuf, len); ++ if (err >= 0) ++ memcpy(buf, tbuf, len); ++ ++ kfree(tbuf); ++ ++ return err >= 0 ? len : 0; + } + + static u8 ds9490r_reset(void *data) |