aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2007-07-11 21:51:31 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-07-16 07:04:20 -0400
commite450992d13ffaec6dde4bbbd308b834ae9fc3708 (patch)
treea42083aff5e9435c60ced16846fd14b9475ad96b
parent13077d80286205e02eebe1c2786a914a4bbd2588 (diff)
[SPARC64]: Initial domain-services driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/sparc64/kernel/Makefile2
-rw-r--r--arch/sparc64/kernel/ds.c654
2 files changed, 655 insertions, 1 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 719ab23b1938..70e6c501392a 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -26,7 +26,7 @@ obj-$(CONFIG_MODULES) += module.o
26obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o 26obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
27obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o 27obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
28obj-$(CONFIG_KPROBES) += kprobes.o 28obj-$(CONFIG_KPROBES) += kprobes.o
29obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o 29obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o
30obj-$(CONFIG_AUDIT) += audit.o 30obj-$(CONFIG_AUDIT) += audit.o
31obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o 31obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o
32obj-y += $(obj-yy) 32obj-y += $(obj-yy)
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c
new file mode 100644
index 000000000000..22517dfd021d
--- /dev/null
+++ b/arch/sparc64/kernel/ds.c
@@ -0,0 +1,654 @@
1/* ds.c: Domain Services driver for Logical Domains
2 *
3 * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
4 */
5
6#include <linux/kernel.h>
7#include <linux/module.h>
8#include <linux/types.h>
9#include <linux/module.h>
10#include <linux/string.h>
11#include <linux/slab.h>
12#include <linux/sched.h>
13#include <linux/delay.h>
14
15#include <asm/ldc.h>
16#include <asm/vio.h>
17#include <asm/power.h>
18
19#define DRV_MODULE_NAME "ds"
20#define PFX DRV_MODULE_NAME ": "
21#define DRV_MODULE_VERSION "1.0"
22#define DRV_MODULE_RELDATE "Jul 11, 2007"
23
24static char version[] __devinitdata =
25 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
26MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
27MODULE_DESCRIPTION("Sun LDOM domain services driver");
28MODULE_LICENSE("GPL");
29MODULE_VERSION(DRV_MODULE_VERSION);
30
31struct ds_msg_tag {
32 __u32 type;
33#define DS_INIT_REQ 0x00
34#define DS_INIT_ACK 0x01
35#define DS_INIT_NACK 0x02
36#define DS_REG_REQ 0x03
37#define DS_REG_ACK 0x04
38#define DS_REG_NACK 0x05
39#define DS_UNREG_REQ 0x06
40#define DS_UNREG_ACK 0x07
41#define DS_UNREG_NACK 0x08
42#define DS_DATA 0x09
43#define DS_NACK 0x0a
44
45 __u32 len;
46};
47
48/* Result codes */
49#define DS_OK 0x00
50#define DS_REG_VER_NACK 0x01
51#define DS_REG_DUP 0x02
52#define DS_INV_HDL 0x03
53#define DS_TYPE_UNKNOWN 0x04
54
55struct ds_version {
56 __u16 major;
57 __u16 minor;
58};
59
60struct ds_ver_req {
61 struct ds_msg_tag tag;
62 struct ds_version ver;
63};
64
65struct ds_ver_ack {
66 struct ds_msg_tag tag;
67 __u16 minor;
68};
69
70struct ds_ver_nack {
71 struct ds_msg_tag tag;
72 __u16 major;
73};
74
75struct ds_reg_req {
76 struct ds_msg_tag tag;
77 __u64 handle;
78 __u16 major;
79 __u16 minor;
80 char svc_id[0];
81};
82
83struct ds_reg_ack {
84 struct ds_msg_tag tag;
85 __u64 handle;
86 __u16 minor;
87};
88
89struct ds_reg_nack {
90 struct ds_msg_tag tag;
91 __u64 handle;
92 __u16 major;
93};
94
95struct ds_unreg_req {
96 struct ds_msg_tag tag;
97 __u64 handle;
98};
99
100struct ds_unreg_ack {
101 struct ds_msg_tag tag;
102 __u64 handle;
103};
104
105struct ds_unreg_nack {
106 struct ds_msg_tag tag;
107 __u64 handle;
108};
109
110struct ds_data {
111 struct ds_msg_tag tag;
112 __u64 handle;
113};
114
115struct ds_data_nack {
116 struct ds_msg_tag tag;
117 __u64 handle;
118 __u64 result;
119};
120
121struct ds_cap_state {
122 __u64 handle;
123
124 void (*data)(struct ldc_channel *lp,
125 struct ds_cap_state *dp,
126 void *buf, int len);
127
128 const char *service_id;
129
130 u8 state;
131#define CAP_STATE_UNKNOWN 0x00
132#define CAP_STATE_REG_SENT 0x01
133#define CAP_STATE_REGISTERED 0x02
134};
135
136static int ds_send(struct ldc_channel *lp, void *data, int len)
137{
138 int err, limit = 1000;
139
140 err = -EINVAL;
141 while (limit-- > 0) {
142 err = ldc_write(lp, data, len);
143 if (!err || (err != -EAGAIN))
144 break;
145 udelay(1);
146 }
147
148 return err;
149}
150
151struct ds_md_update_req {
152 __u64 req_num;
153};
154
155struct ds_md_update_res {
156 __u64 req_num;
157 __u32 result;
158};
159
160static void md_update_data(struct ldc_channel *lp,
161 struct ds_cap_state *dp,
162 void *buf, int len)
163{
164 struct ds_data *dpkt = buf;
165 struct ds_md_update_req *rp;
166 struct {
167 struct ds_data data;
168 struct ds_md_update_res res;
169 } pkt;
170
171 rp = (struct ds_md_update_req *) (dpkt + 1);
172
173 printk(KERN_ERR PFX "MD update REQ [%lx] len=%d\n",
174 rp->req_num, len);
175
176 memset(&pkt, 0, sizeof(pkt));
177 pkt.data.tag.type = DS_DATA;
178 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
179 pkt.data.handle = dp->handle;
180 pkt.res.req_num = rp->req_num;
181 pkt.res.result = DS_OK;
182
183 ds_send(lp, &pkt, sizeof(pkt));
184}
185
186struct ds_shutdown_req {
187 __u64 req_num;
188 __u32 ms_delay;
189};
190
191struct ds_shutdown_res {
192 __u64 req_num;
193 __u32 result;
194 char reason[1];
195};
196
197static void domain_shutdown_data(struct ldc_channel *lp,
198 struct ds_cap_state *dp,
199 void *buf, int len)
200{
201 struct ds_data *dpkt = buf;
202 struct ds_shutdown_req *rp;
203 struct {
204 struct ds_data data;
205 struct ds_shutdown_res res;
206 } pkt;
207
208 rp = (struct ds_shutdown_req *) (dpkt + 1);
209
210 printk(KERN_ALERT PFX "Shutdown request from "
211 "LDOM manager received.\n");
212
213 memset(&pkt, 0, sizeof(pkt));
214 pkt.data.tag.type = DS_DATA;
215 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
216 pkt.data.handle = dp->handle;
217 pkt.res.req_num = rp->req_num;
218 pkt.res.result = DS_OK;
219 pkt.res.reason[0] = 0;
220
221 ds_send(lp, &pkt, sizeof(pkt));
222
223 wake_up_powerd();
224}
225
226struct ds_panic_req {
227 __u64 req_num;
228};
229
230struct ds_panic_res {
231 __u64 req_num;
232 __u32 result;
233 char reason[1];
234};
235
236static void domain_panic_data(struct ldc_channel *lp,
237 struct ds_cap_state *dp,
238 void *buf, int len)
239{
240 struct ds_data *dpkt = buf;
241 struct ds_panic_req *rp;
242 struct {
243 struct ds_data data;
244 struct ds_panic_res res;
245 } pkt;
246
247 rp = (struct ds_panic_req *) (dpkt + 1);
248
249 printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n",
250 rp->req_num, len);
251
252 memset(&pkt, 0, sizeof(pkt));
253 pkt.data.tag.type = DS_DATA;
254 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
255 pkt.data.handle = dp->handle;
256 pkt.res.req_num = rp->req_num;
257 pkt.res.result = DS_OK;
258 pkt.res.reason[0] = 0;
259
260 ds_send(lp, &pkt, sizeof(pkt));
261
262 panic("PANIC requested by LDOM manager.");
263}
264
265struct ds_cpu_tag {
266 __u64 req_num;
267 __u32 type;
268#define DS_CPU_CONFIGURE 0x43
269#define DS_CPU_UNCONFIGURE 0x55
270#define DS_CPU_FORCE_UNCONFIGURE 0x46
271#define DS_CPU_STATUS 0x53
272
273/* Responses */
274#define DS_CPU_OK 0x6f
275#define DS_CPU_ERROR 0x65
276
277 __u32 num_records;
278};
279
280struct ds_cpu_record {
281 __u32 cpu_id;
282};
283
284static void dr_cpu_data(struct ldc_channel *lp,
285 struct ds_cap_state *dp,
286 void *buf, int len)
287{
288 struct ds_data *dpkt = buf;
289 struct ds_cpu_tag *rp;
290
291 rp = (struct ds_cpu_tag *) (dpkt + 1);
292
293 printk(KERN_ERR PFX "CPU REQ [%lx:%x], len=%d\n",
294 rp->req_num, rp->type, len);
295}
296
297struct ds_pri_msg {
298 __u64 req_num;
299 __u64 type;
300#define DS_PRI_REQUEST 0x00
301#define DS_PRI_DATA 0x01
302#define DS_PRI_UPDATE 0x02
303};
304
305static void ds_pri_data(struct ldc_channel *lp,
306 struct ds_cap_state *dp,
307 void *buf, int len)
308{
309 struct ds_data *dpkt = buf;
310 struct ds_pri_msg *rp;
311
312 rp = (struct ds_pri_msg *) (dpkt + 1);
313
314 printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n",
315 rp->req_num, rp->type, len);
316}
317
318struct ds_cap_state ds_states[] = {
319 {
320 .service_id = "md-update",
321 .data = md_update_data,
322 },
323 {
324 .service_id = "domain-shutdown",
325 .data = domain_shutdown_data,
326 },
327 {
328 .service_id = "domain-panic",
329 .data = domain_panic_data,
330 },
331 {
332 .service_id = "dr-cpu",
333 .data = dr_cpu_data,
334 },
335 {
336 .service_id = "pri",
337 .data = ds_pri_data,
338 },
339};
340
341static struct ds_cap_state *find_cap(u64 handle)
342{
343 unsigned int index = handle >> 32;
344
345 if (index >= ARRAY_SIZE(ds_states))
346 return NULL;
347 return &ds_states[index];
348}
349
350static DEFINE_SPINLOCK(ds_lock);
351
352struct ds_info {
353 struct ldc_channel *lp;
354 u8 hs_state;
355#define DS_HS_START 0x01
356#define DS_HS_DONE 0x02
357
358 void *rcv_buf;
359 int rcv_buf_len;
360};
361
362static void ds_conn_reset(struct ds_info *dp)
363{
364 printk(KERN_ERR PFX "ds_conn_reset() from %p\n",
365 __builtin_return_address(0));
366}
367
368static int register_services(struct ds_info *dp)
369{
370 struct ldc_channel *lp = dp->lp;
371 int i;
372
373 for (i = 0; i < ARRAY_SIZE(ds_states); i++) {
374 struct {
375 struct ds_reg_req req;
376 u8 id_buf[256];
377 } pbuf;
378 struct ds_cap_state *cp = &ds_states[i];
379 int err, msg_len;
380 u64 new_count;
381
382 if (cp->state == CAP_STATE_REGISTERED)
383 continue;
384
385 new_count = sched_clock() & 0xffffffff;
386 cp->handle = ((u64) i << 32) | new_count;
387
388 msg_len = (sizeof(struct ds_reg_req) +
389 strlen(cp->service_id));
390
391 memset(&pbuf, 0, sizeof(pbuf));
392 pbuf.req.tag.type = DS_REG_REQ;
393 pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag));
394 pbuf.req.handle = cp->handle;
395 pbuf.req.major = 1;
396 pbuf.req.minor = 0;
397 strcpy(pbuf.req.svc_id, cp->service_id);
398
399 err = ds_send(lp, &pbuf, msg_len);
400 if (err > 0)
401 cp->state = CAP_STATE_REG_SENT;
402 }
403 return 0;
404}
405
406static int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt)
407{
408
409 if (dp->hs_state == DS_HS_START) {
410 if (pkt->type != DS_INIT_ACK)
411 goto conn_reset;
412
413 dp->hs_state = DS_HS_DONE;
414
415 return register_services(dp);
416 }
417
418 if (dp->hs_state != DS_HS_DONE)
419 goto conn_reset;
420
421 if (pkt->type == DS_REG_ACK) {
422 struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt;
423 struct ds_cap_state *cp = find_cap(ap->handle);
424
425 if (!cp) {
426 printk(KERN_ERR PFX "REG ACK for unknown handle %lx\n",
427 ap->handle);
428 return 0;
429 }
430 printk(KERN_INFO PFX "Registered %s service.\n",
431 cp->service_id);
432 cp->state = CAP_STATE_REGISTERED;
433 } else if (pkt->type == DS_REG_NACK) {
434 struct ds_reg_nack *np = (struct ds_reg_nack *) pkt;
435 struct ds_cap_state *cp = find_cap(np->handle);
436
437 if (!cp) {
438 printk(KERN_ERR PFX "REG NACK for "
439 "unknown handle %lx\n",
440 np->handle);
441 return 0;
442 }
443 printk(KERN_ERR PFX "Could not register %s service\n",
444 cp->service_id);
445 cp->state = CAP_STATE_UNKNOWN;
446 }
447
448 return 0;
449
450conn_reset:
451 ds_conn_reset(dp);
452 return -ECONNRESET;
453}
454
455static int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len)
456{
457 struct ds_data *dpkt = (struct ds_data *) pkt;
458 struct ds_cap_state *cp = find_cap(dpkt->handle);
459
460 if (!cp) {
461 struct ds_data_nack nack = {
462 .tag = {
463 .type = DS_NACK,
464 .len = (sizeof(struct ds_data_nack) -
465 sizeof(struct ds_msg_tag)),
466 },
467 .handle = dpkt->handle,
468 .result = DS_INV_HDL,
469 };
470
471 printk(KERN_ERR PFX "Data for unknown handle %lu\n",
472 dpkt->handle);
473 ds_send(dp->lp, &nack, sizeof(nack));
474 } else {
475 cp->data(dp->lp, cp, dpkt, len);
476 }
477 return 0;
478}
479
480static void ds_up(struct ds_info *dp)
481{
482 struct ldc_channel *lp = dp->lp;
483 struct ds_ver_req req;
484 int err;
485
486 req.tag.type = DS_INIT_REQ;
487 req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag);
488 req.ver.major = 1;
489 req.ver.minor = 0;
490
491 err = ds_send(lp, &req, sizeof(req));
492 if (err > 0)
493 dp->hs_state = DS_HS_START;
494}
495
496static void ds_event(void *arg, int event)
497{
498 struct ds_info *dp = arg;
499 struct ldc_channel *lp = dp->lp;
500 unsigned long flags;
501 int err;
502
503 spin_lock_irqsave(&ds_lock, flags);
504
505 if (event == LDC_EVENT_UP) {
506 ds_up(dp);
507 spin_unlock_irqrestore(&ds_lock, flags);
508 return;
509 }
510
511 if (event != LDC_EVENT_DATA_READY) {
512 printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event);
513 spin_unlock_irqrestore(&ds_lock, flags);
514 return;
515 }
516
517 err = 0;
518 while (1) {
519 struct ds_msg_tag *tag;
520
521 err = ldc_read(lp, dp->rcv_buf, sizeof(*tag));
522
523 if (unlikely(err < 0)) {
524 if (err == -ECONNRESET)
525 ds_conn_reset(dp);
526 break;
527 }
528 if (err == 0)
529 break;
530
531 tag = dp->rcv_buf;
532 err = ldc_read(lp, tag + 1, tag->len);
533
534 if (unlikely(err < 0)) {
535 if (err == -ECONNRESET)
536 ds_conn_reset(dp);
537 break;
538 }
539 if (err < tag->len)
540 break;
541
542 if (tag->type < DS_DATA)
543 err = ds_handshake(dp, dp->rcv_buf);
544 else
545 err = ds_data(dp, dp->rcv_buf,
546 sizeof(*tag) + err);
547 if (err == -ECONNRESET)
548 break;
549 }
550
551 spin_unlock_irqrestore(&ds_lock, flags);
552}
553
554static int __devinit ds_probe(struct vio_dev *vdev,
555 const struct vio_device_id *id)
556{
557 static int ds_version_printed;
558 struct mdesc_node *endp;
559 struct ldc_channel_config ds_cfg = {
560 .event = ds_event,
561 .mtu = 4096,
562 .mode = LDC_MODE_STREAM,
563 };
564 struct ldc_channel *lp;
565 struct ds_info *dp;
566 const u64 *chan_id;
567 int err;
568
569 if (ds_version_printed++ == 0)
570 printk(KERN_INFO "%s", version);
571
572 endp = vio_find_endpoint(vdev);
573 if (!endp)
574 return -ENODEV;
575
576 chan_id = md_get_property(endp, "id", NULL);
577 if (!chan_id)
578 return -ENODEV;
579
580 dp = kzalloc(sizeof(*dp), GFP_KERNEL);
581 err = -ENOMEM;
582 if (!dp)
583 goto out_err;
584
585 dp->rcv_buf = kzalloc(4096, GFP_KERNEL);
586 if (!dp->rcv_buf)
587 goto out_free_dp;
588
589 dp->rcv_buf_len = 4096;
590
591 ds_cfg.tx_irq = endp->irqs[0];
592 ds_cfg.rx_irq = endp->irqs[1];
593
594 lp = ldc_alloc(*chan_id, &ds_cfg, dp);
595 if (IS_ERR(lp)) {
596 err = PTR_ERR(lp);
597 goto out_free_rcv_buf;
598 }
599 dp->lp = lp;
600
601 err = ldc_bind(lp);
602 if (err)
603 goto out_free_ldc;
604
605 start_powerd();
606
607 return err;
608
609out_free_ldc:
610 ldc_free(dp->lp);
611
612out_free_rcv_buf:
613 kfree(dp->rcv_buf);
614
615out_free_dp:
616 kfree(dp);
617
618out_err:
619 return err;
620}
621
622static int ds_remove(struct vio_dev *vdev)
623{
624 return 0;
625}
626
627static struct vio_device_id ds_match[] = {
628 {
629 .type = "domain-services-port",
630 },
631 {},
632};
633
634static struct vio_driver ds_driver = {
635 .id_table = ds_match,
636 .probe = ds_probe,
637 .remove = ds_remove,
638 .driver = {
639 .name = "ds",
640 .owner = THIS_MODULE,
641 }
642};
643
644static int __init ds_init(void)
645{
646 int i;
647
648 for (i = 0; i < ARRAY_SIZE(ds_states); i++)
649 ds_states[i].handle = ((u64)i << 32);
650
651 return vio_register_driver(&ds_driver);
652}
653
654subsys_initcall(ds_init);