Skip to content
Permalink
Browse files
Use a bounce buffer for rtlsdr on ARM to work around zero-copy problems.
On 5.x kernels with the USB mmap problems fixed, the distributed librtlsdr
will use a zero-copy mapping for USB buffers. Unfortunately, there is
something about the nature of the mapping on ARM (at least on Pis) that
makes most access to the data extremely slow. The uc8_nodc converter is
about 35x slower in this case compared to working on a heap-allocated buffer.

Luckily, a plain memcpy() of the buffer is still reasonably fast, so
we can use a bounce buffer and copy the data out of the slow mapping, then
pass the copy to the converter. This mitigates most of the problem,
at the expense of always needing that extra copy (which does somewhat
defeat the purpose of zero-copy!)

Unfortunately, librtlsdr provides no reliable way to control or
detect the use of zero-copy mappings, so we have to assume the problem
is always there (at least on ARM) and pay the cost of an unnecessary
copy when zerocopy is _not_ in use, too.
  • Loading branch information
mutability committed Aug 5, 2020
1 parent d5d0406 commit 653ad6127a57b88af78b30e9790bb97f80f8eff7
Showing 1 changed file with 23 additions and 3 deletions.
@@ -53,12 +53,17 @@

#include <rtl-sdr.h>

#ifdef __arm__
// Assume we need to use a bounce buffer to avoid performance problems on Pis running kernel 5.x and using zerocopy
# define USE_BOUNCE_BUFFER
#endif

static struct {
rtlsdr_dev_t *dev;
bool digital_agc;
int ppm_error;
int direct_sampling;

uint8_t *bounce_buffer;
iq_convert_fn converter;
struct converter_state *converter_state;
} RTLSDR;
@@ -73,6 +78,7 @@ void rtlsdrInitConfig()
RTLSDR.digital_agc = false;
RTLSDR.ppm_error = 0;
RTLSDR.direct_sampling = 0;
RTLSDR.bounce_buffer = NULL;
RTLSDR.converter = NULL;
RTLSDR.converter_state = NULL;
}
@@ -262,6 +268,14 @@ bool rtlsdrOpen(void) {
return false;
}

#ifdef USE_BOUNCE_BUFFER
if (!(RTLSDR.bounce_buffer = malloc(MODES_RTL_BUF_SIZE))) {
fprintf(stderr, "rtlsdr: can't allocate bounce buffer\n");
rtlsdrClose();
return false;
}
#endif

return true;
}

@@ -317,10 +331,13 @@ static void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx)
dropped = samples_read - to_convert;
}


#ifdef USE_BOUNCE_BUFFER
// Work around zero-copy slowness on Pis with 5.x kernels
memcpy(RTLSDR.bounce_buffer, buf, to_convert * 2);
buf = RTLSDR.bounce_buffer;
#endif

RTLSDR.converter(buf, &outbuf->data[outbuf->overlap], to_convert, RTLSDR.converter_state, &outbuf->mean_level, &outbuf->mean_power);

outbuf->validLength = outbuf->overlap + to_convert;

// Push to the demodulation thread
@@ -353,4 +370,7 @@ void rtlsdrClose()
RTLSDR.converter = NULL;
RTLSDR.converter_state = NULL;
}

free(RTLSDR.bounce_buffer);
RTLSDR.bounce_buffer = NULL;
}

2 comments on commit 653ad61

@jvanwouw
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this perhaps related to the following message that I have gotten with multiple RTL-SDR applications (notably rtl_433) on NetBSD earmv6hf:
"Please increase your allowed usbfs buffer size with the following command: echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb"

@mutability
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related. That message is librtlsdr telling you that it failed to submit a transfer to libusb: https://github.com/steve-m/librtlsdr/blob/master/src/librtlsdr.c#L1894
(that message is linux-specific; I don't know what the failure mode on netbsd you're seeing is, but it might be that you're hitting a similar limit to what happens on linux)

Please sign in to comment.