mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 09:50:04 +00:00
![Kees Cook](/assets/img/avatar_default.png)
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
411 lines
8.9 KiB
C
411 lines
8.9 KiB
C
/*
|
|
* X.25 Packet Layer release 002
|
|
*
|
|
* This is ALPHA test software. This code may break your machine,
|
|
* randomly fail to work with new releases, misbehave and/or generally
|
|
* screw up. It might even work.
|
|
*
|
|
* This code REQUIRES 2.1.15 or higher
|
|
*
|
|
* This module:
|
|
* This module is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
* History
|
|
* X.25 001 Jonathan Naylor Started coding.
|
|
* X.25 002 Jonathan Naylor New timer architecture.
|
|
* mar/20/00 Daniela Squassoni Disabling/enabling of facilities
|
|
* negotiation.
|
|
* 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "X25: " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/init.h>
|
|
#include <net/x25.h>
|
|
|
|
LIST_HEAD(x25_neigh_list);
|
|
DEFINE_RWLOCK(x25_neigh_list_lock);
|
|
|
|
static void x25_t20timer_expiry(struct timer_list *);
|
|
|
|
static void x25_transmit_restart_confirmation(struct x25_neigh *nb);
|
|
static void x25_transmit_restart_request(struct x25_neigh *nb);
|
|
|
|
/*
|
|
* Linux set/reset timer routines
|
|
*/
|
|
static inline void x25_start_t20timer(struct x25_neigh *nb)
|
|
{
|
|
mod_timer(&nb->t20timer, jiffies + nb->t20);
|
|
}
|
|
|
|
static void x25_t20timer_expiry(struct timer_list *t)
|
|
{
|
|
struct x25_neigh *nb = from_timer(nb, t, t20timer);
|
|
|
|
x25_transmit_restart_request(nb);
|
|
|
|
x25_start_t20timer(nb);
|
|
}
|
|
|
|
static inline void x25_stop_t20timer(struct x25_neigh *nb)
|
|
{
|
|
del_timer(&nb->t20timer);
|
|
}
|
|
|
|
static inline int x25_t20timer_pending(struct x25_neigh *nb)
|
|
{
|
|
return timer_pending(&nb->t20timer);
|
|
}
|
|
|
|
/*
|
|
* This handles all restart and diagnostic frames.
|
|
*/
|
|
void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb,
|
|
unsigned short frametype)
|
|
{
|
|
struct sk_buff *skbn;
|
|
int confirm;
|
|
|
|
switch (frametype) {
|
|
case X25_RESTART_REQUEST:
|
|
confirm = !x25_t20timer_pending(nb);
|
|
x25_stop_t20timer(nb);
|
|
nb->state = X25_LINK_STATE_3;
|
|
if (confirm)
|
|
x25_transmit_restart_confirmation(nb);
|
|
break;
|
|
|
|
case X25_RESTART_CONFIRMATION:
|
|
x25_stop_t20timer(nb);
|
|
nb->state = X25_LINK_STATE_3;
|
|
break;
|
|
|
|
case X25_DIAGNOSTIC:
|
|
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 4))
|
|
break;
|
|
|
|
pr_warn("diagnostic #%d - %02X %02X %02X\n",
|
|
skb->data[3], skb->data[4],
|
|
skb->data[5], skb->data[6]);
|
|
break;
|
|
|
|
default:
|
|
pr_warn("received unknown %02X with LCI 000\n",
|
|
frametype);
|
|
break;
|
|
}
|
|
|
|
if (nb->state == X25_LINK_STATE_3)
|
|
while ((skbn = skb_dequeue(&nb->queue)) != NULL)
|
|
x25_send_frame(skbn, nb);
|
|
}
|
|
|
|
/*
|
|
* This routine is called when a Restart Request is needed
|
|
*/
|
|
static void x25_transmit_restart_request(struct x25_neigh *nb)
|
|
{
|
|
unsigned char *dptr;
|
|
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
|
|
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
return;
|
|
|
|
skb_reserve(skb, X25_MAX_L2_LEN);
|
|
|
|
dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
|
|
|
|
*dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
|
|
*dptr++ = 0x00;
|
|
*dptr++ = X25_RESTART_REQUEST;
|
|
*dptr++ = 0x00;
|
|
*dptr++ = 0;
|
|
|
|
skb->sk = NULL;
|
|
|
|
x25_send_frame(skb, nb);
|
|
}
|
|
|
|
/*
|
|
* This routine is called when a Restart Confirmation is needed
|
|
*/
|
|
static void x25_transmit_restart_confirmation(struct x25_neigh *nb)
|
|
{
|
|
unsigned char *dptr;
|
|
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN;
|
|
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
return;
|
|
|
|
skb_reserve(skb, X25_MAX_L2_LEN);
|
|
|
|
dptr = skb_put(skb, X25_STD_MIN_LEN);
|
|
|
|
*dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
|
|
*dptr++ = 0x00;
|
|
*dptr++ = X25_RESTART_CONFIRMATION;
|
|
|
|
skb->sk = NULL;
|
|
|
|
x25_send_frame(skb, nb);
|
|
}
|
|
|
|
/*
|
|
* This routine is called when a Clear Request is needed outside of the context
|
|
* of a connected socket.
|
|
*/
|
|
void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci,
|
|
unsigned char cause)
|
|
{
|
|
unsigned char *dptr;
|
|
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
|
|
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
return;
|
|
|
|
skb_reserve(skb, X25_MAX_L2_LEN);
|
|
|
|
dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
|
|
|
|
*dptr++ = ((lci >> 8) & 0x0F) | (nb->extended ?
|
|
X25_GFI_EXTSEQ :
|
|
X25_GFI_STDSEQ);
|
|
*dptr++ = (lci >> 0) & 0xFF;
|
|
*dptr++ = X25_CLEAR_REQUEST;
|
|
*dptr++ = cause;
|
|
*dptr++ = 0x00;
|
|
|
|
skb->sk = NULL;
|
|
|
|
x25_send_frame(skb, nb);
|
|
}
|
|
|
|
void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb)
|
|
{
|
|
switch (nb->state) {
|
|
case X25_LINK_STATE_0:
|
|
skb_queue_tail(&nb->queue, skb);
|
|
nb->state = X25_LINK_STATE_1;
|
|
x25_establish_link(nb);
|
|
break;
|
|
case X25_LINK_STATE_1:
|
|
case X25_LINK_STATE_2:
|
|
skb_queue_tail(&nb->queue, skb);
|
|
break;
|
|
case X25_LINK_STATE_3:
|
|
x25_send_frame(skb, nb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when the link layer has become established.
|
|
*/
|
|
void x25_link_established(struct x25_neigh *nb)
|
|
{
|
|
switch (nb->state) {
|
|
case X25_LINK_STATE_0:
|
|
nb->state = X25_LINK_STATE_2;
|
|
break;
|
|
case X25_LINK_STATE_1:
|
|
x25_transmit_restart_request(nb);
|
|
nb->state = X25_LINK_STATE_2;
|
|
x25_start_t20timer(nb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when the link layer has terminated, or an establishment
|
|
* request has failed.
|
|
*/
|
|
|
|
void x25_link_terminated(struct x25_neigh *nb)
|
|
{
|
|
nb->state = X25_LINK_STATE_0;
|
|
/* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */
|
|
x25_kill_by_neigh(nb);
|
|
}
|
|
|
|
/*
|
|
* Add a new device.
|
|
*/
|
|
void x25_link_device_up(struct net_device *dev)
|
|
{
|
|
struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC);
|
|
|
|
if (!nb)
|
|
return;
|
|
|
|
skb_queue_head_init(&nb->queue);
|
|
timer_setup(&nb->t20timer, x25_t20timer_expiry, 0);
|
|
|
|
dev_hold(dev);
|
|
nb->dev = dev;
|
|
nb->state = X25_LINK_STATE_0;
|
|
nb->extended = 0;
|
|
/*
|
|
* Enables negotiation
|
|
*/
|
|
nb->global_facil_mask = X25_MASK_REVERSE |
|
|
X25_MASK_THROUGHPUT |
|
|
X25_MASK_PACKET_SIZE |
|
|
X25_MASK_WINDOW_SIZE;
|
|
nb->t20 = sysctl_x25_restart_request_timeout;
|
|
refcount_set(&nb->refcnt, 1);
|
|
|
|
write_lock_bh(&x25_neigh_list_lock);
|
|
list_add(&nb->node, &x25_neigh_list);
|
|
write_unlock_bh(&x25_neigh_list_lock);
|
|
}
|
|
|
|
/**
|
|
* __x25_remove_neigh - remove neighbour from x25_neigh_list
|
|
* @nb - neigh to remove
|
|
*
|
|
* Remove neighbour from x25_neigh_list. If it was there.
|
|
* Caller must hold x25_neigh_list_lock.
|
|
*/
|
|
static void __x25_remove_neigh(struct x25_neigh *nb)
|
|
{
|
|
skb_queue_purge(&nb->queue);
|
|
x25_stop_t20timer(nb);
|
|
|
|
if (nb->node.next) {
|
|
list_del(&nb->node);
|
|
x25_neigh_put(nb);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A device has been removed, remove its links.
|
|
*/
|
|
void x25_link_device_down(struct net_device *dev)
|
|
{
|
|
struct x25_neigh *nb;
|
|
struct list_head *entry, *tmp;
|
|
|
|
write_lock_bh(&x25_neigh_list_lock);
|
|
|
|
list_for_each_safe(entry, tmp, &x25_neigh_list) {
|
|
nb = list_entry(entry, struct x25_neigh, node);
|
|
|
|
if (nb->dev == dev) {
|
|
__x25_remove_neigh(nb);
|
|
dev_put(dev);
|
|
}
|
|
}
|
|
|
|
write_unlock_bh(&x25_neigh_list_lock);
|
|
}
|
|
|
|
/*
|
|
* Given a device, return the neighbour address.
|
|
*/
|
|
struct x25_neigh *x25_get_neigh(struct net_device *dev)
|
|
{
|
|
struct x25_neigh *nb, *use = NULL;
|
|
struct list_head *entry;
|
|
|
|
read_lock_bh(&x25_neigh_list_lock);
|
|
list_for_each(entry, &x25_neigh_list) {
|
|
nb = list_entry(entry, struct x25_neigh, node);
|
|
|
|
if (nb->dev == dev) {
|
|
use = nb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (use)
|
|
x25_neigh_hold(use);
|
|
read_unlock_bh(&x25_neigh_list_lock);
|
|
return use;
|
|
}
|
|
|
|
/*
|
|
* Handle the ioctls that control the subscription functions.
|
|
*/
|
|
int x25_subscr_ioctl(unsigned int cmd, void __user *arg)
|
|
{
|
|
struct x25_subscrip_struct x25_subscr;
|
|
struct x25_neigh *nb;
|
|
struct net_device *dev;
|
|
int rc = -EINVAL;
|
|
|
|
if (cmd != SIOCX25GSUBSCRIP && cmd != SIOCX25SSUBSCRIP)
|
|
goto out;
|
|
|
|
rc = -EFAULT;
|
|
if (copy_from_user(&x25_subscr, arg, sizeof(x25_subscr)))
|
|
goto out;
|
|
|
|
rc = -EINVAL;
|
|
if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
|
|
goto out;
|
|
|
|
if ((nb = x25_get_neigh(dev)) == NULL)
|
|
goto out_dev_put;
|
|
|
|
dev_put(dev);
|
|
|
|
if (cmd == SIOCX25GSUBSCRIP) {
|
|
read_lock_bh(&x25_neigh_list_lock);
|
|
x25_subscr.extended = nb->extended;
|
|
x25_subscr.global_facil_mask = nb->global_facil_mask;
|
|
read_unlock_bh(&x25_neigh_list_lock);
|
|
rc = copy_to_user(arg, &x25_subscr,
|
|
sizeof(x25_subscr)) ? -EFAULT : 0;
|
|
} else {
|
|
rc = -EINVAL;
|
|
if (!(x25_subscr.extended && x25_subscr.extended != 1)) {
|
|
rc = 0;
|
|
write_lock_bh(&x25_neigh_list_lock);
|
|
nb->extended = x25_subscr.extended;
|
|
nb->global_facil_mask = x25_subscr.global_facil_mask;
|
|
write_unlock_bh(&x25_neigh_list_lock);
|
|
}
|
|
}
|
|
x25_neigh_put(nb);
|
|
out:
|
|
return rc;
|
|
out_dev_put:
|
|
dev_put(dev);
|
|
goto out;
|
|
}
|
|
|
|
|
|
/*
|
|
* Release all memory associated with X.25 neighbour structures.
|
|
*/
|
|
void __exit x25_link_free(void)
|
|
{
|
|
struct x25_neigh *nb;
|
|
struct list_head *entry, *tmp;
|
|
|
|
write_lock_bh(&x25_neigh_list_lock);
|
|
|
|
list_for_each_safe(entry, tmp, &x25_neigh_list) {
|
|
struct net_device *dev;
|
|
|
|
nb = list_entry(entry, struct x25_neigh, node);
|
|
dev = nb->dev;
|
|
__x25_remove_neigh(nb);
|
|
dev_put(dev);
|
|
}
|
|
write_unlock_bh(&x25_neigh_list_lock);
|
|
}
|