This pull request contains the following changes for UML:

- Improve support for non-glibc systems
 - Vector: Add support for scripting and dynamic tap devices
 - Various fixes for the vector networking driver
 - Various fixes for time travel mode
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCAA0FiEEdgfidid8lnn52cLTZvlZhesYu8EFAl+JksYWHHJpY2hhcmRA
 c2lnbWEtc3Rhci5hdAAKCRBm+VmF6xi7wcUyEAC8CF5NEymDBr5ABptOwnA3GVlR
 4ed/Iy1h1pGnM24/2B16te+YWVNUNXyN5GJ8F16Z3nsgB9ehQmHktmcJ76gC9A1s
 AQOF9qHiomzdkS0d9DFAveEfSs72zH2ypCDeqiDFLsmYH+fYSkVVuilCBryIngrL
 AsXbM9x9rAL+o7+A1yBmsxLYcqJkikUBiQuP8uXGmRRx8eqZrpmVnkqzDkeNnMqW
 rmmYv5AQreApA1C3zgs9qVGXBJD8OGTMKPsqnWvydFhsW9jmXGY6MUD5DHayO6xM
 7Ws7fkhF0LG68UbGTGnCW2mXEsOxeUuJaFPDw8MMxslImU34ZO/0OHui+KBzvJmk
 tmL+GvHpKzyT7tsv9Kpyr957cXM1oIG1yfLVLhPG7t3f9fxG3X/gebXIUYPQNyWv
 IEnE4EoF+BY+Zuds3llJPiFYuNW4J25HTpu1+ILCbOPlkDQ98TUekzKzwHEY2XZg
 ORP4mTDV4jemYmfFFJdUBmPZ6OjaCWH1+t7ws68Ne/0h32aIDagYj+B8ubgJBH5S
 GH4/mxxQ4AlfmTSbU47wxuKDhv6mEMyOKIMTyDXqpYgDloI/g9IKj1Pfz+RN6qbb
 LVssoJI+lr0L9NPDnVZ2BNoTCDhryMfctOUkfCA0RWXdnygQWVbyizbx56VK78NJ
 ZPcGjo3BOxg9TRRDNQ==
 =OzDf
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml

Pull UML updates from Richard Weinberger:

 - Improve support for non-glibc systems

 - Vector: Add support for scripting and dynamic tap devices

 - Various fixes for the vector networking driver

 - Various fixes for time travel mode

* tag 'for-linus-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  um: vector: Add dynamic tap interfaces and scripting
  um: Clean up stacktrace dump
  um: Fix incorrect assumptions about max pid length
  um: Remove dead usage of TIF_IA32
  um: Remove redundant NULL check
  um: change sigio_spinlock to a mutex
  um: time-travel: Return the sequence number in ACK messages
  um: time-travel: Fix IRQ handling in time_travel_handle_message()
  um: Allow static linking for non-glibc implementations
  um: Some fixes to build UML with musl
  um: vector: Use GFP_ATOMIC under spin lock
  um: Fix null pointer dereference in vector_user_bpf
This commit is contained in:
Linus Torvalds 2020-10-18 10:03:23 -07:00
Родитель 429731277d f06885b3f3
Коммит 9453b2d469
14 изменённых файлов: 91 добавлений и 61 удалений

Просмотреть файл

@ -62,12 +62,12 @@ config NR_CPUS
source "arch/$(HEADER_ARCH)/um/Kconfig" source "arch/$(HEADER_ARCH)/um/Kconfig"
config FORBID_STATIC_LINK config MAY_HAVE_RUNTIME_DEPS
bool bool
config STATIC_LINK config STATIC_LINK
bool "Force a static link" bool "Force a static link"
depends on !FORBID_STATIC_LINK depends on CC_CAN_LINK_STATIC_NO_RUNTIME_DEPS || !MAY_HAVE_RUNTIME_DEPS
help help
This option gives you the ability to force a static link of UML. This option gives you the ability to force a static link of UML.
Normally, UML is linked as a shared binary. This is inconvenient for Normally, UML is linked as a shared binary. This is inconvenient for

Просмотреть файл

@ -234,7 +234,7 @@ config UML_NET_DAEMON
config UML_NET_VECTOR config UML_NET_VECTOR
bool "Vector I/O high performance network devices" bool "Vector I/O high performance network devices"
depends on UML_NET depends on UML_NET
select FORBID_STATIC_LINK select MAY_HAVE_RUNTIME_DEPS
help help
This User-Mode Linux network driver uses multi-message send This User-Mode Linux network driver uses multi-message send
and receive functions. The host running the UML guest must have and receive functions. The host running the UML guest must have
@ -246,7 +246,7 @@ config UML_NET_VECTOR
config UML_NET_VDE config UML_NET_VDE
bool "VDE transport (obsolete)" bool "VDE transport (obsolete)"
depends on UML_NET depends on UML_NET
select FORBID_STATIC_LINK select MAY_HAVE_RUNTIME_DEPS
help help
This User-Mode Linux network transport allows one or more running This User-Mode Linux network transport allows one or more running
UMLs on a single host to communicate with each other and also UMLs on a single host to communicate with each other and also
@ -294,7 +294,7 @@ config UML_NET_MCAST
config UML_NET_PCAP config UML_NET_PCAP
bool "pcap transport (obsolete)" bool "pcap transport (obsolete)"
depends on UML_NET depends on UML_NET
select FORBID_STATIC_LINK select MAY_HAVE_RUNTIME_DEPS
help help
The pcap transport makes a pcap packet stream on the host look The pcap transport makes a pcap packet stream on the host look
like an ethernet device inside UML. This is useful for making like an ethernet device inside UML. This is useful for making

Просмотреть файл

@ -7,6 +7,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>

Просмотреть файл

@ -32,7 +32,7 @@ static int pcap_user_init(void *data, void *dev)
return 0; return 0;
} }
static int pcap_open(void *data) static int pcap_user_open(void *data)
{ {
struct pcap_data *pri = data; struct pcap_data *pri = data;
__u32 netmask; __u32 netmask;
@ -44,14 +44,14 @@ static int pcap_open(void *data)
if (pri->filter != NULL) { if (pri->filter != NULL) {
err = dev_netmask(pri->dev, &netmask); err = dev_netmask(pri->dev, &netmask);
if (err < 0) { if (err < 0) {
printk(UM_KERN_ERR "pcap_open : dev_netmask failed\n"); printk(UM_KERN_ERR "pcap_user_open : dev_netmask failed\n");
return -EIO; return -EIO;
} }
pri->compiled = uml_kmalloc(sizeof(struct bpf_program), pri->compiled = uml_kmalloc(sizeof(struct bpf_program),
UM_GFP_KERNEL); UM_GFP_KERNEL);
if (pri->compiled == NULL) { if (pri->compiled == NULL) {
printk(UM_KERN_ERR "pcap_open : kmalloc failed\n"); printk(UM_KERN_ERR "pcap_user_open : kmalloc failed\n");
return -ENOMEM; return -ENOMEM;
} }
@ -59,14 +59,14 @@ static int pcap_open(void *data)
(struct bpf_program *) pri->compiled, (struct bpf_program *) pri->compiled,
pri->filter, pri->optimize, netmask); pri->filter, pri->optimize, netmask);
if (err < 0) { if (err < 0) {
printk(UM_KERN_ERR "pcap_open : pcap_compile failed - " printk(UM_KERN_ERR "pcap_user_open : pcap_compile failed - "
"'%s'\n", pcap_geterr(pri->pcap)); "'%s'\n", pcap_geterr(pri->pcap));
goto out; goto out;
} }
err = pcap_setfilter(pri->pcap, pri->compiled); err = pcap_setfilter(pri->pcap, pri->compiled);
if (err < 0) { if (err < 0) {
printk(UM_KERN_ERR "pcap_open : pcap_setfilter " printk(UM_KERN_ERR "pcap_user_open : pcap_setfilter "
"failed - '%s'\n", pcap_geterr(pri->pcap)); "failed - '%s'\n", pcap_geterr(pri->pcap));
goto out; goto out;
} }
@ -127,7 +127,7 @@ int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri)
const struct net_user_info pcap_user_info = { const struct net_user_info pcap_user_info = {
.init = pcap_user_init, .init = pcap_user_init,
.open = pcap_open, .open = pcap_user_open,
.close = NULL, .close = NULL,
.remove = pcap_remove, .remove = pcap_remove,
.add_address = NULL, .add_address = NULL,

Просмотреть файл

@ -9,7 +9,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <sys/termios.h> #include <termios.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <net_user.h> #include <net_user.h>
#include <os.h> #include <os.h>

Просмотреть файл

@ -1403,7 +1403,7 @@ static int vector_net_load_bpf_flash(struct net_device *dev,
kfree(vp->bpf->filter); kfree(vp->bpf->filter);
vp->bpf->filter = NULL; vp->bpf->filter = NULL;
} else { } else {
vp->bpf = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL); vp->bpf = kmalloc(sizeof(struct sock_fprog), GFP_ATOMIC);
if (vp->bpf == NULL) { if (vp->bpf == NULL) {
netdev_err(dev, "failed to allocate memory for firmware\n"); netdev_err(dev, "failed to allocate memory for firmware\n");
goto flash_fail; goto flash_fail;
@ -1415,7 +1415,7 @@ static int vector_net_load_bpf_flash(struct net_device *dev,
if (request_firmware(&fw, efl->data, &vdevice->pdev.dev)) if (request_firmware(&fw, efl->data, &vdevice->pdev.dev))
goto flash_fail; goto flash_fail;
vp->bpf->filter = kmemdup(fw->data, fw->size, GFP_KERNEL); vp->bpf->filter = kmemdup(fw->data, fw->size, GFP_ATOMIC);
if (!vp->bpf->filter) if (!vp->bpf->filter)
goto free_buffer; goto free_buffer;

Просмотреть файл

@ -18,9 +18,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <net/ethernet.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <netinet/ether.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/if_packet.h> #include <linux/if_packet.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -39,6 +37,7 @@
#define ID_MAX 2 #define ID_MAX 2
#define TOKEN_IFNAME "ifname" #define TOKEN_IFNAME "ifname"
#define TOKEN_SCRIPT "ifup"
#define TRANS_RAW "raw" #define TRANS_RAW "raw"
#define TRANS_RAW_LEN strlen(TRANS_RAW) #define TRANS_RAW_LEN strlen(TRANS_RAW)
@ -55,6 +54,9 @@
#define MAX_UN_LEN 107 #define MAX_UN_LEN 107
static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char *template = "tapXXXXXX";
/* This is very ugly and brute force lookup, but it is done /* This is very ugly and brute force lookup, but it is done
* only once at initialization so not worth doing hashes or * only once at initialization so not worth doing hashes or
* anything more intelligent * anything more intelligent
@ -191,16 +193,21 @@ raw_fd_cleanup:
return err; return err;
} }
static struct vector_fds *user_init_tap_fds(struct arglist *ifspec) static struct vector_fds *user_init_tap_fds(struct arglist *ifspec)
{ {
int fd = -1; int fd = -1, i;
char *iface; char *iface;
struct vector_fds *result = NULL; struct vector_fds *result = NULL;
bool dynamic = false;
char dynamic_ifname[IFNAMSIZ];
char *argv[] = {NULL, NULL, NULL, NULL};
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME); iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL) { if (iface == NULL) {
printk(UM_KERN_ERR "uml_tap: failed to parse interface spec\n"); dynamic = true;
goto tap_cleanup; iface = dynamic_ifname;
srand(getpid());
} }
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL); result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
@ -214,14 +221,30 @@ static struct vector_fds *user_init_tap_fds(struct arglist *ifspec)
result->remote_addr_size = 0; result->remote_addr_size = 0;
/* TAP */ /* TAP */
do {
if (dynamic) {
strcpy(iface, template);
for (i = 0; i < strlen(iface); i++) {
if (iface[i] == 'X') {
iface[i] = padchar[rand() % strlen(padchar)];
}
}
}
fd = create_tap_fd(iface);
if ((fd < 0) && (!dynamic)) {
printk(UM_KERN_ERR "uml_tap: failed to create tun interface\n");
goto tap_cleanup;
}
result->tx_fd = fd;
result->rx_fd = fd;
} while (fd < 0);
fd = create_tap_fd(iface); argv[0] = uml_vector_fetch_arg(ifspec, TOKEN_SCRIPT);
if (fd < 0) { if (argv[0]) {
printk(UM_KERN_ERR "uml_tap: failed to create tun interface\n"); argv[1] = iface;
goto tap_cleanup; run_helper(NULL, NULL, argv);
} }
result->tx_fd = fd;
result->rx_fd = fd;
return result; return result;
tap_cleanup: tap_cleanup:
printk(UM_KERN_ERR "user_init_tap: init failed, error %d", fd); printk(UM_KERN_ERR "user_init_tap: init failed, error %d", fd);
@ -233,6 +256,7 @@ static struct vector_fds *user_init_hybrid_fds(struct arglist *ifspec)
{ {
char *iface; char *iface;
struct vector_fds *result = NULL; struct vector_fds *result = NULL;
char *argv[] = {NULL, NULL, NULL, NULL};
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME); iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL) { if (iface == NULL) {
@ -266,6 +290,12 @@ static struct vector_fds *user_init_hybrid_fds(struct arglist *ifspec)
"uml_tap: failed to create paired raw socket: %i\n", result->rx_fd); "uml_tap: failed to create paired raw socket: %i\n", result->rx_fd);
goto hybrid_cleanup; goto hybrid_cleanup;
} }
argv[0] = uml_vector_fetch_arg(ifspec, TOKEN_SCRIPT);
if (argv[0]) {
argv[1] = iface;
run_helper(NULL, NULL, argv);
}
return result; return result;
hybrid_cleanup: hybrid_cleanup:
printk(UM_KERN_ERR "user_init_hybrid: init failed"); printk(UM_KERN_ERR "user_init_hybrid: init failed");
@ -332,7 +362,7 @@ static struct vector_fds *user_init_unix_fds(struct arglist *ifspec, int id)
} }
switch (id) { switch (id) {
case ID_BESS: case ID_BESS:
if (connect(fd, remote_addr, sizeof(struct sockaddr_un)) < 0) { if (connect(fd, (const struct sockaddr *) remote_addr, sizeof(struct sockaddr_un)) < 0) {
printk(UM_KERN_ERR "bess open:cannot connect to %s %i", remote_addr->sun_path, -errno); printk(UM_KERN_ERR "bess open:cannot connect to %s %i", remote_addr->sun_path, -errno);
goto unix_cleanup; goto unix_cleanup;
} }
@ -399,8 +429,7 @@ static struct vector_fds *user_init_fd_fds(struct arglist *ifspec)
fd_cleanup: fd_cleanup:
if (fd >= 0) if (fd >= 0)
os_close_file(fd); os_close_file(fd);
if (result != NULL) kfree(result);
kfree(result);
return NULL; return NULL;
} }
@ -410,6 +439,7 @@ static struct vector_fds *user_init_raw_fds(struct arglist *ifspec)
int err = -ENOMEM; int err = -ENOMEM;
char *iface; char *iface;
struct vector_fds *result = NULL; struct vector_fds *result = NULL;
char *argv[] = {NULL, NULL, NULL, NULL};
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME); iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL) if (iface == NULL)
@ -432,6 +462,11 @@ static struct vector_fds *user_init_raw_fds(struct arglist *ifspec)
result->remote_addr = NULL; result->remote_addr = NULL;
result->remote_addr_size = 0; result->remote_addr_size = 0;
} }
argv[0] = uml_vector_fetch_arg(ifspec, TOKEN_SCRIPT);
if (argv[0]) {
argv[1] = iface;
run_helper(NULL, NULL, argv);
}
return result; return result;
raw_cleanup: raw_cleanup:
printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err); printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err);
@ -789,10 +824,12 @@ void *uml_vector_user_bpf(char *filename)
return false; return false;
} }
bpf_prog = uml_kmalloc(sizeof(struct sock_fprog), UM_GFP_KERNEL); bpf_prog = uml_kmalloc(sizeof(struct sock_fprog), UM_GFP_KERNEL);
if (bpf_prog != NULL) { if (bpf_prog == NULL) {
bpf_prog->len = statbuf.st_size / sizeof(struct sock_filter); printk(KERN_ERR "Failed to allocate bpf prog buffer");
bpf_prog->filter = NULL; return NULL;
} }
bpf_prog->len = statbuf.st_size / sizeof(struct sock_filter);
bpf_prog->filter = NULL;
ffd = os_open_file(filename, of_read(OPENFLAGS()), 0); ffd = os_open_file(filename, of_read(OPENFLAGS()), 0);
if (ffd < 0) { if (ffd < 0) {
printk(KERN_ERR "Error %d opening bpf file", -errno); printk(KERN_ERR "Error %d opening bpf file", -errno);

Просмотреть файл

@ -35,14 +35,14 @@ int write_sigio_irq(int fd)
} }
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */ /* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
static DEFINE_SPINLOCK(sigio_spinlock); static DEFINE_MUTEX(sigio_mutex);
void sigio_lock(void) void sigio_lock(void)
{ {
spin_lock(&sigio_spinlock); mutex_lock(&sigio_mutex);
} }
void sigio_unlock(void) void sigio_unlock(void)
{ {
spin_unlock(&sigio_spinlock); mutex_unlock(&sigio_mutex);
} }

Просмотреть файл

@ -47,12 +47,10 @@ void show_stack(struct task_struct *task, unsigned long *stack,
if (kstack_end(stack)) if (kstack_end(stack))
break; break;
if (i && ((i % STACKSLOTS_PER_LINE) == 0)) if (i && ((i % STACKSLOTS_PER_LINE) == 0))
printk("%s\n", loglvl); pr_cont("\n");
pr_cont(" %08lx", *stack++); pr_cont(" %08lx", *stack++);
} }
printk("%s\n", loglvl);
printk("%sCall Trace:\n", loglvl); printk("%sCall Trace:\n", loglvl);
dump_trace(current, &stackops, (void *)loglvl); dump_trace(current, &stackops, (void *)loglvl);
printk("%s\n", loglvl);
} }

Просмотреть файл

@ -70,13 +70,17 @@ static void time_travel_handle_message(struct um_timetravel_msg *msg,
* read of the message and write of the ACK. * read of the message and write of the ACK.
*/ */
if (mode != TTMH_READ) { if (mode != TTMH_READ) {
bool disabled = irqs_disabled();
BUG_ON(mode == TTMH_IDLE && !disabled);
if (disabled)
local_irq_enable();
while (os_poll(1, &time_travel_ext_fd) != 0) { while (os_poll(1, &time_travel_ext_fd) != 0) {
if (mode == TTMH_IDLE) { /* nothing */
BUG_ON(!irqs_disabled());
local_irq_enable();
local_irq_disable();
}
} }
if (disabled)
local_irq_disable();
} }
ret = os_read_file(time_travel_ext_fd, msg, sizeof(*msg)); ret = os_read_file(time_travel_ext_fd, msg, sizeof(*msg));
@ -102,6 +106,7 @@ static void time_travel_handle_message(struct um_timetravel_msg *msg,
break; break;
} }
resp.seq = msg->seq;
os_write_file(time_travel_ext_fd, &resp, sizeof(resp)); os_write_file(time_travel_ext_fd, &resp, sizeof(resp));
} }

Просмотреть файл

@ -97,7 +97,7 @@ static int remove_files_and_dir(char *dir)
while ((ent = readdir(directory)) != NULL) { while ((ent = readdir(directory)) != NULL) {
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue; continue;
len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; len = strlen(dir) + strlen("/") + strlen(ent->d_name) + 1;
if (len > sizeof(file)) { if (len > sizeof(file)) {
ret = -E2BIG; ret = -E2BIG;
goto out; goto out;
@ -135,7 +135,7 @@ out:
*/ */
static inline int is_umdir_used(char *dir) static inline int is_umdir_used(char *dir)
{ {
char pid[sizeof("nnnnn\0")], *end, *file; char pid[sizeof("nnnnnnnnn")], *end, *file;
int dead, fd, p, n, err; int dead, fd, p, n, err;
size_t filelen; size_t filelen;
@ -217,10 +217,10 @@ static int umdir_take_if_dead(char *dir)
static void __init create_pid_file(void) static void __init create_pid_file(void)
{ {
char pid[sizeof("nnnnn\0")], *file; char pid[sizeof("nnnnnnnnn")], *file;
int fd, n; int fd, n;
n = strlen(uml_dir) + UMID_LEN + sizeof("/pid\0"); n = strlen(uml_dir) + UMID_LEN + sizeof("/pid");
file = malloc(n); file = malloc(n);
if (!file) if (!file)
return; return;

Просмотреть файл

@ -10,7 +10,7 @@
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <termios.h> #include <termios.h>
#include <wait.h> #include <sys/wait.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <init.h> #include <init.h>

Просмотреть файл

@ -52,14 +52,6 @@ static const int reg_offsets[] =
int putreg(struct task_struct *child, int regno, unsigned long value) int putreg(struct task_struct *child, int regno, unsigned long value)
{ {
#ifdef TIF_IA32
/*
* Some code in the 64bit emulation may not be 64bit clean.
* Don't take any chances.
*/
if (test_tsk_thread_flag(child, TIF_IA32))
value &= 0xffffffff;
#endif
switch (regno) { switch (regno) {
case R8: case R8:
case R9: case R9:
@ -137,10 +129,7 @@ int poke_user(struct task_struct *child, long addr, long data)
unsigned long getreg(struct task_struct *child, int regno) unsigned long getreg(struct task_struct *child, int regno)
{ {
unsigned long mask = ~0UL; unsigned long mask = ~0UL;
#ifdef TIF_IA32
if (test_tsk_thread_flag(child, TIF_IA32))
mask = 0xffffffff;
#endif
switch (regno) { switch (regno) {
case R8: case R8:
case R9: case R9:

Просмотреть файл

@ -2,7 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <signal.h> #include <signal.h>
#include <sys/poll.h> #include <poll.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/user.h> #include <sys/user.h>
#define __FRAME_OFFSETS #define __FRAME_OFFSETS