blob: 449123858339e7eae42549243f8022899388c280 [file] [log] [blame]
/*
* Copyright (C) 2003-2009 Chelsio Communications. All rights reserved.
*
* Written by Dimitris Michailidis (dm@chelsio.com)
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
* release for licensing terms and conditions.
*/
#ifndef _NET_OFFLOAD_H
#define _NET_OFFLOAD_H
#include <net/tcp.h>
#if defined(CONFIG_TCP_OFFLOAD_MODULE)
# define SOCK_OFFLOADED (31) // connected socket is offloaded
# define SOCK_NO_DDP (30) // socket should not do DDP
#endif
enum {
OFFLOAD_LISTEN_START,
OFFLOAD_LISTEN_STOP
};
struct sock;
struct sk_buff;
struct toedev;
struct notifier_block;
struct pipe_inode_info;
/*
* Extended 'struct proto' with additional members used by offloaded
* connections.
*/
struct sk_ofld_proto {
struct proto proto; /* keep this first */
int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
ssize_t (*splice_read)(struct sock *sk, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
};
/* Per-skb backlog handler. Run when a socket's backlog is processed. */
struct blog_skb_cb {
void (*backlog_rcv) (struct sock *sk, struct sk_buff *skb);
struct toedev *dev;
};
#define BLOG_SKB_CB(skb) ((struct blog_skb_cb *)(skb)->cb)
#ifndef LINUX_2_4
struct offload_req {
__be32 sip[4];
__be32 dip[4];
__be16 sport;
__be16 dport;
__u8 ipvers_opentype;
__u8 tos;
__be16 vlan;
__u32 mark;
};
enum { OPEN_TYPE_LISTEN, OPEN_TYPE_ACTIVE, OPEN_TYPE_PASSIVE };
struct offload_settings {
__u8 offload;
__s8 ddp;
__s8 rx_coalesce;
__s8 cong_algo;
__s32 rssq;
__s16 sched_class;
__s8 tstamp;
__s8 sack;
};
struct ofld_prog_inst { /* offload policy program "instructions" */
s32 offset;
u32 mask;
u32 value;
s32 next[2];
};
struct offload_policy {
struct rcu_head rcu_head;
int match_all;
int use_opt;
const struct offload_settings *settings;
const u32 *opt_prog_start;
struct ofld_prog_inst prog[0];
};
struct ofld_policy_file {
unsigned int vers;
int output_everything;
unsigned int nrules;
unsigned int prog_size;
unsigned int opt_prog_size;
unsigned int nsettings;
const struct ofld_prog_inst prog[0];
};
#endif /* !LINUX_2_4 */
#if defined(CONFIG_TCP_OFFLOAD) || \
(defined(CONFIG_TCP_OFFLOAD_MODULE) && defined(MODULE))
int register_listen_offload_notifier(struct notifier_block *nb);
int unregister_listen_offload_notifier(struct notifier_block *nb);
int start_listen_offload(struct sock *sk);
int stop_listen_offload(struct sock *sk);
int tcp_connect_offload(struct sock *sk);
void security_inet_conn_estab(struct sock *sk, struct sk_buff *skb);
void walk_listens(void *handle, int (*func)(void *handle, struct sock *sk));
int set_offload_policy(struct toedev *dev, const struct ofld_policy_file *f);
void offload_req_from_sk(struct offload_req *req, struct sock *sk, int otype);
const struct offload_settings *
lookup_ofld_policy(const struct toedev *dev, const struct offload_req *req,
int cop_managed_offloading);
ssize_t tcp_sendpage_offload(struct socket *sock, struct page *page,
int offset, size_t size, int flags);
int tcp_sendmsg_offload(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size);
ssize_t tcp_splice_read_offload(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
#else
static inline int tcp_connect_offload(struct sock *sk)
{
return 0;
}
static inline int start_listen_offload(struct sock *sk)
{
return -EPROTONOSUPPORT;
}
static inline int stop_listen_offload(struct sock *sk)
{
return -EPROTONOSUPPORT;
}
#endif
#if defined(CONFIG_TCP_OFFLOAD_MODULE)
int install_special_data_ready(struct sock *sk);
void restore_special_data_ready(struct sock *sk);
int skb_splice_bits_pub(struct sk_buff *skb, unsigned int offset,
struct pipe_inode_info *pipe, unsigned int len,
unsigned int flags);
#else
static inline int install_special_data_ready(struct sock *sk) { return 0; }
static inline void restore_special_data_ready(struct sock *sk) {}
#define skb_splice_bits_pub skb_splice_bits
#endif
#if defined(CONFIG_DEBUG_RODATA) && defined(CONFIG_TCP_OFFLOAD_MODULE)
void offload_socket_ops(struct sock *sk);
void restore_socket_ops(struct sock *sk);
#else
static inline void offload_socket_ops(struct sock *sk) {}
static inline void restore_socket_ops(struct sock *sk) {}
#endif
/*
* List of sockets in SYN_RCV state.
* ---------------------------------
*/
/*
* Definitions for SYN_RCV state socket doubly-linked queue hung off of a
* listening socket. These overlap fields in (struct tcp_sock) which we know
* are free for sockets in this state. We'd like to get rid of this
* overloading by putting the fields into the offloaded socket state data but
* we can have a parent listening socket which is not offloaded and children
* sockets which are offloaded. And then there's also the issue of wanting to
* support more than one kind of offloaded socket ... So, in the absense of
* well-defined fields for this purpose, we hijack others ...
*
* Also note that we must reset the state of these fields after we're done
* with them. Thus, the synq_empty() and reset_synq() functions are Linux-
* version dependent and are defined in the compatibility header along with
* the SYN queue next/prev fields ...
*
* We use the (stuct tcp_sock):ucopy.prequeue.{head,tail} fields to implement
* the Offload SYN Queue. Each listening socket keeps a doubly linked list of
* its children sockets in SYN_RCV state, i.e., the sockets on its SYN queue.
*/
#define synq_next_tp_field ucopy.prequeue.next
#define synq_prev_tp_field ucopy.prequeue.prev
#define synq_next(sk) (*(struct sock **)&(tcp_sk(sk)->synq_next_tp_field))
#define synq_prev(sk) (*(struct sock **)&(tcp_sk(sk)->synq_prev_tp_field))
static inline int synq_empty(struct sock *sk)
{
return skb_queue_empty(&tcp_sk(sk)->ucopy.prequeue);
}
static inline void reset_synq(struct sock *sk)
{
skb_queue_head_init(&tcp_sk(sk)->ucopy.prequeue);
}
static inline void synq_add(struct sock *parent, struct sock *child)
{
/* Add the child socket onto the front of the list */
if (synq_empty(parent)) {
/* this is the first child */
synq_next(child) = parent;
synq_prev(parent) = child;
} else {
synq_next(child) = synq_next(parent);
synq_prev(synq_next(parent)) = child;
}
synq_next(parent) = child;
synq_prev(child) = parent;
}
static inline void synq_remove(struct sock *child)
{
struct sock *next = synq_next(child);
struct sock *prev = synq_prev(child);
if (next == prev) {
/* sole child */
reset_synq(next);
} else {
synq_next(prev) = synq_next(child);
synq_prev(next) = synq_prev(child);
}
reset_synq(child);
}
#endif /* !_NET_OFFLOAD_H */