diff options
Diffstat (limited to 'net/can')
| -rw-r--r-- | net/can/af_can.c | 36 | ||||
| -rw-r--r-- | net/can/af_can.h | 15 | ||||
| -rw-r--r-- | net/can/bcm.c | 8 | ||||
| -rw-r--r-- | net/can/gw.c | 25 | ||||
| -rw-r--r-- | net/can/proc.c | 76 | ||||
| -rw-r--r-- | net/can/raw.c | 31 | 
6 files changed, 121 insertions, 70 deletions
diff --git a/net/can/af_can.c b/net/can/af_can.c index 3ab8dd2e128..ce82337521f 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -57,6 +57,7 @@  #include <linux/skbuff.h>  #include <linux/can.h>  #include <linux/can/core.h> +#include <linux/can/skb.h>  #include <linux/ratelimit.h>  #include <net/net_namespace.h>  #include <net/sock.h> @@ -290,7 +291,7 @@ int can_send(struct sk_buff *skb, int loop)  				return -ENOMEM;  			} -			newskb->sk = skb->sk; +			can_skb_set_owner(newskb, skb->sk);  			newskb->ip_summed = CHECKSUM_UNNECESSARY;  			newskb->pkt_type = PACKET_BROADCAST;  		} @@ -337,6 +338,29 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)  }  /** + * effhash - hash function for 29 bit CAN identifier reduction + * @can_id: 29 bit CAN identifier + * + * Description: + *  To reduce the linear traversal in one linked list of _single_ EFF CAN + *  frame subscriptions the 29 bit identifier is mapped to 10 bits. + *  (see CAN_EFF_RCV_HASH_BITS definition) + * + * Return: + *  Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask ) + */ +static unsigned int effhash(canid_t can_id) +{ +	unsigned int hash; + +	hash = can_id; +	hash ^= can_id >> CAN_EFF_RCV_HASH_BITS; +	hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS); + +	return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1); +} + +/**   * find_rcv_list - determine optimal filterlist inside device filter struct   * @can_id: pointer to CAN identifier of a given can_filter   * @mask: pointer to CAN mask of a given can_filter @@ -399,10 +423,8 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,  	    !(*can_id & CAN_RTR_FLAG)) {  		if (*can_id & CAN_EFF_FLAG) { -			if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) { -				/* RFC: a future use-case for hash-tables? */ -				return &d->rx[RX_EFF]; -			} +			if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) +				return &d->rx_eff[effhash(*can_id)];  		} else {  			if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))  				return &d->rx_sff[*can_id]; @@ -420,7 +442,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,   * @mask: CAN mask (see description)   * @func: callback function on filter match   * @data: returned parameter for callback function - * @ident: string for calling module indentification + * @ident: string for calling module identification   *   * Description:   *  Invokes the callback function with the received sk_buff and the given @@ -631,7 +653,7 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)  		return matches;  	if (can_id & CAN_EFF_FLAG) { -		hlist_for_each_entry_rcu(r, &d->rx[RX_EFF], list) { +		hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {  			if (r->can_id == can_id) {  				deliver(skb, r);  				matches++; diff --git a/net/can/af_can.h b/net/can/af_can.h index 1dccb4c3389..fca0fe9fc45 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -59,12 +59,17 @@ struct receiver {  	char *ident;  }; -enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX }; +#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS) +#define CAN_EFF_RCV_HASH_BITS 10 +#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS) + +enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };  /* per device receive filters linked at dev->ml_priv */  struct dev_rcv_lists {  	struct hlist_head rx[RX_MAX]; -	struct hlist_head rx_sff[0x800]; +	struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ]; +	struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];  	int remove_on_zero_entries;  	int entries;  }; @@ -108,9 +113,9 @@ struct s_pstats {  extern struct dev_rcv_lists can_rx_alldev_list;  /* function prototypes for the CAN networklayer procfs (proc.c) */ -extern void can_init_proc(void); -extern void can_remove_proc(void); -extern void can_stat_update(unsigned long data); +void can_init_proc(void); +void can_remove_proc(void); +void can_stat_update(unsigned long data);  /* structures and variables from af_can.c needed in proc.c for reading */  extern struct timer_list can_stattimer;    /* timer for statistics update */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 46f20bfafc0..dcb75c0e66c 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -268,7 +268,7 @@ static void bcm_can_tx(struct bcm_op *op)  	/* send with loopback */  	skb->dev = dev; -	skb->sk = op->sk; +	can_skb_set_owner(skb, op->sk);  	can_send(skb, 1);  	/* update statistics */ @@ -1223,7 +1223,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)  	can_skb_prv(skb)->ifindex = dev->ifindex;  	skb->dev = dev; -	skb->sk  = sk; +	can_skb_set_owner(skb, sk);  	err = can_send(skb, 1); /* send with loopback */  	dev_put(dev); @@ -1256,8 +1256,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,  	if (!ifindex && msg->msg_name) {  		/* no bound device as default => check msg_name */ -		struct sockaddr_can *addr = -			(struct sockaddr_can *)msg->msg_name; +		DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);  		if (msg->msg_namelen < sizeof(*addr))  			return -EINVAL; @@ -1568,6 +1567,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,  	sock_recv_ts_and_drops(msg, sk, skb);  	if (msg->msg_name) { +		__sockaddr_check_size(sizeof(struct sockaddr_can));  		msg->msg_namelen = sizeof(struct sockaddr_can);  		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);  	} diff --git a/net/can/gw.c b/net/can/gw.c index 3f9b0f3a281..050a2110d43 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -804,7 +804,7 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)  	u8 limhops = 0;  	int err = 0; -	if (!capable(CAP_NET_ADMIN)) +	if (!netlink_capable(skb, CAP_NET_ADMIN))  		return -EPERM;  	if (nlmsg_len(nlh) < sizeof(*r)) @@ -839,23 +839,21 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)  	if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)  		goto out; -	gwj->src.dev = dev_get_by_index(&init_net, gwj->ccgw.src_idx); +	gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);  	if (!gwj->src.dev)  		goto out; -	/* check for CAN netdev not using header_ops - see gw_rcv() */ -	if (gwj->src.dev->type != ARPHRD_CAN || gwj->src.dev->header_ops) -		goto put_src_out; +	if (gwj->src.dev->type != ARPHRD_CAN) +		goto out; -	gwj->dst.dev = dev_get_by_index(&init_net, gwj->ccgw.dst_idx); +	gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);  	if (!gwj->dst.dev) -		goto put_src_out; +		goto out; -	/* check for CAN netdev not using header_ops - see gw_rcv() */ -	if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops) -		goto put_src_dst_out; +	if (gwj->dst.dev->type != ARPHRD_CAN) +		goto out;  	gwj->limit_hops = limhops; @@ -864,11 +862,6 @@ static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)  	err = cgw_register_filter(gwj);  	if (!err)  		hlist_add_head_rcu(&gwj->list, &cgw_list); - -put_src_dst_out: -	dev_put(gwj->dst.dev); -put_src_out: -	dev_put(gwj->src.dev);  out:  	if (err)  		kmem_cache_free(cgw_cache, gwj); @@ -900,7 +893,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)  	u8 limhops = 0;  	int err = 0; -	if (!capable(CAP_NET_ADMIN)) +	if (!netlink_capable(skb, CAP_NET_ADMIN))  		return -EPERM;  	if (nlmsg_len(nlh) < sizeof(*r)) diff --git a/net/can/proc.c b/net/can/proc.c index b543470c8f8..1a19b985a86 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -80,7 +80,6 @@ static const char rx_list_name[][8] = {  	[RX_ALL] = "rx_all",  	[RX_FIL] = "rx_fil",  	[RX_INV] = "rx_inv", -	[RX_EFF] = "rx_eff",  };  /* @@ -389,25 +388,26 @@ static const struct file_operations can_rcvlist_proc_fops = {  	.release	= single_release,  }; -static inline void can_rcvlist_sff_proc_show_one(struct seq_file *m, -						 struct net_device *dev, -						 struct dev_rcv_lists *d) +static inline void can_rcvlist_proc_show_array(struct seq_file *m, +					       struct net_device *dev, +					       struct hlist_head *rcv_array, +					       unsigned int rcv_array_sz)  { -	int i; +	unsigned int i;  	int all_empty = 1;  	/* check whether at least one list is non-empty */ -	for (i = 0; i < 0x800; i++) -		if (!hlist_empty(&d->rx_sff[i])) { +	for (i = 0; i < rcv_array_sz; i++) +		if (!hlist_empty(&rcv_array[i])) {  			all_empty = 0;  			break;  		}  	if (!all_empty) {  		can_print_recv_banner(m); -		for (i = 0; i < 0x800; i++) { -			if (!hlist_empty(&d->rx_sff[i])) -				can_print_rcvlist(m, &d->rx_sff[i], dev); +		for (i = 0; i < rcv_array_sz; i++) { +			if (!hlist_empty(&rcv_array[i])) +				can_print_rcvlist(m, &rcv_array[i], dev);  		}  	} else  		seq_printf(m, "  (%s: no entry)\n", DNAME(dev)); @@ -425,12 +425,15 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)  	/* sff receive list for 'all' CAN devices (dev == NULL) */  	d = &can_rx_alldev_list; -	can_rcvlist_sff_proc_show_one(m, NULL, d); +	can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));  	/* sff receive list for registered CAN devices */  	for_each_netdev_rcu(&init_net, dev) { -		if (dev->type == ARPHRD_CAN && dev->ml_priv) -			can_rcvlist_sff_proc_show_one(m, dev, dev->ml_priv); +		if (dev->type == ARPHRD_CAN && dev->ml_priv) { +			d = dev->ml_priv; +			can_rcvlist_proc_show_array(m, dev, d->rx_sff, +						    ARRAY_SIZE(d->rx_sff)); +		}  	}  	rcu_read_unlock(); @@ -452,6 +455,49 @@ static const struct file_operations can_rcvlist_sff_proc_fops = {  	.release	= single_release,  }; + +static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) +{ +	struct net_device *dev; +	struct dev_rcv_lists *d; + +	/* RX_EFF */ +	seq_puts(m, "\nreceive list 'rx_eff':\n"); + +	rcu_read_lock(); + +	/* eff receive list for 'all' CAN devices (dev == NULL) */ +	d = &can_rx_alldev_list; +	can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff)); + +	/* eff receive list for registered CAN devices */ +	for_each_netdev_rcu(&init_net, dev) { +		if (dev->type == ARPHRD_CAN && dev->ml_priv) { +			d = dev->ml_priv; +			can_rcvlist_proc_show_array(m, dev, d->rx_eff, +						    ARRAY_SIZE(d->rx_eff)); +		} +	} + +	rcu_read_unlock(); + +	seq_putc(m, '\n'); +	return 0; +} + +static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file) +{ +	return single_open(file, can_rcvlist_eff_proc_show, NULL); +} + +static const struct file_operations can_rcvlist_eff_proc_fops = { +	.owner		= THIS_MODULE, +	.open		= can_rcvlist_eff_proc_open, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +}; +  /*   * proc utility functions   */ @@ -491,8 +537,8 @@ void can_init_proc(void)  					   &can_rcvlist_proc_fops, (void *)RX_FIL);  	pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,  					   &can_rcvlist_proc_fops, (void *)RX_INV); -	pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir, -					   &can_rcvlist_proc_fops, (void *)RX_EFF); +	pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir, +				      &can_rcvlist_eff_proc_fops);  	pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,  				      &can_rcvlist_sff_proc_fops);  } diff --git a/net/can/raw.c b/net/can/raw.c index 641e1c89512..081e81fd017 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -121,13 +121,9 @@ static void raw_rcv(struct sk_buff *oskb, void *data)  	if (!ro->recv_own_msgs && oskb->sk == sk)  		return; -	/* do not pass frames with DLC > 8 to a legacy socket */ -	if (!ro->fd_frames) { -		struct canfd_frame *cfd = (struct canfd_frame *)oskb->data; - -		if (unlikely(cfd->len > CAN_MAX_DLEN)) -			return; -	} +	/* do not pass non-CAN2.0 frames to a legacy socket */ +	if (!ro->fd_frames && oskb->len != CAN_MTU) +		return;  	/* clone the given skb to be able to enqueue it into the rcv queue */  	skb = skb_clone(oskb, GFP_ATOMIC); @@ -675,8 +671,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,  	int err;  	if (msg->msg_name) { -		struct sockaddr_can *addr = -			(struct sockaddr_can *)msg->msg_name; +		DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);  		if (msg->msg_namelen < sizeof(*addr))  			return -EINVAL; @@ -716,6 +711,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,  	skb->dev = dev;  	skb->sk  = sk; +	skb->priority = sk->sk_priority;  	err = can_send(skb, ro->loopback); @@ -738,9 +734,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,  		       struct msghdr *msg, size_t size, int flags)  {  	struct sock *sk = sock->sk; -	struct raw_sock *ro = raw_sk(sk);  	struct sk_buff *skb; -	int rxmtu;  	int err = 0;  	int noblock; @@ -751,20 +745,10 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (!skb)  		return err; -	/* -	 * when serving a legacy socket the DLC <= 8 is already checked inside -	 * raw_rcv(). Now check if we need to pass a canfd_frame to a legacy -	 * socket and cut the possible CANFD_MTU/CAN_MTU length to CAN_MTU -	 */ -	if (!ro->fd_frames) -		rxmtu = CAN_MTU; -	else -		rxmtu = skb->len; - -	if (size < rxmtu) +	if (size < skb->len)  		msg->msg_flags |= MSG_TRUNC;  	else -		size = rxmtu; +		size = skb->len;  	err = memcpy_toiovec(msg->msg_iov, skb->data, size);  	if (err < 0) { @@ -775,6 +759,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,  	sock_recv_ts_and_drops(msg, sk, skb);  	if (msg->msg_name) { +		__sockaddr_check_size(sizeof(struct sockaddr_can));  		msg->msg_namelen = sizeof(struct sockaddr_can);  		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);  	}  | 
