OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [ipc/] [msg.c] - Blame information for rev 1765

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 199 simons
/*
2
 * linux/ipc/msg.c
3
 * Copyright (C) 1992 Krishna Balasubramanian
4
 *
5
 * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995, and May 1996
6
 *
7
 * See <linux/kerneld.h> for the (optional) new kerneld protocol
8
 */
9
 
10
#include <linux/config.h>
11
#include <linux/errno.h>
12
#include <linux/sched.h>
13
#include <linux/msg.h>
14
#include <linux/stat.h>
15
#include <linux/malloc.h>
16
#include <linux/kerneld.h>
17
#include <linux/interrupt.h>
18
 
19
#include <asm/segment.h>
20
 
21
extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
22
 
23
static void freeque (int id);
24
static int newque (key_t key, int msgflg);
25
static int findkey (key_t key);
26
 
27
static struct msqid_ds *msgque[MSGMNI];
28
static int msgbytes = 0;
29
static int msghdrs = 0;
30
static unsigned short msg_seq = 0;
31
static int used_queues = 0;
32
static int max_msqid = 0;
33
static struct wait_queue *msg_lock = NULL;
34
static int kerneld_msqid = -1;
35
 
36
#define MAX_KERNELDS 20
37
static int kerneld_arr[MAX_KERNELDS];
38
static int n_kernelds = 0;
39
 
40
void msg_init (void)
41
{
42
        int id;
43
 
44
        for (id = 0; id < MSGMNI; id++)
45
                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
46
        msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
47
        msg_lock = NULL;
48
        return;
49
}
50
 
51
/*
52
 * If the send queue is full, try to free any old messages.
53
 * These are most probably unwanted, since no one has picked them up...
54
 */
55
#define MSG_FLUSH_TIME 10 /* seconds */
56
static void flush_msg(struct msqid_ds *msq)
57
{
58
        struct msg *nmsg;
59
        unsigned long flags;
60
        int flushed = 0;
61
 
62
        save_flags(flags);
63
        cli();
64
 
65
        /* messages were put on the queue in time order */
66
        while ( (nmsg = msq->msg_first) &&
67
                ((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) {
68
                msgbytes -= nmsg->msg_ts;
69
                msghdrs--;
70
                msq->msg_cbytes -= nmsg->msg_ts;
71
                msq->msg_qnum--;
72
                msq->msg_first = nmsg->msg_next;
73
                ++flushed;
74
                kfree(nmsg);
75
        }
76
 
77
        if (msq->msg_qnum == 0)
78
                msq->msg_first = msq->msg_last = NULL;
79
        restore_flags(flags);
80
        if (flushed)
81
                printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
82
}
83
 
84
static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
85
{
86
        int id, err;
87
        struct msqid_ds *msq;
88
        struct ipc_perm *ipcp;
89
        struct msg *msgh;
90
        long mtype;
91
        unsigned long flags;
92
 
93
        if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
94
                return -EINVAL;
95
        if (!msgp)
96
                return -EFAULT;
97
        /*
98
         * Calls from kernel level (IPC_KERNELD set)
99
         * have the message somewhere in kernel space already!
100
         */
101
        if ((msgflg & IPC_KERNELD))
102
                mtype = msgp->mtype;
103
        else {
104
                err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
105
                if (err)
106
                        return err;
107
                if ((mtype = get_user (&msgp->mtype)) < 1)
108
                        return -EINVAL;
109
        }
110
        id = (unsigned int) msqid % MSGMNI;
111
        msq = msgque [id];
112
        if (msq == IPC_UNUSED || msq == IPC_NOID)
113
                return -EINVAL;
114
        ipcp = &msq->msg_perm;
115
 
116
 slept:
117
        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
118
                return -EIDRM;
119
        /*
120
         * Non-root kernel level processes may send to kerneld!
121
         * i.e. no permission check if called from the kernel
122
         * otoh we don't want user level non-root snoopers...
123
         */
124
        if ((msgflg & IPC_KERNELD) == 0)
125
                if (ipcperms(ipcp, S_IWUGO))
126
                        return -EACCES;
127
 
128
        if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
129
                if ((kerneld_msqid != -1) && (kerneld_msqid == msqid))
130
                        flush_msg(msq); /* flush the kerneld channel only */
131
                if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
132
                        /* still no space in queue */
133
                        if (msgflg & IPC_NOWAIT)
134
                                return -EAGAIN;
135
                        if (current->signal & ~current->blocked)
136
                                return -EINTR;
137
                        if (intr_count) {
138
                                /* Very unlikely, but better safe than sorry */
139
                                printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n");
140
                                return -EINTR;
141
                        }
142
                        interruptible_sleep_on (&msq->wwait);
143
                        goto slept;
144
                }
145
        }
146
 
147
        /* allocate message header and text space*/
148
        msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_ATOMIC);
149
        if (!msgh)
150
                return -ENOMEM;
151
        msgh->msg_spot = (char *) (msgh + 1);
152
 
153
        /*
154
         * Calls from kernel level (IPC_KERNELD set)
155
         * have the message somewhere in kernel space already!
156
         */
157
        if (msgflg & IPC_KERNELD) {
158
                struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
159
 
160
                /*
161
                 * Note that the kernel supplies a pointer
162
                 * but the user-level kerneld uses a char array...
163
                 */
164
                memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR);
165
                memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR);
166
        }
167
        else
168
                memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
169
 
170
        if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
171
                || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
172
                kfree(msgh);
173
                return -EIDRM;
174
        }
175
 
176
        msgh->msg_next = NULL;
177
        msgh->msg_ts = msgsz;
178
        msgh->msg_type = mtype;
179
        msgh->msg_stime = CURRENT_TIME;
180
 
181
        save_flags(flags);
182
        cli();
183
        if (!msq->msg_first)
184
                msq->msg_first = msq->msg_last = msgh;
185
        else {
186
                msq->msg_last->msg_next = msgh;
187
                msq->msg_last = msgh;
188
        }
189
        msq->msg_cbytes += msgsz;
190
        msgbytes  += msgsz;
191
        msghdrs++;
192
        msq->msg_qnum++;
193
        msq->msg_lspid = current->pid;
194
        msq->msg_stime = CURRENT_TIME;
195
        restore_flags(flags);
196
        wake_up (&msq->rwait);
197
        return 0;
198
}
199
 
200
/*
201
 * Take care of missing kerneld, especially in case of multiple daemons
202
 */
203
#define KERNELD_TIMEOUT 1 * (HZ)
204
#define DROP_TIMER del_timer(&kd_timer)
205
/*#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)*/
206
 
207
static void kd_timeout(unsigned long msgid)
208
{
209
        struct msqid_ds *msq;
210
        struct msg *tmsg;
211
        unsigned long flags;
212
 
213
        msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ];
214
        if (msq == IPC_NOID || msq == IPC_UNUSED)
215
                return;
216
 
217
        save_flags(flags);
218
        cli();
219
        for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next)
220
                if (*(long *)(tmsg->msg_spot) == msgid)
221
                        break;
222
        restore_flags(flags);
223
        if (tmsg) { /* still there! */
224
                struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" };
225
 
226
                printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid);
227
                kmsp.id = -ENODEV;
228
                real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR,
229
                        S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR);
230
        }
231
}
232
 
233
static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
234
{
235
        struct timer_list kd_timer = { NULL, NULL, 0, 0, 0};
236
        struct msqid_ds *msq;
237
        struct ipc_perm *ipcp;
238
        struct msg *tmsg, *leastp = NULL;
239
        struct msg *nmsg = NULL;
240
        int id, err;
241
        unsigned long flags;
242
 
243
        if (msqid < 0 || (long) msgsz < 0)
244
                return -EINVAL;
245
        if (!msgp || !msgp->mtext)
246
            return -EFAULT;
247
        /*
248
         * Calls from kernel level (IPC_KERNELD set)
249
         * wants the message put in kernel space!
250
         */
251
        if ((msgflg & IPC_KERNELD) == 0) {
252
                err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
253
                if (err)
254
                        return err;
255
        }
256
 
257
        id = (unsigned int) msqid % MSGMNI;
258
        msq = msgque [id];
259
        if (msq == IPC_NOID || msq == IPC_UNUSED)
260
                return -EINVAL;
261
        ipcp = &msq->msg_perm;
262
 
263
        /*
264
         * Start timer for missing kerneld
265
         */
266
        if (msgflg & IPC_KERNELD) {
267
                kd_timer.data = (unsigned long)msgtyp;
268
                kd_timer.expires = jiffies + KERNELD_TIMEOUT;
269
                kd_timer.function = kd_timeout;
270
                add_timer(&kd_timer);
271
        }
272
 
273
        /*
274
         *  find message of correct type.
275
         *  msgtyp = 0 => get first.
276
         *  msgtyp > 0 => get first message of matching type.
277
         *  msgtyp < 0 => get message with least type must be < abs(msgtype).
278
         */
279
        while (!nmsg) {
280
                if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
281
                        DROP_TIMER;
282
                        return -EIDRM;
283
                }
284
                if ((msgflg & IPC_KERNELD) == 0) {
285
                        /*
286
                         * All kernel level processes may receive from kerneld!
287
                         * i.e. no permission check if called from the kernel
288
                         * otoh we don't want user level non-root snoopers...
289
                         */
290
                        if (ipcperms (ipcp, S_IRUGO)) {
291
                                DROP_TIMER; /* Not needed, but doesn't hurt */
292
                                return -EACCES;
293
                        }
294
                }
295
 
296
                save_flags(flags);
297
                cli();
298
                if (msgtyp == 0)
299
                        nmsg = msq->msg_first;
300
                else if (msgtyp > 0) {
301
                        if (msgflg & MSG_EXCEPT) {
302
                                for (tmsg = msq->msg_first; tmsg;
303
                                     tmsg = tmsg->msg_next)
304
                                        if (tmsg->msg_type != msgtyp)
305
                                                break;
306
                                nmsg = tmsg;
307
                        } else {
308
                                for (tmsg = msq->msg_first; tmsg;
309
                                     tmsg = tmsg->msg_next)
310
                                        if (tmsg->msg_type == msgtyp)
311
                                                break;
312
                                nmsg = tmsg;
313
                        }
314
                } else {
315
                        for (leastp = tmsg = msq->msg_first; tmsg;
316
                             tmsg = tmsg->msg_next)
317
                                if (tmsg->msg_type < leastp->msg_type)
318
                                        leastp = tmsg;
319
                        if (leastp && leastp->msg_type <= - msgtyp)
320
                                nmsg = leastp;
321
                }
322
                restore_flags(flags);
323
 
324
                if (nmsg) { /* done finding a message */
325
                        DROP_TIMER;
326
                        if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
327
                                return -E2BIG;
328
                        }
329
                        msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
330
                        save_flags(flags);
331
                        cli();
332
                        if (nmsg ==  msq->msg_first)
333
                                msq->msg_first = nmsg->msg_next;
334
                        else {
335
                                for (tmsg = msq->msg_first; tmsg;
336
                                     tmsg = tmsg->msg_next)
337
                                        if (tmsg->msg_next == nmsg)
338
                                                break;
339
                                tmsg->msg_next = nmsg->msg_next;
340
                                if (nmsg == msq->msg_last)
341
                                        msq->msg_last = tmsg;
342
                        }
343
                        if (!(--msq->msg_qnum))
344
                                msq->msg_last = msq->msg_first = NULL;
345
 
346
                        msq->msg_rtime = CURRENT_TIME;
347
                        msq->msg_lrpid = current->pid;
348
                        msgbytes -= nmsg->msg_ts;
349
                        msghdrs--;
350
                        msq->msg_cbytes -= nmsg->msg_ts;
351
                        restore_flags(flags);
352
                        wake_up (&msq->wwait);
353
                        /*
354
                         * Calls from kernel level (IPC_KERNELD set)
355
                         * wants the message copied to kernel space!
356
                         */
357
                        if (msgflg & IPC_KERNELD) {
358
                                struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
359
 
360
                                memcpy((char *)(&(kdmp->id)),
361
                                        nmsg->msg_spot, KDHDR);
362
                                /*
363
                                 * Note that kdmp->text is a pointer
364
                                 * when called from kernel space!
365
                                 */
366
                                if ((msgsz > KDHDR) && kdmp->text)
367
                                        memcpy(kdmp->text,
368
                                                nmsg->msg_spot + KDHDR,
369
                                                msgsz - KDHDR);
370
                        }
371
                        else {
372
                                put_user (nmsg->msg_type, &msgp->mtype);
373
                                memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
374
                        }
375
                        kfree(nmsg);
376
                        return msgsz;
377
                } else {  /* did not find a message */
378
                        if (msgflg & IPC_NOWAIT) {
379
                                DROP_TIMER;
380
                                return -ENOMSG;
381
                        }
382
                        if (current->signal & ~current->blocked) {
383
                                DROP_TIMER;
384
                                return -EINTR;
385
                        }
386
                        interruptible_sleep_on (&msq->rwait);
387
                }
388
        } /* end while */
389
        DROP_TIMER;
390
        return -1;
391
}
392
 
393
asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
394
{
395
        /* IPC_KERNELD is used as a marker for kernel level calls */
396
        return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
397
}
398
 
399
asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
400
        long msgtyp, int msgflg)
401
{
402
        /* IPC_KERNELD is used as a marker for kernel level calls */
403
        return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
404
}
405
 
406
static int findkey (key_t key)
407
{
408
        int id;
409
        struct msqid_ds *msq;
410
 
411
        for (id = 0; id <= max_msqid; id++) {
412
                while ((msq = msgque[id]) == IPC_NOID)
413
                        interruptible_sleep_on (&msg_lock);
414
                if (msq == IPC_UNUSED)
415
                        continue;
416
                if (key == msq->msg_perm.key)
417
                        return id;
418
        }
419
        return -1;
420
}
421
 
422
static int newque (key_t key, int msgflg)
423
{
424
        int id;
425
        struct msqid_ds *msq;
426
        struct ipc_perm *ipcp;
427
 
428
        for (id = 0; id < MSGMNI; id++)
429
                if (msgque[id] == IPC_UNUSED) {
430
                        msgque[id] = (struct msqid_ds *) IPC_NOID;
431
                        goto found;
432
                }
433
        return -ENOSPC;
434
 
435
found:
436
        msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
437
        if (!msq) {
438
                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
439
                wake_up (&msg_lock);
440
                return -ENOMEM;
441
        }
442
        ipcp = &msq->msg_perm;
443
        ipcp->mode = (msgflg & S_IRWXUGO);
444
        ipcp->key = key;
445
        ipcp->cuid = ipcp->uid = current->euid;
446
        ipcp->gid = ipcp->cgid = current->egid;
447
        msq->msg_perm.seq = msg_seq;
448
        msq->msg_first = msq->msg_last = NULL;
449
        msq->rwait = msq->wwait = NULL;
450
        msq->msg_cbytes = msq->msg_qnum = 0;
451
        msq->msg_lspid = msq->msg_lrpid = 0;
452
        msq->msg_stime = msq->msg_rtime = 0;
453
        msq->msg_qbytes = MSGMNB;
454
        msq->msg_ctime = CURRENT_TIME;
455
        if (id > max_msqid)
456
                max_msqid = id;
457
        msgque[id] = msq;
458
        used_queues++;
459
        wake_up (&msg_lock);
460
        return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
461
}
462
 
463
asmlinkage int sys_msgget (key_t key, int msgflg)
464
{
465
        int id;
466
        struct msqid_ds *msq;
467
 
468
        /*
469
         * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE,
470
         * and a designated kerneld message queue is created/referred to
471
         */
472
        if ((msgflg & IPC_KERNELD)) {
473
                int i;
474
                if (!suser())
475
                        return -EPERM;
476
#ifdef NEW_KERNELD_PROTOCOL
477
                if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) {
478
                        printk(KERN_ALERT "Please recompile your kerneld daemons!\n");
479
                        return -EPERM;
480
                }
481
#endif
482
                if ((kerneld_msqid == -1) && (kerneld_msqid =
483
                                newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0)
484
                        return -ENOSPC;
485
                for (i = 0; i < MAX_KERNELDS; ++i) {
486
                        if (kerneld_arr[i] == 0) {
487
                                kerneld_arr[i] = current->pid;
488
                                ++n_kernelds;
489
                                return kerneld_msqid;
490
                        }
491
                }
492
                return -ENOSPC;
493
        }
494
        /* else it is a "normal" request */
495
        if (key == IPC_PRIVATE)
496
                return newque(key, msgflg);
497
        if ((id = findkey (key)) == -1) { /* key not used */
498
                if (!(msgflg & IPC_CREAT))
499
                        return -ENOENT;
500
                return newque(key, msgflg);
501
        }
502
        if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
503
                return -EEXIST;
504
        msq = msgque[id];
505
        if (msq == IPC_UNUSED || msq == IPC_NOID)
506
                return -EIDRM;
507
        if (ipcperms(&msq->msg_perm, msgflg))
508
                return -EACCES;
509
        return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
510
}
511
 
512
static void freeque (int id)
513
{
514
        struct msqid_ds *msq = msgque[id];
515
        struct msg *msgp, *msgh;
516
 
517
        msq->msg_perm.seq++;
518
        msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
519
        msgbytes -= msq->msg_cbytes;
520
        if (id == max_msqid)
521
                while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
522
        msgque[id] = (struct msqid_ds *) IPC_UNUSED;
523
        used_queues--;
524
        while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
525
                wake_up (&msq->rwait);
526
                wake_up (&msq->wwait);
527
                schedule();
528
        }
529
        for (msgp = msq->msg_first; msgp; msgp = msgh ) {
530
                msgh = msgp->msg_next;
531
                msghdrs--;
532
                kfree(msgp);
533
        }
534
        kfree(msq);
535
}
536
 
537
asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
538
{
539
        int id, err;
540
        struct msqid_ds *msq;
541
        struct msqid_ds tbuf;
542
        struct ipc_perm *ipcp;
543
 
544
        if (msqid < 0 || cmd < 0)
545
                return -EINVAL;
546
        switch (cmd) {
547
        case IPC_INFO:
548
        case MSG_INFO:
549
                if (!buf)
550
                        return -EFAULT;
551
        {
552
                struct msginfo msginfo;
553
                msginfo.msgmni = MSGMNI;
554
                msginfo.msgmax = MSGMAX;
555
                msginfo.msgmnb = MSGMNB;
556
                msginfo.msgmap = MSGMAP;
557
                msginfo.msgpool = MSGPOOL;
558
                msginfo.msgtql = MSGTQL;
559
                msginfo.msgssz = MSGSSZ;
560
                msginfo.msgseg = MSGSEG;
561
                if (cmd == MSG_INFO) {
562
                        msginfo.msgpool = used_queues;
563
                        msginfo.msgmap = msghdrs;
564
                        msginfo.msgtql = msgbytes;
565
                }
566
                err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
567
                if (err)
568
                        return err;
569
                memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
570
                return max_msqid;
571
        }
572
        case MSG_STAT:
573
                if (!buf)
574
                        return -EFAULT;
575
                err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
576
                if (err)
577
                        return err;
578
                if (msqid > max_msqid)
579
                        return -EINVAL;
580
                msq = msgque[msqid];
581
                if (msq == IPC_UNUSED || msq == IPC_NOID)
582
                        return -EINVAL;
583
                if (ipcperms (&msq->msg_perm, S_IRUGO))
584
                        return -EACCES;
585
                id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
586
                tbuf.msg_perm   = msq->msg_perm;
587
                tbuf.msg_stime  = msq->msg_stime;
588
                tbuf.msg_rtime  = msq->msg_rtime;
589
                tbuf.msg_ctime  = msq->msg_ctime;
590
                tbuf.msg_cbytes = msq->msg_cbytes;
591
                tbuf.msg_qnum   = msq->msg_qnum;
592
                tbuf.msg_qbytes = msq->msg_qbytes;
593
                tbuf.msg_lspid  = msq->msg_lspid;
594
                tbuf.msg_lrpid  = msq->msg_lrpid;
595
                memcpy_tofs (buf, &tbuf, sizeof(*buf));
596
                return id;
597
        case IPC_SET:
598
                if (!buf)
599
                        return -EFAULT;
600
                err = verify_area (VERIFY_READ, buf, sizeof (*buf));
601
                if (err)
602
                        return err;
603
                memcpy_fromfs (&tbuf, buf, sizeof (*buf));
604
                break;
605
        case IPC_STAT:
606
                if (!buf)
607
                        return -EFAULT;
608
                err = verify_area (VERIFY_WRITE, buf, sizeof(*buf));
609
                if (err)
610
                        return err;
611
                break;
612
        }
613
 
614
        id = (unsigned int) msqid % MSGMNI;
615
        msq = msgque [id];
616
        if (msq == IPC_UNUSED || msq == IPC_NOID)
617
                return -EINVAL;
618
        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
619
                return -EIDRM;
620
        ipcp = &msq->msg_perm;
621
 
622
        switch (cmd) {
623
        case IPC_STAT:
624
                if (ipcperms (ipcp, S_IRUGO))
625
                        return -EACCES;
626
                tbuf.msg_perm   = msq->msg_perm;
627
                tbuf.msg_stime  = msq->msg_stime;
628
                tbuf.msg_rtime  = msq->msg_rtime;
629
                tbuf.msg_ctime  = msq->msg_ctime;
630
                tbuf.msg_cbytes = msq->msg_cbytes;
631
                tbuf.msg_qnum   = msq->msg_qnum;
632
                tbuf.msg_qbytes = msq->msg_qbytes;
633
                tbuf.msg_lspid  = msq->msg_lspid;
634
                tbuf.msg_lrpid  = msq->msg_lrpid;
635
                memcpy_tofs (buf, &tbuf, sizeof (*buf));
636
                return 0;
637
        case IPC_SET:
638
                if (!suser() && current->euid != ipcp->cuid &&
639
                    current->euid != ipcp->uid)
640
                        return -EPERM;
641
                if (tbuf.msg_qbytes > MSGMNB && !suser())
642
                        return -EPERM;
643
                msq->msg_qbytes = tbuf.msg_qbytes;
644
                ipcp->uid = tbuf.msg_perm.uid;
645
                ipcp->gid =  tbuf.msg_perm.gid;
646
                ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
647
                        (S_IRWXUGO & tbuf.msg_perm.mode);
648
                msq->msg_ctime = CURRENT_TIME;
649
                return 0;
650
        case IPC_RMID:
651
                if (!suser() && current->euid != ipcp->cuid &&
652
                    current->euid != ipcp->uid)
653
                        return -EPERM;
654
                /*
655
                 * There is only one kerneld message queue,
656
                 * mark it as non-existent
657
                 */
658
                if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
659
                        kerneld_msqid = -1;
660
                freeque (id);
661
                return 0;
662
        default:
663
                return -EINVAL;
664
        }
665
}
666
 
667
/*
668
 * We do perhaps need a "flush" for waiting processes,
669
 * so that if they are terminated, a call from do_exit
670
 * will minimize the possibility of orphaned received
671
 * messages in the queue.  For now we just make sure
672
 * that the queue is shut down whenever all kernelds have died.
673
 */
674
void kerneld_exit(void)
675
{
676
        int i;
677
 
678
        if (kerneld_msqid == -1)
679
                return;
680
        for (i = 0; i < MAX_KERNELDS; ++i) {
681
                if (kerneld_arr[i] == current->pid) {
682
                        kerneld_arr[i] = 0;
683
                        --n_kernelds;
684
                        if (n_kernelds == 0)
685
                                sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
686
                        break;
687
                }
688
        }
689
}
690
 
691
/*
692
 * Kerneld internal message format/syntax:
693
 *
694
 * The message type from the kernel to kerneld is used to specify _what_
695
 * function we want kerneld to perform.
696
 *
697
 * The "normal" message area is divided into a header, followed by a char array.
698
 * The header is used to hold the sequence number of the request, which will
699
 * be used as the return message type from kerneld back to the kernel.
700
 * In the return message, the header will be used to store the exit status
701
 * of the kerneld "job", or task.
702
 * The character array is used to pass parameters to kerneld and (optional)
703
 * return information from kerneld back to the kernel.
704
 * It is the responsibility of kerneld and the kernel level caller
705
 * to set usable sizes on the parameter/return value array, since
706
 * that information is _not_ included in the message format
707
 */
708
 
709
/*
710
 * The basic kernel level entry point to kerneld.
711
 *      msgtype should correspond to a task type for (a) kerneld
712
 *      ret_size is the size of the (optional) return _value,
713
 *              OR-ed with KERNELD_WAIT if we want an answer
714
 *      msgsize is the size (in bytes) of the message, not including
715
 *              the header that is always sent first in a kerneld message
716
 *      text is the parameter for the kerneld specific task
717
 *      ret_val is NULL or the kernel address where an expected answer
718
 *              from kerneld should be placed.
719
 *
720
 * See <linux/kerneld.h> for usage (inline convenience functions)
721
 *
722
 */
723
int kerneld_send(int msgtype, int ret_size, int msgsz,
724
                const char *text, const char *ret_val)
725
{
726
        int status = -ENOSYS;
727
#ifdef CONFIG_KERNELD
728
        static int id = KERNELD_MINSEQ;
729
        struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text };
730
        int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
731
        unsigned long flags;
732
 
733
        if (kerneld_msqid == -1)
734
                return -ENODEV;
735
 
736
        /* Do not wait for an answer at interrupt-time! */
737
        if (intr_count)
738
                ret_size &= ~KERNELD_WAIT;
739
#ifdef NEW_KERNELD_PROTOCOL
740
        else
741
                kmsp.pid = current->pid;
742
#endif
743
 
744
        msgsz += KDHDR;
745
        if (ret_size & KERNELD_WAIT) {
746
                save_flags(flags);
747
                cli();
748
                if (++id <= 0) /* overflow */
749
                        id = KERNELD_MINSEQ;
750
                kmsp.id = id;
751
                restore_flags(flags);
752
        }
753
 
754
        status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
755
        if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
756
                ret_size &= ~KERNELD_WAIT;
757
                kmsp.text = (char *)ret_val;
758
                status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
759
                                KDHDR + ((ret_val)?ret_size:0),
760
                                kmsp.id, msgflg);
761
                if (status > 0) /* a valid answer contains at least a long */
762
                        status = kmsp.id;
763
        }
764
 
765
#endif /* CONFIG_KERNELD */
766
        return status;
767
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.