blob: 4ab7b40e8c5af5e4de493f114f3a98c84ba7c9e2 [file] [log] [blame]
Ed L. Cashin26114642006-09-20 14:36:48 -04001/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * aoecmd.c
4 * Filesystem request handling methods
5 */
6
7#include <linux/hdreg.h>
8#include <linux/blkdev.h>
9#include <linux/skbuff.h>
10#include <linux/netdevice.h>
Ed L. Cashin3ae1c242006-01-19 13:46:19 -050011#include <linux/genhd.h>
Ed L. Cashin475172f2005-09-29 12:47:40 -040012#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include "aoe.h"
14
15#define TIMERTICK (HZ / 10)
16#define MINTIMER (2 * TIMERTICK)
17#define MAXTIMER (HZ << 1)
Ed L. Cashinb751e8b2006-09-20 14:36:50 -040018
19static int aoe_deadsecs = 60 * 3;
20module_param(aoe_deadsecs, int, 0644);
21MODULE_PARM_DESC(aoe_deadsecs, "After aoe_deadsecs seconds, give up and fail dev.");
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Ed L. Cashine407a7f2006-09-20 14:36:49 -040023struct sk_buff *
24new_skb(ulong len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
26 struct sk_buff *skb;
27
28 skb = alloc_skb(len, GFP_ATOMIC);
29 if (skb) {
30 skb->nh.raw = skb->mac.raw = skb->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 skb->protocol = __constant_htons(ETH_P_AOE);
32 skb->priority = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 skb->next = skb->prev = NULL;
34
35 /* tell the network layer not to perform IP checksums
36 * or to get the NIC to do it
37 */
38 skb->ip_summed = CHECKSUM_NONE;
39 }
40 return skb;
41}
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043static struct frame *
44getframe(struct aoedev *d, int tag)
45{
46 struct frame *f, *e;
47
48 f = d->frames;
49 e = f + d->nframes;
50 for (; f<e; f++)
51 if (f->tag == tag)
52 return f;
53 return NULL;
54}
55
56/*
57 * Leave the top bit clear so we have tagspace for userland.
58 * The bottom 16 bits are the xmit tick for rexmit/rttavg processing.
59 * This driver reserves tag -1 to mean "unused frame."
60 */
61static int
62newtag(struct aoedev *d)
63{
64 register ulong n;
65
66 n = jiffies & 0xffff;
67 return n |= (++d->lasttag & 0x7fff) << 16;
68}
69
70static int
71aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
72{
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 u32 host_tag = newtag(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75 memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
76 memcpy(h->dst, d->addr, sizeof h->dst);
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -070077 h->type = __constant_cpu_to_be16(ETH_P_AOE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 h->verfl = AOE_HVER;
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -070079 h->major = cpu_to_be16(d->aoemajor);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 h->minor = d->aoeminor;
81 h->cmd = AOECMD_ATA;
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -070082 h->tag = cpu_to_be32(host_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84 return host_tag;
85}
86
Ed L. Cashin19bf2632006-09-20 14:36:49 -040087static inline void
88put_lba(struct aoe_atahdr *ah, sector_t lba)
89{
90 ah->lba0 = lba;
91 ah->lba1 = lba >>= 8;
92 ah->lba2 = lba >>= 8;
93 ah->lba3 = lba >>= 8;
94 ah->lba4 = lba >>= 8;
95 ah->lba5 = lba >>= 8;
96}
97
Linus Torvalds1da177e2005-04-16 15:20:36 -070098static void
99aoecmd_ata_rw(struct aoedev *d, struct frame *f)
100{
101 struct aoe_hdr *h;
102 struct aoe_atahdr *ah;
103 struct buf *buf;
104 struct sk_buff *skb;
105 ulong bcnt;
106 register sector_t sector;
107 char writebit, extbit;
108
109 writebit = 0x10;
110 extbit = 0x4;
111
112 buf = d->inprocess;
113
114 sector = buf->sector;
115 bcnt = buf->bv_resid;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400116 if (bcnt > d->maxbcnt)
117 bcnt = d->maxbcnt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119 /* initialize the headers & frame */
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400120 skb = f->skb;
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300121 h = aoe_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 ah = (struct aoe_atahdr *) (h+1);
Ed L. Cashin19900cd2006-12-22 01:09:21 -0800123 skb_put(skb, sizeof *h + sizeof *ah);
124 memset(h, 0, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 f->tag = aoehdr_atainit(d, h);
126 f->waited = 0;
127 f->buf = buf;
128 f->bufaddr = buf->bufaddr;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400129 f->bcnt = bcnt;
130 f->lba = sector;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 /* set up ata header */
133 ah->scnt = bcnt >> 9;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400134 put_lba(ah, sector);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 if (d->flags & DEVFL_EXT) {
136 ah->aflags |= AOEAFL_EXT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 } else {
138 extbit = 0;
139 ah->lba3 &= 0x0f;
140 ah->lba3 |= 0xe0; /* LBA bit + obsolete 0xa0 */
141 }
142
143 if (bio_data_dir(buf->bio) == WRITE) {
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400144 skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
145 offset_in_page(f->bufaddr), bcnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 ah->aflags |= AOEAFL_WRITE;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400147 skb->len += bcnt;
148 skb->data_len = bcnt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 } else {
150 writebit = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 }
152
153 ah->cmdstat = WIN_READ | writebit | extbit;
154
155 /* mark all tracking fields and load out */
156 buf->nframesout += 1;
157 buf->bufaddr += bcnt;
158 buf->bv_resid -= bcnt;
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400159/* printk(KERN_DEBUG "aoe: bv_resid=%ld\n", buf->bv_resid); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 buf->resid -= bcnt;
161 buf->sector += bcnt >> 9;
162 if (buf->resid == 0) {
163 d->inprocess = NULL;
164 } else if (buf->bv_resid == 0) {
165 buf->bv++;
Ed L. Cashin392e4842006-09-20 14:36:50 -0400166 WARN_ON(buf->bv->bv_len == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 buf->bv_resid = buf->bv->bv_len;
168 buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
169 }
170
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400171 skb->dev = d->ifp;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400172 skb = skb_clone(skb, GFP_ATOMIC);
173 if (skb == NULL)
174 return;
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400175 if (d->sendq_hd)
176 d->sendq_tl->next = skb;
177 else
178 d->sendq_hd = skb;
179 d->sendq_tl = skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500182/* some callers cannot sleep, and they can call this function,
183 * transmitting the packets later, when interrupts are on
184 */
185static struct sk_buff *
186aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
187{
188 struct aoe_hdr *h;
189 struct aoe_cfghdr *ch;
190 struct sk_buff *skb, *sl, *sl_tail;
191 struct net_device *ifp;
192
193 sl = sl_tail = NULL;
194
195 read_lock(&dev_base_lock);
196 for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
197 dev_hold(ifp);
198 if (!is_aoe_netif(ifp))
199 continue;
200
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400201 skb = new_skb(sizeof *h + sizeof *ch);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500202 if (skb == NULL) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400203 printk(KERN_INFO "aoe: skb alloc failure\n");
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500204 continue;
205 }
Ed L. Cashin19900cd2006-12-22 01:09:21 -0800206 skb_put(skb, sizeof *h + sizeof *ch);
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400207 skb->dev = ifp;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500208 if (sl_tail == NULL)
209 sl_tail = skb;
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300210 h = aoe_hdr(skb);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500211 memset(h, 0, sizeof *h + sizeof *ch);
212
213 memset(h->dst, 0xff, sizeof h->dst);
214 memcpy(h->src, ifp->dev_addr, sizeof h->src);
215 h->type = __constant_cpu_to_be16(ETH_P_AOE);
216 h->verfl = AOE_HVER;
217 h->major = cpu_to_be16(aoemajor);
218 h->minor = aoeminor;
219 h->cmd = AOECMD_CFG;
220
221 skb->next = sl;
222 sl = skb;
223 }
224 read_unlock(&dev_base_lock);
225
226 if (tail != NULL)
227 *tail = sl_tail;
228 return sl;
229}
230
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400231static struct frame *
232freeframe(struct aoedev *d)
233{
234 struct frame *f, *e;
235 int n = 0;
236
237 f = d->frames;
238 e = f + d->nframes;
239 for (; f<e; f++) {
240 if (f->tag != FREETAG)
241 continue;
242 if (atomic_read(&skb_shinfo(f->skb)->dataref) == 1) {
243 skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
Ed L. Cashin19900cd2006-12-22 01:09:21 -0800244 skb_trim(f->skb, 0);
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400245 return f;
246 }
247 n++;
248 }
249 if (n == d->nframes) /* wait for network layer */
250 d->flags |= DEVFL_KICKME;
251
252 return NULL;
253}
254
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255/* enters with d->lock held */
256void
257aoecmd_work(struct aoedev *d)
258{
259 struct frame *f;
260 struct buf *buf;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500261
262 if (d->flags & DEVFL_PAUSE) {
263 if (!aoedev_isbusy(d))
264 d->sendq_hd = aoecmd_cfg_pkts(d->aoemajor,
265 d->aoeminor, &d->sendq_tl);
266 return;
267 }
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269loop:
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400270 f = freeframe(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 if (f == NULL)
272 return;
273 if (d->inprocess == NULL) {
274 if (list_empty(&d->bufq))
275 return;
276 buf = container_of(d->bufq.next, struct buf, bufs);
277 list_del(d->bufq.next);
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400278/*printk(KERN_DEBUG "aoe: bi_size=%ld\n", buf->bio->bi_size); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 d->inprocess = buf;
280 }
281 aoecmd_ata_rw(d, f);
282 goto loop;
283}
284
285static void
286rexmit(struct aoedev *d, struct frame *f)
287{
288 struct sk_buff *skb;
289 struct aoe_hdr *h;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400290 struct aoe_atahdr *ah;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 char buf[128];
292 u32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 n = newtag(d);
295
296 snprintf(buf, sizeof buf,
297 "%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
298 "retransmit",
299 d->aoemajor, d->aoeminor, f->tag, jiffies, n);
300 aoechr_error(buf);
301
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400302 skb = f->skb;
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300303 h = aoe_hdr(skb);
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400304 ah = (struct aoe_atahdr *) (h+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 f->tag = n;
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -0700306 h->tag = cpu_to_be32(n);
Ed L. Cashin2dd5e422006-01-19 13:46:25 -0500307 memcpy(h->dst, d->addr, sizeof h->dst);
308 memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400310 n = DEFAULTBCNT / 512;
311 if (ah->scnt > n) {
312 ah->scnt = n;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400313 if (ah->aflags & AOEAFL_WRITE) {
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400314 skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
315 offset_in_page(f->bufaddr), DEFAULTBCNT);
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400316 skb->len = sizeof *h + sizeof *ah + DEFAULTBCNT;
317 skb->data_len = DEFAULTBCNT;
318 }
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400319 if (++d->lostjumbo > (d->nframes << 1))
320 if (d->maxbcnt != DEFAULTBCNT) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400321 printk(KERN_INFO "aoe: e%ld.%ld: too many lost jumbo on %s - using 1KB frames.\n",
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400322 d->aoemajor, d->aoeminor, d->ifp->name);
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400323 d->maxbcnt = DEFAULTBCNT;
324 d->flags |= DEVFL_MAXBCNT;
325 }
326 }
327
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400328 skb->dev = d->ifp;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400329 skb = skb_clone(skb, GFP_ATOMIC);
330 if (skb == NULL)
331 return;
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400332 if (d->sendq_hd)
333 d->sendq_tl->next = skb;
334 else
335 d->sendq_hd = skb;
336 d->sendq_tl = skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337}
338
339static int
340tsince(int tag)
341{
342 int n;
343
344 n = jiffies & 0xffff;
345 n -= tag & 0xffff;
346 if (n < 0)
347 n += 1<<16;
348 return n;
349}
350
351static void
352rexmit_timer(ulong vp)
353{
354 struct aoedev *d;
355 struct frame *f, *e;
356 struct sk_buff *sl;
357 register long timeout;
358 ulong flags, n;
359
360 d = (struct aoedev *) vp;
361 sl = NULL;
362
363 /* timeout is always ~150% of the moving average */
364 timeout = d->rttavg;
365 timeout += timeout >> 1;
366
367 spin_lock_irqsave(&d->lock, flags);
368
369 if (d->flags & DEVFL_TKILL) {
Ed L. Cashin1c6f3fc2006-01-25 13:54:44 -0500370 spin_unlock_irqrestore(&d->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 return;
372 }
373 f = d->frames;
374 e = f + d->nframes;
375 for (; f<e; f++) {
376 if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
377 n = f->waited += timeout;
378 n /= HZ;
Ed L. Cashinb751e8b2006-09-20 14:36:50 -0400379 if (n > aoe_deadsecs) { /* waited too long for response */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 aoedev_downdev(d);
Ed L. Cashin1c6f3fc2006-01-25 13:54:44 -0500381 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383 rexmit(d, f);
384 }
385 }
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400386 if (d->flags & DEVFL_KICKME) {
387 d->flags &= ~DEVFL_KICKME;
388 aoecmd_work(d);
389 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
ecashin@coraid.coma4b38362005-04-18 22:00:22 -0700391 sl = d->sendq_hd;
392 d->sendq_hd = d->sendq_tl = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if (sl) {
394 n = d->rttavg <<= 1;
395 if (n > MAXTIMER)
396 d->rttavg = MAXTIMER;
397 }
398
399 d->timer.expires = jiffies + TIMERTICK;
400 add_timer(&d->timer);
401
402 spin_unlock_irqrestore(&d->lock, flags);
403
404 aoenet_xmit(sl);
405}
406
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500407/* this function performs work that has been deferred until sleeping is OK
408 */
409void
David Howellsc4028952006-11-22 14:57:56 +0000410aoecmd_sleepwork(struct work_struct *work)
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500411{
David Howellsc4028952006-11-22 14:57:56 +0000412 struct aoedev *d = container_of(work, struct aoedev, work);
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500413
414 if (d->flags & DEVFL_GDALLOC)
415 aoeblk_gdalloc(d);
416
417 if (d->flags & DEVFL_NEWSIZE) {
418 struct block_device *bd;
419 unsigned long flags;
420 u64 ssize;
421
422 ssize = d->gd->capacity;
423 bd = bdget_disk(d->gd, 0);
424
425 if (bd) {
426 mutex_lock(&bd->bd_inode->i_mutex);
427 i_size_write(bd->bd_inode, (loff_t)ssize<<9);
428 mutex_unlock(&bd->bd_inode->i_mutex);
429 bdput(bd);
430 }
431 spin_lock_irqsave(&d->lock, flags);
432 d->flags |= DEVFL_UP;
433 d->flags &= ~DEVFL_NEWSIZE;
434 spin_unlock_irqrestore(&d->lock, flags);
435 }
436}
437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438static void
439ataid_complete(struct aoedev *d, unsigned char *id)
440{
441 u64 ssize;
442 u16 n;
443
444 /* word 83: command set supported */
Ed L. Cashin475172f2005-09-29 12:47:40 -0400445 n = le16_to_cpu(get_unaligned((__le16 *) &id[83<<1]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
447 /* word 86: command set/feature enabled */
Ed L. Cashin475172f2005-09-29 12:47:40 -0400448 n |= le16_to_cpu(get_unaligned((__le16 *) &id[86<<1]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450 if (n & (1<<10)) { /* bit 10: LBA 48 */
451 d->flags |= DEVFL_EXT;
452
453 /* word 100: number lba48 sectors */
Ed L. Cashin475172f2005-09-29 12:47:40 -0400454 ssize = le64_to_cpu(get_unaligned((__le64 *) &id[100<<1]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456 /* set as in ide-disk.c:init_idedisk_capacity */
457 d->geo.cylinders = ssize;
458 d->geo.cylinders /= (255 * 63);
459 d->geo.heads = 255;
460 d->geo.sectors = 63;
461 } else {
462 d->flags &= ~DEVFL_EXT;
463
464 /* number lba28 sectors */
Ed L. Cashin475172f2005-09-29 12:47:40 -0400465 ssize = le32_to_cpu(get_unaligned((__le32 *) &id[60<<1]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
467 /* NOTE: obsolete in ATA 6 */
Ed L. Cashin475172f2005-09-29 12:47:40 -0400468 d->geo.cylinders = le16_to_cpu(get_unaligned((__le16 *) &id[54<<1]));
469 d->geo.heads = le16_to_cpu(get_unaligned((__le16 *) &id[55<<1]));
470 d->geo.sectors = le16_to_cpu(get_unaligned((__le16 *) &id[56<<1]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 }
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500472
473 if (d->ssize != ssize)
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400474 printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu sectors\n",
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400475 (unsigned long long)mac_addr(d->addr),
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500476 d->aoemajor, d->aoeminor,
477 d->fw_ver, (long long)ssize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 d->ssize = ssize;
479 d->geo.start = 0;
480 if (d->gd != NULL) {
481 d->gd->capacity = ssize;
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500482 d->flags |= DEVFL_NEWSIZE;
483 } else {
484 if (d->flags & DEVFL_GDALLOC) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400485 printk(KERN_ERR "aoe: can't schedule work for e%lu.%lu, %s\n",
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500486 d->aoemajor, d->aoeminor,
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400487 "it's already on! This shouldn't happen.\n");
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500488 return;
489 }
490 d->flags |= DEVFL_GDALLOC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 schedule_work(&d->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493}
494
495static void
496calc_rttavg(struct aoedev *d, int rtt)
497{
498 register long n;
499
500 n = rtt;
Ed L. Cashindced3a02006-09-20 14:36:49 -0400501 if (n < 0) {
502 n = -rtt;
503 if (n < MINTIMER)
504 n = MINTIMER;
505 else if (n > MAXTIMER)
506 n = MAXTIMER;
507 d->mintimer += (n - d->mintimer) >> 1;
508 } else if (n < d->mintimer)
509 n = d->mintimer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 else if (n > MAXTIMER)
511 n = MAXTIMER;
512
513 /* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
514 n -= d->rttavg;
515 d->rttavg += n >> 2;
516}
517
518void
519aoecmd_ata_rsp(struct sk_buff *skb)
520{
521 struct aoedev *d;
Ed L. Cashinddec63e2006-09-20 14:36:49 -0400522 struct aoe_hdr *hin, *hout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 struct aoe_atahdr *ahin, *ahout;
524 struct frame *f;
525 struct buf *buf;
526 struct sk_buff *sl;
527 register long n;
528 ulong flags;
529 char ebuf[128];
ecashin@coraid.com32465c62005-04-18 22:00:18 -0700530 u16 aoemajor;
531
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300532 hin = aoe_hdr(skb);
David S. Miller43ecf522007-03-01 18:30:08 -0800533 aoemajor = be16_to_cpu(get_unaligned(&hin->major));
ecashin@coraid.com32465c62005-04-18 22:00:18 -0700534 d = aoedev_by_aoeaddr(aoemajor, hin->minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (d == NULL) {
536 snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
537 "for unknown device %d.%d\n",
ecashin@coraid.com32465c62005-04-18 22:00:18 -0700538 aoemajor, hin->minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 aoechr_error(ebuf);
540 return;
541 }
542
543 spin_lock_irqsave(&d->lock, flags);
544
David S. Miller43ecf522007-03-01 18:30:08 -0800545 n = be32_to_cpu(get_unaligned(&hin->tag));
Ed L. Cashindced3a02006-09-20 14:36:49 -0400546 f = getframe(d, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (f == NULL) {
Ed L. Cashindced3a02006-09-20 14:36:49 -0400548 calc_rttavg(d, -tsince(n));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 spin_unlock_irqrestore(&d->lock, flags);
550 snprintf(ebuf, sizeof ebuf,
551 "%15s e%d.%d tag=%08x@%08lx\n",
552 "unexpected rsp",
David S. Miller43ecf522007-03-01 18:30:08 -0800553 be16_to_cpu(get_unaligned(&hin->major)),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 hin->minor,
David S. Miller43ecf522007-03-01 18:30:08 -0800555 be32_to_cpu(get_unaligned(&hin->tag)),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 jiffies);
557 aoechr_error(ebuf);
558 return;
559 }
560
561 calc_rttavg(d, tsince(f->tag));
562
563 ahin = (struct aoe_atahdr *) (hin+1);
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300564 hout = aoe_hdr(f->skb);
Ed L. Cashinddec63e2006-09-20 14:36:49 -0400565 ahout = (struct aoe_atahdr *) (hout+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 buf = f->buf;
567
Ed L. Cashin9d419652006-02-07 11:37:24 -0500568 if (ahout->cmdstat == WIN_IDENTIFY)
569 d->flags &= ~DEVFL_PAUSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 if (ahin->cmdstat & 0xa9) { /* these bits cleared on success */
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400571 printk(KERN_ERR
572 "aoe: ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%ld\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 ahout->cmdstat, ahin->cmdstat,
574 d->aoemajor, d->aoeminor);
575 if (buf)
576 buf->flags |= BUFFL_FAIL;
577 } else {
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400578 n = ahout->scnt << 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 switch (ahout->cmdstat) {
580 case WIN_READ:
581 case WIN_READ_EXT:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 if (skb->len - sizeof *hin - sizeof *ahin < n) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400583 printk(KERN_ERR
584 "aoe: runt data size in read. skb->len=%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 skb->len);
586 /* fail frame f? just returning will rexmit. */
587 spin_unlock_irqrestore(&d->lock, flags);
588 return;
589 }
590 memcpy(f->bufaddr, ahin+1, n);
591 case WIN_WRITE:
592 case WIN_WRITE_EXT:
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400593 if (f->bcnt -= n) {
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400594 skb = f->skb;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400595 f->bufaddr += n;
596 put_lba(ahout, f->lba += ahout->scnt);
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400597 n = f->bcnt;
598 if (n > DEFAULTBCNT)
599 n = DEFAULTBCNT;
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400600 ahout->scnt = n >> 9;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400601 if (ahout->aflags & AOEAFL_WRITE) {
602 skb_fill_page_desc(skb, 0,
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400603 virt_to_page(f->bufaddr),
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400604 offset_in_page(f->bufaddr), n);
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400605 skb->len = sizeof *hout + sizeof *ahout + n;
606 skb->data_len = n;
607 }
Ed L. Cashinddec63e2006-09-20 14:36:49 -0400608 f->tag = newtag(d);
609 hout->tag = cpu_to_be32(f->tag);
610 skb->dev = d->ifp;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400611 skb = skb_clone(skb, GFP_ATOMIC);
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400612 spin_unlock_irqrestore(&d->lock, flags);
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400613 if (skb)
614 aoenet_xmit(skb);
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400615 return;
616 }
617 if (n > DEFAULTBCNT)
618 d->lostjumbo = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 break;
620 case WIN_IDENTIFY:
621 if (skb->len - sizeof *hin - sizeof *ahin < 512) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400622 printk(KERN_INFO
623 "aoe: runt data size in ataid. skb->len=%d\n",
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400624 skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 spin_unlock_irqrestore(&d->lock, flags);
626 return;
627 }
628 ataid_complete(d, (char *) (ahin+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 break;
630 default:
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400631 printk(KERN_INFO
632 "aoe: unrecognized ata command %2.2Xh for %d.%d\n",
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400633 ahout->cmdstat,
David S. Miller43ecf522007-03-01 18:30:08 -0800634 be16_to_cpu(get_unaligned(&hin->major)),
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400635 hin->minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 }
637 }
638
639 if (buf) {
640 buf->nframesout -= 1;
641 if (buf->nframesout == 0 && buf->resid == 0) {
ecashin@coraid.com0c6f0e72005-04-18 22:00:22 -0700642 unsigned long duration = jiffies - buf->start_time;
643 unsigned long n_sect = buf->bio->bi_size >> 9;
644 struct gendisk *disk = d->gd;
Jens Axboe496456c2005-11-01 09:54:23 +0100645 const int rw = bio_data_dir(buf->bio);
ecashin@coraid.com0c6f0e72005-04-18 22:00:22 -0700646
Jens Axboe496456c2005-11-01 09:54:23 +0100647 disk_stat_inc(disk, ios[rw]);
648 disk_stat_add(disk, ticks[rw], duration);
649 disk_stat_add(disk, sectors[rw], n_sect);
ecashin@coraid.com0c6f0e72005-04-18 22:00:22 -0700650 disk_stat_add(disk, io_ticks, duration);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
652 bio_endio(buf->bio, buf->bio->bi_size, n);
653 mempool_free(buf, d->bufpool);
654 }
655 }
656
657 f->buf = NULL;
658 f->tag = FREETAG;
659
660 aoecmd_work(d);
ecashin@coraid.coma4b38362005-04-18 22:00:22 -0700661 sl = d->sendq_hd;
662 d->sendq_hd = d->sendq_tl = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 spin_unlock_irqrestore(&d->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 aoenet_xmit(sl);
666}
667
668void
669aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
670{
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500671 struct sk_buff *sl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500673 sl = aoecmd_cfg_pkts(aoemajor, aoeminor, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 aoenet_xmit(sl);
676}
677
678/*
679 * Since we only call this in one place (and it only prepares one frame)
ecashin@coraid.coma4b38362005-04-18 22:00:22 -0700680 * we just return the skb. Usually we'd chain it up to the aoedev sendq.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 */
682static struct sk_buff *
683aoecmd_ata_id(struct aoedev *d)
684{
685 struct aoe_hdr *h;
686 struct aoe_atahdr *ah;
687 struct frame *f;
688 struct sk_buff *skb;
689
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400690 f = freeframe(d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (f == NULL) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400692 printk(KERN_ERR "aoe: can't get a frame. This shouldn't happen.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return NULL;
694 }
695
696 /* initialize the headers & frame */
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400697 skb = f->skb;
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300698 h = aoe_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 ah = (struct aoe_atahdr *) (h+1);
Ed L. Cashin19900cd2006-12-22 01:09:21 -0800700 skb_put(skb, sizeof *h + sizeof *ah);
701 memset(h, 0, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 f->tag = aoehdr_atainit(d, h);
703 f->waited = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 /* set up ata header */
706 ah->scnt = 1;
707 ah->cmdstat = WIN_IDENTIFY;
708 ah->lba3 = 0xa0;
709
Ed L. Cashine407a7f2006-09-20 14:36:49 -0400710 skb->dev = d->ifp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500712 d->rttavg = MAXTIMER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 d->timer.function = rexmit_timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400715 return skb_clone(skb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716}
717
718void
719aoecmd_cfg_rsp(struct sk_buff *skb)
720{
721 struct aoedev *d;
722 struct aoe_hdr *h;
723 struct aoe_cfghdr *ch;
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -0700724 ulong flags, sysminor, aoemajor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 struct sk_buff *sl;
Ed L. Cashineaf0a3c2006-01-19 13:46:20 -0500726 enum { MAXFRAMES = 16 };
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400727 u16 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Arnaldo Carvalho de Melo029720f2007-03-10 11:20:07 -0300729 h = aoe_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 ch = (struct aoe_cfghdr *) (h+1);
731
732 /*
733 * Enough people have their dip switches set backwards to
734 * warrant a loud message for this special case.
735 */
David S. Miller43ecf522007-03-01 18:30:08 -0800736 aoemajor = be16_to_cpu(get_unaligned(&h->major));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 if (aoemajor == 0xfff) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400738 printk(KERN_ERR "aoe: Warning: shelf address is all ones. "
Ed L. Cashin6bb62852006-09-20 14:36:49 -0400739 "Check shelf dip switches.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 return;
741 }
742
743 sysminor = SYSMINOR(aoemajor, h->minor);
ecashin@coraid.comfc458dc2005-04-18 22:00:17 -0700744 if (sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400745 printk(KERN_INFO "aoe: e%ld.%d: minor number too large\n",
ecashin@coraid.comfc458dc2005-04-18 22:00:17 -0700746 aoemajor, (int) h->minor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 return;
748 }
749
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400750 n = be16_to_cpu(ch->bufcnt);
751 if (n > MAXFRAMES) /* keep it reasonable */
752 n = MAXFRAMES;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400754 d = aoedev_by_sysminor_m(sysminor, n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 if (d == NULL) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400756 printk(KERN_INFO "aoe: device sysminor_m failure\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 return;
758 }
759
760 spin_lock_irqsave(&d->lock, flags);
761
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500762 /* permit device to migrate mac and network interface */
763 d->ifp = skb->dev;
764 memcpy(d->addr, h->src, sizeof d->addr);
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400765 if (!(d->flags & DEVFL_MAXBCNT)) {
766 n = d->ifp->mtu;
767 n -= sizeof (struct aoe_hdr) + sizeof (struct aoe_atahdr);
768 n /= 512;
769 if (n > ch->scnt)
770 n = ch->scnt;
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400771 n = n ? n * 512 : DEFAULTBCNT;
772 if (n != d->maxbcnt) {
Ed L. Cashina12c93f2006-09-20 14:36:51 -0400773 printk(KERN_INFO
774 "aoe: e%ld.%ld: setting %d byte data frames on %s\n",
Ed L. Cashin4f51dc52006-09-20 14:36:49 -0400775 d->aoemajor, d->aoeminor, n, d->ifp->name);
776 d->maxbcnt = n;
777 }
Ed L. Cashin19bf2632006-09-20 14:36:49 -0400778 }
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500779
780 /* don't change users' perspective */
781 if (d->nopen && !(d->flags & DEVFL_PAUSE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 spin_unlock_irqrestore(&d->lock, flags);
783 return;
784 }
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500785 d->flags |= DEVFL_PAUSE; /* force pause */
Ed L. Cashindced3a02006-09-20 14:36:49 -0400786 d->mintimer = MINTIMER;
ecashin@coraid.com63e9cc52005-04-18 22:00:20 -0700787 d->fw_ver = be16_to_cpu(ch->fwver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Ed L. Cashin3ae1c242006-01-19 13:46:19 -0500789 /* check for already outstanding ataid */
790 sl = aoedev_isbusy(d) == 0 ? aoecmd_ata_id(d) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 spin_unlock_irqrestore(&d->lock, flags);
793
794 aoenet_xmit(sl);
795}
796