Commit 896831f5 authored by Ed Cashin's avatar Ed Cashin Committed by Linus Torvalds

aoe: kernel thread handles I/O completions for simple locking

Make the frames the aoe driver uses to track the relationship between bios
and packets more flexible and detached, so that they can be passed to an
"aoe_ktio" thread for completion of I/O.

The frames are handled much like skbs, with a capped amount of
preallocation so that real-world use cases are likely to run smoothly and
degenerate gracefully even under memory pressure.

Decoupling I/O completion from the receive path and serializing it in a
process makes it easier to think about the correctness of the locking in
the driver, especially in the case of a remote MAC address becoming
unusable.

[dan.carpenter@oracle.com: cleanup an allocation a bit]
Signed-off-by: default avatarEd Cashin <ecashin@coraid.com>
Signed-off-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 3d5b0605
...@@ -91,6 +91,7 @@ enum { ...@@ -91,6 +91,7 @@ enum {
NTARGETS = 8, NTARGETS = 8,
NAOEIFS = 8, NAOEIFS = 8,
NSKBPOOLMAX = 128, NSKBPOOLMAX = 128,
NFACTIVE = 17,
TIMERTICK = HZ / 10, TIMERTICK = HZ / 10,
MINTIMER = HZ >> 2, MINTIMER = HZ >> 2,
...@@ -112,13 +113,16 @@ struct buf { ...@@ -112,13 +113,16 @@ struct buf {
}; };
struct frame { struct frame {
int tag; struct list_head head;
u32 tag;
ulong waited; ulong waited;
struct buf *buf; struct buf *buf;
struct aoetgt *t; /* parent target I belong to */
char *bufaddr; char *bufaddr;
ulong bcnt; ulong bcnt;
sector_t lba; sector_t lba;
struct sk_buff *skb; struct sk_buff *skb; /* command skb freed on module exit */
struct sk_buff *r_skb; /* response skb for async processing */
struct bio_vec *bv; struct bio_vec *bv;
ulong bv_off; ulong bv_off;
}; };
...@@ -133,16 +137,18 @@ struct aoeif { ...@@ -133,16 +137,18 @@ struct aoeif {
struct aoetgt { struct aoetgt {
unsigned char addr[6]; unsigned char addr[6];
ushort nframes; ushort nframes;
struct frame *frames; struct aoedev *d; /* parent device I belong to */
struct list_head factive[NFACTIVE]; /* hash of active frames */
struct list_head ffree; /* list of free frames */
struct aoeif ifs[NAOEIFS]; struct aoeif ifs[NAOEIFS];
struct aoeif *ifp; /* current aoeif in use */ struct aoeif *ifp; /* current aoeif in use */
ushort nout; ushort nout;
ushort maxout; ushort maxout;
u16 lasttag; /* last tag sent */ u16 lasttag; /* last tag sent */
u16 useme; u16 useme;
ulong falloc;
ulong lastwadj; /* last window adjustment */ ulong lastwadj; /* last window adjustment */
int wpkts, rpkts; int wpkts, rpkts;
int dataref;
}; };
struct aoedev { struct aoedev {
...@@ -169,9 +175,20 @@ struct aoedev { ...@@ -169,9 +175,20 @@ struct aoedev {
struct buf *inprocess; /* the one we're currently working on */ struct buf *inprocess; /* the one we're currently working on */
struct aoetgt *targets[NTARGETS]; struct aoetgt *targets[NTARGETS];
struct aoetgt **tgt; /* target in use when working */ struct aoetgt **tgt; /* target in use when working */
struct aoetgt **htgt; /* target needing rexmit assistance */ struct aoetgt *htgt; /* target needing rexmit assistance */
ulong ntargets;
ulong kicked;
}; };
/* kthread tracking */
struct ktstate {
struct completion rendez;
struct task_struct *task;
wait_queue_head_t *waitq;
int (*fn) (void);
char *name;
spinlock_t *lock;
};
int aoeblk_init(void); int aoeblk_init(void);
void aoeblk_exit(void); void aoeblk_exit(void);
...@@ -184,11 +201,14 @@ void aoechr_error(char *); ...@@ -184,11 +201,14 @@ void aoechr_error(char *);
void aoecmd_work(struct aoedev *d); void aoecmd_work(struct aoedev *d);
void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor); void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor);
void aoecmd_ata_rsp(struct sk_buff *); struct sk_buff *aoecmd_ata_rsp(struct sk_buff *);
void aoecmd_cfg_rsp(struct sk_buff *); void aoecmd_cfg_rsp(struct sk_buff *);
void aoecmd_sleepwork(struct work_struct *); void aoecmd_sleepwork(struct work_struct *);
void aoecmd_cleanslate(struct aoedev *); void aoecmd_cleanslate(struct aoedev *);
void aoecmd_exit(void);
int aoecmd_init(void);
struct sk_buff *aoecmd_ata_id(struct aoedev *); struct sk_buff *aoecmd_ata_id(struct aoedev *);
void aoe_freetframe(struct frame *);
int aoedev_init(void); int aoedev_init(void);
void aoedev_exit(void); void aoedev_exit(void);
...@@ -196,6 +216,7 @@ struct aoedev *aoedev_by_aoeaddr(int maj, int min); ...@@ -196,6 +216,7 @@ struct aoedev *aoedev_by_aoeaddr(int maj, int min);
struct aoedev *aoedev_by_sysminor_m(ulong sysminor); struct aoedev *aoedev_by_sysminor_m(ulong sysminor);
void aoedev_downdev(struct aoedev *d); void aoedev_downdev(struct aoedev *d);
int aoedev_flush(const char __user *str, size_t size); int aoedev_flush(const char __user *str, size_t size);
void aoe_failbuf(struct aoedev *d, struct buf *buf);
int aoenet_init(void); int aoenet_init(void);
void aoenet_exit(void); void aoenet_exit(void);
......
...@@ -86,10 +86,9 @@ revalidate(const char __user *str, size_t size) ...@@ -86,10 +86,9 @@ revalidate(const char __user *str, size_t size)
if (copy_from_user(buf, str, size)) if (copy_from_user(buf, str, size))
return -EFAULT; return -EFAULT;
/* should be e%d.%d format */
n = sscanf(buf, "e%d.%d", &major, &minor); n = sscanf(buf, "e%d.%d", &major, &minor);
if (n != 2) { if (n != 2) {
printk(KERN_ERR "aoe: invalid device specification\n"); pr_err("aoe: invalid device specification %s\n", buf);
return -EINVAL; return -EINVAL;
} }
d = aoedev_by_aoeaddr(major, minor); d = aoedev_by_aoeaddr(major, minor);
......
This diff is collapsed.
...@@ -48,47 +48,60 @@ dummy_timer(ulong vp) ...@@ -48,47 +48,60 @@ dummy_timer(ulong vp)
} }
void void
aoedev_downdev(struct aoedev *d) aoe_failbuf(struct aoedev *d, struct buf *buf)
{ {
struct aoetgt **t, **te;
struct frame *f, *e;
struct buf *buf;
struct bio *bio; struct bio *bio;
t = d->targets; if (buf == NULL)
te = t + NTARGETS; return;
for (; t < te && *t; t++) { buf->flags |= BUFFL_FAIL;
f = (*t)->frames; if (buf->nframesout == 0) {
e = f + (*t)->nframes; if (buf == d->inprocess) /* ensure we only process this once */
for (; f < e; f->tag = FREETAG, f->buf = NULL, f++) { d->inprocess = NULL;
if (f->tag == FREETAG || f->buf == NULL)
continue;
buf = f->buf;
bio = buf->bio; bio = buf->bio;
if (--buf->nframesout == 0
&& buf != d->inprocess) {
mempool_free(buf, d->bufpool); mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO); bio_endio(bio, -EIO);
} }
}
void
aoedev_downdev(struct aoedev *d)
{
struct aoetgt *t, **tt, **te;
struct frame *f;
struct list_head *head, *pos, *nx;
int i;
/* clean out active buffers on all targets */
tt = d->targets;
te = tt + NTARGETS;
for (; tt < te && (t = *tt); tt++) {
for (i = 0; i < NFACTIVE; i++) {
head = &t->factive[i];
list_for_each_safe(pos, nx, head) {
list_del(pos);
f = list_entry(pos, struct frame, head);
if (f->buf) {
f->buf->nframesout--;
aoe_failbuf(d, f->buf);
} }
(*t)->maxout = (*t)->nframes; aoe_freetframe(f);
(*t)->nout = 0;
} }
buf = d->inprocess;
if (buf) {
bio = buf->bio;
mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO);
} }
t->maxout = t->nframes;
t->nout = 0;
}
/* clean out the in-process buffer (if any) */
aoe_failbuf(d, d->inprocess);
d->inprocess = NULL; d->inprocess = NULL;
d->htgt = NULL; d->htgt = NULL;
/* clean out all pending I/O */
while (!list_empty(&d->bufq)) { while (!list_empty(&d->bufq)) {
buf = container_of(d->bufq.next, struct buf, bufs); struct buf *buf = container_of(d->bufq.next, struct buf, bufs);
list_del(d->bufq.next); list_del(d->bufq.next);
bio = buf->bio; aoe_failbuf(d, buf);
mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO);
} }
if (d->gd) if (d->gd)
...@@ -242,13 +255,16 @@ aoedev_by_sysminor_m(ulong sysminor) ...@@ -242,13 +255,16 @@ aoedev_by_sysminor_m(ulong sysminor)
static void static void
freetgt(struct aoedev *d, struct aoetgt *t) freetgt(struct aoedev *d, struct aoetgt *t)
{ {
struct frame *f, *e; struct frame *f;
struct list_head *pos, *nx, *head;
f = t->frames; head = &t->ffree;
e = f + t->nframes; list_for_each_safe(pos, nx, head) {
for (; f < e; f++) list_del(pos);
f = list_entry(pos, struct frame, head);
skbfree(f->skb); skbfree(f->skb);
kfree(t->frames); kfree(f);
}
kfree(t); kfree(t);
} }
......
...@@ -61,6 +61,7 @@ aoe_exit(void) ...@@ -61,6 +61,7 @@ aoe_exit(void)
aoenet_exit(); aoenet_exit();
unregister_blkdev(AOE_MAJOR, DEVICE_NAME); unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
aoecmd_exit();
aoechr_exit(); aoechr_exit();
aoedev_exit(); aoedev_exit();
aoeblk_exit(); /* free cache after de-allocating bufs */ aoeblk_exit(); /* free cache after de-allocating bufs */
...@@ -83,17 +84,20 @@ aoe_init(void) ...@@ -83,17 +84,20 @@ aoe_init(void)
ret = aoenet_init(); ret = aoenet_init();
if (ret) if (ret)
goto net_fail; goto net_fail;
ret = aoecmd_init();
if (ret)
goto cmd_fail;
ret = register_blkdev(AOE_MAJOR, DEVICE_NAME); ret = register_blkdev(AOE_MAJOR, DEVICE_NAME);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "aoe: can't register major\n"); printk(KERN_ERR "aoe: can't register major\n");
goto blkreg_fail; goto blkreg_fail;
} }
printk(KERN_INFO "aoe: AoE v%s initialised.\n", VERSION); printk(KERN_INFO "aoe: AoE v%s initialised.\n", VERSION);
discover_timer(TINIT); discover_timer(TINIT);
return 0; return 0;
blkreg_fail: blkreg_fail:
aoecmd_exit();
cmd_fail:
aoenet_exit(); aoenet_exit();
net_fail: net_fail:
aoeblk_exit(); aoeblk_exit();
......
...@@ -142,7 +142,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, ...@@ -142,7 +142,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt,
switch (h->cmd) { switch (h->cmd) {
case AOECMD_ATA: case AOECMD_ATA:
aoecmd_ata_rsp(skb); /* ata_rsp may keep skb for later processing or give it back */
skb = aoecmd_ata_rsp(skb);
break; break;
case AOECMD_CFG: case AOECMD_CFG:
aoecmd_cfg_rsp(skb); aoecmd_cfg_rsp(skb);
...@@ -152,6 +153,9 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, ...@@ -152,6 +153,9 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt,
break; /* don't complain about vendor commands */ break; /* don't complain about vendor commands */
printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd); printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd);
} }
if (!skb)
return 0;
exit: exit:
dev_kfree_skb(skb); dev_kfree_skb(skb);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment