diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/block/aoe/aoechr.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/block/aoe/aoechr.c')
-rw-r--r-- | drivers/block/aoe/aoechr.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c new file mode 100644 index 000000000000..14aeca3e2e8c --- /dev/null +++ b/drivers/block/aoe/aoechr.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */ | ||
2 | /* | ||
3 | * aoechr.c | ||
4 | * AoE character device driver | ||
5 | */ | ||
6 | |||
7 | #include <linux/hdreg.h> | ||
8 | #include <linux/blkdev.h> | ||
9 | #include "aoe.h" | ||
10 | |||
11 | enum { | ||
12 | //MINOR_STAT = 1, (moved to sysfs) | ||
13 | MINOR_ERR = 2, | ||
14 | MINOR_DISCOVER, | ||
15 | MINOR_INTERFACES, | ||
16 | MSGSZ = 2048, | ||
17 | NARGS = 10, | ||
18 | NMSG = 100, /* message backlog to retain */ | ||
19 | }; | ||
20 | |||
21 | struct aoe_chardev { | ||
22 | ulong minor; | ||
23 | char name[32]; | ||
24 | }; | ||
25 | |||
26 | enum { EMFL_VALID = 1 }; | ||
27 | |||
28 | struct ErrMsg { | ||
29 | short flags; | ||
30 | short len; | ||
31 | char *msg; | ||
32 | }; | ||
33 | |||
34 | static struct ErrMsg emsgs[NMSG]; | ||
35 | static int emsgs_head_idx, emsgs_tail_idx; | ||
36 | static struct semaphore emsgs_sema; | ||
37 | static spinlock_t emsgs_lock; | ||
38 | static int nblocked_emsgs_readers; | ||
39 | static struct class_simple *aoe_class; | ||
40 | static struct aoe_chardev chardevs[] = { | ||
41 | { MINOR_ERR, "err" }, | ||
42 | { MINOR_DISCOVER, "discover" }, | ||
43 | { MINOR_INTERFACES, "interfaces" }, | ||
44 | }; | ||
45 | |||
46 | static int | ||
47 | discover(void) | ||
48 | { | ||
49 | aoecmd_cfg(0xffff, 0xff); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int | ||
54 | interfaces(const char __user *str, size_t size) | ||
55 | { | ||
56 | if (set_aoe_iflist(str, size)) { | ||
57 | printk(KERN_CRIT | ||
58 | "%s: could not set interface list: %s\n", | ||
59 | __FUNCTION__, "too many interfaces"); | ||
60 | return -EINVAL; | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | void | ||
66 | aoechr_error(char *msg) | ||
67 | { | ||
68 | struct ErrMsg *em; | ||
69 | char *mp; | ||
70 | ulong flags, n; | ||
71 | |||
72 | n = strlen(msg); | ||
73 | |||
74 | spin_lock_irqsave(&emsgs_lock, flags); | ||
75 | |||
76 | em = emsgs + emsgs_tail_idx; | ||
77 | if ((em->flags & EMFL_VALID)) { | ||
78 | bail: spin_unlock_irqrestore(&emsgs_lock, flags); | ||
79 | return; | ||
80 | } | ||
81 | |||
82 | mp = kmalloc(n, GFP_ATOMIC); | ||
83 | if (mp == NULL) { | ||
84 | printk(KERN_CRIT "aoe: aoechr_error: allocation failure, len=%ld\n", n); | ||
85 | goto bail; | ||
86 | } | ||
87 | |||
88 | memcpy(mp, msg, n); | ||
89 | em->msg = mp; | ||
90 | em->flags |= EMFL_VALID; | ||
91 | em->len = n; | ||
92 | |||
93 | emsgs_tail_idx++; | ||
94 | emsgs_tail_idx %= ARRAY_SIZE(emsgs); | ||
95 | |||
96 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
97 | |||
98 | if (nblocked_emsgs_readers) | ||
99 | up(&emsgs_sema); | ||
100 | } | ||
101 | |||
102 | static ssize_t | ||
103 | aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp) | ||
104 | { | ||
105 | int ret = -EINVAL; | ||
106 | |||
107 | switch ((unsigned long) filp->private_data) { | ||
108 | default: | ||
109 | printk(KERN_INFO "aoe: aoechr_write: can't write to that file.\n"); | ||
110 | break; | ||
111 | case MINOR_DISCOVER: | ||
112 | ret = discover(); | ||
113 | break; | ||
114 | case MINOR_INTERFACES: | ||
115 | ret = interfaces(buf, cnt); | ||
116 | break; | ||
117 | } | ||
118 | if (ret == 0) | ||
119 | ret = cnt; | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static int | ||
124 | aoechr_open(struct inode *inode, struct file *filp) | ||
125 | { | ||
126 | int n, i; | ||
127 | |||
128 | n = MINOR(inode->i_rdev); | ||
129 | filp->private_data = (void *) (unsigned long) n; | ||
130 | |||
131 | for (i = 0; i < ARRAY_SIZE(chardevs); ++i) | ||
132 | if (chardevs[i].minor == n) | ||
133 | return 0; | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | static int | ||
138 | aoechr_rel(struct inode *inode, struct file *filp) | ||
139 | { | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static ssize_t | ||
144 | aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) | ||
145 | { | ||
146 | unsigned long n; | ||
147 | char *mp; | ||
148 | struct ErrMsg *em; | ||
149 | ssize_t len; | ||
150 | ulong flags; | ||
151 | |||
152 | n = (unsigned long) filp->private_data; | ||
153 | switch (n) { | ||
154 | case MINOR_ERR: | ||
155 | spin_lock_irqsave(&emsgs_lock, flags); | ||
156 | loop: | ||
157 | em = emsgs + emsgs_head_idx; | ||
158 | if ((em->flags & EMFL_VALID) == 0) { | ||
159 | if (filp->f_flags & O_NDELAY) { | ||
160 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
161 | return -EAGAIN; | ||
162 | } | ||
163 | nblocked_emsgs_readers++; | ||
164 | |||
165 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
166 | |||
167 | n = down_interruptible(&emsgs_sema); | ||
168 | |||
169 | spin_lock_irqsave(&emsgs_lock, flags); | ||
170 | |||
171 | nblocked_emsgs_readers--; | ||
172 | |||
173 | if (n) { | ||
174 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
175 | return -ERESTARTSYS; | ||
176 | } | ||
177 | goto loop; | ||
178 | } | ||
179 | if (em->len > cnt) { | ||
180 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
181 | return -EAGAIN; | ||
182 | } | ||
183 | mp = em->msg; | ||
184 | len = em->len; | ||
185 | em->msg = NULL; | ||
186 | em->flags &= ~EMFL_VALID; | ||
187 | |||
188 | emsgs_head_idx++; | ||
189 | emsgs_head_idx %= ARRAY_SIZE(emsgs); | ||
190 | |||
191 | spin_unlock_irqrestore(&emsgs_lock, flags); | ||
192 | |||
193 | n = copy_to_user(buf, mp, len); | ||
194 | kfree(mp); | ||
195 | return n == 0 ? len : -EFAULT; | ||
196 | default: | ||
197 | return -EFAULT; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | static struct file_operations aoe_fops = { | ||
202 | .write = aoechr_write, | ||
203 | .read = aoechr_read, | ||
204 | .open = aoechr_open, | ||
205 | .release = aoechr_rel, | ||
206 | .owner = THIS_MODULE, | ||
207 | }; | ||
208 | |||
209 | int __init | ||
210 | aoechr_init(void) | ||
211 | { | ||
212 | int n, i; | ||
213 | |||
214 | n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops); | ||
215 | if (n < 0) { | ||
216 | printk(KERN_ERR "aoe: aoechr_init: can't register char device\n"); | ||
217 | return n; | ||
218 | } | ||
219 | sema_init(&emsgs_sema, 0); | ||
220 | spin_lock_init(&emsgs_lock); | ||
221 | aoe_class = class_simple_create(THIS_MODULE, "aoe"); | ||
222 | if (IS_ERR(aoe_class)) { | ||
223 | unregister_chrdev(AOE_MAJOR, "aoechr"); | ||
224 | return PTR_ERR(aoe_class); | ||
225 | } | ||
226 | for (i = 0; i < ARRAY_SIZE(chardevs); ++i) | ||
227 | class_simple_device_add(aoe_class, | ||
228 | MKDEV(AOE_MAJOR, chardevs[i].minor), | ||
229 | NULL, chardevs[i].name); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | void | ||
235 | aoechr_exit(void) | ||
236 | { | ||
237 | int i; | ||
238 | |||
239 | for (i = 0; i < ARRAY_SIZE(chardevs); ++i) | ||
240 | class_simple_device_remove(MKDEV(AOE_MAJOR, chardevs[i].minor)); | ||
241 | class_simple_destroy(aoe_class); | ||
242 | unregister_chrdev(AOE_MAJOR, "aoechr"); | ||
243 | } | ||
244 | |||