cregit-Linux how code gets into the kernel

Release 4.7 drivers/block/aoe/aoedev.c

/* Copyright (c) 2013 Coraid, Inc.  See COPYING for GPL terms. */
/*
 * aoedev.c
 * AoE device utility functions; maintains device list.
 */

#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/kdev_t.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
#include "aoe.h"

static void dummy_timer(ulong);
static void freetgt(struct aoedev *d, struct aoetgt *t);
static void skbpoolfree(struct aoedev *d);


static int aoe_dyndevs = 1;
module_param(aoe_dyndevs, int, 0644);
MODULE_PARM_DESC(aoe_dyndevs, "Use dynamic minor numbers for devices.");


static struct aoedev *devlist;
static DEFINE_SPINLOCK(devlist_lock);

/* Because some systems will have one, many, or no
 *   - partitions,
 *   - slots per shelf,
 *   - or shelves,
 * we need some flexibility in the way the minor numbers
 * are allocated.  So they are dynamic.
 */

#define N_DEVS ((1U<<MINORBITS)/AOE_PARTITIONS)

static DEFINE_SPINLOCK(used_minors_lock);
static DECLARE_BITMAP(used_minors, N_DEVS);


static int minor_get_dyn(ulong *sysminor) { ulong flags; ulong n; int error = 0; spin_lock_irqsave(&used_minors_lock, flags); n = find_first_zero_bit(used_minors, N_DEVS); if (n < N_DEVS) set_bit(n, used_minors); else error = -1; spin_unlock_irqrestore(&used_minors_lock, flags); *sysminor = n * AOE_PARTITIONS; return error; }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin75100.00%5100.00%
Total75100.00%5100.00%


static int minor_get_static(ulong *sysminor, ulong aoemaj, int aoemin) { ulong flags; ulong n; int error = 0; enum { /* for backwards compatibility when !aoe_dyndevs, * a static number of supported slots per shelf */ NPERSHELF = 16, }; if (aoemin >= NPERSHELF) { pr_err("aoe: %s %d slots per shelf\n", "static minor device numbers support only", NPERSHELF); error = -1; goto out; } n = aoemaj * NPERSHELF + aoemin; if (n >= N_DEVS) { pr_err("aoe: %s with e%ld.%d\n", "cannot use static minor device numbers", aoemaj, aoemin); error = -1; goto out; } spin_lock_irqsave(&used_minors_lock, flags); if (test_bit(n, used_minors)) { pr_err("aoe: %s %lu\n", "existing device already has static minor number", n); error = -1; } else set_bit(n, used_minors); spin_unlock_irqrestore(&used_minors_lock, flags); *sysminor = n * AOE_PARTITIONS; out: return error; }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin156100.00%4100.00%
Total156100.00%4100.00%


static int minor_get(ulong *sysminor, ulong aoemaj, int aoemin) { if (aoe_dyndevs) return minor_get_dyn(sysminor); else return minor_get_static(sysminor, aoemaj, aoemin); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin37100.00%1100.00%
Total37100.00%1100.00%


static void minor_free(ulong minor) { ulong flags; minor /= AOE_PARTITIONS; BUG_ON(minor >= N_DEVS); spin_lock_irqsave(&used_minors_lock, flags); BUG_ON(!test_bit(minor, used_minors)); clear_bit(minor, used_minors); spin_unlock_irqrestore(&used_minors_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin57100.00%2100.00%
Total57100.00%2100.00%

/* * Users who grab a pointer to the device with aoedev_by_aoeaddr * automatically get a reference count and must be responsible * for performing a aoedev_put. With the addition of async * kthread processing I'm no longer confident that we can * guarantee consistency in the face of device flushes. * * For the time being, we only bother to add extra references for * frames sitting on the iocq. When the kthreads finish processing * these frames, they will aoedev_put the device. */
void aoedev_put(struct aoedev *d) { ulong flags; spin_lock_irqsave(&devlist_lock, flags); d->ref--; spin_unlock_irqrestore(&devlist_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin34100.00%1100.00%
Total34100.00%1100.00%


static void dummy_timer(ulong vp) { struct aoedev *d; d = (struct aoedev *)vp; if (d->flags & DEVFL_TKILL) return; d->timer.expires = jiffies + HZ; add_timer(&d->timer); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin50100.00%1100.00%
Total50100.00%1100.00%


static void aoe_failip(struct aoedev *d) { struct request *rq; struct bio *bio; unsigned long n; aoe_failbuf(d, d->ip.buf); rq = d->ip.rq; if (rq == NULL) return; while ((bio = d->ip.nxbio)) { bio->bi_error = -EIO; d->ip.nxbio = bio->bi_next; n = (unsigned long) rq->special; rq->special = (void *) --n; } if ((unsigned long) rq->special == 0) aoe_end_request(d, rq, 0); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin12096.77%375.00%
christoph hellwigchristoph hellwig43.23%125.00%
Total124100.00%4100.00%


static void downdev_frame(struct list_head *pos) { struct frame *f; f = list_entry(pos, struct frame, head); list_del(pos); if (f->buf) { f->buf->nframesout--; aoe_failbuf(f->t->d, f->buf); } aoe_freetframe(f); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin66100.00%1100.00%
Total66100.00%1100.00%


void aoedev_downdev(struct aoedev *d) { struct aoetgt *t, **tt, **te; struct list_head *head, *pos, *nx; struct request *rq; int i; d->flags &= ~DEVFL_UP; /* clean out active and to-be-retransmitted buffers */ for (i = 0; i < NFACTIVE; i++) { head = &d->factive[i]; list_for_each_safe(pos, nx, head) downdev_frame(pos); } head = &d->rexmitq; list_for_each_safe(pos, nx, head) downdev_frame(pos); /* reset window dressings */ tt = d->targets; te = tt + d->ntargets; for (; tt < te && (t = *tt); tt++) { aoecmd_wreset(t); t->nout = 0; } /* clean out the in-process request (if any) */ aoe_failip(d); /* fast fail all pending I/O */ if (d->blkq) { while ((rq = blk_peek_request(d->blkq))) { blk_start_request(rq); aoe_end_request(d, rq, 1); } } if (d->gd) set_capacity(d->gd, 0); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin20898.11%990.00%
tejun heotejun heo41.89%110.00%
Total212100.00%10100.00%

/* return whether the user asked for this particular * device to be flushed */
static int user_req(char *s, size_t slen, struct aoedev *d) { const char *p; size_t lim; if (!d->gd) return 0; p = kbasename(d->gd->disk_name); lim = sizeof(d->gd->disk_name); lim -= p - d->gd->disk_name; if (slen < lim) lim = slen; return !strncmp(s, p, lim); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin8797.75%150.00%
andy shevchenkoandy shevchenko22.25%150.00%
Total89100.00%2100.00%


static void freedev(struct aoedev *d) { struct aoetgt **t, **e; int freeing = 0; unsigned long flags; spin_lock_irqsave(&d->lock, flags); if (d->flags & DEVFL_TKILL && !(d->flags & DEVFL_FREEING)) { d->flags |= DEVFL_FREEING; freeing = 1; } spin_unlock_irqrestore(&d->lock, flags); if (!freeing) return; del_timer_sync(&d->timer); if (d->gd) { aoedisk_rm_debugfs(d); aoedisk_rm_sysfs(d); del_gendisk(d->gd); put_disk(d->gd); blk_cleanup_queue(d->blkq); } t = d->targets; e = t + d->ntargets; for (; t < e && *t; t++) freetgt(d, *t); if (d->bufpool) mempool_destroy(d->bufpool); skbpoolfree(d); minor_free(d->sysminor); spin_lock_irqsave(&d->lock, flags); d->flags |= DEVFL_FREED; spin_unlock_irqrestore(&d->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin218100.00%5100.00%
Total218100.00%5100.00%

enum flush_parms { NOT_EXITING = 0, EXITING = 1, };
static int flush(const char __user *str, size_t cnt, int exiting) { ulong flags; struct aoedev *d, **dd; char buf[16]; int all = 0; int specified = 0; /* flush a specific device */ unsigned int skipflags; skipflags = DEVFL_GDALLOC | DEVFL_NEWSIZE | DEVFL_TKILL; if (!exiting && cnt >= 3) { if (cnt > sizeof buf) cnt = sizeof buf; if (copy_from_user(buf, str, cnt)) return -EFAULT; all = !strncmp(buf, "all", 3); if (!all) specified = 1; } flush_scheduled_work(); /* pass one: without sleeping, do aoedev_downdev */ spin_lock_irqsave(&devlist_lock, flags); for (d = devlist; d; d = d->next) { spin_lock(&d->lock); if (exiting) { /* unconditionally take each device down */ } else if (specified) { if (!user_req(buf, cnt, d)) goto cont; } else if ((!all && (d->flags & DEVFL_UP)) || d->flags & skipflags || d->nopen || d->ref) goto cont; aoedev_downdev(d); d->flags |= DEVFL_TKILL; cont: spin_unlock(&d->lock); } spin_unlock_irqrestore(&devlist_lock, flags); /* pass two: call freedev, which might sleep, * for aoedevs marked with DEVFL_TKILL */ restart: spin_lock_irqsave(&devlist_lock, flags); for (d = devlist; d; d = d->next) { spin_lock(&d->lock); if (d->flags & DEVFL_TKILL && !(d->flags & DEVFL_FREEING)) { spin_unlock(&d->lock); spin_unlock_irqrestore(&devlist_lock, flags); freedev(d); goto restart; } spin_unlock(&d->lock); } /* pass three: remove aoedevs marked with DEVFL_FREED */ for (dd = &devlist, d = *dd; d; d = *dd) { struct aoedev *doomed = NULL; spin_lock(&d->lock); if (d->flags & DEVFL_FREED) { *dd = d->next; doomed = d; } else { dd = &d->next; } spin_unlock(&d->lock); if (doomed) kfree(doomed->targets); kfree(doomed); } spin_unlock_irqrestore(&devlist_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin434100.00%5100.00%
Total434100.00%5100.00%


int aoedev_flush(const char __user *str, size_t cnt) { return flush(str, cnt, NOT_EXITING); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin24100.00%1100.00%
Total24100.00%1100.00%

/* This has been confirmed to occur once with Tms=3*1000 due to the * driver changing link and not processing its transmit ring. The * problem is hard enough to solve by returning an error that I'm * still punting on "solving" this. */
static void skbfree(struct sk_buff *skb) { enum { Sms = 250, Tms = 30 * 1000}; int i = Tms / Sms; if (skb == NULL) return; while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0) msleep(Sms); if (i < 0) { printk(KERN_ERR "aoe: %s holds ref: %s\n", skb->dev ? skb->dev->name : "netif", "cannot free skb -- memory leaked."); return; } skb->truesize -= skb->data_len; skb_shinfo(skb)->nr_frags = skb->data_len = 0; skb_trim(skb, 0); dev_kfree_skb(skb); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin12499.20%375.00%
roel kluinroel kluin10.80%125.00%
Total125100.00%4100.00%


static void skbpoolfree(struct aoedev *d) { struct sk_buff *skb, *tmp; skb_queue_walk_safe(&d->skbpool, skb, tmp) skbfree(skb); __skb_queue_head_init(&d->skbpool); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin2460.00%150.00%
david s. millerdavid s. miller1640.00%150.00%
Total40100.00%2100.00%

/* find it or allocate it */
struct aoedev * aoedev_by_aoeaddr(ulong maj, int min, int do_alloc) { struct aoedev *d; int i; ulong flags; ulong sysminor = 0; spin_lock_irqsave(&devlist_lock, flags); for (d=devlist; d; d=d->next) if (d->aoemajor == maj && d->aoeminor == min) { spin_lock(&d->lock); if (d->flags & DEVFL_TKILL) { spin_unlock(&d->lock); d = NULL; goto out; } d->ref++; spin_unlock(&d->lock); break; } if (d || !do_alloc || minor_get(&sysminor, maj, min) < 0) goto out; d = kcalloc(1, sizeof *d, GFP_ATOMIC); if (!d) goto out; d->targets = kcalloc(NTARGETS, sizeof(*d->targets), GFP_ATOMIC); if (!d->targets) { kfree(d); d = NULL; goto out; } d->ntargets = NTARGETS; INIT_WORK(&d->work, aoecmd_sleepwork); spin_lock_init(&d->lock); skb_queue_head_init(&d->skbpool); init_timer(&d->timer); d->timer.data = (ulong) d; d->timer.function = dummy_timer; d->timer.expires = jiffies + HZ; add_timer(&d->timer); d->bufpool = NULL; /* defer to aoeblk_gdalloc */ d->tgt = d->targets; d->ref = 1; for (i = 0; i < NFACTIVE; i++) INIT_LIST_HEAD(&d->factive[i]); INIT_LIST_HEAD(&d->rexmitq); d->sysminor = sysminor; d->aoemajor = maj; d->aoeminor = min; d->rttavg = RTTAVG_INIT; d->rttdev = RTTDEV_INIT; d->next = devlist; devlist = d; out: spin_unlock_irqrestore(&devlist_lock, flags); return d; }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin37196.87%1184.62%
david s. millerdavid s. miller82.09%17.69%
dan carpenterdan carpenter41.04%17.69%
Total383100.00%13100.00%


static void freetgt(struct aoedev *d, struct aoetgt *t) { struct frame *f; struct list_head *pos, *nx, *head; struct aoeif *ifp; for (ifp = t->ifs; ifp < &t->ifs[NAOEIFS]; ++ifp) { if (!ifp->nd) break; dev_put(ifp->nd); } head = &t->ffree; list_for_each_safe(pos, nx, head) { list_del(pos); f = list_entry(pos, struct frame, head); skbfree(f->skb); kfree(f); } kfree(t); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin126100.00%6100.00%
Total126100.00%6100.00%


void aoedev_exit(void) { flush_scheduled_work(); flush(NULL, 0, EXITING); }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin19100.00%3100.00%
Total19100.00%3100.00%


int __init aoedev_init(void) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin11100.00%1100.00%
Total11100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
ed cashined cashin236497.97%2676.47%
david s. millerdavid s. miller240.99%12.94%
tejun heotejun heo70.29%25.88%
andy shevchenkoandy shevchenko50.21%12.94%
andrew mortonandrew morton40.17%12.94%
dan carpenterdan carpenter40.17%12.94%
christoph hellwigchristoph hellwig40.17%12.94%
roel kluinroel kluin10.04%12.94%
Total2413100.00%34100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}