On Mon, 10 May 2010 22:00:46 -0400
Andrew Morton <a...@linux-foundation.org> wrote:
> > +#define thm_readb(off) readb(ips->regmap + (off))
> > +#define thm_readw(off) readw(ips->regmap + (off))
> > +#define thm_readl(off) readl(ips->regmap + (off))
> > +#define thm_readq(off) readq(ips->regmap + (off))
> > +
> > +#define thm_writeb(off, val) writeb((val), ips->regmap + (off))
> > +#define thm_writew(off, val) writew((val), ips->regmap + (off))
> > +#define thm_writel(off, val) writel((val), ips->regmap + (off))
> 
> ick.
> 
> static inline unsigned short thm_readw(struct ips_driver *ips, unsigned 
> offset)
> {
>       readw(ips->regmap + offset);
> }
> 
> would be nicer.

Yes, it would.

> >
> > ...
> >
> > +static void ips_enable_cpu_turbo(struct ips_driver *ips)
> > +{
> > +   /* Already on, no need to mess with MSRs */
> > +   if (ips->__cpu_turbo_on)
> > +           return;
> > +
> > +   on_each_cpu(do_enable_cpu_turbo, ips, 1);
> > +
> > +   ips->__cpu_turbo_on = true;
> > +}
> 
> How does the code ensure that a hot-added CPU comes up in the correct
> turbo state?

It doesn't, I guess I'll have to add code to handle the hotplug case.

> > +static bool cpu_exceeded(struct ips_driver *ips, int cpu)
> > +{
> > +   unsigned long flags;
> > +   int avg;
> > +   bool ret = false;
> > +
> > +   spin_lock_irqsave(&ips->turbo_status_lock, flags);
> > +   avg = cpu ? ips->ctv2_avg_temp : ips->ctv1_avg_temp;
> > +   if (avg > (ips->limits->core_temp_limit * 100))
> > +           ret = true;
> > +   if (ips->cpu_avg_power > ips->core_power_limit)
> > +           ret = true;
> > +   spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
> > +
> > +   if (ret)
> > +           printk(KERN_CRIT "CPU power or thermal limit exceeded\n");
> > +
> > +   return ret;
> > +}
> 
> afacit these messages might come out at one-per-five-seconds max?
> 
> I bet the driver blows up and someone's logs get spammed ;)

Possibly. :)  I added these at the request of Pavel; I could make them
just print one time though...

> > +sleep:
> > +           
> > schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD));
> > +   } while(!kthread_should_stop());
> 
> please run checkpatch.
> 
> > +   dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n");
> > +
> > +   return 0;
> > +}
> 
> Did we really need a new kernel thread for this?  Can't use
> schedule_delayed_work() or something?  Maybe slow-work, or one of the
> other just-like-workqueues-only-different things we seem to keep
> adding?

Possibly, but I'm not sure if it would be a net code or complexity
win...  kthreads seem simple enough for this case, and since the driver
only works on small CPU count machines, the extra threads don't seem to
be a big deal.

> 
> > +/*
> > + * Helpers for reading out temp/power values and calculating their
> > + * averages for the decision making and monitoring functions.
> > + */
> > +
> > +static u16 calc_avg_temp(struct ips_driver *ips, u16 *array)
> > +{
> > +   u64 total = 0;
> > +   int i;
> > +   u16 avg;
> > +
> > +   for (i = 0; i < IPS_SAMPLE_COUNT; i++)
> > +           total += (u64)(array[i] * 100);
> 
> Actually, that does work.  Somehow the compiler will promote array[i]
> to u64 _before_ doing the multiplication.  I think.  Still, it looks
> like a deliberate attempt to trick the compiler into doing a
> multiplicative overflow ;)
> 
> > +   avg = (u16)(total / (u64)IPS_SAMPLE_COUNT);
> 
> Are you sure this won't emit a call to a non-existent 64-bit-divide
> library function on i386?
> 
> Did you mean for the driver to be available on 32-bit?

Oh I forgot to include the 32 bit fixes here; I'll need to convert
these to do_div64 I suppose.


> > +           last_msecs = jiffies_to_msecs(jiffies);
> > +           expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD);
> > +           mod_timer(&timer, expire);
> > +
> > +           __set_current_state(TASK_UNINTERRUPTIBLE);
> 
> This looks racy.  Should set TASK_UNINTERRUPTIBLE _before_ arming the
> timer.

Ah ok, will fix.

> 
> > +           schedule();
> > +           __set_current_state(TASK_RUNNING);
> 
> Unneeded - schedule() always returns in state TASK_RUNNING.

Great.

> 
> > +           /* Calculate actual sample period for power averaging */
> > +           last_sample_period = jiffies_to_msecs(jiffies) - last_msecs;
> > +           if (!last_sample_period)
> > +                   last_sample_period = 1;
> > +   } while(!kthread_should_stop());
> > +
> > +   del_timer(&timer);
> 
> Should be del_timer_sync(), I suspect.

Wouldn't hurt at least.

> > +static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
> > +{
> > +   u64 turbo_power, misc_en;
> > +   struct ips_mcp_limits *limits = NULL;
> > +   u16 tdp;
> > +
> > +   if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) {
> 
> We don't have #defines for these things?

I don't think so; they correspond to long marketing strings afaik.

> > +           dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n");
> > +           goto out;
> > +   }
> > +
> > +   rdmsrl(IA32_MISC_ENABLE, misc_en);
> > +   /*
> > +    * If the turbo enable bit isn't set, we shouldn't try to enable/disable
> > +    * turbo manually or we'll get an illegal MSR access, even though
> > +    * turbo will still be available.
> > +    */
> > +   if (!(misc_en & IA32_MISC_TURBO_EN))
> > +           ; /* add turbo MSR write allowed flag if necessary */
> > +
> > +   if (strstr(boot_cpu_data.x86_model_id, "CPU       M"))
> > +           limits = &ips_sv_limits;
> > +   else if (strstr(boot_cpu_data.x86_model_id, "CPU       L"))
> > +           limits = &ips_lv_limits;
> > +   else if (strstr(boot_cpu_data.x86_model_id, "CPU       U"))
> > +           limits = &ips_ulv_limits;
> > +   else
> > +           dev_info(&ips->dev->dev, "No CPUID match found.\n");
> > +
> > +   rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power);
> > +   tdp = turbo_power & TURBO_TDP_MASK;
> > +
> > +   /* Sanity check TDP against CPU */
> > +   if (limits->mcp_power_limit != (tdp / 8) * 1000) {
> > +           dev_warn(&ips->dev->dev, "Warning: CPU TDP doesn't match 
> > expected value (found %d, expected %d)\n",
> > +                    tdp / 8, limits->mcp_power_limit / 1000);
> > +   }
> > +
> > +out:
> > +   return limits;
> > +}
> >
> > ...
> >
> > +static struct pci_device_id ips_id_table[] = {
> 
> DEFINE_PCI_DEVICE_TABLE()?

I guess that would be a little cleaner.

> 
> > +   { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> > +                PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), },
> > +   { 0, }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(pci, ips_id_table);
> > +
> > +static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
> > +{
> > +   u64 platform_info;
> > +   struct ips_driver *ips;
> > +   u32 hts;
> > +   int ret = 0;
> > +   u16 htshi, trc, trc_required_mask;
> > +   u8 tse;
> > +
> > +   ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL);
> > +   if (!ips)
> > +           return -ENOMEM;
> > +
> > +   pci_set_drvdata(dev, ips);
> > +   ips->dev = dev;
> > +
> > +   ips->limits = ips_detect_cpu(ips);
> > +   if (!ips->limits) {
> > +           dev_info(&dev->dev, "IPS not supported on this CPU\n");
> > +           ret = -ENODEV;
> 
> hpa sez ENXIO.

Oh I've never used that value before, fine with me.

> 
> > +           goto error_free;
> > +   }
> > +
> > +   spin_lock_init(&ips->turbo_status_lock);
> > +
> > +   if (!pci_resource_start(dev, 0)) {
> > +           dev_err(&dev->dev, "TBAR not assigned, aborting\n");
> > +           ret = -ENODEV;
> 
> ditto.  And there are more.
> 
> > +           goto error_free;
> > +   }
> > +
> > +   ret = pci_request_regions(dev, "ips thermal sensor");
> > +   if (ret) {
> > +           dev_err(&dev->dev, "thermal resource busy, aborting\n");
> > +           ret = -EBUSY;
> > +           goto error_free;
> > +   }
> 
> There doesn't seem to be much point in assigning the
> pci_request_regions() return value to `ret'.  It could just do
> 
>       if (pci_request_regions(...)) {
>               ...
>       }
> 
> or, better, propagate the pci_request_regions() return value.

Yeah, I should remove the forced -EBUSY; though I think that's the only
things pci_request_regions can return anyway...


> > +   if (ret) {
> > +           dev_err(&dev->dev, "request irq failed, aborting\n");
> > +           ret = -EBUSY;
> 
> Again, don't trash callee's error code - propagate it.

Yep.

> 
> > +           goto error_unmap;
> > +   }
> > +
> > +   /* Enable aux, hot & critical interrupts */
> > +   thm_writeb(THM_TSPIEN, TSPIEN_AUX2_LOHI | TSPIEN_CRIT_LOHI |
> > +              TSPIEN_HOT_LOHI | TSPIEN_AUX_LOHI);
> > +   thm_writeb(THM_TEN, TEN_UPDATE_EN);
> > +
> > +   /* Collect adjustment values */
> > +   ips->cta_val = thm_readw(THM_CTA);
> > +   ips->pta_val = thm_readw(THM_PTA);
> > +   ips->mgta_val = thm_readw(THM_MGTA);
> > +
> > +   /* Save turbo limits & ratios */
> > +   rdmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit);
> > +
> > +   ips_enable_cpu_turbo(ips);
> > +   ips->cpu_turbo_enabled = true;
> > +
> > +   /* Set up the work queue and monitor/adjust threads */
> > +   ips->monitor = kthread_run(ips_monitor, ips, "ips-monitor");
> > +   if (!ips->monitor) {
> > +           dev_err(&dev->dev,
> > +                   "failed to create thermal monitor thread, aborting\n");
> > +           ret = -ENOMEM;
> > +           goto error_free_irq;
> > +   }
> > +
> > +   ips->adjust = kthread_create(ips_adjust, ips, "ips-adjust");
> 
> Nope, kthread_run() returns IS_ERR() on error.

Oops, will fix.


> > +#ifdef CONFIG_PM
> > +   .suspend = ips_suspend,
> > +   .resume = ips_resume,
> > +#endif
> 
> and nuke the ifdefs.

Will do.

> 
> > +   .shutdown = ips_shutdown,
> > +};
> > +
> >
> > ...
> >
> 
> 
> I applied both patches, did `make allmodconfig' and tried to make
> drivers/platform/x86/intel_ips.o:
> 
> drivers/platform/x86/intel_ips.c: In function 'ips_get_i915_syms':
> drivers/platform/x86/intel_ips.c:1361: error: 'i915_read_mch_val' undeclared 
> (first use in this function)
> drivers/platform/x86/intel_ips.c:1361: error: (Each undeclared identifier is 
> reported only once
> drivers/platform/x86/intel_ips.c:1361: error: for each function it appears 
> in.)
> drivers/platform/x86/intel_ips.c:1361: warning: type defaults to 'int' in 
> declaration of 'type name'
> drivers/platform/x86/intel_ips.c:1361: warning: cast from pointer to integer 
> of different size
> drivers/platform/x86/intel_ips.c:1361: warning: assignment makes pointer from 
> integer without a cast
> drivers/platform/x86/intel_ips.c:1364: error: 'i915_gpu_raise' undeclared 
> (first use in this function)
> drivers/platform/x86/intel_ips.c:1364: warning: type defaults to 'int' in 
> declaration of 'type name'
> drivers/platform/x86/intel_ips.c:1364: warning: cast from pointer to integer 
> of different size
> drivers/platform/x86/intel_ips.c:1364: warning: assignment makes pointer from 
> integer without a cast
> drivers/platform/x86/intel_ips.c:1367: error: 'i915_gpu_lower' undeclared 
> (first use in this function)
> drivers/platform/x86/intel_ips.c:1367: warning: type defaults to 'int' in 
> declaration of 'type name'
> drivers/platform/x86/intel_ips.c:1367: warning: cast from pointer to integer 
> of different size
> drivers/platform/x86/intel_ips.c:1367: warning: assignment makes pointer from 
> integer without a cast
> drivers/platform/x86/intel_ips.c:1370: error: 'i915_gpu_busy' undeclared 
> (first use in this function)
> drivers/platform/x86/intel_ips.c:1370: warning: type defaults to 'int' in 
> declaration of 'type name'
> drivers/platform/x86/intel_ips.c:1370: warning: cast from pointer to integer 
> of different size
> drivers/platform/x86/intel_ips.c:1370: warning: assignment makes pointer from 
> integer without a cast
> drivers/platform/x86/intel_ips.c:1373: error: 'i915_gpu_turbo_disable' 
> undeclared (first use in this function)
> drivers/platform/x86/intel_ips.c:1373: warning: type defaults to 'int' in 
> declaration of 'type name'
> drivers/platform/x86/intel_ips.c:1373: warning: cast from pointer to integer 
> of different size
> drivers/platform/x86/intel_ips.c:1373: warning: assignment makes pointer from 
> integer without a cast
> 
> Both x86_64 and i386 fail.

Arg, forgot to include the i915_drm.h changes in the patch (they're
sitting right here in my tree preventing compiler errors); will fix.

-- 
Jesse Barnes, Intel Open Source Technology Center
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to