From dd05add7b2b6004c5fa3a1f276f56d8668f86b65 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Sat, 26 Sep 2026 19:20:30 -0300 Subject: netfs: zero the tail of a short DIO/unbuffered read The buffered read collector zero-fills the tail of a short read that stops below the inode's i_size (netfs_clear_unread()), so a read that races an extending write still returns the expected number of bytes. The non-buffered collector path does no such thing: it just records how much was transferred. Add the same zero-fill for the non-buffered case, gated on NETFS_SREQ_CLEAR_TAIL: a subreq's source sets that flag when a short result from it is known to be safe to treat as a hole, as opposed to NETFS_SREQ_HIT_EOF, which means the read genuinely ran off the end of the file and should be reported short as-is. Only CLEAR_TAIL should zero-fill here; a real EOF must stay a real short read. No source currently sets CLEAR_TAIL on an unbuffered/DIO subrequest, so this is inert on its own -- a following change teaches cifs to set it in the one case that needs it. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") Reviewed-by: David Howells Reviewed-by: Namjae Jeon Signed-off-by: Paulo Alcantara Cc: Christian Brauner Cc: Matthew Wilcox Cc: Ronnie Sahlberg Cc: Shyam Prasad N Cc: Tom Talpey Cc: Bharath SM Cc: stable@vger.kernel.org --- drivers/hid/hid-roccat-lua.c | 215 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 drivers/hid/hid-roccat-lua.c (limited to 'drivers/hid/hid-roccat-lua.c') diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c new file mode 100644 index 000000000..8e35e1a06 --- /dev/null +++ b/drivers/hid/hid-roccat-lua.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Roccat Lua driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + */ + +/* + * Roccat Lua is a gamer mouse which cpi, button and light settings can be + * configured. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-lua.h" + +static ssize_t lua_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = kobj_to_dev(kobj); + struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&lua->lua_lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = kobj_to_dev(kobj); + struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&lua->lua_lock); + retval = roccat_common2_send(usb_dev, command, buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +#define LUA_SYSFS_W(thingy, THINGY) \ +static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_write(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_SYSFS_R(thingy, THINGY) \ +static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_read(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +LUA_SYSFS_W(thingy, THINGY) \ +LUA_SYSFS_R(thingy, THINGY) \ +static const struct bin_attribute lua_ ## thingy ## _attr = { \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = LUA_SIZE_ ## THINGY, \ + .read = lua_sysfs_read_ ## thingy, \ + .write = lua_sysfs_write_ ## thingy \ +}; + +LUA_BIN_ATTRIBUTE_RW(control, CONTROL) + +static int lua_create_sysfs_attributes(struct usb_interface *intf) +{ + return sysfs_create_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static void lua_remove_sysfs_attributes(struct usb_interface *intf) +{ + sysfs_remove_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static int lua_init_lua_device_struct(struct usb_device *usb_dev, + struct lua_device *lua) +{ + mutex_init(&lua->lua_lock); + + return 0; +} + +static int lua_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct lua_device *lua; + int retval; + + lua = kzalloc_obj(*lua); + if (!lua) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, lua); + + retval = lua_init_lua_device_struct(usb_dev, lua); + if (retval) { + hid_err(hdev, "couldn't init struct lua_device\n"); + goto exit; + } + + retval = lua_create_sysfs_attributes(intf); + if (retval) { + hid_err(hdev, "cannot create sysfs files\n"); + goto exit; + } + + return 0; +exit: + kfree(lua); + return retval; +} + +static void lua_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct lua_device *lua; + + lua_remove_sysfs_attributes(intf); + + lua = hid_get_drvdata(hdev); + kfree(lua); +} + +static int lua_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + if (!hid_is_usb(hdev)) + return -EINVAL; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = lua_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void lua_remove(struct hid_device *hdev) +{ + lua_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id lua_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, lua_devices); + +static struct hid_driver lua_driver = { + .name = "lua", + .id_table = lua_devices, + .probe = lua_probe, + .remove = lua_remove +}; +module_hid_driver(lua_driver); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Lua driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.3.1