~ubuntu-branches/ubuntu/quantal/linux-linaro-mx51/quantal

« back to all changes in this revision

Viewing changes to net/caif/cfmuxl.c

  • Committer: Package Import Robot
  • Author(s): John Rigby, John Rigby
  • Date: 2011-09-26 10:44:23 UTC
  • Revision ID: package-import@ubuntu.com-20110926104423-3o58a3c1bj7x00rs
Tags: 3.0.0-1007.9
[ John Rigby ]

Enable crypto modules and remove crypto-modules from
exclude-module files
LP: #826021

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
#include <linux/stddef.h>
10
10
#include <linux/spinlock.h>
11
11
#include <linux/slab.h>
 
12
#include <linux/rculist.h>
12
13
#include <net/caif/cfpkt.h>
13
14
#include <net/caif/cfmuxl.h>
14
15
#include <net/caif/cfsrvl.h>
61
62
        return &this->layer;
62
63
}
63
64
 
64
 
int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
65
 
{
66
 
        struct cfmuxl *muxl = container_obj(layr);
67
 
        spin_lock(&muxl->receive_lock);
68
 
        cfsrvl_get(up);
69
 
        list_add(&up->node, &muxl->srvl_list);
70
 
        spin_unlock(&muxl->receive_lock);
71
 
        return 0;
72
 
}
73
 
 
74
 
bool cfmuxl_is_phy_inuse(struct cflayer *layr, u8 phyid)
75
 
{
76
 
        struct list_head *node;
77
 
        struct cflayer *layer;
78
 
        struct cfmuxl *muxl = container_obj(layr);
79
 
        bool match = false;
80
 
        spin_lock(&muxl->receive_lock);
81
 
 
82
 
        list_for_each(node, &muxl->srvl_list) {
83
 
                layer = list_entry(node, struct cflayer, node);
84
 
                if (cfsrvl_phyid_match(layer, phyid)) {
85
 
                        match = true;
86
 
                        break;
87
 
                }
88
 
 
89
 
        }
90
 
        spin_unlock(&muxl->receive_lock);
91
 
        return match;
92
 
}
93
 
 
94
 
u8 cfmuxl_get_phyid(struct cflayer *layr, u8 channel_id)
95
 
{
96
 
        struct cflayer *up;
97
 
        int phyid;
98
 
        struct cfmuxl *muxl = container_obj(layr);
99
 
        spin_lock(&muxl->receive_lock);
100
 
        up = get_up(muxl, channel_id);
101
 
        if (up != NULL)
102
 
                phyid = cfsrvl_getphyid(up);
103
 
        else
104
 
                phyid = 0;
105
 
        spin_unlock(&muxl->receive_lock);
106
 
        return phyid;
107
 
}
108
 
 
109
65
int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
110
66
{
111
67
        struct cfmuxl *muxl = (struct cfmuxl *) layr;
112
 
        spin_lock(&muxl->transmit_lock);
113
 
        list_add(&dn->node, &muxl->frml_list);
114
 
        spin_unlock(&muxl->transmit_lock);
 
68
 
 
69
        spin_lock_bh(&muxl->transmit_lock);
 
70
        list_add_rcu(&dn->node, &muxl->frml_list);
 
71
        spin_unlock_bh(&muxl->transmit_lock);
115
72
        return 0;
116
73
}
117
74
 
118
75
static struct cflayer *get_from_id(struct list_head *list, u16 id)
119
76
{
120
 
        struct list_head *node;
121
 
        struct cflayer *layer;
122
 
        list_for_each(node, list) {
123
 
                layer = list_entry(node, struct cflayer, node);
124
 
                if (layer->id == id)
125
 
                        return layer;
 
77
        struct cflayer *lyr;
 
78
        list_for_each_entry_rcu(lyr, list, node) {
 
79
                if (lyr->id == id)
 
80
                        return lyr;
126
81
        }
 
82
 
127
83
        return NULL;
128
84
}
129
85
 
 
86
int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
 
87
{
 
88
        struct cfmuxl *muxl = container_obj(layr);
 
89
        struct cflayer *old;
 
90
 
 
91
        spin_lock_bh(&muxl->receive_lock);
 
92
 
 
93
        /* Two entries with same id is wrong, so remove old layer from mux */
 
94
        old = get_from_id(&muxl->srvl_list, linkid);
 
95
        if (old != NULL)
 
96
                list_del_rcu(&old->node);
 
97
 
 
98
        list_add_rcu(&up->node, &muxl->srvl_list);
 
99
        spin_unlock_bh(&muxl->receive_lock);
 
100
 
 
101
        return 0;
 
102
}
 
103
 
130
104
struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
131
105
{
132
106
        struct cfmuxl *muxl = container_obj(layr);
133
107
        struct cflayer *dn;
134
 
        spin_lock(&muxl->transmit_lock);
135
 
        memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
 
108
        int idx = phyid % DN_CACHE_SIZE;
 
109
 
 
110
        spin_lock_bh(&muxl->transmit_lock);
 
111
        rcu_assign_pointer(muxl->dn_cache[idx], NULL);
136
112
        dn = get_from_id(&muxl->frml_list, phyid);
137
 
        if (dn == NULL) {
138
 
                spin_unlock(&muxl->transmit_lock);
139
 
                return NULL;
140
 
        }
141
 
        list_del(&dn->node);
 
113
        if (dn == NULL)
 
114
                goto out;
 
115
 
 
116
        list_del_rcu(&dn->node);
142
117
        caif_assert(dn != NULL);
143
 
        spin_unlock(&muxl->transmit_lock);
 
118
out:
 
119
        spin_unlock_bh(&muxl->transmit_lock);
144
120
        return dn;
145
121
}
146
122
 
147
 
/* Invariant: lock is taken */
148
123
static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
149
124
{
150
125
        struct cflayer *up;
151
126
        int idx = id % UP_CACHE_SIZE;
152
 
        up = muxl->up_cache[idx];
 
127
        up = rcu_dereference(muxl->up_cache[idx]);
153
128
        if (up == NULL || up->id != id) {
 
129
                spin_lock_bh(&muxl->receive_lock);
154
130
                up = get_from_id(&muxl->srvl_list, id);
155
 
                muxl->up_cache[idx] = up;
 
131
                rcu_assign_pointer(muxl->up_cache[idx], up);
 
132
                spin_unlock_bh(&muxl->receive_lock);
156
133
        }
157
134
        return up;
158
135
}
159
136
 
160
 
/* Invariant: lock is taken */
161
137
static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
162
138
{
163
139
        struct cflayer *dn;
164
140
        int idx = dev_info->id % DN_CACHE_SIZE;
165
 
        dn = muxl->dn_cache[idx];
 
141
        dn = rcu_dereference(muxl->dn_cache[idx]);
166
142
        if (dn == NULL || dn->id != dev_info->id) {
 
143
                spin_lock_bh(&muxl->transmit_lock);
167
144
                dn = get_from_id(&muxl->frml_list, dev_info->id);
168
 
                muxl->dn_cache[idx] = dn;
 
145
                rcu_assign_pointer(muxl->dn_cache[idx], dn);
 
146
                spin_unlock_bh(&muxl->transmit_lock);
169
147
        }
170
148
        return dn;
171
149
}
174
152
{
175
153
        struct cflayer *up;
176
154
        struct cfmuxl *muxl = container_obj(layr);
177
 
        spin_lock(&muxl->receive_lock);
178
 
        up = get_up(muxl, id);
 
155
        int idx = id % UP_CACHE_SIZE;
 
156
 
 
157
        if (id == 0) {
 
158
                pr_warn("Trying to remove control layer\n");
 
159
                return NULL;
 
160
        }
 
161
 
 
162
        spin_lock_bh(&muxl->receive_lock);
 
163
        up = get_from_id(&muxl->srvl_list, id);
179
164
        if (up == NULL)
180
165
                goto out;
181
 
        memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
182
 
        list_del(&up->node);
183
 
        cfsrvl_put(up);
 
166
 
 
167
        rcu_assign_pointer(muxl->up_cache[idx], NULL);
 
168
        list_del_rcu(&up->node);
184
169
out:
185
 
        spin_unlock(&muxl->receive_lock);
 
170
        spin_unlock_bh(&muxl->receive_lock);
186
171
        return up;
187
172
}
188
173
 
197
182
                cfpkt_destroy(pkt);
198
183
                return -EPROTO;
199
184
        }
 
185
        rcu_read_lock();
 
186
        up = get_up(muxl, id);
200
187
 
201
 
        spin_lock(&muxl->receive_lock);
202
 
        up = get_up(muxl, id);
203
 
        spin_unlock(&muxl->receive_lock);
204
188
        if (up == NULL) {
205
 
                pr_info("Received data on unknown link ID = %d (0x%x) up == NULL",
206
 
                        id, id);
 
189
                pr_debug("Received data on unknown link ID = %d (0x%x)"
 
190
                        " up == NULL", id, id);
207
191
                cfpkt_destroy(pkt);
208
192
                /*
209
193
                 * Don't return ERROR, since modem misbehaves and sends out
210
194
                 * flow on before linksetup response.
211
195
                 */
 
196
 
 
197
                rcu_read_unlock();
212
198
                return /* CFGLU_EPROT; */ 0;
213
199
        }
 
200
 
 
201
        /* We can't hold rcu_lock during receive, so take a ref count instead */
214
202
        cfsrvl_get(up);
 
203
        rcu_read_unlock();
 
204
 
215
205
        ret = up->receive(up, pkt);
 
206
 
216
207
        cfsrvl_put(up);
217
208
        return ret;
218
209
}
219
210
 
220
211
static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
221
212
{
222
 
        int ret;
223
213
        struct cfmuxl *muxl = container_obj(layr);
 
214
        int err;
224
215
        u8 linkid;
225
216
        struct cflayer *dn;
226
217
        struct caif_payload_info *info = cfpkt_info(pkt);
227
 
        dn = get_dn(muxl, cfpkt_info(pkt)->dev_info);
 
218
        BUG_ON(!info);
 
219
 
 
220
        rcu_read_lock();
 
221
 
 
222
        dn = get_dn(muxl, info->dev_info);
228
223
        if (dn == NULL) {
229
 
                pr_warn("Send data on unknown phy ID = %d (0x%x)\n",
 
224
                pr_debug("Send data on unknown phy ID = %d (0x%x)\n",
230
225
                        info->dev_info->id, info->dev_info->id);
 
226
                rcu_read_unlock();
 
227
                cfpkt_destroy(pkt);
231
228
                return -ENOTCONN;
232
229
        }
 
230
 
233
231
        info->hdr_len += 1;
234
232
        linkid = info->channel_id;
235
233
        cfpkt_add_head(pkt, &linkid, 1);
236
 
        ret = dn->transmit(dn, pkt);
237
 
        /* Remove MUX protocol header upon error. */
238
 
        if (ret < 0)
239
 
                cfpkt_extr_head(pkt, &linkid, 1);
240
 
        return ret;
 
234
 
 
235
        /* We can't hold rcu_lock during receive, so take a ref count instead */
 
236
        cffrml_hold(dn);
 
237
 
 
238
        rcu_read_unlock();
 
239
 
 
240
        err = dn->transmit(dn, pkt);
 
241
 
 
242
        cffrml_put(dn);
 
243
        return err;
241
244
}
242
245
 
243
246
static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
244
247
                                int phyid)
245
248
{
246
249
        struct cfmuxl *muxl = container_obj(layr);
247
 
        struct list_head *node, *next;
248
250
        struct cflayer *layer;
249
 
        list_for_each_safe(node, next, &muxl->srvl_list) {
250
 
                layer = list_entry(node, struct cflayer, node);
251
 
                if (cfsrvl_phyid_match(layer, phyid))
 
251
        int idx;
 
252
 
 
253
        rcu_read_lock();
 
254
        list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
 
255
 
 
256
                if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {
 
257
 
 
258
                        if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND ||
 
259
                                ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
 
260
                                        layer->id != 0) {
 
261
 
 
262
                                idx = layer->id % UP_CACHE_SIZE;
 
263
                                spin_lock_bh(&muxl->receive_lock);
 
264
                                rcu_assign_pointer(muxl->up_cache[idx], NULL);
 
265
                                list_del_rcu(&layer->node);
 
266
                                spin_unlock_bh(&muxl->receive_lock);
 
267
                        }
 
268
                        /* NOTE: ctrlcmd is not allowed to block */
252
269
                        layer->ctrlcmd(layer, ctrl, phyid);
 
270
                }
253
271
        }
 
272
        rcu_read_unlock();
254
273
}