From: Greg Kroah-Hartman Date: Mon, 11 Nov 2019 09:43:51 +0000 (+0100) Subject: 4.19-stable patches X-Git-Tag: v4.4.201~29 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f6a9cd986fcc2671377cf329266d6a48ba48d73e;p=thirdparty%2Fkernel%2Fstable-queue.git 4.19-stable patches added patches: alsa-usb-audio-clean-up-check_input_term.patch alsa-usb-audio-fix-copy-paste-error-in-the-validator.patch alsa-usb-audio-fix-possible-null-dereference-at-create_yamaha_midi_quirk.patch alsa-usb-audio-more-validations-of-descriptor-units.patch alsa-usb-audio-remove-some-dead-code.patch alsa-usb-audio-remove-superfluous-blength-checks.patch alsa-usb-audio-simplify-parse_audio_unit.patch alsa-usb-audio-unify-the-release-of-usb_mixer_elem_info-objects.patch sched-fair-fix-low-cpu-usage-with-high-throttling-by-removing-expiration-of-cpu-local-slices.patch sched-fair-fix-wunused-but-set-variable-warnings.patch usbip-fix-vhci_urb_enqueue-urb-null-transfer-buffer-error-path.patch usbip-implement-sg-support-to-vhci-hcd-and-stub-driver.patch --- diff --git a/queue-4.19/alsa-usb-audio-clean-up-check_input_term.patch b/queue-4.19/alsa-usb-audio-clean-up-check_input_term.patch new file mode 100644 index 00000000000..bcd95691b94 --- /dev/null +++ b/queue-4.19/alsa-usb-audio-clean-up-check_input_term.patch @@ -0,0 +1,477 @@ +From e0ccdef92653f8867e2d1667facfd3c23699f540 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 23 Aug 2019 12:38:07 +0200 +Subject: ALSA: usb-audio: Clean up check_input_term() + +From: Takashi Iwai + +commit e0ccdef92653f8867e2d1667facfd3c23699f540 upstream. + +The primary changes in this patch are cleanups of __check_input_term() +and move to a non-nested switch-case block by evaluating the pair of +UAC version and the unit type, as we've done for parse_audio_unit(). +Also each parser is split into the function for readability. + +Now, a slight behavior change by this cleanup is the handling of +processing and extension units. Formerly we've dealt with them +differently between UAC1/2 and UAC3; the latter returns an error if no +input sources are available, while the former continues to parse. + +In this patch, unify the behavior in all cases: when input sources are +available, it parses recursively, then override the type and the id, +as well as channel information if not provided yet. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/mixer.c | 407 ++++++++++++++++++++++++++++-------------------------- + 1 file changed, 212 insertions(+), 195 deletions(-) + +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -773,224 +773,242 @@ static int uac_mixer_unit_get_channels(s + } + + /* +- * parse the source unit recursively until it reaches to a terminal +- * or a branched unit. ++ * Parse Input Terminal Unit + */ + static int __check_input_term(struct mixer_build *state, int id, +- struct usb_audio_term *term) ++ struct usb_audio_term *term); ++ ++static int parse_term_uac1_iterm_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac_input_terminal_descriptor *d = p1; ++ ++ term->type = le16_to_cpu(d->wTerminalType); ++ term->channels = d->bNrChannels; ++ term->chconfig = le16_to_cpu(d->wChannelConfig); ++ term->name = d->iTerminal; ++ return 0; ++} ++ ++static int parse_term_uac2_iterm_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) + { +- int protocol = state->mixer->protocol; ++ struct uac2_input_terminal_descriptor *d = p1; + int err; +- void *p1; +- unsigned char *hdr; + +- memset(term, 0, sizeof(*term)); +- for (;;) { +- /* a loop in the terminal chain? */ +- if (test_and_set_bit(id, state->termbitmap)) +- return -EINVAL; ++ /* call recursively to verify the referenced clock entity */ ++ err = __check_input_term(state, d->bCSourceID, term); ++ if (err < 0) ++ return err; + +- p1 = find_audio_control_unit(state, id); +- if (!p1) +- break; +- if (!snd_usb_validate_audio_desc(p1, protocol)) +- break; /* bad descriptor */ ++ /* save input term properties after recursion, ++ * to ensure they are not overriden by the recursion calls ++ */ ++ term->id = id; ++ term->type = le16_to_cpu(d->wTerminalType); ++ term->channels = d->bNrChannels; ++ term->chconfig = le32_to_cpu(d->bmChannelConfig); ++ term->name = d->iTerminal; ++ return 0; ++} + +- hdr = p1; +- term->id = id; ++static int parse_term_uac3_iterm_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac3_input_terminal_descriptor *d = p1; ++ int err; + +- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { +- switch (hdr[2]) { +- case UAC_INPUT_TERMINAL: +- if (protocol == UAC_VERSION_1) { +- struct uac_input_terminal_descriptor *d = p1; +- +- term->type = le16_to_cpu(d->wTerminalType); +- term->channels = d->bNrChannels; +- term->chconfig = le16_to_cpu(d->wChannelConfig); +- term->name = d->iTerminal; +- } else { /* UAC_VERSION_2 */ +- struct uac2_input_terminal_descriptor *d = p1; +- +- /* call recursively to verify that the +- * referenced clock entity is valid */ +- err = __check_input_term(state, d->bCSourceID, term); +- if (err < 0) +- return err; +- +- /* save input term properties after recursion, +- * to ensure they are not overriden by the +- * recursion calls */ +- term->id = id; +- term->type = le16_to_cpu(d->wTerminalType); +- term->channels = d->bNrChannels; +- term->chconfig = le32_to_cpu(d->bmChannelConfig); +- term->name = d->iTerminal; +- } +- return 0; +- case UAC_FEATURE_UNIT: { +- /* the header is the same for v1 and v2 */ +- struct uac_feature_unit_descriptor *d = p1; ++ /* call recursively to verify the referenced clock entity */ ++ err = __check_input_term(state, d->bCSourceID, term); ++ if (err < 0) ++ return err; + +- id = d->bSourceID; +- break; /* continue to parse */ +- } +- case UAC_MIXER_UNIT: { +- struct uac_mixer_unit_descriptor *d = p1; ++ /* save input term properties after recursion, ++ * to ensure they are not overriden by the recursion calls ++ */ ++ term->id = id; ++ term->type = le16_to_cpu(d->wTerminalType); + +- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ +- term->channels = uac_mixer_unit_bNrChannels(d); +- term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); +- term->name = uac_mixer_unit_iMixer(d); +- return 0; +- } +- case UAC_SELECTOR_UNIT: +- case UAC2_CLOCK_SELECTOR: { +- struct uac_selector_unit_descriptor *d = p1; +- /* call recursively to retrieve the channel info */ +- err = __check_input_term(state, d->baSourceID[0], term); +- if (err < 0) +- return err; +- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ +- term->id = id; +- term->name = uac_selector_unit_iSelector(d); +- return 0; +- } +- case UAC1_PROCESSING_UNIT: +- /* UAC2_EFFECT_UNIT */ +- if (protocol == UAC_VERSION_1) +- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ +- else /* UAC_VERSION_2 */ +- term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ +- /* fall through */ +- case UAC1_EXTENSION_UNIT: +- /* UAC2_PROCESSING_UNIT_V2 */ +- if (protocol == UAC_VERSION_1 && !term->type) +- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */ +- else if (protocol == UAC_VERSION_2 && !term->type) +- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ +- /* fall through */ +- case UAC2_EXTENSION_UNIT_V2: { +- struct uac_processing_unit_descriptor *d = p1; +- +- if (protocol == UAC_VERSION_2 && +- hdr[2] == UAC2_EFFECT_UNIT) { +- /* UAC2/UAC1 unit IDs overlap here in an +- * uncompatible way. Ignore this unit for now. +- */ +- return 0; +- } ++ err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); ++ if (err < 0) ++ return err; ++ term->channels = err; + +- if (d->bNrInPins) { +- id = d->baSourceID[0]; +- break; /* continue to parse */ +- } +- if (!term->type) +- term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */ ++ /* REVISIT: UAC3 IT doesn't have channels cfg */ ++ term->chconfig = 0; + +- term->channels = uac_processing_unit_bNrChannels(d); +- term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); +- term->name = uac_processing_unit_iProcessing(d, protocol); +- return 0; +- } +- case UAC2_CLOCK_SOURCE: { +- struct uac_clock_source_descriptor *d = p1; ++ term->name = le16_to_cpu(d->wTerminalDescrStr); ++ return 0; ++} + +- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ +- term->id = id; +- term->name = d->iClockSource; +- return 0; +- } +- default: +- return -ENODEV; +- } +- } else { /* UAC_VERSION_3 */ +- switch (hdr[2]) { +- case UAC_INPUT_TERMINAL: { +- struct uac3_input_terminal_descriptor *d = p1; +- +- /* call recursively to verify that the +- * referenced clock entity is valid */ +- err = __check_input_term(state, d->bCSourceID, term); +- if (err < 0) +- return err; +- +- /* save input term properties after recursion, +- * to ensure they are not overriden by the +- * recursion calls */ +- term->id = id; +- term->type = le16_to_cpu(d->wTerminalType); +- +- err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); +- if (err < 0) +- return err; +- term->channels = err; ++static int parse_term_mixer_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac_mixer_unit_descriptor *d = p1; ++ int protocol = state->mixer->protocol; ++ int err; + +- /* REVISIT: UAC3 IT doesn't have channels cfg */ +- term->chconfig = 0; ++ err = uac_mixer_unit_get_channels(state, d); ++ if (err <= 0) ++ return err; + +- term->name = le16_to_cpu(d->wTerminalDescrStr); +- return 0; +- } +- case UAC3_FEATURE_UNIT: { +- struct uac3_feature_unit_descriptor *d = p1; ++ term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ ++ term->channels = err; ++ if (protocol != UAC_VERSION_3) { ++ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); ++ term->name = uac_mixer_unit_iMixer(d); ++ } ++ return 0; ++} + +- id = d->bSourceID; +- break; /* continue to parse */ +- } +- case UAC3_CLOCK_SOURCE: { +- struct uac3_clock_source_descriptor *d = p1; ++static int parse_term_selector_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac_selector_unit_descriptor *d = p1; ++ int err; + +- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ +- term->id = id; +- term->name = le16_to_cpu(d->wClockSourceStr); +- return 0; +- } +- case UAC3_MIXER_UNIT: { +- struct uac_mixer_unit_descriptor *d = p1; ++ /* call recursively to retrieve the channel info */ ++ err = __check_input_term(state, d->baSourceID[0], term); ++ if (err < 0) ++ return err; ++ term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ ++ term->id = id; ++ if (state->mixer->protocol != UAC_VERSION_3) ++ term->name = uac_selector_unit_iSelector(d); ++ return 0; ++} + +- err = uac_mixer_unit_get_channels(state, d); +- if (err <= 0) +- return err; ++static int parse_term_proc_unit(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id, int vtype) ++{ ++ struct uac_processing_unit_descriptor *d = p1; ++ int protocol = state->mixer->protocol; ++ int err; + +- term->channels = err; +- term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ ++ if (d->bNrInPins) { ++ /* call recursively to retrieve the channel info */ ++ err = __check_input_term(state, d->baSourceID[0], term); ++ if (err < 0) ++ return err; ++ } + +- return 0; +- } +- case UAC3_SELECTOR_UNIT: +- case UAC3_CLOCK_SELECTOR: { +- struct uac_selector_unit_descriptor *d = p1; +- /* call recursively to retrieve the channel info */ +- err = __check_input_term(state, d->baSourceID[0], term); +- if (err < 0) +- return err; +- term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ +- term->id = id; +- term->name = 0; /* TODO: UAC3 Class-specific strings */ ++ term->type = vtype << 16; /* virtual type */ ++ term->id = id; + +- return 0; +- } +- case UAC3_PROCESSING_UNIT: { +- struct uac_processing_unit_descriptor *d = p1; ++ if (protocol == UAC_VERSION_3) ++ return 0; + +- if (!d->bNrInPins) +- return -EINVAL; ++ if (!term->channels) { ++ term->channels = uac_processing_unit_bNrChannels(d); ++ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); ++ } ++ term->name = uac_processing_unit_iProcessing(d, protocol); ++ return 0; ++} ++ ++static int parse_term_uac2_clock_source(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac_clock_source_descriptor *d = p1; ++ ++ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ ++ term->id = id; ++ term->name = d->iClockSource; ++ return 0; ++} + +- /* call recursively to retrieve the channel info */ +- err = __check_input_term(state, d->baSourceID[0], term); +- if (err < 0) +- return err; +- +- term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ +- term->id = id; +- term->name = 0; /* TODO: UAC3 Class-specific strings */ ++static int parse_term_uac3_clock_source(struct mixer_build *state, ++ struct usb_audio_term *term, ++ void *p1, int id) ++{ ++ struct uac3_clock_source_descriptor *d = p1; ++ ++ term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ ++ term->id = id; ++ term->name = le16_to_cpu(d->wClockSourceStr); ++ return 0; ++} + +- return 0; +- } +- default: +- return -ENODEV; +- } ++#define PTYPE(a, b) ((a) << 8 | (b)) ++ ++/* ++ * parse the source unit recursively until it reaches to a terminal ++ * or a branched unit. ++ */ ++static int __check_input_term(struct mixer_build *state, int id, ++ struct usb_audio_term *term) ++{ ++ int protocol = state->mixer->protocol; ++ void *p1; ++ unsigned char *hdr; ++ ++ for (;;) { ++ /* a loop in the terminal chain? */ ++ if (test_and_set_bit(id, state->termbitmap)) ++ return -EINVAL; ++ ++ p1 = find_audio_control_unit(state, id); ++ if (!p1) ++ break; ++ if (!snd_usb_validate_audio_desc(p1, protocol)) ++ break; /* bad descriptor */ ++ ++ hdr = p1; ++ term->id = id; ++ ++ switch (PTYPE(protocol, hdr[2])) { ++ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): { ++ /* the header is the same for all versions */ ++ struct uac_feature_unit_descriptor *d = p1; ++ ++ id = d->bSourceID; ++ break; /* continue to parse */ ++ } ++ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): ++ return parse_term_uac1_iterm_unit(state, term, p1, id); ++ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): ++ return parse_term_uac2_iterm_unit(state, term, p1, id); ++ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): ++ return parse_term_uac3_iterm_unit(state, term, p1, id); ++ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): ++ return parse_term_mixer_unit(state, term, p1, id); ++ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): ++ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): ++ return parse_term_selector_unit(state, term, p1, id); ++ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): ++ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): ++ return parse_term_proc_unit(state, term, p1, id, ++ UAC3_PROCESSING_UNIT); ++ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): ++ return parse_term_proc_unit(state, term, p1, id, ++ UAC3_EFFECT_UNIT); ++ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): ++ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): ++ return parse_term_proc_unit(state, term, p1, id, ++ UAC3_EXTENSION_UNIT); ++ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): ++ return parse_term_uac2_clock_source(state, term, p1, id); ++ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): ++ return parse_term_uac3_clock_source(state, term, p1, id); ++ default: ++ return -ENODEV; + } + } + return -ENODEV; +@@ -2731,7 +2749,6 @@ static int parse_audio_unit(struct mixer + return 0; /* skip invalid unit */ + } + +-#define PTYPE(a, b) ((a) << 8 | (b)) + switch (PTYPE(protocol, p1[2])) { + case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): + case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): diff --git a/queue-4.19/alsa-usb-audio-fix-copy-paste-error-in-the-validator.patch b/queue-4.19/alsa-usb-audio-fix-copy-paste-error-in-the-validator.patch new file mode 100644 index 00000000000..bc931ea9141 --- /dev/null +++ b/queue-4.19/alsa-usb-audio-fix-copy-paste-error-in-the-validator.patch @@ -0,0 +1,35 @@ +From ba8bf0967a154796be15c4983603aad0b05c3138 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 22 Oct 2019 17:45:14 +0200 +Subject: ALSA: usb-audio: Fix copy&paste error in the validator + +From: Takashi Iwai + +commit ba8bf0967a154796be15c4983603aad0b05c3138 upstream. + +The recently introduced USB-audio descriptor validator had a stupid +copy&paste error that may lead to an unexpected overlook of too short +descriptors for processing and extension units. It's likely the cause +of the report triggered by syzkaller fuzzer. Let's fix it. + +Fixes: 57f8770620e9 ("ALSA: usb-audio: More validations of descriptor units") +Reported-by: syzbot+0620f79a1978b1133fd7@syzkaller.appspotmail.com +Link: https://lore.kernel.org/r/s5hsgnkdbsl.wl-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/validate.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/usb/validate.c ++++ b/sound/usb/validate.c +@@ -75,7 +75,7 @@ static bool validate_processing_unit(con + + if (d->bLength < sizeof(*d)) + return false; +- len = d->bLength < sizeof(*d) + d->bNrInPins; ++ len = sizeof(*d) + d->bNrInPins; + if (d->bLength < len) + return false; + switch (v->protocol) { diff --git a/queue-4.19/alsa-usb-audio-fix-possible-null-dereference-at-create_yamaha_midi_quirk.patch b/queue-4.19/alsa-usb-audio-fix-possible-null-dereference-at-create_yamaha_midi_quirk.patch new file mode 100644 index 00000000000..0020da750ed --- /dev/null +++ b/queue-4.19/alsa-usb-audio-fix-possible-null-dereference-at-create_yamaha_midi_quirk.patch @@ -0,0 +1,35 @@ +From 60849562a5db4a1eee2160167e4dce4590d3eafe Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 26 Aug 2019 13:55:21 +0200 +Subject: ALSA: usb-audio: Fix possible NULL dereference at create_yamaha_midi_quirk() + +From: Takashi Iwai + +commit 60849562a5db4a1eee2160167e4dce4590d3eafe upstream. + +The previous addition of descriptor validation may lead to a NULL +dereference at create_yamaha_midi_quirk() when either injd or outjd is +NULL. Add proper non-NULL checks. + +Fixes: 57f8770620e9 ("ALSA: usb-audio: More validations of descriptor units") +Reported-by: Dan Carpenter +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/quirks.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -259,8 +259,8 @@ static int create_yamaha_midi_quirk(stru + NULL, USB_MS_MIDI_OUT_JACK); + if (!injd && !outjd) + return -ENODEV; +- if (!snd_usb_validate_midi_desc(injd) || +- !snd_usb_validate_midi_desc(outjd)) ++ if (!(injd && snd_usb_validate_midi_desc(injd)) || ++ !(outjd && snd_usb_validate_midi_desc(outjd))) + return -ENODEV; + if (injd && (injd->bLength < 5 || + (injd->bJackType != USB_MS_EMBEDDED && diff --git a/queue-4.19/alsa-usb-audio-more-validations-of-descriptor-units.patch b/queue-4.19/alsa-usb-audio-more-validations-of-descriptor-units.patch new file mode 100644 index 00000000000..12932956acf --- /dev/null +++ b/queue-4.19/alsa-usb-audio-more-validations-of-descriptor-units.patch @@ -0,0 +1,533 @@ +From 57f8770620e9b51c61089751f0b5ad3dbe376ff2 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 20 Aug 2019 17:17:09 +0200 +Subject: ALSA: usb-audio: More validations of descriptor units + +From: Takashi Iwai + +commit 57f8770620e9b51c61089751f0b5ad3dbe376ff2 upstream. + +Introduce a new helper to validate each audio descriptor unit before +and check the unit before actually accessing it. This should harden +against the OOB access cases with malformed descriptors that have been +recently frequently reported by fuzzers. + +The existing descriptor checks are still kept although they become +superfluous after this patch. They'll be cleaned up eventually +later. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/Makefile | 3 + sound/usb/helper.h | 4 + sound/usb/mixer.c | 10 + + sound/usb/power.c | 2 + sound/usb/quirks.c | 3 + sound/usb/stream.c | 25 +-- + sound/usb/validate.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 7 files changed, 366 insertions(+), 13 deletions(-) + +--- a/sound/usb/Makefile ++++ b/sound/usb/Makefile +@@ -16,7 +16,8 @@ snd-usb-audio-objs := card.o \ + power.o \ + proc.o \ + quirks.o \ +- stream.o ++ stream.o \ ++ validate.o + + snd-usbmidi-lib-objs := midi.o + +--- a/sound/usb/helper.h ++++ b/sound/usb/helper.h +@@ -30,4 +30,8 @@ static inline int snd_usb_ctrl_intf(stru + return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber; + } + ++/* in validate.c */ ++bool snd_usb_validate_audio_desc(void *p, int protocol); ++bool snd_usb_validate_midi_desc(void *p); ++ + #endif /* __USBAUDIO_HELPER_H */ +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -800,6 +800,8 @@ static int __check_input_term(struct mix + p1 = find_audio_control_unit(state, id); + if (!p1) + break; ++ if (!snd_usb_validate_audio_desc(p1, protocol)) ++ break; /* bad descriptor */ + + hdr = p1; + term->id = id; +@@ -2794,6 +2796,11 @@ static int parse_audio_unit(struct mixer + return -EINVAL; + } + ++ if (!snd_usb_validate_audio_desc(p1, protocol)) { ++ usb_audio_dbg(state->chip, "invalid unit %d\n", unitid); ++ return 0; /* skip invalid unit */ ++ } ++ + if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { + switch (p1[2]) { + case UAC_INPUT_TERMINAL: +@@ -3164,6 +3171,9 @@ static int snd_usb_mixer_controls(struct + while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, + mixer->hostif->extralen, + p, UAC_OUTPUT_TERMINAL)) != NULL) { ++ if (!snd_usb_validate_audio_desc(p, mixer->protocol)) ++ continue; /* skip invalid descriptor */ ++ + if (mixer->protocol == UAC_VERSION_1) { + struct uac1_output_terminal_descriptor *desc = p; + +--- a/sound/usb/power.c ++++ b/sound/usb/power.c +@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_hos + struct uac3_power_domain_descriptor *pd_desc = p; + int i; + ++ if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3)) ++ continue; + for (i = 0; i < pd_desc->bNrEntities; i++) { + if (pd_desc->baEntityID[i] == id) { + pd->pd_id = pd_desc->bPowerDomainID; +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -259,6 +259,9 @@ static int create_yamaha_midi_quirk(stru + NULL, USB_MS_MIDI_OUT_JACK); + if (!injd && !outjd) + return -ENODEV; ++ if (!snd_usb_validate_midi_desc(injd) || ++ !snd_usb_validate_midi_desc(outjd)) ++ return -ENODEV; + if (injd && (injd->bLength < 5 || + (injd->bJackType != USB_MS_EMBEDDED && + injd->bJackType != USB_MS_EXTERNAL))) +--- a/sound/usb/stream.c ++++ b/sound/usb/stream.c +@@ -637,16 +637,14 @@ static int parse_uac_endpoint_attributes + */ + static void * + snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, +- int terminal_id, bool uac23) ++ int terminal_id, int protocol) + { + struct uac2_input_terminal_descriptor *term = NULL; +- size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) : +- sizeof(struct uac_input_terminal_descriptor); + + while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, + ctrl_iface->extralen, + term, UAC_INPUT_TERMINAL))) { +- if (term->bLength < minlen) ++ if (!snd_usb_validate_audio_desc(term, protocol)) + continue; + if (term->bTerminalID == terminal_id) + return term; +@@ -657,7 +655,7 @@ snd_usb_find_input_terminal_descriptor(s + + static void * + snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, +- int terminal_id) ++ int terminal_id, int protocol) + { + /* OK to use with both UAC2 and UAC3 */ + struct uac2_output_terminal_descriptor *term = NULL; +@@ -665,8 +663,9 @@ snd_usb_find_output_terminal_descriptor( + while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, + ctrl_iface->extralen, + term, UAC_OUTPUT_TERMINAL))) { +- if (term->bLength >= sizeof(*term) && +- term->bTerminalID == terminal_id) ++ if (!snd_usb_validate_audio_desc(term, protocol)) ++ continue; ++ if (term->bTerminalID == terminal_id) + return term; + } + +@@ -741,7 +740,7 @@ snd_usb_get_audioformat_uac12(struct snd + + iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink, +- false); ++ protocol); + if (iterm) { + num_channels = iterm->bNrChannels; + chconfig = le16_to_cpu(iterm->wChannelConfig); +@@ -777,7 +776,7 @@ snd_usb_get_audioformat_uac12(struct snd + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink, +- true); ++ protocol); + if (input_term) { + clock = input_term->bCSourceID; + if (!chconfig && (num_channels == input_term->bNrChannels)) +@@ -786,7 +785,8 @@ snd_usb_get_audioformat_uac12(struct snd + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, +- as->bTerminalLink); ++ as->bTerminalLink, ++ protocol); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; +@@ -1012,14 +1012,15 @@ snd_usb_get_audioformat_uac3(struct snd_ + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink, +- true); ++ UAC_VERSION_3); + if (input_term) { + clock = input_term->bCSourceID; + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, +- as->bTerminalLink); ++ as->bTerminalLink, ++ UAC_VERSION_3); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; +--- /dev/null ++++ b/sound/usb/validate.c +@@ -0,0 +1,332 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// ++// Validation of USB-audio class descriptors ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "usbaudio.h" ++#include "helper.h" ++ ++struct usb_desc_validator { ++ unsigned char protocol; ++ unsigned char type; ++ bool (*func)(const void *p, const struct usb_desc_validator *v); ++ size_t size; ++}; ++ ++#define UAC_VERSION_ALL (unsigned char)(-1) ++ ++/* UAC1 only */ ++static bool validate_uac1_header(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac1_ac_header_descriptor *d = p; ++ ++ return d->bLength >= sizeof(*d) && ++ d->bLength >= sizeof(*d) + d->bInCollection; ++} ++ ++/* for mixer unit; covering all UACs */ ++static bool validate_mixer_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac_mixer_unit_descriptor *d = p; ++ size_t len; ++ ++ if (d->bLength < sizeof(*d) || !d->bNrInPins) ++ return false; ++ len = sizeof(*d) + d->bNrInPins; ++ /* We can't determine the bitmap size only from this unit descriptor, ++ * so just check with the remaining length. ++ * The actual bitmap is checked at mixer unit parser. ++ */ ++ switch (v->protocol) { ++ case UAC_VERSION_1: ++ default: ++ len += 2 + 1; /* wChannelConfig, iChannelNames */ ++ /* bmControls[n*m] */ ++ len += 1; /* iMixer */ ++ break; ++ case UAC_VERSION_2: ++ len += 4 + 1; /* bmChannelConfig, iChannelNames */ ++ /* bmMixerControls[n*m] */ ++ len += 1 + 1; /* bmControls, iMixer */ ++ break; ++ case UAC_VERSION_3: ++ len += 2; /* wClusterDescrID */ ++ /* bmMixerControls[n*m] */ ++ break; ++ } ++ return d->bLength >= len; ++} ++ ++/* both for processing and extension units; covering all UACs */ ++static bool validate_processing_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac_processing_unit_descriptor *d = p; ++ const unsigned char *hdr = p; ++ size_t len, m; ++ ++ if (d->bLength < sizeof(*d)) ++ return false; ++ len = d->bLength < sizeof(*d) + d->bNrInPins; ++ if (d->bLength < len) ++ return false; ++ switch (v->protocol) { ++ case UAC_VERSION_1: ++ default: ++ /* bNrChannels, wChannelConfig, iChannelNames, bControlSize */ ++ len += 1 + 2 + 1 + 1; ++ if (d->bLength < len) /* bControlSize */ ++ return false; ++ m = hdr[len]; ++ len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */ ++ break; ++ case UAC_VERSION_2: ++ /* bNrChannels, bmChannelConfig, iChannelNames */ ++ len += 1 + 4 + 1; ++ if (v->type == UAC2_PROCESSING_UNIT_V2) ++ len += 2; /* bmControls -- 2 bytes for PU */ ++ else ++ len += 1; /* bmControls -- 1 byte for EU */ ++ len += 1; /* iProcessing */ ++ break; ++ case UAC_VERSION_3: ++ /* wProcessingDescrStr, bmControls */ ++ len += 2 + 4; ++ break; ++ } ++ if (d->bLength < len) ++ return false; ++ ++ switch (v->protocol) { ++ case UAC_VERSION_1: ++ default: ++ if (v->type == UAC1_EXTENSION_UNIT) ++ return true; /* OK */ ++ switch (d->wProcessType) { ++ case UAC_PROCESS_UP_DOWNMIX: ++ case UAC_PROCESS_DOLBY_PROLOGIC: ++ if (d->bLength < len + 1) /* bNrModes */ ++ return false; ++ m = hdr[len]; ++ len += 1 + m * 2; /* bNrModes, waModes(n) */ ++ break; ++ default: ++ break; ++ } ++ break; ++ case UAC_VERSION_2: ++ if (v->type == UAC2_EXTENSION_UNIT_V2) ++ return true; /* OK */ ++ switch (d->wProcessType) { ++ case UAC2_PROCESS_UP_DOWNMIX: ++ case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */ ++ if (d->bLength < len + 1) /* bNrModes */ ++ return false; ++ m = hdr[len]; ++ len += 1 + m * 4; /* bNrModes, daModes(n) */ ++ break; ++ default: ++ break; ++ } ++ break; ++ case UAC_VERSION_3: ++ if (v->type == UAC3_EXTENSION_UNIT) { ++ len += 2; /* wClusterDescrID */ ++ break; ++ } ++ switch (d->wProcessType) { ++ case UAC3_PROCESS_UP_DOWNMIX: ++ if (d->bLength < len + 1) /* bNrModes */ ++ return false; ++ m = hdr[len]; ++ len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */ ++ break; ++ case UAC3_PROCESS_MULTI_FUNCTION: ++ len += 2 + 4; /* wClusterDescrID, bmAlgorighms */ ++ break; ++ default: ++ break; ++ } ++ break; ++ } ++ if (d->bLength < len) ++ return false; ++ ++ return true; ++} ++ ++/* both for selector and clock selector units; covering all UACs */ ++static bool validate_selector_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac_selector_unit_descriptor *d = p; ++ size_t len; ++ ++ if (d->bLength < sizeof(*d)) ++ return false; ++ len = sizeof(*d) + d->bNrInPins; ++ switch (v->protocol) { ++ case UAC_VERSION_1: ++ default: ++ len += 1; /* iSelector */ ++ break; ++ case UAC_VERSION_2: ++ len += 1 + 1; /* bmControls, iSelector */ ++ break; ++ case UAC_VERSION_3: ++ len += 4 + 2; /* bmControls, wSelectorDescrStr */ ++ break; ++ } ++ return d->bLength >= len; ++} ++ ++static bool validate_uac1_feature_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac_feature_unit_descriptor *d = p; ++ ++ if (d->bLength < sizeof(*d) || !d->bControlSize) ++ return false; ++ /* at least bmaControls(0) for master channel + iFeature */ ++ return d->bLength >= sizeof(*d) + d->bControlSize + 1; ++} ++ ++static bool validate_uac2_feature_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac2_feature_unit_descriptor *d = p; ++ ++ if (d->bLength < sizeof(*d)) ++ return false; ++ /* at least bmaControls(0) for master channel + iFeature */ ++ return d->bLength >= sizeof(*d) + 4 + 1; ++} ++ ++static bool validate_uac3_feature_unit(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct uac3_feature_unit_descriptor *d = p; ++ ++ if (d->bLength < sizeof(*d)) ++ return false; ++ /* at least bmaControls(0) for master channel + wFeatureDescrStr */ ++ return d->bLength >= sizeof(*d) + 4 + 2; ++} ++ ++static bool validate_midi_out_jack(const void *p, ++ const struct usb_desc_validator *v) ++{ ++ const struct usb_midi_out_jack_descriptor *d = p; ++ ++ return d->bLength >= sizeof(*d) && ++ d->bLength >= sizeof(*d) + d->bNrInputPins * 2; ++} ++ ++#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) } ++#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) } ++ ++static struct usb_desc_validator audio_validators[] = { ++ /* UAC1 */ ++ FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header), ++ FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL, ++ struct uac_input_terminal_descriptor), ++ FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL, ++ struct uac1_output_terminal_descriptor), ++ FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit), ++ FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit), ++ FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit), ++ FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit), ++ FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit), ++ ++ /* UAC2 */ ++ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor), ++ FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL, ++ struct uac2_input_terminal_descriptor), ++ FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL, ++ struct uac2_output_terminal_descriptor), ++ FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit), ++ FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit), ++ FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit), ++ /* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */ ++ FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit), ++ FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit), ++ FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE, ++ struct uac_clock_source_descriptor), ++ FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit), ++ FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER, ++ struct uac_clock_multiplier_descriptor), ++ /* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */ ++ ++ /* UAC3 */ ++ FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor), ++ FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL, ++ struct uac3_input_terminal_descriptor), ++ FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL, ++ struct uac3_output_terminal_descriptor), ++ /* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */ ++ FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit), ++ FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit), ++ FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit), ++ /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */ ++ FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit), ++ FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit), ++ FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE, ++ struct uac3_clock_source_descriptor), ++ FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit), ++ FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER, ++ struct uac3_clock_multiplier_descriptor), ++ /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */ ++ /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */ ++ { } /* terminator */ ++}; ++ ++static struct usb_desc_validator midi_validators[] = { ++ FIXED(UAC_VERSION_ALL, USB_MS_HEADER, ++ struct usb_ms_header_descriptor), ++ FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK, ++ struct usb_midi_in_jack_descriptor), ++ FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK, ++ validate_midi_out_jack), ++ { } /* terminator */ ++}; ++ ++ ++/* Validate the given unit descriptor, return true if it's OK */ ++static bool validate_desc(unsigned char *hdr, int protocol, ++ const struct usb_desc_validator *v) ++{ ++ if (hdr[1] != USB_DT_CS_INTERFACE) ++ return true; /* don't care */ ++ ++ for (; v->type; v++) { ++ if (v->type == hdr[2] && ++ (v->protocol == UAC_VERSION_ALL || ++ v->protocol == protocol)) { ++ if (v->func) ++ return v->func(hdr, v); ++ /* check for the fixed size */ ++ return hdr[0] >= v->size; ++ } ++ } ++ ++ return true; /* not matching, skip validation */ ++} ++ ++bool snd_usb_validate_audio_desc(void *p, int protocol) ++{ ++ return validate_desc(p, protocol, audio_validators); ++} ++ ++bool snd_usb_validate_midi_desc(void *p) ++{ ++ return validate_desc(p, UAC_VERSION_1, midi_validators); ++} ++ diff --git a/queue-4.19/alsa-usb-audio-remove-some-dead-code.patch b/queue-4.19/alsa-usb-audio-remove-some-dead-code.patch new file mode 100644 index 00000000000..20c78f71da6 --- /dev/null +++ b/queue-4.19/alsa-usb-audio-remove-some-dead-code.patch @@ -0,0 +1,32 @@ +From b39e077fcb283dd96dd251a3abeba585402c61fe Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Mon, 26 Aug 2019 16:45:50 +0300 +Subject: ALSA: usb-audio: remove some dead code + +From: Dan Carpenter + +commit b39e077fcb283dd96dd251a3abeba585402c61fe upstream. + +We recently cleaned up the error handling in commit 52c3e317a857 ("ALSA: +usb-audio: Unify the release of usb_mixer_elem_info objects") but +accidentally left this stray return. + +Fixes: 52c3e317a857 ("ALSA: usb-audio: Unify the release of usb_mixer_elem_info objects") +Signed-off-by: Dan Carpenter +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/mixer.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -2670,7 +2670,6 @@ static int parse_audio_selector_unit(str + usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + err = -ENOMEM; + goto error_name; +- return -ENOMEM; + } + kctl->private_value = (unsigned long)namelist; + kctl->private_free = usb_mixer_selector_elem_free; diff --git a/queue-4.19/alsa-usb-audio-remove-superfluous-blength-checks.patch b/queue-4.19/alsa-usb-audio-remove-superfluous-blength-checks.patch new file mode 100644 index 00000000000..f4481b0ee0e --- /dev/null +++ b/queue-4.19/alsa-usb-audio-remove-superfluous-blength-checks.patch @@ -0,0 +1,238 @@ +From b8e4f1fdfa422398c2d6c47bfb7d1feb3046d70a Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 22 Aug 2019 09:25:27 +0200 +Subject: ALSA: usb-audio: Remove superfluous bLength checks + +From: Takashi Iwai + +commit b8e4f1fdfa422398c2d6c47bfb7d1feb3046d70a upstream. + +Now that we got the more comprehensive validation code for USB-audio +descriptors, the check of overflow in each descriptor unit parser +became superfluous. Drop some of the obvious cases. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/clock.c | 14 +++------ + sound/usb/mixer.c | 84 ------------------------------------------------------ + 2 files changed, 6 insertions(+), 92 deletions(-) + +--- a/sound/usb/clock.c ++++ b/sound/usb/clock.c +@@ -52,39 +52,37 @@ static void *find_uac_clock_desc(struct + static bool validate_clock_source_v2(void *p, int id) + { + struct uac_clock_source_descriptor *cs = p; +- return cs->bLength == sizeof(*cs) && cs->bClockID == id; ++ return cs->bClockID == id; + } + + static bool validate_clock_source_v3(void *p, int id) + { + struct uac3_clock_source_descriptor *cs = p; +- return cs->bLength == sizeof(*cs) && cs->bClockID == id; ++ return cs->bClockID == id; + } + + static bool validate_clock_selector_v2(void *p, int id) + { + struct uac_clock_selector_descriptor *cs = p; +- return cs->bLength >= sizeof(*cs) && cs->bClockID == id && +- cs->bLength == 7 + cs->bNrInPins; ++ return cs->bClockID == id; + } + + static bool validate_clock_selector_v3(void *p, int id) + { + struct uac3_clock_selector_descriptor *cs = p; +- return cs->bLength >= sizeof(*cs) && cs->bClockID == id && +- cs->bLength == 11 + cs->bNrInPins; ++ return cs->bClockID == id; + } + + static bool validate_clock_multiplier_v2(void *p, int id) + { + struct uac_clock_multiplier_descriptor *cs = p; +- return cs->bLength == sizeof(*cs) && cs->bClockID == id; ++ return cs->bClockID == id; + } + + static bool validate_clock_multiplier_v3(void *p, int id) + { + struct uac3_clock_multiplier_descriptor *cs = p; +- return cs->bLength == sizeof(*cs) && cs->bClockID == id; ++ return cs->bClockID == id; + } + + #define DEFINE_FIND_HELPER(name, obj, validator, type) \ +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -755,13 +755,6 @@ static int uac_mixer_unit_get_channels(s + { + int mu_channels; + +- if (desc->bLength < sizeof(*desc)) +- return -EINVAL; +- if (!desc->bNrInPins) +- return -EINVAL; +- if (desc->bLength < sizeof(*desc) + desc->bNrInPins) +- return -EINVAL; +- + switch (state->mixer->protocol) { + case UAC_VERSION_1: + case UAC_VERSION_2: +@@ -1796,13 +1789,6 @@ static int parse_clock_source_unit(struc + if (state->mixer->protocol != UAC_VERSION_2) + return -EINVAL; + +- if (hdr->bLength != sizeof(*hdr)) { +- usb_audio_dbg(state->chip, +- "Bogus clock source descriptor length of %d, ignoring.\n", +- hdr->bLength); +- return 0; +- } +- + /* + * The only property of this unit we are interested in is the + * clock source validity. If that isn't readable, just bail out. +@@ -1861,62 +1847,20 @@ static int parse_audio_feature_unit(stru + __u8 *bmaControls; + + if (state->mixer->protocol == UAC_VERSION_1) { +- if (hdr->bLength < 7) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + csize = hdr->bControlSize; +- if (!csize) { +- usb_audio_dbg(state->chip, +- "unit %u: invalid bControlSize == 0\n", +- unitid); +- return -EINVAL; +- } + channels = (hdr->bLength - 7) / csize - 1; + bmaControls = hdr->bmaControls; +- if (hdr->bLength < 7 + csize) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + } else if (state->mixer->protocol == UAC_VERSION_2) { + struct uac2_feature_unit_descriptor *ftr = _ftr; +- if (hdr->bLength < 6) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + csize = 4; + channels = (hdr->bLength - 6) / 4 - 1; + bmaControls = ftr->bmaControls; +- if (hdr->bLength < 6 + csize) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + } else { /* UAC_VERSION_3 */ + struct uac3_feature_unit_descriptor *ftr = _ftr; + +- if (hdr->bLength < 7) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + csize = 4; + channels = (ftr->bLength - 7) / 4 - 1; + bmaControls = ftr->bmaControls; +- if (hdr->bLength < 7 + csize) { +- usb_audio_err(state->chip, +- "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", +- unitid); +- return -EINVAL; +- } + } + + /* parse the source unit */ +@@ -2120,15 +2064,11 @@ static int parse_audio_input_terminal(st + + if (state->mixer->protocol == UAC_VERSION_2) { + struct uac2_input_terminal_descriptor *d_v2 = raw_desc; +- if (d_v2->bLength < sizeof(*d_v2)) +- return -EINVAL; + control = UAC2_TE_CONNECTOR; + term_id = d_v2->bTerminalID; + bmctls = le16_to_cpu(d_v2->bmControls); + } else if (state->mixer->protocol == UAC_VERSION_3) { + struct uac3_input_terminal_descriptor *d_v3 = raw_desc; +- if (d_v3->bLength < sizeof(*d_v3)) +- return -EINVAL; + control = UAC3_TE_INSERTION; + term_id = d_v3->bTerminalID; + bmctls = le32_to_cpu(d_v3->bmControls); +@@ -2390,18 +2330,7 @@ static int build_audio_procunit(struct m + const char *name = extension_unit ? + "Extension Unit" : "Processing Unit"; + +- if (desc->bLength < 13) { +- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); +- return -EINVAL; +- } +- + num_ins = desc->bNrInPins; +- if (desc->bLength < 13 + num_ins || +- desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) { +- usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); +- return -EINVAL; +- } +- + for (i = 0; i < num_ins; i++) { + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) +@@ -2656,13 +2585,6 @@ static int parse_audio_selector_unit(str + const struct usbmix_name_map *map; + char **namelist; + +- if (desc->bLength < 5 || !desc->bNrInPins || +- desc->bLength < 5 + desc->bNrInPins) { +- usb_audio_err(state->chip, +- "invalid SELECTOR UNIT descriptor %d\n", unitid); +- return -EINVAL; +- } +- + for (i = 0; i < desc->bNrInPins; i++) { + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) +@@ -3168,8 +3090,6 @@ static int snd_usb_mixer_controls(struct + if (mixer->protocol == UAC_VERSION_1) { + struct uac1_output_terminal_descriptor *desc = p; + +- if (desc->bLength < sizeof(*desc)) +- continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; +@@ -3181,8 +3101,6 @@ static int snd_usb_mixer_controls(struct + } else if (mixer->protocol == UAC_VERSION_2) { + struct uac2_output_terminal_descriptor *desc = p; + +- if (desc->bLength < sizeof(*desc)) +- continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; +@@ -3208,8 +3126,6 @@ static int snd_usb_mixer_controls(struct + } else { /* UAC_VERSION_3 */ + struct uac3_output_terminal_descriptor *desc = p; + +- if (desc->bLength < sizeof(*desc)) +- continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; diff --git a/queue-4.19/alsa-usb-audio-simplify-parse_audio_unit.patch b/queue-4.19/alsa-usb-audio-simplify-parse_audio_unit.patch new file mode 100644 index 00000000000..a8a87526959 --- /dev/null +++ b/queue-4.19/alsa-usb-audio-simplify-parse_audio_unit.patch @@ -0,0 +1,125 @@ +From 68e9fde245591d18200f8a9054cac22339437adb Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 15 Aug 2019 16:30:39 +0200 +Subject: ALSA: usb-audio: Simplify parse_audio_unit() + +From: Takashi Iwai + +commit 68e9fde245591d18200f8a9054cac22339437adb upstream. + +Minor code refactoring by combining the UAC version and the type in +the switch-case flow, so that we reduce the indentation and +redundancy. One good bonus is that the duplicated definition of the +same type value (e.g. UAC2_EFFECT_UNIT) can be handled more cleanly. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/mixer.c | 95 ++++++++++++++++++++++-------------------------------- + 1 file changed, 39 insertions(+), 56 deletions(-) + +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -2801,62 +2801,45 @@ static int parse_audio_unit(struct mixer + return 0; /* skip invalid unit */ + } + +- if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { +- switch (p1[2]) { +- case UAC_INPUT_TERMINAL: +- return parse_audio_input_terminal(state, unitid, p1); +- case UAC_MIXER_UNIT: +- return parse_audio_mixer_unit(state, unitid, p1); +- case UAC2_CLOCK_SOURCE: +- return parse_clock_source_unit(state, unitid, p1); +- case UAC_SELECTOR_UNIT: +- case UAC2_CLOCK_SELECTOR: +- return parse_audio_selector_unit(state, unitid, p1); +- case UAC_FEATURE_UNIT: +- return parse_audio_feature_unit(state, unitid, p1); +- case UAC1_PROCESSING_UNIT: +- /* UAC2_EFFECT_UNIT has the same value */ +- if (protocol == UAC_VERSION_1) +- return parse_audio_processing_unit(state, unitid, p1); +- else +- return 0; /* FIXME - effect units not implemented yet */ +- case UAC1_EXTENSION_UNIT: +- /* UAC2_PROCESSING_UNIT_V2 has the same value */ +- if (protocol == UAC_VERSION_1) +- return parse_audio_extension_unit(state, unitid, p1); +- else /* UAC_VERSION_2 */ +- return parse_audio_processing_unit(state, unitid, p1); +- case UAC2_EXTENSION_UNIT_V2: +- return parse_audio_extension_unit(state, unitid, p1); +- default: +- usb_audio_err(state->chip, +- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); +- return -EINVAL; +- } +- } else { /* UAC_VERSION_3 */ +- switch (p1[2]) { +- case UAC_INPUT_TERMINAL: +- return parse_audio_input_terminal(state, unitid, p1); +- case UAC3_MIXER_UNIT: +- return parse_audio_mixer_unit(state, unitid, p1); +- case UAC3_CLOCK_SOURCE: +- return parse_clock_source_unit(state, unitid, p1); +- case UAC3_SELECTOR_UNIT: +- case UAC3_CLOCK_SELECTOR: +- return parse_audio_selector_unit(state, unitid, p1); +- case UAC3_FEATURE_UNIT: +- return parse_audio_feature_unit(state, unitid, p1); +- case UAC3_EFFECT_UNIT: +- return 0; /* FIXME - effect units not implemented yet */ +- case UAC3_PROCESSING_UNIT: +- return parse_audio_processing_unit(state, unitid, p1); +- case UAC3_EXTENSION_UNIT: +- return parse_audio_extension_unit(state, unitid, p1); +- default: +- usb_audio_err(state->chip, +- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); +- return -EINVAL; +- } ++#define PTYPE(a, b) ((a) << 8 | (b)) ++ switch (PTYPE(protocol, p1[2])) { ++ case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): ++ case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): ++ case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): ++ return parse_audio_input_terminal(state, unitid, p1); ++ case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): ++ return parse_audio_mixer_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): ++ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): ++ return parse_clock_source_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): ++ case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): ++ return parse_audio_selector_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): ++ return parse_audio_feature_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): ++ case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): ++ return parse_audio_processing_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): ++ case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): ++ case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): ++ return parse_audio_extension_unit(state, unitid, p1); ++ case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): ++ case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): ++ return 0; /* FIXME - effect units not implemented yet */ ++ default: ++ usb_audio_err(state->chip, ++ "unit %u: unexpected type 0x%02x\n", ++ unitid, p1[2]); ++ return -EINVAL; + } + } + diff --git a/queue-4.19/alsa-usb-audio-unify-the-release-of-usb_mixer_elem_info-objects.patch b/queue-4.19/alsa-usb-audio-unify-the-release-of-usb_mixer_elem_info-objects.patch new file mode 100644 index 00000000000..72d2165f21b --- /dev/null +++ b/queue-4.19/alsa-usb-audio-unify-the-release-of-usb_mixer_elem_info-objects.patch @@ -0,0 +1,161 @@ +From 52c3e317a857091fd746e15179a637f32be4d337 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 22 Aug 2019 08:23:10 +0200 +Subject: ALSA: usb-audio: Unify the release of usb_mixer_elem_info objects + +From: Takashi Iwai + +commit 52c3e317a857091fd746e15179a637f32be4d337 upstream. + +Instead of the direct kfree() calls, introduce a new local helper to +release the usb_mixer_elem_info object. This will be extended to do +more than a single kfree() in the later patches. + +Also, use the standard goto instead of multiple calls in +parse_audio_selector_unit() error paths. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/usb/mixer.c | 48 ++++++++++++++++++++++++++++-------------------- + 1 file changed, 28 insertions(+), 20 deletions(-) + +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -1041,10 +1041,15 @@ static struct usb_feature_control_info a + { UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, + }; + ++static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval) ++{ ++ kfree(cval); ++} ++ + /* private_free callback */ + void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) + { +- kfree(kctl->private_data); ++ usb_mixer_elem_info_free(kctl->private_data); + kctl->private_data = NULL; + } + +@@ -1567,7 +1572,7 @@ static void __build_feature_ctl(struct u + + ctl_info = get_feature_control_info(control); + if (!ctl_info) { +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return; + } + if (mixer->protocol == UAC_VERSION_1) +@@ -1600,7 +1605,7 @@ static void __build_feature_ctl(struct u + + if (!kctl) { + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return; + } + kctl->private_free = snd_usb_mixer_elem_free; +@@ -1770,7 +1775,7 @@ static void build_connector_control(stru + kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); + if (!kctl) { + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return; + } + get_connector_control_name(mixer, term, is_input, kctl->id.name, +@@ -1823,7 +1828,7 @@ static int parse_clock_source_unit(struc + kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); + + if (!kctl) { +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return -ENOMEM; + } + +@@ -2089,7 +2094,7 @@ static void build_mixer_unit_ctl(struct + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + if (!kctl) { + usb_audio_err(state->chip, "cannot malloc kcontrol\n"); +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return; + } + kctl->private_free = snd_usb_mixer_elem_free; +@@ -2487,7 +2492,7 @@ static int build_audio_procunit(struct m + + kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); + if (!kctl) { +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; +@@ -2625,7 +2630,7 @@ static void usb_mixer_selector_elem_free + if (kctl->private_data) { + struct usb_mixer_elem_info *cval = kctl->private_data; + num_ins = cval->max; +- kfree(cval); ++ usb_mixer_elem_info_free(cval); + kctl->private_data = NULL; + } + if (kctl->private_value) { +@@ -2697,10 +2702,10 @@ static int parse_audio_selector_unit(str + break; + } + +- namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL); ++ namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL); + if (!namelist) { +- kfree(cval); +- return -ENOMEM; ++ err = -ENOMEM; ++ goto error_cval; + } + #define MAX_ITEM_NAME_LEN 64 + for (i = 0; i < desc->bNrInPins; i++) { +@@ -2708,11 +2713,8 @@ static int parse_audio_selector_unit(str + len = 0; + namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); + if (!namelist[i]) { +- while (i--) +- kfree(namelist[i]); +- kfree(namelist); +- kfree(cval); +- return -ENOMEM; ++ err = -ENOMEM; ++ goto error_name; + } + len = check_mapped_selector_name(state, unitid, i, namelist[i], + MAX_ITEM_NAME_LEN); +@@ -2726,10 +2728,8 @@ static int parse_audio_selector_unit(str + kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); + if (! kctl) { + usb_audio_err(state->chip, "cannot malloc kcontrol\n"); +- for (i = 0; i < desc->bNrInPins; i++) +- kfree(namelist[i]); +- kfree(namelist); +- kfree(cval); ++ err = -ENOMEM; ++ goto error_name; + return -ENOMEM; + } + kctl->private_value = (unsigned long)namelist; +@@ -2776,6 +2776,14 @@ static int parse_audio_selector_unit(str + usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", + cval->head.id, kctl->id.name, desc->bNrInPins); + return snd_usb_mixer_add_control(&cval->head, kctl); ++ ++ error_name: ++ for (i = 0; i < desc->bNrInPins; i++) ++ kfree(namelist[i]); ++ kfree(namelist); ++ error_cval: ++ usb_mixer_elem_info_free(cval); ++ return err; + } + + /* diff --git a/queue-4.19/sched-fair-fix-low-cpu-usage-with-high-throttling-by-removing-expiration-of-cpu-local-slices.patch b/queue-4.19/sched-fair-fix-low-cpu-usage-with-high-throttling-by-removing-expiration-of-cpu-local-slices.patch new file mode 100644 index 00000000000..bedc7e3f0da --- /dev/null +++ b/queue-4.19/sched-fair-fix-low-cpu-usage-with-high-throttling-by-removing-expiration-of-cpu-local-slices.patch @@ -0,0 +1,341 @@ +From de53fd7aedb100f03e5d2231cfce0e4993282425 Mon Sep 17 00:00:00 2001 +From: Dave Chiluk +Date: Tue, 23 Jul 2019 11:44:26 -0500 +Subject: sched/fair: Fix low cpu usage with high throttling by removing expiration of cpu-local slices + +From: Dave Chiluk + +commit de53fd7aedb100f03e5d2231cfce0e4993282425 upstream. + +It has been observed, that highly-threaded, non-cpu-bound applications +running under cpu.cfs_quota_us constraints can hit a high percentage of +periods throttled while simultaneously not consuming the allocated +amount of quota. This use case is typical of user-interactive non-cpu +bound applications, such as those running in kubernetes or mesos when +run on multiple cpu cores. + +This has been root caused to cpu-local run queue being allocated per cpu +bandwidth slices, and then not fully using that slice within the period. +At which point the slice and quota expires. This expiration of unused +slice results in applications not being able to utilize the quota for +which they are allocated. + +The non-expiration of per-cpu slices was recently fixed by +'commit 512ac999d275 ("sched/fair: Fix bandwidth timer clock drift +condition")'. Prior to that it appears that this had been broken since +at least 'commit 51f2176d74ac ("sched/fair: Fix unlocked reads of some +cfs_b->quota/period")' which was introduced in v3.16-rc1 in 2014. That +added the following conditional which resulted in slices never being +expired. + +if (cfs_rq->runtime_expires != cfs_b->runtime_expires) { + /* extend local deadline, drift is bounded above by 2 ticks */ + cfs_rq->runtime_expires += TICK_NSEC; + +Because this was broken for nearly 5 years, and has recently been fixed +and is now being noticed by many users running kubernetes +(https://github.com/kubernetes/kubernetes/issues/67577) it is my opinion +that the mechanisms around expiring runtime should be removed +altogether. + +This allows quota already allocated to per-cpu run-queues to live longer +than the period boundary. This allows threads on runqueues that do not +use much CPU to continue to use their remaining slice over a longer +period of time than cpu.cfs_period_us. However, this helps prevent the +above condition of hitting throttling while also not fully utilizing +your cpu quota. + +This theoretically allows a machine to use slightly more than its +allotted quota in some periods. This overflow would be bounded by the +remaining quota left on each per-cpu runqueueu. This is typically no +more than min_cfs_rq_runtime=1ms per cpu. For CPU bound tasks this will +change nothing, as they should theoretically fully utilize all of their +quota in each period. For user-interactive tasks as described above this +provides a much better user/application experience as their cpu +utilization will more closely match the amount they requested when they +hit throttling. This means that cpu limits no longer strictly apply per +period for non-cpu bound applications, but that they are still accurate +over longer timeframes. + +This greatly improves performance of high-thread-count, non-cpu bound +applications with low cfs_quota_us allocation on high-core-count +machines. In the case of an artificial testcase (10ms/100ms of quota on +80 CPU machine), this commit resulted in almost 30x performance +improvement, while still maintaining correct cpu quota restrictions. +That testcase is available at https://github.com/indeedeng/fibtest. + +Fixes: 512ac999d275 ("sched/fair: Fix bandwidth timer clock drift condition") +Signed-off-by: Dave Chiluk +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Phil Auld +Reviewed-by: Ben Segall +Cc: Ingo Molnar +Cc: John Hammond +Cc: Jonathan Corbet +Cc: Kyle Anderson +Cc: Gabriel Munos +Cc: Peter Oskolkov +Cc: Cong Wang +Cc: Brendan Gregg +Link: https://lkml.kernel.org/r/1563900266-19734-2-git-send-email-chiluk+linux@indeed.com +Signed-off-by: Greg Kroah-Hartman + + +--- + Documentation/scheduler/sched-bwc.txt | 45 +++++++++++++++++++++ + kernel/sched/fair.c | 72 +++------------------------------- + kernel/sched/sched.h | 4 - + 3 files changed, 52 insertions(+), 69 deletions(-) + +--- a/Documentation/scheduler/sched-bwc.txt ++++ b/Documentation/scheduler/sched-bwc.txt +@@ -90,6 +90,51 @@ There are two ways in which a group may + In case b) above, even though the child may have runtime remaining it will not + be allowed to until the parent's runtime is refreshed. + ++CFS Bandwidth Quota Caveats ++--------------------------- ++Once a slice is assigned to a cpu it does not expire. However all but 1ms of ++the slice may be returned to the global pool if all threads on that cpu become ++unrunnable. This is configured at compile time by the min_cfs_rq_runtime ++variable. This is a performance tweak that helps prevent added contention on ++the global lock. ++ ++The fact that cpu-local slices do not expire results in some interesting corner ++cases that should be understood. ++ ++For cgroup cpu constrained applications that are cpu limited this is a ++relatively moot point because they will naturally consume the entirety of their ++quota as well as the entirety of each cpu-local slice in each period. As a ++result it is expected that nr_periods roughly equal nr_throttled, and that ++cpuacct.usage will increase roughly equal to cfs_quota_us in each period. ++ ++For highly-threaded, non-cpu bound applications this non-expiration nuance ++allows applications to briefly burst past their quota limits by the amount of ++unused slice on each cpu that the task group is running on (typically at most ++1ms per cpu or as defined by min_cfs_rq_runtime). This slight burst only ++applies if quota had been assigned to a cpu and then not fully used or returned ++in previous periods. This burst amount will not be transferred between cores. ++As a result, this mechanism still strictly limits the task group to quota ++average usage, albeit over a longer time window than a single period. This ++also limits the burst ability to no more than 1ms per cpu. This provides ++better more predictable user experience for highly threaded applications with ++small quota limits on high core count machines. It also eliminates the ++propensity to throttle these applications while simultanously using less than ++quota amounts of cpu. Another way to say this, is that by allowing the unused ++portion of a slice to remain valid across periods we have decreased the ++possibility of wastefully expiring quota on cpu-local silos that don't need a ++full slice's amount of cpu time. ++ ++The interaction between cpu-bound and non-cpu-bound-interactive applications ++should also be considered, especially when single core usage hits 100%. If you ++gave each of these applications half of a cpu-core and they both got scheduled ++on the same CPU it is theoretically possible that the non-cpu bound application ++will use up to 1ms additional quota in some periods, thereby preventing the ++cpu-bound application from fully using its quota by that same amount. In these ++instances it will be up to the CFS algorithm (see sched-design-CFS.rst) to ++decide which application is chosen to run, as they will both be runnable and ++have remaining quota. This runtime discrepancy will be made up in the following ++periods when the interactive application idles. ++ + Examples + -------- + 1. Limit a group to 1 CPU worth of runtime. +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -4320,8 +4320,6 @@ void __refill_cfs_bandwidth_runtime(stru + + now = sched_clock_cpu(smp_processor_id()); + cfs_b->runtime = cfs_b->quota; +- cfs_b->runtime_expires = now + ktime_to_ns(cfs_b->period); +- cfs_b->expires_seq++; + } + + static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg) +@@ -4343,8 +4341,7 @@ static int assign_cfs_rq_runtime(struct + { + struct task_group *tg = cfs_rq->tg; + struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(tg); +- u64 amount = 0, min_amount, expires; +- int expires_seq; ++ u64 amount = 0, min_amount; + + /* note: this is a positive sum as runtime_remaining <= 0 */ + min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining; +@@ -4361,61 +4358,17 @@ static int assign_cfs_rq_runtime(struct + cfs_b->idle = 0; + } + } +- expires_seq = cfs_b->expires_seq; +- expires = cfs_b->runtime_expires; + raw_spin_unlock(&cfs_b->lock); + + cfs_rq->runtime_remaining += amount; +- /* +- * we may have advanced our local expiration to account for allowed +- * spread between our sched_clock and the one on which runtime was +- * issued. +- */ +- if (cfs_rq->expires_seq != expires_seq) { +- cfs_rq->expires_seq = expires_seq; +- cfs_rq->runtime_expires = expires; +- } + + return cfs_rq->runtime_remaining > 0; + } + +-/* +- * Note: This depends on the synchronization provided by sched_clock and the +- * fact that rq->clock snapshots this value. +- */ +-static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq) +-{ +- struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg); +- +- /* if the deadline is ahead of our clock, nothing to do */ +- if (likely((s64)(rq_clock(rq_of(cfs_rq)) - cfs_rq->runtime_expires) < 0)) +- return; +- +- if (cfs_rq->runtime_remaining < 0) +- return; +- +- /* +- * If the local deadline has passed we have to consider the +- * possibility that our sched_clock is 'fast' and the global deadline +- * has not truly expired. +- * +- * Fortunately we can check determine whether this the case by checking +- * whether the global deadline(cfs_b->expires_seq) has advanced. +- */ +- if (cfs_rq->expires_seq == cfs_b->expires_seq) { +- /* extend local deadline, drift is bounded above by 2 ticks */ +- cfs_rq->runtime_expires += TICK_NSEC; +- } else { +- /* global deadline is ahead, expiration has passed */ +- cfs_rq->runtime_remaining = 0; +- } +-} +- + static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) + { + /* dock delta_exec before expiring quota (as it could span periods) */ + cfs_rq->runtime_remaining -= delta_exec; +- expire_cfs_rq_runtime(cfs_rq); + + if (likely(cfs_rq->runtime_remaining > 0)) + return; +@@ -4600,8 +4553,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cf + resched_curr(rq); + } + +-static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, +- u64 remaining, u64 expires) ++static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, u64 remaining) + { + struct cfs_rq *cfs_rq; + u64 runtime; +@@ -4626,7 +4578,6 @@ static u64 distribute_cfs_runtime(struct + remaining -= runtime; + + cfs_rq->runtime_remaining += runtime; +- cfs_rq->runtime_expires = expires; + + /* we check whether we're throttled above */ + if (cfs_rq->runtime_remaining > 0) +@@ -4651,7 +4602,7 @@ next: + */ + static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun) + { +- u64 runtime, runtime_expires; ++ u64 runtime; + int throttled; + + /* no need to continue the timer with no bandwidth constraint */ +@@ -4679,8 +4630,6 @@ static int do_sched_cfs_period_timer(str + /* account preceding periods in which throttling occurred */ + cfs_b->nr_throttled += overrun; + +- runtime_expires = cfs_b->runtime_expires; +- + /* + * This check is repeated as we are holding onto the new bandwidth while + * we unthrottle. This can potentially race with an unthrottled group +@@ -4693,8 +4642,7 @@ static int do_sched_cfs_period_timer(str + cfs_b->distribute_running = 1; + raw_spin_unlock(&cfs_b->lock); + /* we can't nest cfs_b->lock while distributing bandwidth */ +- runtime = distribute_cfs_runtime(cfs_b, runtime, +- runtime_expires); ++ runtime = distribute_cfs_runtime(cfs_b, runtime); + raw_spin_lock(&cfs_b->lock); + + cfs_b->distribute_running = 0; +@@ -4771,8 +4719,7 @@ static void __return_cfs_rq_runtime(stru + return; + + raw_spin_lock(&cfs_b->lock); +- if (cfs_b->quota != RUNTIME_INF && +- cfs_rq->runtime_expires == cfs_b->runtime_expires) { ++ if (cfs_b->quota != RUNTIME_INF) { + cfs_b->runtime += slack_runtime; + + /* we are under rq->lock, defer unthrottling using a timer */ +@@ -4804,7 +4751,6 @@ static __always_inline void return_cfs_r + static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b) + { + u64 runtime = 0, slice = sched_cfs_bandwidth_slice(); +- u64 expires; + + /* confirm we're still not at a refresh boundary */ + raw_spin_lock(&cfs_b->lock); +@@ -4821,7 +4767,6 @@ static void do_sched_cfs_slack_timer(str + if (cfs_b->quota != RUNTIME_INF && cfs_b->runtime > slice) + runtime = cfs_b->runtime; + +- expires = cfs_b->runtime_expires; + if (runtime) + cfs_b->distribute_running = 1; + +@@ -4830,11 +4775,10 @@ static void do_sched_cfs_slack_timer(str + if (!runtime) + return; + +- runtime = distribute_cfs_runtime(cfs_b, runtime, expires); ++ runtime = distribute_cfs_runtime(cfs_b, runtime); + + raw_spin_lock(&cfs_b->lock); +- if (expires == cfs_b->runtime_expires) +- cfs_b->runtime -= min(runtime, cfs_b->runtime); ++ cfs_b->runtime -= min(runtime, cfs_b->runtime); + cfs_b->distribute_running = 0; + raw_spin_unlock(&cfs_b->lock); + } +@@ -4989,8 +4933,6 @@ void start_cfs_bandwidth(struct cfs_band + + cfs_b->period_active = 1; + overrun = hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period); +- cfs_b->runtime_expires += (overrun + 1) * ktime_to_ns(cfs_b->period); +- cfs_b->expires_seq++; + hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED); + } + +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -334,8 +334,6 @@ struct cfs_bandwidth { + u64 quota; + u64 runtime; + s64 hierarchical_quota; +- u64 runtime_expires; +- int expires_seq; + + short idle; + short period_active; +@@ -555,8 +553,6 @@ struct cfs_rq { + + #ifdef CONFIG_CFS_BANDWIDTH + int runtime_enabled; +- int expires_seq; +- u64 runtime_expires; + s64 runtime_remaining; + + u64 throttled_clock; diff --git a/queue-4.19/sched-fair-fix-wunused-but-set-variable-warnings.patch b/queue-4.19/sched-fair-fix-wunused-but-set-variable-warnings.patch new file mode 100644 index 00000000000..89b448c6527 --- /dev/null +++ b/queue-4.19/sched-fair-fix-wunused-but-set-variable-warnings.patch @@ -0,0 +1,87 @@ +From 763a9ec06c409dcde2a761aac4bb83ff3938e0b3 Mon Sep 17 00:00:00 2001 +From: Qian Cai +Date: Tue, 20 Aug 2019 14:40:55 -0400 +Subject: sched/fair: Fix -Wunused-but-set-variable warnings + +From: Qian Cai + +commit 763a9ec06c409dcde2a761aac4bb83ff3938e0b3 upstream. + +Commit: + + de53fd7aedb1 ("sched/fair: Fix low cpu usage with high throttling by removing expiration of cpu-local slices") + +introduced a few compilation warnings: + + kernel/sched/fair.c: In function '__refill_cfs_bandwidth_runtime': + kernel/sched/fair.c:4365:6: warning: variable 'now' set but not used [-Wunused-but-set-variable] + kernel/sched/fair.c: In function 'start_cfs_bandwidth': + kernel/sched/fair.c:4992:6: warning: variable 'overrun' set but not used [-Wunused-but-set-variable] + +Also, __refill_cfs_bandwidth_runtime() does no longer update the +expiration time, so fix the comments accordingly. + +Signed-off-by: Qian Cai +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Ben Segall +Reviewed-by: Dave Chiluk +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Thomas Gleixner +Cc: pauld@redhat.com +Fixes: de53fd7aedb1 ("sched/fair: Fix low cpu usage with high throttling by removing expiration of cpu-local slices") +Link: https://lkml.kernel.org/r/1566326455-8038-1-git-send-email-cai@lca.pw +Signed-off-by: Ingo Molnar +Signed-off-by: Greg Kroah-Hartman + + +--- + kernel/sched/fair.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -4305,21 +4305,16 @@ static inline u64 sched_cfs_bandwidth_sl + } + + /* +- * Replenish runtime according to assigned quota and update expiration time. +- * We use sched_clock_cpu directly instead of rq->clock to avoid adding +- * additional synchronization around rq->lock. ++ * Replenish runtime according to assigned quota. We use sched_clock_cpu ++ * directly instead of rq->clock to avoid adding additional synchronization ++ * around rq->lock. + * + * requires cfs_b->lock + */ + void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b) + { +- u64 now; +- +- if (cfs_b->quota == RUNTIME_INF) +- return; +- +- now = sched_clock_cpu(smp_processor_id()); +- cfs_b->runtime = cfs_b->quota; ++ if (cfs_b->quota != RUNTIME_INF) ++ cfs_b->runtime = cfs_b->quota; + } + + static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg) +@@ -4924,15 +4919,13 @@ static void init_cfs_rq_runtime(struct c + + void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b) + { +- u64 overrun; +- + lockdep_assert_held(&cfs_b->lock); + + if (cfs_b->period_active) + return; + + cfs_b->period_active = 1; +- overrun = hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period); ++ hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period); + hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED); + } + diff --git a/queue-4.19/series b/queue-4.19/series index 7cb91da85e8..7badd22a477 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -54,3 +54,15 @@ configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch configfs-new-object-reprsenting-tree-fragments.patch configfs-provide-exclusion-between-io-and-removals.patch configfs-fix-a-deadlock-in-configfs_symlink.patch +alsa-usb-audio-more-validations-of-descriptor-units.patch +alsa-usb-audio-simplify-parse_audio_unit.patch +alsa-usb-audio-unify-the-release-of-usb_mixer_elem_info-objects.patch +alsa-usb-audio-remove-superfluous-blength-checks.patch +alsa-usb-audio-clean-up-check_input_term.patch +alsa-usb-audio-fix-possible-null-dereference-at-create_yamaha_midi_quirk.patch +alsa-usb-audio-remove-some-dead-code.patch +alsa-usb-audio-fix-copy-paste-error-in-the-validator.patch +sched-fair-fix-low-cpu-usage-with-high-throttling-by-removing-expiration-of-cpu-local-slices.patch +sched-fair-fix-wunused-but-set-variable-warnings.patch +usbip-fix-vhci_urb_enqueue-urb-null-transfer-buffer-error-path.patch +usbip-implement-sg-support-to-vhci-hcd-and-stub-driver.patch diff --git a/queue-4.19/usbip-fix-vhci_urb_enqueue-urb-null-transfer-buffer-error-path.patch b/queue-4.19/usbip-fix-vhci_urb_enqueue-urb-null-transfer-buffer-error-path.patch new file mode 100644 index 00000000000..526fbd9f370 --- /dev/null +++ b/queue-4.19/usbip-fix-vhci_urb_enqueue-urb-null-transfer-buffer-error-path.patch @@ -0,0 +1,34 @@ +From 2c904963b1dd2acd4bc785b6c72e10a6283c2081 Mon Sep 17 00:00:00 2001 +From: Shuah Khan +Date: Thu, 24 Jan 2019 14:46:42 -0700 +Subject: usbip: Fix vhci_urb_enqueue() URB null transfer buffer error path + +From: Shuah Khan + +commit 2c904963b1dd2acd4bc785b6c72e10a6283c2081 upstream. + +Fix vhci_urb_enqueue() to print debug msg and return error instead of +failing with BUG_ON. + +Signed-off-by: Shuah Khan +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/usbip/vhci_hcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/usb/usbip/vhci_hcd.c ++++ b/drivers/usb/usbip/vhci_hcd.c +@@ -702,8 +702,10 @@ static int vhci_urb_enqueue(struct usb_h + } + vdev = &vhci_hcd->vdev[portnum-1]; + +- /* patch to usb_sg_init() is in 2.5.60 */ +- BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); ++ if (!urb->transfer_buffer && urb->transfer_buffer_length) { ++ dev_dbg(dev, "Null URB transfer buffer\n"); ++ return -EINVAL; ++ } + + spin_lock_irqsave(&vhci->lock, flags); + diff --git a/queue-4.19/usbip-implement-sg-support-to-vhci-hcd-and-stub-driver.patch b/queue-4.19/usbip-implement-sg-support-to-vhci-hcd-and-stub-driver.patch new file mode 100644 index 00000000000..59052e84b42 --- /dev/null +++ b/queue-4.19/usbip-implement-sg-support-to-vhci-hcd-and-stub-driver.patch @@ -0,0 +1,949 @@ +From ea44d190764b4422af4d1c29eaeb9e69e353b406 Mon Sep 17 00:00:00 2001 +From: Suwan Kim +Date: Wed, 28 Aug 2019 12:27:41 +0900 +Subject: usbip: Implement SG support to vhci-hcd and stub driver + +From: Suwan Kim + +commit ea44d190764b4422af4d1c29eaeb9e69e353b406 upstream. + +There are bugs on vhci with usb 3.0 storage device. In USB, each SG +list entry buffer should be divisible by the bulk max packet size. +But with native SG support, this problem doesn't matter because the +SG buffer is treated as contiguous buffer. But without native SG +support, USB storage driver breaks SG list into several URBs and the +error occurs because of a buffer size of URB that cannot be divided +by the bulk max packet size. The error situation is as follows. + +When USB Storage driver requests 31.5 KB data and has SG list which +has 3584 bytes buffer followed by 7 4096 bytes buffer for some +reason. USB Storage driver splits this SG list into several URBs +because VHCI doesn't support SG and sends them separately. So the +first URB buffer size is 3584 bytes. When receiving data from device, +USB 3.0 device sends data packet of 1024 bytes size because the max +packet size of BULK pipe is 1024 bytes. So device sends 4096 bytes. +But the first URB buffer has only 3584 bytes buffer size. So host +controller terminates the transfer even though there is more data to +receive. So, vhci needs to support SG transfer to prevent this error. + +In this patch, vhci supports SG regardless of whether the server's +host controller supports SG or not, because stub driver splits SG +list into several URBs if the server's host controller doesn't +support SG. + +To support SG, vhci sets URB_DMA_MAP_SG flag in urb->transfer_flags +if URB has SG list and this flag will tell stub driver to use SG +list. After receiving urb from stub driver, vhci clear URB_DMA_MAP_SG +flag to avoid unnecessary DMA unmapping in HCD. + +vhci sends each SG list entry to stub driver. Then, stub driver sees +the total length of the buffer and allocates SG table and pages +according to the total buffer length calling sgl_alloc(). After stub +driver receives completed URB, it again sends each SG list entry to +vhci. + +If the server's host controller doesn't support SG, stub driver +breaks a single SG request into several URBs and submits them to +the server's host controller. When all the split URBs are completed, +stub driver reassembles the URBs into a single return command and +sends it to vhci. + +Moreover, in the situation where vhci supports SG, but stub driver +does not, or vice versa, usbip works normally. Because there is no +protocol modification, there is no problem in communication between +server and client even if the one has a kernel without SG support. + +In the case of vhci supports SG and stub driver doesn't, because +vhci sends only the total length of the buffer to stub driver as +it did before the patch applied, stub driver only needs to allocate +the required length of buffers using only kmalloc() regardless of +whether vhci supports SG or not. But stub driver has to allocate +buffer with kmalloc() as much as the total length of SG buffer which +is quite huge when vhci sends SG request, so it has overhead in +buffer allocation in this situation. + +If stub driver needs to send data buffer to vhci because of IN pipe, +stub driver also sends only total length of buffer as metadata and +then sends real data as vhci does. Then vhci receive data from stub +driver and store it to the corresponding buffer of SG list entry. + +And for the case of stub driver supports SG and vhci doesn't, since +the USB storage driver checks that vhci doesn't support SG and sends +the request to stub driver by splitting the SG list into multiple +URBs, stub driver allocates a buffer for each URB with kmalloc() as +it did before this patch. + +* Test environment + +Test uses two difference machines and two different kernel version +to make mismatch situation between the client and the server where +vhci supports SG, but stub driver does not, or vice versa. All tests +are conducted in both full SG support that both vhci and stub support +SG and half SG support that is the mismatch situation. Test kernel +version is 5.3-rc6 with commit "usb: add a HCD_DMA flag instead of +guestimating DMA capabilities" to avoid unnecessary DMA mapping and +unmapping. + + - Test kernel version + - 5.3-rc6 with SG support + - 5.1.20-200.fc29.x86_64 without SG support + +* SG support test + + - Test devices + - Super-speed storage device - SanDisk Ultra USB 3.0 + - High-speed storage device - SMI corporation USB 2.0 flash drive + + - Test description + +Test read and write operation of mass storage device that uses the +BULK transfer. In test, the client reads and writes files whose size +is over 1G and it works normally. + +* Regression test + + - Test devices + - Super-speed device - Logitech Brio webcam + - High-speed device - Logitech C920 HD Pro webcam + - Full-speed device - Logitech bluetooth mouse + - Britz BR-Orion speaker + - Low-speed device - Logitech wired mouse + + - Test description + +Moving and click test for mouse. To test the webcam, use gnome-cheese. +To test the speaker, play music and video on the client. All works +normally. + +* VUDC compatibility test + +VUDC also works well with this patch. Tests are done with two USB +gadget created by CONFIGFS USB gadget. Both use the BULK pipe. + + 1. Serial gadget + 2. Mass storage gadget + + - Serial gadget test + +Serial gadget on the host sends and receives data using cat command +on the /dev/ttyGS. The client uses minicom to communicate with +the serial gadget. + + - Mass storage gadget test + +After connecting the gadget with vhci, use "dd" to test read and +write operation on the client side. + +Read - dd if=/dev/sd iflag=direct of=/dev/null bs=1G count=1 +Write - dd if= iflag=direct of=/dev/sd bs=1G count=1 + +Signed-off-by: Suwan Kim +Acked-by: Shuah khan +Link: https://lore.kernel.org/r/20190828032741.12234-1-suwan.kim027@gmail.com +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/usbip/stub.h | 7 + + drivers/usb/usbip/stub_main.c | 57 ++++++++--- + drivers/usb/usbip/stub_rx.c | 202 +++++++++++++++++++++++++++------------ + drivers/usb/usbip/stub_tx.c | 99 ++++++++++++++----- + drivers/usb/usbip/usbip_common.c | 59 +++++++---- + drivers/usb/usbip/vhci_hcd.c | 12 ++ + drivers/usb/usbip/vhci_rx.c | 3 + drivers/usb/usbip/vhci_tx.c | 66 ++++++++++-- + 8 files changed, 379 insertions(+), 126 deletions(-) + +--- a/drivers/usb/usbip/stub.h ++++ b/drivers/usb/usbip/stub.h +@@ -52,7 +52,11 @@ struct stub_priv { + unsigned long seqnum; + struct list_head list; + struct stub_device *sdev; +- struct urb *urb; ++ struct urb **urbs; ++ struct scatterlist *sgl; ++ int num_urbs; ++ int completed_urbs; ++ int urb_status; + + int unlinking; + }; +@@ -86,6 +90,7 @@ extern struct usb_device_driver stub_dri + struct bus_id_priv *get_busid_priv(const char *busid); + void put_busid_priv(struct bus_id_priv *bid); + int del_match_busid(char *busid); ++void stub_free_priv_and_urb(struct stub_priv *priv); + void stub_device_cleanup_urbs(struct stub_device *sdev); + + /* stub_rx.c */ +--- a/drivers/usb/usbip/stub_main.c ++++ b/drivers/usb/usbip/stub_main.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + #include "usbip_common.h" + #include "stub.h" +@@ -283,13 +284,49 @@ static struct stub_priv *stub_priv_pop_f + struct stub_priv *priv, *tmp; + + list_for_each_entry_safe(priv, tmp, listhead, list) { +- list_del(&priv->list); ++ list_del_init(&priv->list); + return priv; + } + + return NULL; + } + ++void stub_free_priv_and_urb(struct stub_priv *priv) ++{ ++ struct urb *urb; ++ int i; ++ ++ for (i = 0; i < priv->num_urbs; i++) { ++ urb = priv->urbs[i]; ++ ++ if (!urb) ++ return; ++ ++ kfree(urb->setup_packet); ++ urb->setup_packet = NULL; ++ ++ ++ if (urb->transfer_buffer && !priv->sgl) { ++ kfree(urb->transfer_buffer); ++ urb->transfer_buffer = NULL; ++ } ++ ++ if (urb->num_sgs) { ++ sgl_free(urb->sg); ++ urb->sg = NULL; ++ urb->num_sgs = 0; ++ } ++ ++ usb_free_urb(urb); ++ } ++ if (!list_empty(&priv->list)) ++ list_del(&priv->list); ++ if (priv->sgl) ++ sgl_free(priv->sgl); ++ kfree(priv->urbs); ++ kmem_cache_free(stub_priv_cache, priv); ++} ++ + static struct stub_priv *stub_priv_pop(struct stub_device *sdev) + { + unsigned long flags; +@@ -316,25 +353,15 @@ done: + void stub_device_cleanup_urbs(struct stub_device *sdev) + { + struct stub_priv *priv; +- struct urb *urb; ++ int i; + + dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n"); + + while ((priv = stub_priv_pop(sdev))) { +- urb = priv->urb; +- dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n", +- priv->seqnum); +- usb_kill_urb(urb); +- +- kmem_cache_free(stub_priv_cache, priv); ++ for (i = 0; i < priv->num_urbs; i++) ++ usb_kill_urb(priv->urbs[i]); + +- kfree(urb->transfer_buffer); +- urb->transfer_buffer = NULL; +- +- kfree(urb->setup_packet); +- urb->setup_packet = NULL; +- +- usb_free_urb(urb); ++ stub_free_priv_and_urb(priv); + } + } + +--- a/drivers/usb/usbip/stub_rx.c ++++ b/drivers/usb/usbip/stub_rx.c +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + #include "usbip_common.h" + #include "stub.h" +@@ -201,7 +202,7 @@ static void tweak_special_requests(struc + static int stub_recv_cmd_unlink(struct stub_device *sdev, + struct usbip_header *pdu) + { +- int ret; ++ int ret, i; + unsigned long flags; + struct stub_priv *priv; + +@@ -246,12 +247,14 @@ static int stub_recv_cmd_unlink(struct s + * so a driver in a client host will know the failure + * of the unlink request ? + */ +- ret = usb_unlink_urb(priv->urb); +- if (ret != -EINPROGRESS) +- dev_err(&priv->urb->dev->dev, +- "failed to unlink a urb # %lu, ret %d\n", +- priv->seqnum, ret); +- ++ for (i = priv->completed_urbs; i < priv->num_urbs; i++) { ++ ret = usb_unlink_urb(priv->urbs[i]); ++ if (ret != -EINPROGRESS) ++ dev_err(&priv->urbs[i]->dev->dev, ++ "failed to unlink %d/%d urb of seqnum %lu, ret %d\n", ++ i + 1, priv->num_urbs, ++ priv->seqnum, ret); ++ } + return 0; + } + +@@ -433,14 +436,36 @@ static void masking_bogus_flags(struct u + urb->transfer_flags &= allowed; + } + ++static int stub_recv_xbuff(struct usbip_device *ud, struct stub_priv *priv) ++{ ++ int ret; ++ int i; ++ ++ for (i = 0; i < priv->num_urbs; i++) { ++ ret = usbip_recv_xbuff(ud, priv->urbs[i]); ++ if (ret < 0) ++ break; ++ } ++ ++ return ret; ++} ++ + static void stub_recv_cmd_submit(struct stub_device *sdev, + struct usbip_header *pdu) + { +- int ret; + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + struct usb_device *udev = sdev->udev; ++ struct scatterlist *sgl = NULL, *sg; ++ void *buffer = NULL; ++ unsigned long long buf_len; ++ int nents; ++ int num_urbs = 1; + int pipe = get_pipe(sdev, pdu); ++ int use_sg = pdu->u.cmd_submit.transfer_flags & URB_DMA_MAP_SG; ++ int support_sg = 1; ++ int np = 0; ++ int ret, i; + + if (pipe == -1) + return; +@@ -449,76 +474,139 @@ static void stub_recv_cmd_submit(struct + if (!priv) + return; + +- /* setup a urb */ +- if (usb_pipeisoc(pipe)) +- priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, +- GFP_KERNEL); +- else +- priv->urb = usb_alloc_urb(0, GFP_KERNEL); ++ buf_len = (unsigned long long)pdu->u.cmd_submit.transfer_buffer_length; + +- if (!priv->urb) { +- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); +- return; ++ /* allocate urb transfer buffer, if needed */ ++ if (buf_len) { ++ if (use_sg) { ++ sgl = sgl_alloc(buf_len, GFP_KERNEL, &nents); ++ if (!sgl) ++ goto err_malloc; ++ } else { ++ buffer = kzalloc(buf_len, GFP_KERNEL); ++ if (!buffer) ++ goto err_malloc; ++ } + } + +- /* allocate urb transfer buffer, if needed */ +- if (pdu->u.cmd_submit.transfer_buffer_length > 0) { +- priv->urb->transfer_buffer = +- kzalloc(pdu->u.cmd_submit.transfer_buffer_length, +- GFP_KERNEL); +- if (!priv->urb->transfer_buffer) { ++ /* Check if the server's HCD supports SG */ ++ if (use_sg && !udev->bus->sg_tablesize) { ++ /* ++ * If the server's HCD doesn't support SG, break a single SG ++ * request into several URBs and map each SG list entry to ++ * corresponding URB buffer. The previously allocated SG ++ * list is stored in priv->sgl (If the server's HCD support SG, ++ * SG list is stored only in urb->sg) and it is used as an ++ * indicator that the server split single SG request into ++ * several URBs. Later, priv->sgl is used by stub_complete() and ++ * stub_send_ret_submit() to reassemble the divied URBs. ++ */ ++ support_sg = 0; ++ num_urbs = nents; ++ priv->completed_urbs = 0; ++ pdu->u.cmd_submit.transfer_flags &= ~URB_DMA_MAP_SG; ++ } ++ ++ /* allocate urb array */ ++ priv->num_urbs = num_urbs; ++ priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL); ++ if (!priv->urbs) ++ goto err_urbs; ++ ++ /* setup a urb */ ++ if (support_sg) { ++ if (usb_pipeisoc(pipe)) ++ np = pdu->u.cmd_submit.number_of_packets; ++ ++ priv->urbs[0] = usb_alloc_urb(np, GFP_KERNEL); ++ if (!priv->urbs[0]) ++ goto err_urb; ++ ++ if (buf_len) { ++ if (use_sg) { ++ priv->urbs[0]->sg = sgl; ++ priv->urbs[0]->num_sgs = nents; ++ priv->urbs[0]->transfer_buffer = NULL; ++ } else { ++ priv->urbs[0]->transfer_buffer = buffer; ++ } ++ } ++ ++ /* copy urb setup packet */ ++ priv->urbs[0]->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, ++ 8, GFP_KERNEL); ++ if (!priv->urbs[0]->setup_packet) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } +- } + +- /* copy urb setup packet */ +- priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, +- GFP_KERNEL); +- if (!priv->urb->setup_packet) { +- dev_err(&udev->dev, "allocate setup_packet\n"); +- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); +- return; ++ usbip_pack_pdu(pdu, priv->urbs[0], USBIP_CMD_SUBMIT, 0); ++ } else { ++ for_each_sg(sgl, sg, nents, i) { ++ priv->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); ++ /* The URBs which is previously allocated will be freed ++ * in stub_device_cleanup_urbs() if error occurs. ++ */ ++ if (!priv->urbs[i]) ++ goto err_urb; ++ ++ usbip_pack_pdu(pdu, priv->urbs[i], USBIP_CMD_SUBMIT, 0); ++ priv->urbs[i]->transfer_buffer = sg_virt(sg); ++ priv->urbs[i]->transfer_buffer_length = sg->length; ++ } ++ priv->sgl = sgl; + } + +- /* set other members from the base header of pdu */ +- priv->urb->context = (void *) priv; +- priv->urb->dev = udev; +- priv->urb->pipe = pipe; +- priv->urb->complete = stub_complete; ++ for (i = 0; i < num_urbs; i++) { ++ /* set other members from the base header of pdu */ ++ priv->urbs[i]->context = (void *) priv; ++ priv->urbs[i]->dev = udev; ++ priv->urbs[i]->pipe = pipe; ++ priv->urbs[i]->complete = stub_complete; + +- usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); ++ /* no need to submit an intercepted request, but harmless? */ ++ tweak_special_requests(priv->urbs[i]); + ++ masking_bogus_flags(priv->urbs[i]); ++ } + +- if (usbip_recv_xbuff(ud, priv->urb) < 0) ++ if (stub_recv_xbuff(ud, priv) < 0) + return; + +- if (usbip_recv_iso(ud, priv->urb) < 0) ++ if (usbip_recv_iso(ud, priv->urbs[0]) < 0) + return; + +- /* no need to submit an intercepted request, but harmless? */ +- tweak_special_requests(priv->urb); +- +- masking_bogus_flags(priv->urb); + /* urb is now ready to submit */ +- ret = usb_submit_urb(priv->urb, GFP_KERNEL); +- +- if (ret == 0) +- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", +- pdu->base.seqnum); +- else { +- dev_err(&udev->dev, "submit_urb error, %d\n", ret); +- usbip_dump_header(pdu); +- usbip_dump_urb(priv->urb); ++ for (i = 0; i < priv->num_urbs; i++) { ++ ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL); + +- /* +- * Pessimistic. +- * This connection will be discarded. +- */ +- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); ++ if (ret == 0) ++ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", ++ pdu->base.seqnum); ++ else { ++ dev_err(&udev->dev, "submit_urb error, %d\n", ret); ++ usbip_dump_header(pdu); ++ usbip_dump_urb(priv->urbs[i]); ++ ++ /* ++ * Pessimistic. ++ * This connection will be discarded. ++ */ ++ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); ++ break; ++ } + } + + usbip_dbg_stub_rx("Leave\n"); ++ return; ++ ++err_urb: ++ kfree(priv->urbs); ++err_urbs: ++ kfree(buffer); ++ sgl_free(sgl); ++err_malloc: ++ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + } + + /* recv a pdu */ +--- a/drivers/usb/usbip/stub_tx.c ++++ b/drivers/usb/usbip/stub_tx.c +@@ -5,25 +5,11 @@ + + #include + #include ++#include + + #include "usbip_common.h" + #include "stub.h" + +-static void stub_free_priv_and_urb(struct stub_priv *priv) +-{ +- struct urb *urb = priv->urb; +- +- kfree(urb->setup_packet); +- urb->setup_packet = NULL; +- +- kfree(urb->transfer_buffer); +- urb->transfer_buffer = NULL; +- +- list_del(&priv->list); +- kmem_cache_free(stub_priv_cache, priv); +- usb_free_urb(urb); +-} +- + /* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ + void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status) +@@ -85,6 +71,22 @@ void stub_complete(struct urb *urb) + break; + } + ++ /* ++ * If the server breaks single SG request into the several URBs, the ++ * URBs must be reassembled before sending completed URB to the vhci. ++ * Don't wake up the tx thread until all the URBs are completed. ++ */ ++ if (priv->sgl) { ++ priv->completed_urbs++; ++ ++ /* Only save the first error status */ ++ if (urb->status && !priv->urb_status) ++ priv->urb_status = urb->status; ++ ++ if (priv->completed_urbs < priv->num_urbs) ++ return; ++ } ++ + /* link a urb to the queue of tx. */ + spin_lock_irqsave(&sdev->priv_lock, flags); + if (sdev->ud.tcp_socket == NULL) { +@@ -156,18 +158,22 @@ static int stub_send_ret_submit(struct s + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { +- int ret; +- struct urb *urb = priv->urb; ++ struct urb *urb = priv->urbs[0]; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct kvec *iov = NULL; ++ struct scatterlist *sg; ++ u32 actual_length = 0; + int iovnum = 0; ++ int ret; ++ int i; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + +- if (urb->actual_length > 0 && !urb->transfer_buffer) { ++ if (urb->actual_length > 0 && !urb->transfer_buffer && ++ !urb->num_sgs) { + dev_err(&sdev->udev->dev, + "urb: actual_length %d transfer_buffer null\n", + urb->actual_length); +@@ -176,6 +182,11 @@ static int stub_send_ret_submit(struct s + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; ++ else if (usb_pipein(urb->pipe) && urb->actual_length > 0 && ++ urb->num_sgs) ++ iovnum = 1 + urb->num_sgs; ++ else if (usb_pipein(urb->pipe) && priv->sgl) ++ iovnum = 1 + priv->num_urbs; + else + iovnum = 2; + +@@ -192,6 +203,15 @@ static int stub_send_ret_submit(struct s + setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d\n", + pdu_header.base.seqnum); ++ ++ if (priv->sgl) { ++ for (i = 0; i < priv->num_urbs; i++) ++ actual_length += priv->urbs[i]->actual_length; ++ ++ pdu_header.u.ret_submit.status = priv->urb_status; ++ pdu_header.u.ret_submit.actual_length = actual_length; ++ } ++ + usbip_header_correct_endian(&pdu_header, 1); + + iov[iovnum].iov_base = &pdu_header; +@@ -200,12 +220,47 @@ static int stub_send_ret_submit(struct s + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ +- if (usb_pipein(urb->pipe) && ++ if (usb_pipein(urb->pipe) && priv->sgl) { ++ /* If the server split a single SG request into several ++ * URBs because the server's HCD doesn't support SG, ++ * reassemble the split URB buffers into a single ++ * return command. ++ */ ++ for (i = 0; i < priv->num_urbs; i++) { ++ iov[iovnum].iov_base = ++ priv->urbs[i]->transfer_buffer; ++ iov[iovnum].iov_len = ++ priv->urbs[i]->actual_length; ++ iovnum++; ++ } ++ txsize += actual_length; ++ } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { +- iov[iovnum].iov_base = urb->transfer_buffer; +- iov[iovnum].iov_len = urb->actual_length; +- iovnum++; ++ if (urb->num_sgs) { ++ unsigned int copy = urb->actual_length; ++ int size; ++ ++ for_each_sg(urb->sg, sg, urb->num_sgs, i) { ++ if (copy == 0) ++ break; ++ ++ if (copy < sg->length) ++ size = copy; ++ else ++ size = sg->length; ++ ++ iov[iovnum].iov_base = sg_virt(sg); ++ iov[iovnum].iov_len = size; ++ ++ iovnum++; ++ copy -= size; ++ } ++ } else { ++ iov[iovnum].iov_base = urb->transfer_buffer; ++ iov[iovnum].iov_len = urb->actual_length; ++ iovnum++; ++ } + txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { +--- a/drivers/usb/usbip/usbip_common.c ++++ b/drivers/usb/usbip/usbip_common.c +@@ -680,8 +680,12 @@ EXPORT_SYMBOL_GPL(usbip_pad_iso); + /* some members of urb must be substituted before. */ + int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) + { +- int ret; ++ struct scatterlist *sg; ++ int ret = 0; ++ int recv; + int size; ++ int copy; ++ int i; + + if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) { + /* the direction of urb must be OUT. */ +@@ -701,29 +705,48 @@ int usbip_recv_xbuff(struct usbip_device + if (!(size > 0)) + return 0; + +- if (size > urb->transfer_buffer_length) { ++ if (size > urb->transfer_buffer_length) + /* should not happen, probably malicious packet */ +- if (ud->side == USBIP_STUB) { +- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); +- return 0; +- } else { +- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); +- return -EPIPE; +- } +- } ++ goto error; ++ ++ if (urb->num_sgs) { ++ copy = size; ++ for_each_sg(urb->sg, sg, urb->num_sgs, i) { ++ int recv_size; ++ ++ if (copy < sg->length) ++ recv_size = copy; ++ else ++ recv_size = sg->length; ++ ++ recv = usbip_recv(ud->tcp_socket, sg_virt(sg), ++ recv_size); + +- ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); +- if (ret != size) { +- dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); +- if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) { +- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); +- } else { +- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); +- return -EPIPE; ++ if (recv != recv_size) ++ goto error; ++ ++ copy -= recv; ++ ret += recv; + } ++ ++ if (ret != size) ++ goto error; ++ } else { ++ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); ++ if (ret != size) ++ goto error; + } + + return ret; ++ ++error: ++ dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); ++ if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) ++ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); ++ else ++ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); ++ ++ return -EPIPE; + } + EXPORT_SYMBOL_GPL(usbip_recv_xbuff); + +--- a/drivers/usb/usbip/vhci_hcd.c ++++ b/drivers/usb/usbip/vhci_hcd.c +@@ -702,7 +702,8 @@ static int vhci_urb_enqueue(struct usb_h + } + vdev = &vhci_hcd->vdev[portnum-1]; + +- if (!urb->transfer_buffer && urb->transfer_buffer_length) { ++ if (!urb->transfer_buffer && !urb->num_sgs && ++ urb->transfer_buffer_length) { + dev_dbg(dev, "Null URB transfer buffer\n"); + return -EINVAL; + } +@@ -1148,6 +1149,15 @@ static int vhci_setup(struct usb_hcd *hc + hcd->speed = HCD_USB3; + hcd->self.root_hub->speed = USB_SPEED_SUPER; + } ++ ++ /* ++ * Support SG. ++ * sg_tablesize is an arbitrary value to alleviate memory pressure ++ * on the host. ++ */ ++ hcd->self.sg_tablesize = 32; ++ hcd->self.no_sg_constraint = 1; ++ + return 0; + } + +--- a/drivers/usb/usbip/vhci_rx.c ++++ b/drivers/usb/usbip/vhci_rx.c +@@ -90,6 +90,9 @@ static void vhci_recv_ret_submit(struct + if (usbip_dbg_flag_vhci_rx) + usbip_dump_urb(urb); + ++ if (urb->num_sgs) ++ urb->transfer_flags &= ~URB_DMA_MAP_SG; ++ + usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); + + spin_lock_irqsave(&vhci->lock, flags); +--- a/drivers/usb/usbip/vhci_tx.c ++++ b/drivers/usb/usbip/vhci_tx.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #include "usbip_common.h" + #include "vhci.h" +@@ -50,19 +51,23 @@ static struct vhci_priv *dequeue_from_pr + + static int vhci_send_cmd_submit(struct vhci_device *vdev) + { ++ struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct vhci_priv *priv = NULL; ++ struct scatterlist *sg; + + struct msghdr msg; +- struct kvec iov[3]; ++ struct kvec *iov; + size_t txsize; + + size_t total_size = 0; ++ int iovnum; ++ int err = -ENOMEM; ++ int i; + + while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; +- struct usbip_iso_packet_descriptor *iso_buffer = NULL; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); +@@ -72,18 +77,45 @@ static int vhci_send_cmd_submit(struct v + usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n", + priv->seqnum); + ++ if (urb->num_sgs && usb_pipeout(urb->pipe)) ++ iovnum = 2 + urb->num_sgs; ++ else ++ iovnum = 3; ++ ++ iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL); ++ if (!iov) { ++ usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC); ++ return -ENOMEM; ++ } ++ ++ if (urb->num_sgs) ++ urb->transfer_flags |= URB_DMA_MAP_SG; ++ + /* 1. setup usbip_header */ + setup_cmd_submit_pdu(&pdu_header, urb); + usbip_header_correct_endian(&pdu_header, 1); ++ iovnum = 0; + +- iov[0].iov_base = &pdu_header; +- iov[0].iov_len = sizeof(pdu_header); ++ iov[iovnum].iov_base = &pdu_header; ++ iov[iovnum].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); ++ iovnum++; + + /* 2. setup transfer buffer */ + if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { +- iov[1].iov_base = urb->transfer_buffer; +- iov[1].iov_len = urb->transfer_buffer_length; ++ if (urb->num_sgs && ++ !usb_endpoint_xfer_isoc(&urb->ep->desc)) { ++ for_each_sg(urb->sg, sg, urb->num_sgs, i) { ++ iov[iovnum].iov_base = sg_virt(sg); ++ iov[iovnum].iov_len = sg->length; ++ iovnum++; ++ } ++ } else { ++ iov[iovnum].iov_base = urb->transfer_buffer; ++ iov[iovnum].iov_len = ++ urb->transfer_buffer_length; ++ iovnum++; ++ } + txsize += urb->transfer_buffer_length; + } + +@@ -95,23 +127,26 @@ static int vhci_send_cmd_submit(struct v + if (!iso_buffer) { + usbip_event_add(&vdev->ud, + SDEV_EVENT_ERROR_MALLOC); +- return -1; ++ goto err_iso_buffer; + } + +- iov[2].iov_base = iso_buffer; +- iov[2].iov_len = len; ++ iov[iovnum].iov_base = iso_buffer; ++ iov[iovnum].iov_len = len; ++ iovnum++; + txsize += len; + } + +- ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); ++ ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, iovnum, ++ txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); +- kfree(iso_buffer); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); +- return -1; ++ err = -EPIPE; ++ goto err_tx; + } + ++ kfree(iov); + kfree(iso_buffer); + usbip_dbg_vhci_tx("send txdata\n"); + +@@ -119,6 +154,13 @@ static int vhci_send_cmd_submit(struct v + } + + return total_size; ++ ++err_tx: ++ kfree(iso_buffer); ++err_iso_buffer: ++ kfree(iov); ++ ++ return err; + } + + static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)