From 57edbe902b658c9bfc4dfe33ed8f51063c61f50e Mon Sep 17 00:00:00 2001 From: Thomas 'Dent' Mirlacher Date: Sun, 14 Jul 2002 22:51:51 -0700 Subject: [PATCH] redundant declarations (#10_15) this patch fixes redundant declarations in 2.5.24 (same as sent yesterday, but this time automacially splitted into several mails) --- include/linux/vt_kern.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 227ad0934c79..6f668c171107 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -91,6 +91,5 @@ void complete_change_console(unsigned int new_console); int vt_waitactive(int vt); void change_console(unsigned int); void reset_vc(unsigned int new_console); -int vt_waitactive(int vt); #endif /* _VT_KERN_H */ -- cgit v1.2.3 From 201a35607110a97ac590f05bbd01e22d11a7a4f4 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sun, 14 Jul 2002 22:53:00 -0700 Subject: [PATCH] Make CRIS use generic copy_siginfo_to_user The CRIS architecture is also able to use the generic copy_siginfo_to_user. --- arch/cris/kernel/signal.c | 36 ------------------------------------ include/asm-cris/siginfo.h | 2 -- 2 files changed, 38 deletions(-) (limited to 'include') diff --git a/arch/cris/kernel/signal.c b/arch/cris/kernel/signal.c index 2e75f5c0298e..bfd339c104d2 100644 --- a/arch/cris/kernel/signal.c +++ b/arch/cris/kernel/signal.c @@ -43,42 +43,6 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); -int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) -{ - if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) - return -EFAULT; - if (from->si_code < 0) - return __copy_to_user(to, from, sizeof(siginfo_t)); - else { - int err; - - /* If you change siginfo_t structure, please be sure - this code is fixed accordingly. - It should never copy any pad contained in the structure - to avoid security leaks, but must copy the generic - 3 ints plus the relevant union member. */ - err = __put_user(from->si_signo, &to->si_signo); - err |= __put_user(from->si_errno, &to->si_errno); - err |= __put_user((short)from->si_code, &to->si_code); - /* First 32bits of unions are always present. */ - err |= __put_user(from->si_pid, &to->si_pid); - switch (from->si_code >> 16) { - case __SI_FAULT >> 16: - err |= __put_user(from->si_addr, &to->si_addr); - break; - case __SI_CHLD >> 16: - err |= __put_user(from->si_utime, &to->si_utime); - err |= __put_user(from->si_stime, &to->si_stime); - err |= __put_user(from->si_status, &to->si_status); - default: - err |= __put_user(from->si_uid, &to->si_uid); - break; - /* case __SI_RT: This is not generated by the kernel as of now. */ - } - return err; - } -} - /* * Atomically swap in the new signal mask, and wait for a signal. Define * dummy arguments to be able to reach the regs argument. (Note that this diff --git a/include/asm-cris/siginfo.h b/include/asm-cris/siginfo.h index a8c8c74dc9df..c1cd6d16928b 100644 --- a/include/asm-cris/siginfo.h +++ b/include/asm-cris/siginfo.h @@ -1,8 +1,6 @@ #ifndef _CRIS_SIGINFO_H #define _CRIS_SIGINFO_H -#define HAVE_ARCH_COPY_SIGINFO_TO_USER - #include #endif -- cgit v1.2.3 From 1844607eec6cf0ccec86bd132f8cf73b18f9368d Mon Sep 17 00:00:00 2001 From: Thomas 'Dent' Mirlacher Date: Sun, 14 Jul 2002 22:55:02 -0700 Subject: [PATCH] redundant declarations (#8_15) this patch fixes redundant declarations in 2.5.24 (same as sent yesterday, but this time automacially splitted into several mails) --- include/linux/nfs_xdr.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 2a3e2dd69dc0..110e5b661b58 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1,9 +1,6 @@ #ifndef _LINUX_NFS_XDR_H #define _LINUX_NFS_XDR_H -extern struct rpc_program nfs_program; -extern struct rpc_stat nfs_rpcstat; - struct nfs_fattr { unsigned short valid; /* which fields are valid */ __u64 pre_size; /* pre_op_attr.size */ @@ -354,5 +351,6 @@ extern struct nfs_rpc_ops nfs_v3_clientops; extern struct rpc_version nfs_version2; extern struct rpc_version nfs_version3; extern struct rpc_program nfs_program; +extern struct rpc_stat nfs_rpcstat; #endif -- cgit v1.2.3 From 82103c8396f5c701d7fb1f470becffacb9254d85 Mon Sep 17 00:00:00 2001 From: James Mayer Date: Sun, 14 Jul 2002 22:56:42 -0700 Subject: [PATCH] Typo in linux_include_linux_brlock.h --- include/linux/brlock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/brlock.h b/include/linux/brlock.h index 68c200512604..81d87c72ca1d 100644 --- a/include/linux/brlock.h +++ b/include/linux/brlock.h @@ -19,7 +19,7 @@ * * David S. Miller * - * David has an implementation that doesnt use atomic operations in + * David has an implementation that doesn't use atomic operations in * the read branch via memory ordering tricks - i guess we need to * split this up into a per-arch thing? The atomicity issue is a * secondary item in profiles, at least on x86 platforms. -- cgit v1.2.3 From 88cfaf7ab8b87ade2097b65ce423f36a8d3da2a7 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sun, 14 Jul 2002 22:58:05 -0700 Subject: [PATCH] make Alpha use generic copy_siginfo_to_user Having looked closer than before, it seems that Alpha can use the generic copy_siginfo_to_user function. --- arch/alpha/kernel/signal.c | 30 ------------------------------ include/asm-alpha/siginfo.h | 1 - 2 files changed, 31 deletions(-) (limited to 'include') diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 565d4af63843..839dffd2e077 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -36,36 +36,6 @@ static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, unsigned long, unsigned long); -int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) -{ - if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) - return -EFAULT; - if (from->si_code < 0) - return __copy_to_user(to, from, sizeof(siginfo_t)); - else { - int err; - - /* If you change siginfo_t structure, please be sure - this code is fixed accordingly. - It should never copy any pad contained in the structure - to avoid security leaks, but must copy the generic - 3 ints plus the relevant union member. */ - err = __put_user(*(long *)&from->si_signo, (long *)&to->si_signo); - err |= __put_user((short)from->si_code, &to->si_code); - switch (from->si_code >> 16) { - case __SI_CHLD >> 16: - err |= __put_user(from->si_utime, &to->si_utime); - err |= __put_user(from->si_stime, &to->si_stime); - err |= __put_user(from->si_status, &to->si_status); - default: - err |= __put_user(from->si_addr, &to->si_addr); - break; - /* case __SI_RT: This is not generated by the kernel as of now. */ - } - return err; - } -} - /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. diff --git a/include/asm-alpha/siginfo.h b/include/asm-alpha/siginfo.h index 42eb85ee5760..7f54a8019e10 100644 --- a/include/asm-alpha/siginfo.h +++ b/include/asm-alpha/siginfo.h @@ -6,7 +6,6 @@ #define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4) #define HAVE_ARCH_COPY_SIGINFO -#define HAVE_ARCH_COPY_SIGINFO_TO_USER #include -- cgit v1.2.3 From 1938d56c78d40d77e940a6ae890957383e6b0325 Mon Sep 17 00:00:00 2001 From: Thomas 'Dent' Mirlacher Date: Sun, 14 Jul 2002 23:00:06 -0700 Subject: [PATCH] redundant declarations (#11_15) this patch fixes redundant declarations in 2.5.24 --- include/net/tcp.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8a9322cd0754..ae3b35eb282e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -456,7 +456,6 @@ extern int sysctl_tcp_syncookies; extern int sysctl_tcp_retrans_collapse; extern int sysctl_tcp_stdurg; extern int sysctl_tcp_rfc1337; -extern int sysctl_tcp_tw_recycle; extern int sysctl_tcp_abort_on_overflow; extern int sysctl_tcp_max_orphans; extern int sysctl_tcp_max_tw_buckets; -- cgit v1.2.3 From 1aed230ba0f8fe6ff7525dfca9789a4943e20e1d Mon Sep 17 00:00:00 2001 From: James Mayer Date: Sun, 14 Jul 2002 23:01:15 -0700 Subject: [PATCH] Typo in linux_include_asm-cris_pgtable.h --- include/asm-cris/pgtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-cris/pgtable.h b/include/asm-cris/pgtable.h index 57e391330752..37ab897db8be 100644 --- a/include/asm-cris/pgtable.h +++ b/include/asm-cris/pgtable.h @@ -164,7 +164,7 @@ static inline void flush_tlb(void) */ #define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval)) /* - * (pmds are folded into pgds so this doesnt get actually called, + * (pmds are folded into pgds so this doesn't get actually called, * but the define is needed for a generic inline function.) */ #define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) -- cgit v1.2.3 From 94b479f7416b3c784a80a45e47acef97636e692d Mon Sep 17 00:00:00 2001 From: James Mayer Date: Sun, 14 Jul 2002 23:03:37 -0700 Subject: [PATCH] Typo in linux_include_asm-m68k_mac_via.h --- include/asm-m68k/mac_via.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-m68k/mac_via.h b/include/asm-m68k/mac_via.h index ab8e23d9e519..d01672d44b0a 100644 --- a/include/asm-m68k/mac_via.h +++ b/include/asm-m68k/mac_via.h @@ -3,7 +3,7 @@ * * There are two of these on the Mac II. Some IRQ's are vectored * via them as are assorted bits and bobs - eg rtc, adb. The picture - * is a bit incomplete as the Mac documentation doesnt cover this well + * is a bit incomplete as the Mac documentation doesn't cover this well */ #ifndef _ASM_MAC_VIA_H_ -- cgit v1.2.3 From 59d68b91737a46b40d7d6be9208b745a1ef839fa Mon Sep 17 00:00:00 2001 From: James Mayer Date: Sun, 14 Jul 2002 23:04:25 -0700 Subject: [PATCH] Typo in linux_include_linux_raid_md_k.h --- include/linux/raid/md_k.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 7b02c3c82a33..2a9bbed805ad 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -59,7 +59,7 @@ typedef struct mddev_s mddev_t; typedef struct mdk_rdev_s mdk_rdev_t; #if (MINORBITS != 8) -#error MD doesnt handle bigger kdev yet +#error MD does not handle bigger kdev yet #endif #define MAX_MD_DEVS (1< Date: Sun, 14 Jul 2002 23:04:58 -0700 Subject: [PATCH] Typo in linux_include_asm-parisc_semaphore.h --- include/asm-parisc/semaphore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-parisc/semaphore.h b/include/asm-parisc/semaphore.h index 1434908edb46..04eb43929c8a 100644 --- a/include/asm-parisc/semaphore.h +++ b/include/asm-parisc/semaphore.h @@ -53,7 +53,7 @@ extern inline void sema_init (struct semaphore *sem, int val) * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val); * * i'd rather use the more flexible initialization above, but sadly - * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well. + * GCC 2.7.2.3 emits a bogus warning. EGCS doesn't. Oh well. */ atomic_set(&sem->count, val); sem->waking = 0; -- cgit v1.2.3 From d8ee26e78bb8a32067b41e8a062222d5264a5655 Mon Sep 17 00:00:00 2001 From: Thomas 'Dent' Mirlacher Date: Sun, 14 Jul 2002 23:10:15 -0700 Subject: [PATCH] [PATCH] duplicate declarations #2 this one slipped through the last fix for the redeclarations i sent, please apply this on to of the other one. description: umount_tree() is just used in namespace.[ch], so it declaration belongs into namespace.h and not fs.h --- include/linux/fs.h | 1 - include/linux/namespace.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 2b6008d56ca8..2ac85b8e28a3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -988,7 +988,6 @@ extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); -extern void umount_tree(struct vfsmount *); #define kern_umount mntput diff --git a/include/linux/namespace.h b/include/linux/namespace.h index 1113fe62d6cd..acd64b4ca117 100644 --- a/include/linux/namespace.h +++ b/include/linux/namespace.h @@ -14,6 +14,8 @@ struct namespace { void umount_tree(struct vfsmount *mnt); +extern void umount_tree(struct vfsmount *); + static inline void put_namespace(struct namespace *namespace) { if (atomic_dec_and_test(&namespace->count)) { -- cgit v1.2.3 From 06fca2f0df3a7b059bbe06a4553063f0ce2e563f Mon Sep 17 00:00:00 2001 From: James Mayer Date: Sun, 14 Jul 2002 23:11:57 -0700 Subject: [PATCH] Typo in linux_include_asm-sh_pgtable-2level.h --- include/asm-sh/pgtable-2level.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-sh/pgtable-2level.h b/include/asm-sh/pgtable-2level.h index a14015c43370..b11384a849cd 100644 --- a/include/asm-sh/pgtable-2level.h +++ b/include/asm-sh/pgtable-2level.h @@ -42,7 +42,7 @@ static inline void pgd_clear (pgd_t * pgdp) { } */ #define set_pte(pteptr, pteval) (*(pteptr) = pteval) /* - * (pmds are folded into pgds so this doesnt get actually called, + * (pmds are folded into pgds so this doesn't get actually called, * but the define is needed for a generic inline function.) */ #define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) -- cgit v1.2.3 From b24be862cc6543ef76e17c55cc11c69e2dd8bc62 Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Mon, 15 Jul 2002 03:18:42 -0700 Subject: [PATCH] Updates for hiddev by Paul Stewart I've merged a patch Paul Stewart sent me some time ago, which should make life easier for the guys writing UPS daemons. --- Documentation/usb/hiddev.txt | 15 ++++- drivers/usb/input/hid-core.c | 132 +++++++++++++++++++++++++++++----------- drivers/usb/input/hid-debug.h | 6 -- drivers/usb/input/hid-input.c | 7 ++- drivers/usb/input/hid.h | 12 +++- drivers/usb/input/hiddev.c | 138 ++++++++++++++++++++++-------------------- include/linux/hiddev.h | 19 ++++-- 7 files changed, 211 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/Documentation/usb/hiddev.txt b/Documentation/usb/hiddev.txt index 31250f38b45f..470840b5be5f 100644 --- a/Documentation/usb/hiddev.txt +++ b/Documentation/usb/hiddev.txt @@ -18,7 +18,7 @@ normalised event interface - see Documentation/input/input.txt The data flow for a HID event produced by a device is something like the following : - usb.c ---> hid-core.c ----> input.c ----> [keyboard/mouse/joystick/event] + usb.c ---> hid-core.c ----> hid-input.c ----> [keyboard/mouse/joystick/event] | | --> hiddev.c ----> POWER / MONITOR CONTROL @@ -106,6 +106,15 @@ returns -1. You can find out beforehand how many application collections the device has from the num_applications field from the hiddev_devinfo structure. +HIDIOCGCOLLECTIONINFO - struct hiddev_collection_info (read/write) +This returns a superset of the information above, providing not only +application collections, but all the collections the device has. It +also returns the level the collection lives in the hierarchy. +The user passes in a hiddev_collection_info struct with the index +field set to the index that should be returned. The ioctl fills in +the other fields. If the index is larger than the last collection +index, the ioctl returns -1 and sets errno to -EINVAL. + HIDIOCGDEVINFO - struct hiddev_devinfo (read) Gets a hiddev_devinfo structure which describes the device. @@ -172,6 +181,10 @@ Sets the value of a usage in an output report. The user fills in the hiddev_usage_ref structure as above, but additionally fills in the value field. +HIDIOGCOLLECTIONINDEX - struct hiddev_usage_ref (write) +Returns the collection index associated with this usage. This +indicates where in the collection hierarchy this usage sits. + HIDIOCGFLAG - int (read) HIDIOCSFLAG - int (write) These operations respectively inspect and replace the mode flags diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index ca7b3f198390..6f82975e4838 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -127,18 +127,41 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; - if (type == HID_COLLECTION_APPLICATION - && parser->device->maxapplication < HID_MAX_APPLICATIONS) - parser->device->application[parser->device->maxapplication++] = usage; - if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { dbg("collection stack overflow"); return -1; } - collection = parser->collection_stack + parser->collection_stack_ptr++; + if (parser->device->maxcollection == parser->device->collection_size) { + collection = kmalloc(sizeof(struct hid_collection) * + parser->device->collection_size * 2, + GFP_KERNEL); + if (collection == NULL) { + dbg("failed to reallocate collection array"); + return -1; + } + memcpy(collection, parser->device->collection, + sizeof(struct hid_collection) * + parser->device->collection_size); + memset(collection + parser->device->collection_size, 0, + sizeof(struct hid_collection) * + parser->device->collection_size); + kfree(parser->device->collection); + parser->device->collection = collection; + parser->device->collection_size *= 2; + } + + parser->collection_stack[parser->collection_stack_ptr++] = + parser->device->maxcollection; + + collection = parser->device->collection + + parser->device->maxcollection++; collection->type = type; collection->usage = usage; + collection->level = parser->collection_stack_ptr - 1; + + if (type == HID_COLLECTION_APPLICATION) + parser->device->maxapplication++; return 0; } @@ -166,8 +189,8 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) { int n; for (n = parser->collection_stack_ptr - 1; n >= 0; n--) - if (parser->collection_stack[n].type == type) - return parser->collection_stack[n].usage; + if (parser->device->collection[parser->collection_stack[n]].type == type) + return parser->device->collection[parser->collection_stack[n]].usage; return 0; /* we know nothing about this usage type */ } @@ -181,7 +204,11 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage) dbg("usage index exceeded"); return -1; } - parser->local.usage[parser->local.usage_index++] = usage; + parser->local.usage[parser->local.usage_index] = usage; + parser->local.collection_index[parser->local.usage_index] = + parser->collection_stack_ptr ? + parser->collection_stack[parser->collection_stack_ptr - 1] : 0; + parser->local.usage_index++; return 0; } @@ -221,8 +248,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); - for (i = 0; i < usages; i++) + for (i = 0; i < usages; i++) { field->usage[i].hid = parser->local.usage[i]; + field->usage[i].collection_index = + parser->local.collection_index[i]; + } field->maxusage = usages; field->flags = flags; @@ -460,7 +490,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) switch (item->tag) { case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: - ret = open_collection(parser, data & 3); + ret = open_collection(parser, data & 0xff); break; case HID_MAIN_ITEM_TAG_END_COLLECTION: ret = close_collection(parser); @@ -621,17 +651,30 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) return NULL; memset(device, 0, sizeof(struct hid_device)); + if (!(device->collection = kmalloc(sizeof(struct hid_collection) * + HID_DEFAULT_NUM_COLLECTIONS, + GFP_KERNEL))) { + kfree(device); + return NULL; + } + memset(device->collection, 0, sizeof(struct hid_collection) * + HID_DEFAULT_NUM_COLLECTIONS); + device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + for (i = 0; i < HID_REPORT_TYPES; i++) INIT_LIST_HEAD(&device->report_enum[i].report_list); if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) { + kfree(device->collection); kfree(device); return NULL; } memcpy(device->rdesc, start, size); + device->rsize = size; if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { kfree(device->rdesc); + kfree(device->collection); kfree(device); return NULL; } @@ -643,6 +686,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) if (item.format != HID_ITEM_FORMAT_SHORT) { dbg("unexpected long global item"); + kfree(device->rdesc); + kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; @@ -651,6 +696,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) if (dispatch_type[item.type](parser, &item)) { dbg("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); + kfree(device->rdesc); + kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; @@ -659,12 +706,16 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) if (start == end) { if (parser->collection_stack_ptr) { dbg("unbalanced collection at end of report description"); + kfree(device->rdesc); + kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; } if (parser->local.delimiter_depth) { dbg("unbalanced delimiter at end of report description"); + kfree(device->rdesc); + kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; @@ -675,6 +726,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) } dbg("item fetching failed at offset %d\n", (int)(end - start)); + kfree(device->rdesc); + kfree(device->collection); hid_free_device(device); kfree(parser); return NULL; @@ -1284,6 +1337,10 @@ void hid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 +#define USB_VENDOR_ID_MGE 0x0463 +#define USB_DEVICE_ID_MGE_UPS 0xffff +#define USB_DEVICE_ID_MGE_UPS1 0x0001 + struct hid_blacklist { __u16 idVendor; __u16 idProduct; @@ -1301,6 +1358,8 @@ struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_HIDDEV }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_HIDDEV }, { 0, 0 } }; @@ -1438,6 +1497,27 @@ fail: return NULL; } +static void hid_disconnect(struct usb_device *dev, void *ptr) +{ + struct hid_device *hid = ptr; + + usb_unlink_urb(hid->urbin); + usb_unlink_urb(hid->urbout); + usb_unlink_urb(hid->urbctrl); + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + if (hid->claimed & HID_CLAIMED_HIDDEV) + hiddev_disconnect(hid); + + usb_free_urb(hid->urbin); + usb_free_urb(hid->urbctrl); + if (hid->urbout) + usb_free_urb(hid->urbout); + + hid_free_device(hid); +} + static void* hid_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { @@ -1462,7 +1542,7 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, hid->claimed |= HID_CLAIMED_HIDDEV; if (!hid->claimed) { - hid_free_device(hid); + hid_disconnect(dev, hid); return NULL; } @@ -1476,11 +1556,14 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, printk("hiddev%d", hid->minor); c = "Device"; - for (i = 0; i < hid->maxapplication; i++) - if ((hid->application[i] & 0xffff) < ARRAY_SIZE(hid_types)) { - c = hid_types[hid->application[i] & 0xffff]; + for (i = 0; i < hid->maxcollection; i++) { + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && + (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK && + (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) { + c = hid_types[hid->collection[i].usage & 0xffff]; break; } + } usb_make_path(dev, path, 63); @@ -1490,27 +1573,6 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum, return hid; } -static void hid_disconnect(struct usb_device *dev, void *ptr) -{ - struct hid_device *hid = ptr; - - usb_unlink_urb(hid->urbin); - usb_unlink_urb(hid->urbout); - usb_unlink_urb(hid->urbctrl); - - if (hid->claimed & HID_CLAIMED_INPUT) - hidinput_disconnect(hid); - if (hid->claimed & HID_CLAIMED_HIDDEV) - hiddev_disconnect(hid); - - usb_free_urb(hid->urbin); - usb_free_urb(hid->urbctrl); - if (hid->urbout) - usb_free_urb(hid->urbout); - - hid_free_device(hid); -} - static struct usb_device_id hid_usb_ids [] = { { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS, bInterfaceClass: USB_INTERFACE_CLASS_HID }, diff --git a/drivers/usb/input/hid-debug.h b/drivers/usb/input/hid-debug.h index 62757fa2fe05..9ae94a95d7d3 100644 --- a/drivers/usb/input/hid-debug.h +++ b/drivers/usb/input/hid-debug.h @@ -352,12 +352,6 @@ static void __attribute__((unused)) hid_dump_device(struct hid_device *device) { unsigned i,k; static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; - for (i = 0; i < device->maxapplication; i++) { - printk("Application("); - resolv_usage(device->application[i]); - printk(")\n"); - } - for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 37f12424cb6f..a036b23f6fa6 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -474,11 +474,12 @@ int hidinput_connect(struct hid_device *hid) struct list_head *list; int i, j, k; - for (i = 0; i < hid->maxapplication; i++) - if (IS_INPUT_APPLICATION(hid->application[i])) + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == HID_COLLECTION_APPLICATION && + IS_INPUT_APPLICATION(hid->collection[i].usage)) break; - if (i == hid->maxapplication) + if (i == hid->maxcollection) return -1; hid->input.private = hid; diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 7862be448938..93075dee2db7 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -205,6 +205,7 @@ struct hid_item { #define HID_QUIRK_NOTOUCH 0x02 #define HID_QUIRK_IGNORE 0x04 #define HID_QUIRK_NOGET 0x08 +#define HID_QUIRK_HIDDEV 0x10 /* * This is the global enviroment of the parser. This information is @@ -231,10 +232,11 @@ struct hid_global { #define HID_MAX_DESCRIPTOR_SIZE 4096 #define HID_MAX_USAGES 1024 -#define HID_MAX_APPLICATIONS 16 +#define HID_DEFAULT_NUM_COLLECTIONS 16 struct hid_local { unsigned usage[HID_MAX_USAGES]; /* usage array */ + unsigned collection_index[HID_MAX_USAGES]; /* collection index array */ unsigned usage_index; unsigned usage_minimum; unsigned delimiter_depth; @@ -249,10 +251,12 @@ struct hid_local { struct hid_collection { unsigned type; unsigned usage; + unsigned level; }; struct hid_usage { unsigned hid; /* hid usage code */ + unsigned collection_index; /* index into collection array */ __u16 code; /* input driver code */ __u8 type; /* input driver type */ __s8 hat_min; /* hat switch fun */ @@ -319,7 +323,9 @@ struct hid_control_fifo { struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; - unsigned application[HID_MAX_APPLICATIONS]; /* List of HID applications */ + struct hid_collection *collection; /* List of HID collections */ + unsigned collection_size; /* Number of allocated hid_collections */ + unsigned maxcollection; /* Number of parsed collections */ unsigned maxapplication; /* Number of applications */ unsigned version; /* HID version */ unsigned country; /* HID country */ @@ -374,7 +380,7 @@ struct hid_parser { struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; unsigned global_stack_ptr; struct hid_local local; - struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE]; + unsigned collection_stack[HID_COLLECTION_STACK_SIZE]; unsigned collection_stack_ptr; struct hid_device *device; }; diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 8b93b2f0a1bc..8b5cd439eee7 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -80,6 +80,7 @@ extern struct usb_driver hiddev_driver; static struct hid_report * hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) { + unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK; struct hid_report_enum *report_enum; struct list_head *list; @@ -88,27 +89,28 @@ hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) report_enum = hid->report_enum + (rinfo->report_type - HID_REPORT_TYPE_MIN); - if ((rinfo->report_id & ~HID_REPORT_ID_MASK) != 0) { - switch (rinfo->report_id & ~HID_REPORT_ID_MASK) { - case HID_REPORT_ID_FIRST: - list = report_enum->report_list.next; - if (list == &report_enum->report_list) return NULL; - rinfo->report_id = ((struct hid_report *) list)->id; - break; - - case HID_REPORT_ID_NEXT: - list = (struct list_head *) - report_enum->report_id_hash[rinfo->report_id & - HID_REPORT_ID_MASK]; - if (list == NULL) return NULL; - list = list->next; - if (list == &report_enum->report_list) return NULL; - rinfo->report_id = ((struct hid_report *) list)->id; - break; - default: - return NULL; - } + switch (flags) { + case 0: /* Nothing to do -- report_id is already set correctly */ + break; + + case HID_REPORT_ID_FIRST: + list = report_enum->report_list.next; + if (list == &report_enum->report_list) return NULL; + rinfo->report_id = ((struct hid_report *) list)->id; + break; + + case HID_REPORT_ID_NEXT: + list = (struct list_head *) + report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK]; + if (list == NULL) return NULL; + list = list->next; + if (list == &report_enum->report_list) return NULL; + rinfo->report_id = ((struct hid_report *) list)->id; + break; + + default: + return NULL; } return report_enum->report_id_hash[rinfo->report_id]; @@ -256,8 +258,7 @@ static int hiddev_open(struct inode * inode, struct file * file) { /* * "write" file op */ -static ssize_t hiddev_write(struct file * file, const char * buffer, - size_t count, loff_t *ppos) +static ssize_t hiddev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) { return -EINVAL; } @@ -265,8 +266,7 @@ static ssize_t hiddev_write(struct file * file, const char * buffer, /* * "read" file op */ -static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, - loff_t *ppos) +static ssize_t hiddev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) { DECLARE_WAITQUEUE(wait, current); struct hiddev_list *list = file->private_data; @@ -354,17 +354,20 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait) /* * "ioctl" file op */ -static int hiddev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct hiddev_list *list = file->private_data; struct hiddev *hiddev = list->hiddev; struct hid_device *hid = hiddev->hid; struct usb_device *dev = hid->dev; + struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; + struct hiddev_field_info finfo; struct hiddev_usage_ref uref; + struct hiddev_devinfo dinfo; struct hid_report *report; struct hid_field *field; + int i; if (!hiddev->exist) return -EIO; @@ -376,11 +379,18 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, case HIDIOCAPPLICATION: if (arg < 0 || arg >= hid->maxapplication) return -EINVAL; - return hid->application[arg]; + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && arg-- == 0) + break; + + if (i == hid->maxcollection) + return -EINVAL; + + return hid->collection[i].usage; case HIDIOCGDEVINFO: - { - struct hiddev_devinfo dinfo; dinfo.bustype = BUS_USB; dinfo.busnum = dev->bus->busnum; dinfo.devnum = dev->devnum; @@ -390,7 +400,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, dinfo.version = dev->descriptor.bcdDevice; dinfo.num_applications = hid->maxapplication; return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); - } case HIDIOCGFLAG: return put_user(list->flags, (int *) arg); @@ -438,7 +447,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, } case HIDIOCINITREPORT: - hid_init_reports(hid); return 0; @@ -483,8 +491,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, return copy_to_user((void *) arg, &rinfo, sizeof(rinfo)); case HIDIOCGFIELDINFO: - { - struct hiddev_field_info finfo; if (copy_from_user(&finfo, (void *) arg, sizeof(finfo))) return -EFAULT; rinfo.report_type = finfo.report_type; @@ -513,7 +519,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, finfo.unit = field->unit; return copy_to_user((void *) arg, &finfo, sizeof(finfo)); - } case HIDIOCGUCODE: if (copy_from_user(&uref, (void *) arg, sizeof(uref))) @@ -536,9 +541,14 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, return copy_to_user((void *) arg, &uref, sizeof(uref)); case HIDIOCGUSAGE: + case HIDIOCSUSAGE: + case HIDIOCGCOLLECTIONINDEX: if (copy_from_user(&uref, (void *) arg, sizeof(uref))) return -EFAULT; + if (cmd != HIDIOCGUSAGE && uref.report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; + if (uref.report_id == HID_REPORT_ID_UNKNOWN) { field = hiddev_lookup_usage(hid, &uref); if (field == NULL) @@ -557,37 +567,35 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, return -EINVAL; } - uref.value = field->value[uref.usage_index]; - - return copy_to_user((void *) arg, &uref, sizeof(uref)); + switch (cmd) { + case HIDIOCGUSAGE: + uref.value = field->value[uref.usage_index]; + return copy_to_user((void *) arg, &uref, sizeof(uref)); + return 0; - case HIDIOCSUSAGE: - if (copy_from_user(&uref, (void *) arg, sizeof(uref))) - return -EFAULT; + case HIDIOCSUSAGE: + field->value[uref.usage_index] = uref.value; + return 0; - if (uref.report_type == HID_REPORT_TYPE_INPUT) - return -EINVAL; + case HIDIOCGCOLLECTIONINDEX: + return field->usage[uref.usage_index].collection_index; + } - if (uref.report_id == HID_REPORT_ID_UNKNOWN) { - field = hiddev_lookup_usage(hid, &uref); - if (field == NULL) - return -EINVAL; - } else { - rinfo.report_type = uref.report_type; - rinfo.report_id = uref.report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; + return 0; - if (uref.field_index >= report->maxfield) - return -EINVAL; + case HIDIOCGCOLLECTIONINFO: + if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo))) + return -EFAULT; - field = report->field[uref.field_index]; - if (uref.usage_index >= field->maxusage) - return -EINVAL; - } + if (cinfo.index >= hid->maxcollection) + return -EINVAL; - field->value[uref.usage_index] = uref.value; + cinfo.type = hid->collection[cinfo.index].type; + cinfo.usage = hid->collection[cinfo.index].usage; + cinfo.level = hid->collection[cinfo.index].level; + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; return 0; default: @@ -628,11 +636,13 @@ int hiddev_connect(struct hid_device *hid) int retval; char devfs_name[16]; - for (i = 0; i < hid->maxapplication; i++) - if (!IS_INPUT_APPLICATION(hid->application[i])) + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && + !IS_INPUT_APPLICATION(hid->collection[i].usage)) break; - if (i == hid->maxapplication) + if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) return -1; retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor); @@ -657,10 +667,8 @@ int hiddev_connect(struct hid_device *hid) sprintf(devfs_name, "hiddev%d", minor); hiddev->devfs = devfs_register(hiddev_devfs_handle, devfs_name, - DEVFS_FL_DEFAULT, USB_MAJOR, - minor + HIDDEV_MINOR_BASE, - S_IFCHR | S_IRUGO | S_IWUSR, - &hiddev_fops, NULL); + DEVFS_FL_DEFAULT, USB_MAJOR, minor + HIDDEV_MINOR_BASE, + S_IFCHR | S_IRUGO | S_IWUSR, &hiddev_fops, NULL); hid->minor = minor; hid->hiddev = hiddev; diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index eb948560836d..05db98ccfdbb 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -49,6 +49,13 @@ struct hiddev_devinfo { unsigned num_applications; }; +struct hiddev_collection_info { + unsigned index; + unsigned type; + unsigned usage; + unsigned level; +}; + #define HID_STRING_SIZE 256 struct hiddev_string_descriptor { int index; @@ -64,9 +71,9 @@ struct hiddev_report_info { /* To do a GUSAGE/SUSAGE, fill in at least usage_code, report_type and * report_id. Set report_id to REPORT_ID_UNKNOWN if the rest of the fields * are unknown. Otherwise use a usage_ref struct filled in from a previous - * successful GUSAGE/SUSAGE call to save time. To actually send a value - * to the device, perform a SUSAGE first, followed by a SREPORT. If an - * INITREPORT is done, a GREPORT isn't necessary before a GUSAGE. + * successful GUSAGE call to save time. To actually send a value to the + * device, perform a SUSAGE first, followed by a SREPORT. An INITREPORT or a + * GREPORT isn't necessary for a GUSAGE to return valid data. */ #define HID_REPORT_ID_UNKNOWN 0xffffffff #define HID_REPORT_ID_FIRST 0x00000100 @@ -129,7 +136,7 @@ struct hiddev_usage_ref { * Protocol version. */ -#define HID_VERSION 0x010003 +#define HID_VERSION 0x010004 /* * IOCTLs (0x00 - 0x7f) @@ -150,6 +157,8 @@ struct hiddev_usage_ref { #define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) #define HIDIOCGFLAG _IOR('H', 0x0E, int) #define HIDIOCSFLAG _IOW('H', 0x0F, int) +#define HIDIOCGCOLLECTIONINDEX _IOW('H', 0x10, struct hiddev_usage_ref) +#define HIDIOCGCOLLECTIONINFO _IOWR('H', 0x11, struct hiddev_collection_info) /* * Flags to be used in HIDIOCSFLAG @@ -197,7 +206,7 @@ void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref); int __init hiddev_init(void); void __exit hiddev_exit(void); #else -static inline void *hiddev_connect(struct hid_device *hid) { return NULL; } +static inline int hiddev_connect(struct hid_device *hid) { return -1; } static inline void hiddev_disconnect(struct hid_device *hid) { } static inline void hiddev_event(struct hid_device *hid, unsigned int usage, int value) { } static inline int hiddev_init(void) { return 0; } -- cgit v1.2.3 From 2fcd00c2f6da48528ff0188676f4e91fea2a6795 Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Mon, 15 Jul 2002 03:37:47 -0700 Subject: [PATCH] A cleanup of Paul's 2.5 hiddev update. Get rid of #ifdefs in hid-core again. (For you, Greg.) Move the uref generation code from hid-core to hiddev to make things cleaner. --- drivers/usb/input/hid-core.c | 36 +++++------------------------------ drivers/usb/input/hiddev.c | 45 +++++++++++++++++++++++++++++++++++++++----- include/linux/hiddev.h | 8 ++++++-- 3 files changed, 51 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 6f82975e4838..068417749db6 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -793,22 +793,8 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s hid_dump_input(usage, value); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value); -#ifdef CONFIG_USB_HIDDEV - if (hid->claimed & HID_CLAIMED_HIDDEV) { - struct hiddev_usage_ref uref; - unsigned type = field->report_type; - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); - uref.report_id = field->report->id; - uref.field_index = field->index; - uref.usage_index = (usage - field->usage); - uref.usage_code = usage->hid; - uref.value = value; - hiddev_hid_event(hid, &uref); - } -#endif + if (hid->claimed & HID_CLAIMED_HIDDEV) + hiddev_hid_event(hid, field, usage, value); } /* @@ -904,21 +890,6 @@ static int hid_input_report(int type, struct urb *urb) return -1; } -#ifdef CONFIG_USB_HIDDEV - /* Notify listeners that a report has been received */ - if (hid->claimed & HID_CLAIMED_HIDDEV) { - struct hiddev_usage_ref uref; - memset(&uref, 0, sizeof(uref)); - uref.report_type = - (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : - ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); - uref.report_id = report->id; - uref.field_index = HID_FIELD_INDEX_NONE; - hiddev_hid_event(hid, &uref); - } -#endif - size = ((report->size - 1) >> 3) + 1; if (len < size) { @@ -926,6 +897,9 @@ static int hid_input_report(int type, struct urb *urb) return -1; } + if (hid->claimed & HID_CLAIMED_HIDDEV) + hiddev_report_event(hid, report); + for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data); diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 8ac1b9111145..563285d58541 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -154,11 +154,8 @@ hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) return NULL; } -/* - * This is where hid.c calls into hiddev to pass an event that occurred over - * the interrupt pipe - */ -void hiddev_hid_event(struct hid_device *hid, struct hiddev_usage_ref *uref) +static void hiddev_send_event(struct hid_device *hid, + struct hiddev_usage_ref *uref) { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list = hiddev->list; @@ -178,6 +175,44 @@ void hiddev_hid_event(struct hid_device *hid, struct hiddev_usage_ref *uref) wake_up_interruptible(&hiddev->wait); } +/* + * This is where hid.c calls into hiddev to pass an event that occurred over + * the interrupt pipe + */ +void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned type = field->report_type; + struct hiddev_usage_ref uref; + + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + uref.report_id = field->report->id; + uref.field_index = field->index; + uref.usage_index = (usage - field->usage); + uref.usage_code = usage->hid; + uref.value = value; + + hiddev_send_event(hid, &uref); +} + + +void hiddev_report_event(struct hid_device *hid, struct hid_report *report) +{ + unsigned type = report->type; + struct hiddev_usage_ref uref; + + memset(&uref, 0, sizeof(uref)); + uref.report_type = + (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : + ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + uref.report_id = report->id; + + hiddev_send_event(hid, &uref); +} /* * fasync file op */ diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index 05db98ccfdbb..0077b58559f5 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -202,13 +202,17 @@ struct hiddev_usage_ref { #ifdef CONFIG_USB_HIDDEV int hiddev_connect(struct hid_device *); void hiddev_disconnect(struct hid_device *); -void hiddev_hid_event(struct hid_device *, struct hiddev_usage_ref *ref); +void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value); +void hiddev_report_event(struct hid_device *hid, struct hid_report *report); int __init hiddev_init(void); void __exit hiddev_exit(void); #else static inline int hiddev_connect(struct hid_device *hid) { return -1; } static inline void hiddev_disconnect(struct hid_device *hid) { } -static inline void hiddev_event(struct hid_device *hid, unsigned int usage, int value) { } +static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) { } +static inline void hiddev_report_event(struct hid_device *hid, struct hid_report *report) { } static inline int hiddev_init(void) { return 0; } static inline void hiddev_exit(void) { } #endif -- cgit v1.2.3 From 77d7903022dbd92040f4afdfb5e05d9005a88a7f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:53:29 -0700 Subject: [PATCH] RPC over UDP congestion control updates [1/8] Implement the basic round trip timing algorithm in order to adapt the timeout values for the most common NFS operations to the server's rate of response. Algorithm is described in Van Jacobson's paper 1998 paper on http://www-nrg.ee.lbl.gov/nrg-papers.html, and is the same as is used for most TCP stacks. Following the *BSD code, we implement separate rtt timers for GETATTR, LOOKUP, READ/READDIR/READLINK, and WRITE. In addition to this, there is one extra timer for the COMMIT operation. All the remaining RPC calls use the current system in which a fixed timeout value gets set by the 'timeo' mount option. In case of a timeout, the current exponential backoff algoritm is implemented. Subsequent patches will improve this... --- fs/lockd/xdr.c | 9 +++--- fs/lockd/xdr4.c | 11 +++---- fs/nfs/nfs2xdr.c | 49 +++++++++++++++-------------- fs/nfs/nfs3xdr.c | 56 ++++++++++++++++----------------- include/linux/sunrpc/clnt.h | 5 +++ include/linux/sunrpc/timer.h | 23 ++++++++++++++ include/linux/sunrpc/xprt.h | 5 ++- net/sunrpc/Makefile | 3 +- net/sunrpc/clnt.c | 2 ++ net/sunrpc/timer.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ net/sunrpc/xprt.c | 33 ++++++++++++++++---- 11 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 include/linux/sunrpc/timer.h create mode 100644 net/sunrpc/timer.c (limited to 'include') diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index fde2cc59f22b..73845f10a81a 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -561,11 +561,10 @@ nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) #define nlmclt_decode_norep NULL #define PROC(proc, argtype, restype) \ - { "nlm_" #proc, \ - (kxdrproc_t) nlmclt_encode_##argtype, \ - (kxdrproc_t) nlmclt_decode_##restype, \ - MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2, \ - 0 \ + { .p_procname = "nlm_" #proc, \ + .p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \ + .p_decode = (kxdrproc_t) nlmclt_decode_##restype, \ + .p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \ } static struct rpc_procinfo nlm_procedures[] = { diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 6745444edcdf..42ab448d6b33 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -566,12 +566,11 @@ nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) */ #define nlm4clt_decode_norep NULL -#define PROC(proc, argtype, restype) \ - { "nlm4_" #proc, \ - (kxdrproc_t) nlm4clt_encode_##argtype, \ - (kxdrproc_t) nlm4clt_decode_##restype, \ - MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2, \ - 0 \ +#define PROC(proc, argtype, restype) \ + { .p_procname = "nlm4_" #proc, \ + .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \ + .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \ + .p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2 \ } static struct rpc_procinfo nlm4_procedures[] = { diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 660fb7f27c07..df06b7fdbf6a 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -671,33 +671,32 @@ nfs_stat_to_errno(int stat) # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif -#define PROC(proc, argtype, restype) \ - { "nfs_" #proc, \ - (kxdrproc_t) nfs_xdr_##argtype, \ - (kxdrproc_t) nfs_xdr_##restype, \ - MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \ - 0 \ +#define PROC(proc, argtype, restype, timer) \ + { .p_procname = "nfs_" #proc, \ + .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \ + .p_decode = (kxdrproc_t) nfs_xdr_##restype, \ + .p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \ + .p_timer = timer \ } - static struct rpc_procinfo nfs_procedures[18] = { - PROC(null, enc_void, dec_void), - PROC(getattr, fhandle, attrstat), - PROC(setattr, sattrargs, attrstat), - PROC(root, enc_void, dec_void), - PROC(lookup, diropargs, diropres), - PROC(readlink, readlinkargs, readlinkres), - PROC(read, readargs, readres), - PROC(writecache, enc_void, dec_void), - PROC(write, writeargs, writeres), - PROC(create, createargs, diropres), - PROC(remove, diropargs, stat), - PROC(rename, renameargs, stat), - PROC(link, linkargs, stat), - PROC(symlink, symlinkargs, stat), - PROC(mkdir, createargs, diropres), - PROC(rmdir, diropargs, stat), - PROC(readdir, readdirargs, readdirres), - PROC(statfs, fhandle, statfsres), + PROC(null, enc_void, dec_void, 0), + PROC(getattr, fhandle, attrstat, 1), + PROC(setattr, sattrargs, attrstat, 0), + PROC(root, enc_void, dec_void, 0), + PROC(lookup, diropargs, diropres, 2), + PROC(readlink, readlinkargs, readlinkres, 3), + PROC(read, readargs, readres, 3), + PROC(writecache, enc_void, dec_void, 0), + PROC(write, writeargs, writeres, 4), + PROC(create, createargs, diropres, 0), + PROC(remove, diropargs, stat, 0), + PROC(rename, renameargs, stat, 0), + PROC(link, linkargs, stat, 0), + PROC(symlink, symlinkargs, stat, 0), + PROC(mkdir, createargs, diropres, 0), + PROC(rmdir, diropargs, stat, 0), + PROC(readdir, readdirargs, readdirres, 3), + PROC(statfs, fhandle, statfsres, 0), }; struct rpc_version nfs_version2 = { diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index d13b938d9b70..d25de78172ae 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -988,37 +988,37 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif -#define PROC(proc, argtype, restype) \ - { "nfs3_" #proc, \ - (kxdrproc_t) nfs3_xdr_##argtype, \ - (kxdrproc_t) nfs3_xdr_##restype, \ - MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ - 0 \ +#define PROC(proc, argtype, restype, timer) \ + { .p_procname = "nfs3_" #proc, \ + .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \ + .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \ + .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ + .p_timer = timer \ } static struct rpc_procinfo nfs3_procedures[22] = { - PROC(null, enc_void, dec_void), - PROC(getattr, fhandle, attrstat), - PROC(setattr, sattrargs, wccstat), - PROC(lookup, diropargs, lookupres), - PROC(access, accessargs, accessres), - PROC(readlink, readlinkargs, readlinkres), - PROC(read, readargs, readres), - PROC(write, writeargs, writeres), - PROC(create, createargs, createres), - PROC(mkdir, mkdirargs, createres), - PROC(symlink, symlinkargs, createres), - PROC(mknod, mknodargs, createres), - PROC(remove, diropargs, wccstat), - PROC(rmdir, diropargs, wccstat), - PROC(rename, renameargs, renameres), - PROC(link, linkargs, linkres), - PROC(readdir, readdirargs, readdirres), - PROC(readdirplus, readdirargs, readdirres), - PROC(fsstat, fhandle, fsstatres), - PROC(fsinfo, fhandle, fsinfores), - PROC(pathconf, fhandle, pathconfres), - PROC(commit, commitargs, commitres), + PROC(null, enc_void, dec_void, 0), + PROC(getattr, fhandle, attrstat, 1), + PROC(setattr, sattrargs, wccstat, 0), + PROC(lookup, diropargs, lookupres, 2), + PROC(access, accessargs, accessres, 1), + PROC(readlink, readlinkargs, readlinkres, 3), + PROC(read, readargs, readres, 3), + PROC(write, writeargs, writeres, 4), + PROC(create, createargs, createres, 0), + PROC(mkdir, mkdirargs, createres, 0), + PROC(symlink, symlinkargs, createres, 0), + PROC(mknod, mknodargs, createres, 0), + PROC(remove, diropargs, wccstat, 0), + PROC(rmdir, diropargs, wccstat, 0), + PROC(rename, renameargs, renameres, 0), + PROC(link, linkargs, linkres, 0), + PROC(readdir, readdirargs, readdirres, 3), + PROC(readdirplus, readdirargs, readdirres, 3), + PROC(fsstat, fhandle, fsstatres, 0), + PROC(fsinfo, fhandle, fsinfores, 0), + PROC(pathconf, fhandle, pathconfres, 0), + PROC(commit, commitargs, commitres, 5), }; struct rpc_version nfs_version3 = { diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index be4c52c62c1c..d278df00ecb9 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -15,6 +15,7 @@ #include #include #include +#include #include /* @@ -52,6 +53,8 @@ struct rpc_clnt { unsigned int cl_flags; /* misc client flags */ unsigned long cl_hardmax; /* max hard timeout */ + struct rpc_rtt cl_rtt; /* RTO estimator data */ + struct rpc_portmap cl_pmap; /* port mapping */ struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ @@ -91,6 +94,7 @@ struct rpc_procinfo { kxdrproc_t p_decode; /* XDR decode function */ unsigned int p_bufsiz; /* req. buffer size */ unsigned int p_count; /* call count */ + unsigned int p_timer; /* Which RTT timer to use */ }; #define rpcproc_bufsiz(clnt, proc) ((clnt)->cl_procinfo[proc].p_bufsiz) @@ -98,6 +102,7 @@ struct rpc_procinfo { #define rpcproc_decode(clnt, proc) ((clnt)->cl_procinfo[proc].p_decode) #define rpcproc_name(clnt, proc) ((clnt)->cl_procinfo[proc].p_procname) #define rpcproc_count(clnt, proc) ((clnt)->cl_procinfo[proc].p_count) +#define rpcproc_timer(clnt, proc) ((clnt)->cl_procinfo[proc].p_timer) #define RPC_CONGESTED(clnt) (RPCXPRT_CONGESTED((clnt)->cl_xprt)) #define RPC_PEERADDR(clnt) (&(clnt)->cl_xprt->addr) diff --git a/include/linux/sunrpc/timer.h b/include/linux/sunrpc/timer.h new file mode 100644 index 000000000000..5f59407fed49 --- /dev/null +++ b/include/linux/sunrpc/timer.h @@ -0,0 +1,23 @@ +/* + * linux/include/linux/sunrpc/timer.h + * + * Declarations for the RPC transport timer. + * + * Copyright (C) 2002 Trond Myklebust + */ + +#ifndef _LINUX_SUNRPC_TIMER_H +#define _LINUX_SUNRPC_TIMER_H + +struct rpc_rtt { + long timeo; /* default timeout value */ + long srtt[5]; /* smoothed round trip time << 3 */ + long sdrtt[5]; /* soothed medium deviation of RTT */ +}; + + +extern void rpc_init_rtt(struct rpc_rtt *rt, long timeo); +extern void rpc_update_rtt(struct rpc_rtt *rt, int timer, long m); +extern long rpc_calc_rto(struct rpc_rtt *rt, int timer); + +#endif /* _LINUX_SUNRPC_TIMER_H */ diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 31a27bf00e41..2759e979b351 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -98,9 +98,8 @@ struct rpc_rqst { u32 rq_bytes_sent; /* Bytes we have sent */ -#ifdef RPC_PROFILE - unsigned long rq_xtime; /* when transmitted */ -#endif + long rq_xtime; /* when transmitted */ + int rq_nresend; }; #define rq_svec rq_snd_buf.head #define rq_slen rq_snd_buf.len diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index 2f31a59d427c..9aa4c8fe54ab 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile @@ -9,7 +9,8 @@ export-objs := sunrpc_syms.o sunrpc-y := clnt.o xprt.o sched.o \ auth.o auth_null.o auth_unix.o \ svc.o svcsock.o svcauth.o \ - pmap_clnt.o xdr.o sunrpc_syms.o + pmap_clnt.o timer.o xdr.o \ + sunrpc_syms.o sunrpc-$(CONFIG_PROC_FS) += stats.o sunrpc-$(CONFIG_SYSCTL) += sysctl.o sunrpc-objs := $(sunrpc-y) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 0a958d79c364..c6acb6aa4bf7 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -104,6 +104,8 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, if (!clnt->cl_port) clnt->cl_autobind = 1; + rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval); + if (!rpcauth_create(flavor, clnt)) goto out_no_auth; diff --git a/net/sunrpc/timer.c b/net/sunrpc/timer.c new file mode 100644 index 000000000000..00387735a34a --- /dev/null +++ b/net/sunrpc/timer.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#include +#include +#include + +#define RPC_RTO_MAX (60*HZ) +#define RPC_RTO_INIT (HZ/5) +#define RPC_RTO_MIN (2) + +void +rpc_init_rtt(struct rpc_rtt *rt, long timeo) +{ + long t = (timeo - RPC_RTO_INIT) << 3; + int i; + rt->timeo = timeo; + if (t < 0) + t = 0; + for (i = 0; i < 5; i++) { + rt->srtt[i] = t; + rt->sdrtt[i] = RPC_RTO_INIT; + } +} + +void +rpc_update_rtt(struct rpc_rtt *rt, int timer, long m) +{ + long *srtt, *sdrtt; + + if (timer-- == 0) + return; + + if (m == 0) + m = 1; + srtt = &rt->srtt[timer]; + m -= *srtt >> 3; + *srtt += m; + if (m < 0) + m = -m; + sdrtt = &rt->sdrtt[timer]; + m -= *sdrtt >> 2; + *sdrtt += m; + /* Set lower bound on the variance */ + if (*sdrtt < RPC_RTO_MIN) + *sdrtt = RPC_RTO_MIN; +} + +/* + * Estimate rto for an nfs rpc sent via. an unreliable datagram. + * Use the mean and mean deviation of rtt for the appropriate type of rpc + * for the frequent rpcs and a default for the others. + * The justification for doing "other" this way is that these rpcs + * happen so infrequently that timer est. would probably be stale. + * Also, since many of these rpcs are + * non-idempotent, a conservative timeout is desired. + * getattr, lookup, + * read, write, commit - A+4D + * other - timeo + */ + +long +rpc_calc_rto(struct rpc_rtt *rt, int timer) +{ + long res; + if (timer-- == 0) + return rt->timeo; + res = (rt->srtt[timer] >> 3) + rt->sdrtt[timer]; + if (res > RPC_RTO_MAX) + res = RPC_RTO_MAX; + return res; +} diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c4de269c1855..6f791e001589 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -480,13 +480,20 @@ xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) * Complete reply received. * The TCP code relies on us to remove the request from xprt->pending. */ -static inline void +static void xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) { struct rpc_task *task = req->rq_task; + struct rpc_clnt *clnt = task->tk_client; /* Adjust congestion window */ - xprt_adjust_cwnd(xprt, copied); + if (!xprt->nocong) { + xprt_adjust_cwnd(xprt, copied); + if (!req->rq_nresend) { + int timer = rpcproc_timer(clnt, task->tk_msg.rpc_proc); + if (timer) + rpc_update_rtt(&clnt->cl_rtt, timer, (long)jiffies - req->rq_xtime); + } #ifdef RPC_PROFILE /* Profile only reads for now */ @@ -934,6 +941,7 @@ xprt_timer(struct rpc_task *task) spin_lock(&xprt->sock_lock); if (req->rq_received) goto out; + req->rq_nresend++; xprt_adjust_cwnd(xprt, -ETIMEDOUT); dprintk("RPC: %4d xprt_timer (%s request)\n", @@ -982,15 +990,13 @@ xprt_transmit(struct rpc_task *task) if (!xprt_lock_write(xprt, task)) return; -#ifdef RPC_PROFILE - req->rq_xtime = jiffies; -#endif do_xprt_transmit(task); } static void do_xprt_transmit(struct rpc_task *task) { + struct rpc_clnt *clnt = task->tk_client; struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; int status, retry = 0; @@ -1002,6 +1008,7 @@ do_xprt_transmit(struct rpc_task *task) */ while (1) { xprt_clear_wspace(xprt); + req->rq_xtime = jiffies; status = xprt_sendmsg(xprt, req); if (status < 0) @@ -1065,7 +1072,21 @@ do_xprt_transmit(struct rpc_task *task) out_receive: dprintk("RPC: %4d xmit complete\n", task->tk_pid); /* Set the task's receive timeout value */ - task->tk_timeout = req->rq_timeout.to_current; + if (!xprt->nocong) { + int backoff; + task->tk_timeout = rpc_calc_rto(&clnt->cl_rtt, + rpcproc_timer(clnt, task->tk_msg.rpc_proc)); + /* If we are retransmitting, increment the timeout counter */ + backoff = req->rq_nresend; + if (backoff) { + if (backoff > 7) + backoff = 7; + task->tk_timeout <<= backoff; + } + if (task->tk_timeout > req->rq_timeout.to_maxval) + task->tk_timeout = req->rq_timeout.to_maxval; + } else + task->tk_timeout = req->rq_timeout.to_current; spin_lock_bh(&xprt->sock_lock); if (!req->rq_received) rpc_sleep_on(&xprt->pending, task, NULL, xprt_timer); -- cgit v1.2.3 From fa7b279e012a6d72d09e1e7873e0c0f8e06dbc15 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:53:43 -0700 Subject: [PATCH] RPC over UDP congestion control updates [2/8] Implement a count of the number of timeouts that have occured since we last recorded a successful reply from the server. For the moment this information is merely used in order to improve the estimate of whether or not the server is down. It will be used in patch 3/8 in order to improve the timeout backoff algorithm. --- include/linux/sunrpc/timer.h | 18 ++++++++++++++++++ net/sunrpc/clnt.c | 2 +- net/sunrpc/timer.c | 1 + net/sunrpc/xprt.c | 3 +++ 4 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sunrpc/timer.h b/include/linux/sunrpc/timer.h index 5f59407fed49..35b5a5c25170 100644 --- a/include/linux/sunrpc/timer.h +++ b/include/linux/sunrpc/timer.h @@ -9,10 +9,13 @@ #ifndef _LINUX_SUNRPC_TIMER_H #define _LINUX_SUNRPC_TIMER_H +#include + struct rpc_rtt { long timeo; /* default timeout value */ long srtt[5]; /* smoothed round trip time << 3 */ long sdrtt[5]; /* soothed medium deviation of RTT */ + atomic_t ntimeouts; /* Global count of the number of timeouts */ }; @@ -20,4 +23,19 @@ extern void rpc_init_rtt(struct rpc_rtt *rt, long timeo); extern void rpc_update_rtt(struct rpc_rtt *rt, int timer, long m); extern long rpc_calc_rto(struct rpc_rtt *rt, int timer); +static inline void rpc_inc_timeo(struct rpc_rtt *rt) +{ + atomic_inc(&rt->ntimeouts); +} + +static inline void rpc_clear_timeo(struct rpc_rtt *rt) +{ + atomic_set(&rt->ntimeouts, 0); +} + +static inline int rpc_ntimeo(struct rpc_rtt *rt) +{ + return atomic_read(&rt->ntimeouts); +} + #endif /* _LINUX_SUNRPC_TIMER_H */ diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c6acb6aa4bf7..657deb7640ee 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -671,7 +671,7 @@ call_timeout(struct rpc_task *task) rpc_exit(task, -EIO); return; } - if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) { + if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN) && rpc_ntimeo(&clnt->cl_rtt) > 7) { task->tk_flags |= RPC_CALL_MAJORSEEN; if (req) printk(KERN_NOTICE "%s: server %s not responding, still trying\n", diff --git a/net/sunrpc/timer.c b/net/sunrpc/timer.c index 00387735a34a..184017a23143 100644 --- a/net/sunrpc/timer.c +++ b/net/sunrpc/timer.c @@ -22,6 +22,7 @@ rpc_init_rtt(struct rpc_rtt *rt, long timeo) rt->srtt[i] = t; rt->sdrtt[i] = RPC_RTO_INIT; } + atomic_set(&rt->ntimeouts, 0); } void diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 6f791e001589..715143c65089 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -493,6 +493,8 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) int timer = rpcproc_timer(clnt, task->tk_msg.rpc_proc); if (timer) rpc_update_rtt(&clnt->cl_rtt, timer, (long)jiffies - req->rq_xtime); + } + rpc_clear_timeo(&clnt->cl_rtt); } #ifdef RPC_PROFILE @@ -942,6 +944,7 @@ xprt_timer(struct rpc_task *task) if (req->rq_received) goto out; req->rq_nresend++; + rpc_inc_timeo(&task->tk_client->cl_rtt); xprt_adjust_cwnd(xprt, -ETIMEDOUT); dprintk("RPC: %4d xprt_timer (%s request)\n", -- cgit v1.2.3 From 9ba7d22121274656979d735d5d40518420a08364 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:53:56 -0700 Subject: [PATCH] RPC over UDP congestion control updates [3/8] Improve the response to timeouts. As requests time out, we delay timing out the remaining requests (in fact we follow exponential backoff). This is done because we assume either that the round trip time has been underestimated, or that the network/server is congested, and we need to back off the resending of new requests. --- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/xprt.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 2759e979b351..7136dc08f1af 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -99,6 +99,7 @@ struct rpc_rqst { u32 rq_bytes_sent; /* Bytes we have sent */ long rq_xtime; /* when transmitted */ + int rq_ntimeo; int rq_nresend; }; #define rq_svec rq_snd_buf.head diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 715143c65089..507dc15dc032 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -77,6 +77,8 @@ # define RPCDBG_FACILITY RPCDBG_XPRT #endif +#define XPRT_MAX_BACKOFF (8) + /* * Local functions */ @@ -931,6 +933,21 @@ xprt_write_space(struct sock *sk) } } +/* + * Exponential backoff for UDP retries + */ +static inline int +xprt_expbackoff(struct rpc_task *task, struct rpc_rqst *req) +{ + int backoff; + + req->rq_ntimeo++; + backoff = min(rpc_ntimeo(&task->tk_client->cl_rtt), XPRT_MAX_BACKOFF); + if (req->rq_ntimeo < (1 << backoff)) + return 1; + return 0; +} + /* * RPC receive timeout handler. */ @@ -943,9 +960,16 @@ xprt_timer(struct rpc_task *task) spin_lock(&xprt->sock_lock); if (req->rq_received) goto out; + + if (!xprt->nocong) { + if (xprt_expbackoff(task, req)) { + rpc_add_timer(task, xprt_timer); + goto out_unlock; + } + rpc_inc_timeo(&task->tk_client->cl_rtt); + xprt_adjust_cwnd(req->rq_xprt, -ETIMEDOUT); + } req->rq_nresend++; - rpc_inc_timeo(&task->tk_client->cl_rtt); - xprt_adjust_cwnd(xprt, -ETIMEDOUT); dprintk("RPC: %4d xprt_timer (%s request)\n", task->tk_pid, req ? "pending" : "backlogged"); @@ -954,6 +978,7 @@ xprt_timer(struct rpc_task *task) out: task->tk_timeout = 0; rpc_wake_up_task(task); +out_unlock: spin_unlock(&xprt->sock_lock); } @@ -1076,16 +1101,9 @@ do_xprt_transmit(struct rpc_task *task) dprintk("RPC: %4d xmit complete\n", task->tk_pid); /* Set the task's receive timeout value */ if (!xprt->nocong) { - int backoff; task->tk_timeout = rpc_calc_rto(&clnt->cl_rtt, rpcproc_timer(clnt, task->tk_msg.rpc_proc)); - /* If we are retransmitting, increment the timeout counter */ - backoff = req->rq_nresend; - if (backoff) { - if (backoff > 7) - backoff = 7; - task->tk_timeout <<= backoff; - } + req->rq_ntimeo = 0; if (task->tk_timeout > req->rq_timeout.to_maxval) task->tk_timeout = req->rq_timeout.to_maxval; } else -- cgit v1.2.3 From 514349dc39fc12799e7886400d37ed24dc7529ba Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:54:24 -0700 Subject: [PATCH] RPC over UDP congestion control updates [5/8] Clean up the Van Jacobson network congestion control code. --- include/linux/sunrpc/xprt.h | 7 +++---- net/sunrpc/xprt.c | 46 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 7136dc08f1af..9cca8f4d2d80 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -19,7 +19,7 @@ * The transport code maintains an estimate on the maximum number of out- * standing RPC requests, using a smoothed version of the congestion * avoidance implemented in 44BSD. This is basically the Van Jacobson - * slow start algorithm: If a retransmit occurs, the congestion window is + * congestion algorithm: If a retransmit occurs, the congestion window is * halved; otherwise, it is incremented by 1/cwnd when * * - a reply is received and @@ -32,15 +32,13 @@ * Note: on machines with low memory we should probably use a smaller * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment * reassembly will frequently run out of memory. - * Come Linux 2.3, we'll handle fragments directly. */ #define RPC_MAXCONG 16 #define RPC_MAXREQS (RPC_MAXCONG + 1) #define RPC_CWNDSCALE 256 #define RPC_MAXCWND (RPC_MAXCONG * RPC_CWNDSCALE) #define RPC_INITCWND RPC_CWNDSCALE -#define RPCXPRT_CONGESTED(xprt) \ - ((xprt)->cong >= (xprt)->cwnd) +#define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd) /* Default timeout values */ #define RPC_MAX_UDP_TIMEOUT (60*HZ) @@ -83,6 +81,7 @@ struct rpc_rqst { struct rpc_task * rq_task; /* RPC task data */ __u32 rq_xid; /* request XID */ struct rpc_rqst * rq_next; /* free list */ + int rq_cong; /* has incremented xprt->cong */ int rq_received; /* receive completed */ struct list_head rq_list; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 1171cec764d2..3499bccf8789 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -89,6 +89,7 @@ static void xprt_disconnect(struct rpc_xprt *); static void xprt_reconn_status(struct rpc_task *task); static struct socket *xprt_create_socket(int, struct rpc_timeout *); static int xprt_bind_socket(struct rpc_xprt *, struct socket *); +static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); #ifdef RPC_DEBUG_DATA /* @@ -253,6 +254,40 @@ xprt_sendmsg(struct rpc_xprt *xprt, struct rpc_rqst *req) return result; } +/* + * Van Jacobson congestion avoidance. Check if the congestion window + * overflowed. Put the task to sleep if this is the case. + */ +static int +__xprt_get_cong(struct rpc_xprt *xprt, struct rpc_task *task) +{ + struct rpc_rqst *req = task->tk_rqstp; + + if (req->rq_cong) + return 1; + dprintk("RPC: %4d xprt_cwnd_limited cong = %ld cwnd = %ld\n", + task->tk_pid, xprt->cong, xprt->cwnd); + if (RPCXPRT_CONGESTED(xprt)) + return 0; + req->rq_cong = 1; + xprt->cong += RPC_CWNDSCALE; + return 1; +} + +/* + * Adjust the congestion window, and wake up the next task + * that has been sleeping due to congestion + */ +static void +__xprt_put_cong(struct rpc_xprt *xprt, struct rpc_rqst *req) +{ + if (!req->rq_cong) + return; + req->rq_cong = 0; + xprt->cong -= RPC_CWNDSCALE; + __xprt_lock_write_next(xprt); +} + /* * Adjust RPC congestion window * We use a time-smoothed congestion estimator to avoid heavy oscillation. @@ -1146,8 +1181,6 @@ xprt_reserve(struct rpc_task *task) if (task->tk_rqstp) return 0; - dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n", - task->tk_pid, xprt->cong, xprt->cwnd); spin_lock_bh(&xprt->xprt_lock); xprt_reserve_status(task); if (task->tk_rqstp) { @@ -1181,13 +1214,14 @@ xprt_reserve_status(struct rpc_task *task) } else if (task->tk_rqstp) { /* We've already been given a request slot: NOP */ } else { - if (RPCXPRT_CONGESTED(xprt) || !(req = xprt->free)) + if (!(req = xprt->free)) + goto out_nofree; + if (!(xprt->nocong || __xprt_get_cong(xprt, req))) goto out_nofree; /* OK: There's room for us. Grab a free slot and bump * congestion value */ xprt->free = req->rq_next; req->rq_next = NULL; - xprt->cong += RPC_CWNDSCALE; task->tk_rqstp = req; xprt_request_init(task, xprt); @@ -1252,9 +1286,7 @@ xprt_release(struct rpc_task *task) req->rq_next = xprt->free; xprt->free = req; - /* Decrease congestion value. */ - xprt->cong -= RPC_CWNDSCALE; - + __xprt_put_cong(xprt, req); xprt_clear_backlog(xprt); spin_unlock_bh(&xprt->xprt_lock); } -- cgit v1.2.3 From 4edf05550450554c33f25405c31fa0d90942bd69 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:54:38 -0700 Subject: [PATCH] RPC over UDP congestion control updates [6/8] Eliminate the arbitrary timeouts in xprt_adjust_cwnd(). Strict enforcement of the congestion avoidance algorithm as detailed in Van Jacobson's 1998 paper http://www-nrg.ee.lbl.gov/nrg-papers.html Congestion Avoidance and Control. --- include/linux/sunrpc/xprt.h | 9 ++++----- net/sunrpc/xprt.c | 23 ++++++----------------- 2 files changed, 10 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 9cca8f4d2d80..e123b110f719 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -33,11 +33,11 @@ * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment * reassembly will frequently run out of memory. */ -#define RPC_MAXCONG 16 -#define RPC_MAXREQS (RPC_MAXCONG + 1) -#define RPC_CWNDSCALE 256 +#define RPC_MAXCONG (16) +#define RPC_MAXREQS RPC_MAXCONG +#define RPC_CWNDSCALE (256) #define RPC_MAXCWND (RPC_MAXCONG * RPC_CWNDSCALE) -#define RPC_INITCWND RPC_CWNDSCALE +#define RPC_INITCWND (RPC_MAXCWND >> 1) #define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd) /* Default timeout values */ @@ -121,7 +121,6 @@ struct rpc_xprt { unsigned long cong; /* current congestion */ unsigned long cwnd; /* congestion window */ - unsigned long congtime; /* hold cwnd until then */ struct rpc_wait_queue sending; /* requests waiting to send */ struct rpc_wait_queue pending; /* requests in flight */ diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 3499bccf8789..cc2fa828ca10 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -304,30 +304,20 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) */ spin_lock(&xprt->xprt_lock); cwnd = xprt->cwnd; - if (result >= 0) { - if (xprt->cong < cwnd || time_before(jiffies, xprt->congtime)) - goto out; + if (result >= 0 && xprt->cong <= cwnd) { /* The (cwnd >> 1) term makes sure * the result gets rounded properly. */ cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd; if (cwnd > RPC_MAXCWND) cwnd = RPC_MAXCWND; - else - pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd); - xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE; - dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx, " - "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd, - (xprt->congtime-jiffies)*1000/HZ); + __xprt_lock_write_next(xprt); } else if (result == -ETIMEDOUT) { - if ((cwnd >>= 1) < RPC_CWNDSCALE) + cwnd >>= 1; + if (cwnd < RPC_CWNDSCALE) cwnd = RPC_CWNDSCALE; - xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE; - dprintk("RPC: cong %ld, cwnd was %ld, now %ld, " - "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd, - (xprt->congtime-jiffies)*1000/HZ); - pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd); } - + dprintk("RPC: cong %ld, cwnd was %ld, now %ld\n", + xprt->cong, xprt->cwnd, cwnd); xprt->cwnd = cwnd; out: spin_unlock(&xprt->xprt_lock); @@ -1344,7 +1334,6 @@ xprt_setup(struct socket *sock, int proto, xprt->nocong = 1; } else xprt->cwnd = RPC_INITCWND; - xprt->congtime = jiffies; spin_lock_init(&xprt->sock_lock); spin_lock_init(&xprt->xprt_lock); init_waitqueue_head(&xprt->cong_wait); -- cgit v1.2.3 From fefe89f4ca59a29b0ccf9ff57e8d77b0abbf3e60 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 16 Jul 2002 01:55:05 -0700 Subject: [PATCH] RPC over UDP congestion control updates [8/8] When determining who gets access to the socket, give priority to requests that are being resent. Despite the fact that congestion control now applies to resends, we still want to ensure that resends get ACKed as soon as possible (and before we start sending off new requests). --- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/xprt.c | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index e123b110f719..0a247f460ff7 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -123,6 +123,7 @@ struct rpc_xprt { unsigned long cwnd; /* congestion window */ struct rpc_wait_queue sending; /* requests waiting to send */ + struct rpc_wait_queue resend; /* requests waiting to resend */ struct rpc_wait_queue pending; /* requests in flight */ struct rpc_wait_queue backlog; /* waiting for slot */ struct rpc_rqst * free; /* free slots */ diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 1c50e4fbf8a6..fc3bb3841329 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -150,7 +150,10 @@ xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task) task->tk_pid, xprt->snd_task->tk_pid); task->tk_timeout = 0; task->tk_status = -EAGAIN; - rpc_sleep_on(&xprt->sending, task, NULL, NULL); + if (task->tk_rqstp->rq_nresend) + rpc_sleep_on(&xprt->resend, task, NULL, NULL); + else + rpc_sleep_on(&xprt->sending, task, NULL, NULL); } retval = xprt->snd_task == task; spin_unlock_bh(&xprt->sock_lock); @@ -166,9 +169,12 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) return; if (!xprt->nocong && RPCXPRT_CONGESTED(xprt)) return; - task = rpc_wake_up_next(&xprt->sending); - if (!task) - return; + task = rpc_wake_up_next(&xprt->resend); + if (!task) { + task = rpc_wake_up_next(&xprt->sending); + if (!task) + return; + } if (xprt->nocong || __xprt_get_cong(xprt, task)) xprt->snd_task = task; } @@ -1346,6 +1352,7 @@ xprt_setup(struct socket *sock, int proto, INIT_RPC_WAITQ(&xprt->pending, "xprt_pending"); INIT_RPC_WAITQ(&xprt->sending, "xprt_sending"); + INIT_RPC_WAITQ(&xprt->resend, "xprt_resend"); INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog"); /* initialize free list */ @@ -1477,6 +1484,7 @@ xprt_shutdown(struct rpc_xprt *xprt) { xprt->shutdown = 1; rpc_wake_up(&xprt->sending); + rpc_wake_up(&xprt->resend); rpc_wake_up(&xprt->pending); rpc_wake_up(&xprt->backlog); if (waitqueue_active(&xprt->cong_wait)) -- cgit v1.2.3